@warriorteam/redai-zalo-sdk
Version:
Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account v3.0, ZNS with Full Type Safety, Consultation Service, Broadcast Service, Group Messaging with List APIs, Social APIs, Enhanced Article Management, Promotion Service v3.0 with Multip
361 lines • 15.5 kB
JavaScript
;
/**
* Broadcast Service for Zalo Official Account
*
* Gửi tin Truyền thông Broadcast đến nhiều người dùng dựa trên tiêu chí targeting
* API: https://openapi.zalo.me/v2.0/oa/message
*
* TÍNH NĂNG:
* 1. GỬI TIN BROADCAST:
* - Gửi tin nhắn đến nhiều người dùng cùng lúc
* - Targeting theo tuổi, giới tính, địa điểm, platform
* - Chỉ hỗ trợ template type "media" với article attachment
*
* 2. TARGETING CRITERIA:
* - Ages: Nhóm tuổi (0-12, 13-17, 18-24, 25-34, 35-44, 45-54, 55-64, 65+)
* - Gender: Giới tính (All, Male, Female)
* - Cities: Tỉnh thành cụ thể (63 tỉnh thành Việt Nam)
* - Locations: Miền (Bắc, Trung, Nam)
* - Platform: Hệ điều hành (iOS, Android, Windows Phone)
*
* 3. GIỚI HẠN:
* - Chỉ gửi được article attachment
* - Cần có quyền broadcast từ Zalo
* - Tuân thủ chính sách spam và nội dung
*
* LỖI THƯỜNG GẶP:
* - 3001: Không có quyền gửi broadcast
* - 3002: Article attachment không tồn tại
* - 3003: Targeting criteria không hợp lệ
* - 3004: Vượt quá giới hạn broadcast
* - 3005: Nội dung vi phạm chính sách
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BroadcastService = void 0;
const common_1 = require("../types/common");
const broadcast_1 = require("../types/broadcast");
class BroadcastService {
constructor(client) {
this.client = client;
// Zalo API endpoint for broadcast messages
this.broadcastApiUrl = "https://openapi.zalo.me/v2.0/oa/message";
}
/**
* Gửi tin nhắn broadcast đến nhiều người dùng
* @param accessToken Access token của Official Account
* @param recipient Thông tin targeting người nhận
* @param attachmentId ID của article attachment
* @returns Thông tin tin nhắn broadcast đã gửi
*/
async sendBroadcastMessage(accessToken, recipient, attachmentId) {
try {
// Validate input parameters
this.validateBroadcastRequest(recipient, attachmentId);
// Build broadcast message
const message = {
attachment: {
type: "template",
payload: {
template_type: "media",
elements: [
{
media_type: "article",
attachment_id: attachmentId
}
]
}
}
};
// Build request
const request = {
recipient,
message
};
// Send broadcast request
const result = await this.client.apiPost(this.broadcastApiUrl, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to send broadcast message", result.error, result);
}
if (!result.data) {
throw new common_1.ZaloSDKError("No broadcast response data received", -1);
}
return result.data;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to send broadcast message: ${error.message}`, -1, error);
}
}
/**
* Tạo broadcast target với các tiêu chí targeting
* @param criteria Các tiêu chí targeting
* @returns BroadcastTarget object
*/
createBroadcastTarget(criteria) {
const target = {};
// Convert ages to comma-separated string
if (criteria.ages && criteria.ages.length > 0) {
const ageCodes = criteria.ages.map(age => {
const code = broadcast_1.BROADCAST_AGE_CODES[age];
if (!code) {
throw new common_1.ZaloSDKError(`Invalid age group: ${age}`, -1);
}
return code;
});
target.ages = ageCodes.join(",");
}
// Convert gender
if (criteria.gender) {
const genderCode = broadcast_1.BROADCAST_GENDER_CODES[criteria.gender];
if (!genderCode) {
throw new common_1.ZaloSDKError(`Invalid gender: ${criteria.gender}`, -1);
}
target.gender = genderCode;
}
// Convert cities to comma-separated string
if (criteria.cities && criteria.cities.length > 0) {
const cityCodes = criteria.cities.map(city => {
const code = broadcast_1.BROADCAST_CITY_CODES[city];
if (!code) {
throw new common_1.ZaloSDKError(`Invalid city: ${city}`, -1);
}
return code;
});
target.cities = cityCodes.join(",");
}
// Convert locations to comma-separated string
if (criteria.locations && criteria.locations.length > 0) {
const locationCodes = criteria.locations.map(location => {
const code = broadcast_1.BROADCAST_LOCATION_CODES[location];
if (!code) {
throw new common_1.ZaloSDKError(`Invalid location: ${location}`, -1);
}
return code;
});
target.locations = locationCodes.join(",");
}
// Convert platforms to comma-separated string
if (criteria.platforms && criteria.platforms.length > 0) {
const platformCodes = criteria.platforms.map(platform => {
const code = broadcast_1.BROADCAST_PLATFORM_CODES[platform];
if (!code) {
throw new common_1.ZaloSDKError(`Invalid platform: ${platform}`, -1);
}
return code;
});
target.platform = platformCodes.join(",");
}
return target;
}
/**
* Validate broadcast request parameters
* @param recipient Broadcast recipient
* @param attachmentId Article attachment ID
*/
validateBroadcastRequest(recipient, attachmentId) {
// Validate recipient
if (!recipient || !recipient.target) {
throw new common_1.ZaloSDKError("Recipient target is required for broadcast", -1);
}
// Validate attachment ID
if (!attachmentId || attachmentId.trim().length === 0) {
throw new common_1.ZaloSDKError("Article attachment ID is required", -1);
}
// Validate target has at least one criteria
const target = recipient.target;
const hasTargeting = target.ages || target.gender || target.cities ||
target.locations || target.platform;
if (!hasTargeting) {
throw new common_1.ZaloSDKError("At least one targeting criteria is required (ages, gender, cities, locations, or platform)", -1);
}
}
/**
* Get available city codes for targeting
* @returns Object mapping city names to codes
*/
getCityCodes() {
return broadcast_1.BROADCAST_CITY_CODES;
}
/**
* Get available age group codes for targeting
* @returns Object mapping age groups to codes
*/
getAgeGroupCodes() {
return broadcast_1.BROADCAST_AGE_CODES;
}
/**
* Get available gender codes for targeting
* @returns Object mapping genders to codes
*/
getGenderCodes() {
return broadcast_1.BROADCAST_GENDER_CODES;
}
/**
* Get available location codes for targeting
* @returns Object mapping locations to codes
*/
getLocationCodes() {
return broadcast_1.BROADCAST_LOCATION_CODES;
}
/**
* Get available platform codes for targeting
* @returns Object mapping platforms to codes
*/
getPlatformCodes() {
return broadcast_1.BROADCAST_PLATFORM_CODES;
}
/**
* Gửi nhiều tin nhắn broadcast với nhiều attachment IDs
* @param accessToken Access token của Official Account
* @param recipient Thông tin targeting người nhận
* @param attachmentIds Danh sách các article attachment IDs
* @param options Tùy chọn gửi (delay giữa các tin, parallel/sequential)
* @returns Danh sách kết quả gửi broadcast
*/
async sendMultipleBroadcastMessages(accessToken, recipient, attachmentIds, options) {
try {
// Validate input
if (!attachmentIds || attachmentIds.length === 0) {
throw new common_1.ZaloSDKError("At least one attachment ID is required", -1);
}
// Remove duplicates
const uniqueAttachmentIds = [...new Set(attachmentIds)];
const startTime = Date.now();
const results = [];
const mode = options?.mode || 'sequential';
const delay = options?.delay || 0;
if (mode === 'parallel') {
// Gửi song song tất cả
const promises = uniqueAttachmentIds.map(async (attachmentId, index) => {
try {
const result = await this.sendBroadcastMessage(accessToken, recipient, attachmentId);
const messageResult = {
attachmentId,
success: true,
messageId: result.data.message_id,
error: null,
sentAt: new Date()
};
// Report progress
if (options?.onProgress) {
options.onProgress({
total: uniqueAttachmentIds.length,
completed: index + 1,
successful: results.filter(r => r.success).length + 1,
failed: results.filter(r => !r.success).length,
currentAttachmentId: attachmentId,
isCompleted: index === uniqueAttachmentIds.length - 1
});
}
return messageResult;
}
catch (error) {
const messageResult = {
attachmentId,
success: false,
messageId: null,
error: error instanceof common_1.ZaloSDKError ? error : new common_1.ZaloSDKError(error.message, -1),
sentAt: new Date()
};
// Report progress
if (options?.onProgress) {
options.onProgress({
total: uniqueAttachmentIds.length,
completed: index + 1,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length + 1,
currentAttachmentId: attachmentId,
isCompleted: index === uniqueAttachmentIds.length - 1
});
}
return messageResult;
}
});
const parallelResults = await Promise.all(promises);
results.push(...parallelResults);
}
else {
// Gửi tuần tự
for (let i = 0; i < uniqueAttachmentIds.length; i++) {
const attachmentId = uniqueAttachmentIds[i];
try {
const result = await this.sendBroadcastMessage(accessToken, recipient, attachmentId);
const messageResult = {
attachmentId,
success: true,
messageId: result.data.message_id,
error: null,
sentAt: new Date()
};
results.push(messageResult);
// Report progress
if (options?.onProgress) {
options.onProgress({
total: uniqueAttachmentIds.length,
completed: i + 1,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
currentAttachmentId: attachmentId,
isCompleted: i === uniqueAttachmentIds.length - 1
});
}
}
catch (error) {
const messageResult = {
attachmentId,
success: false,
messageId: null,
error: error instanceof common_1.ZaloSDKError ? error : new common_1.ZaloSDKError(error.message, -1),
sentAt: new Date()
};
results.push(messageResult);
// Report progress
if (options?.onProgress) {
options.onProgress({
total: uniqueAttachmentIds.length,
completed: i + 1,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
currentAttachmentId: attachmentId,
isCompleted: i === uniqueAttachmentIds.length - 1
});
}
}
// Delay before next message (except for last one)
if (delay > 0 && i < uniqueAttachmentIds.length - 1) {
await this.sleep(delay);
}
}
}
const totalDuration = Date.now() - startTime;
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
return {
totalMessages: uniqueAttachmentIds.length,
successfulMessages: successful,
failedMessages: failed,
results,
totalDuration,
mode,
targetCriteria: recipient.target
};
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to send multiple broadcast messages: ${error.message}`, -1, error);
}
}
/**
* Sleep utility for delays
* @param ms Milliseconds to sleep
*/
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
exports.BroadcastService = BroadcastService;
//# sourceMappingURL=broadcast.service.js.map