UNPKG

@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
"use strict"; /** * 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