UNPKG

@warriorteam/redai-zalo-sdk

Version:

Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account, ZNS, Consultation Service, Group Messaging, and Social APIs

448 lines 18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ZNSService = void 0; /** * Service for Zalo Notification Service (ZNS) * * Requirements for using ZNS API: * - Official Account must be approved and have ZNS sending permission * - Valid Official Account access token * - ZNS template must be approved before use * - Recipient phone number must be in correct format and valid * - Template data must match defined parameters * - Comply with message quantity limits according to service package * * Reference: https://developers.zalo.me/docs/zalo-notification-service/ */ class ZNSService { constructor(client) { this.client = client; // Zalo ZNS endpoints - organized by functionality this.endpoints = { message: { sendTemplate: "https://business.openapi.zalo.me/message/template", sendHashPhone: "https://business.openapi.zalo.me/message/template/hash_phone", sendDev: "https://business.openapi.zalo.me/message/template/dev", sendRsa: "https://business.openapi.zalo.me/message/template/rsa", sendJourney: "https://business.openapi.zalo.me/message/template/journey", status: "https://business.openapi.zalo.me/message/status", quota: "https://business.openapi.zalo.me/message/quota", templateTag: "https://business.openapi.zalo.me/message/template-tag", }, template: { list: "https://business.openapi.zalo.me/template/all", detail: "https://business.openapi.zalo.me/template/info/v2", create: "https://business.openapi.zalo.me/template/create", edit: "https://business.openapi.zalo.me/template/edit", uploadImage: "https://business.openapi.zalo.me/upload/image", quality: "https://business.openapi.zalo.me/template/quality", sampleData: "https://business.openapi.zalo.me/template/sample-data", }, rating: { get: "https://business.openapi.zalo.me/rating/get", }, quality: { oa: "https://business.openapi.zalo.me/quality", }, }; } /** * Send ZNS message * @param accessToken OA access token * @param message ZNS message data * @returns Send result */ async sendMessage(accessToken, message) { try { const response = await this.client.apiPost(this.endpoints.message.sendTemplate, accessToken, message); return response; } catch (error) { throw this.handleZNSError(error, "Failed to send ZNS message"); } } /** * Send ZNS message with phone hash * @param accessToken OA access token * @param message ZNS message with hashed phone * @returns Send result */ async sendHashPhoneMessage(accessToken, message) { try { const response = await this.client.apiPost(this.endpoints.message.sendHashPhone, accessToken, message); return response; } catch (error) { throw this.handleZNSError(error, "Failed to send ZNS hash phone message"); } } /** * Send ZNS message in development mode * @param accessToken OA access token * @param message ZNS dev mode message * @returns Send result */ async sendDevModeMessage(accessToken, message) { try { const response = await this.client.apiPost(this.endpoints.message.sendDev, accessToken, message); return response; } catch (error) { throw this.handleZNSError(error, "Failed to send ZNS dev mode message"); } } /** * Send ZNS message with RSA encryption * @param accessToken OA access token * @param message ZNS RSA message * @returns Send result */ async sendRsaMessage(accessToken, message) { try { const response = await this.client.apiPost(this.endpoints.message.sendRsa, accessToken, message); return response; } catch (error) { throw this.handleZNSError(error, "Failed to send ZNS RSA message"); } } /** * Send ZNS journey message * @param accessToken OA access token * @param message ZNS journey message * @returns Send result */ async sendJourneyMessage(accessToken, message) { try { const response = await this.client.apiPost(this.endpoints.message.sendJourney, accessToken, message); return response; } catch (error) { throw this.handleZNSError(error, "Failed to send ZNS journey message"); } } /** * Get ZNS message status * @param accessToken OA access token * @param messageId Message ID * @returns Message status info * * Response data: * - delivery_time: Thời gian thiết bị nhận được thông báo * - status: Trạng thái thông báo * * -1: The message does not exist * * 0: The message is pushed successfully to Zalo server but has not yet delivered to user's phone * * 1: The message was delivered to the user's phone * - message: Mô tả trạng thái thông báo */ async getMessageStatus(accessToken, messageId) { try { const response = await this.client.apiGet(this.endpoints.message.status, accessToken, { message_id: messageId }); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get ZNS message status"); } } /** * Get ZNS quota information * @param accessToken OA access token * @returns Quota information * * Response data: * - dailyQuota: Số thông báo ZNS OA được gửi trong 1 ngày * - remainingQuota: Số thông báo ZNS OA được gửi trong ngày còn lại * - dailyQuotaPromotion: Số tin ZNS hậu mãi OA được gửi trong ngày (null từ 1/11) * - remainingQuotaPromotion: Số tin ZNS hậu mãi còn lại OA được gửi trong ngày (null từ 1/11) * - monthlyPromotionQuota: Số tin ZNS hậu mãi OA được gửi trong tháng * - remainingMonthlyPromotionQuota: Số tin ZNS hậu mãi còn lại OA được gửi trong tháng * - estimatedNextMonthPromotionQuota: Số tin ZNS hậu mãi dự kiến mà OA có thể gửi trong tháng tiếp theo */ async getQuotaInfo(accessToken) { try { const response = await this.client.apiGet(this.endpoints.message.quota, accessToken); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get ZNS quota info"); } } /** * Get ZNS allowed content types * @param accessToken OA access token * @returns Allowed content types * * Response data: * - Mảng các loại nội dung mà OA có thể gửi: * * "TRANSACTION": Giao dịch (cấp độ 1) * * "CUSTOMER_CARE": Chăm sóc khách hàng (cấp độ 2) * * "PROMOTION": Hậu mãi (cấp độ 3) * - Dựa theo chất lượng gửi ZNS của OA, Zalo sẽ tự động điều chỉnh loại nội dung OA được gửi */ async getAllowedContentTypes(accessToken) { try { const response = await this.client.apiGet(this.endpoints.message.templateTag, accessToken); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get ZNS allowed content types"); } } /** * Get ZNS template list * @param accessToken OA access token * @param offset Offset for pagination (template được tạo gần nhất có thứ tự 0) * @param limit Limit for pagination (tối đa 100) * @param status Template status filter (optional) * @returns Template list * * Status values: * - 1: ENABLE templates * - 2: PENDING_REVIEW templates * - 3: REJECT templates * - 4: DISABLE templates * - undefined: All templates */ async getTemplateList(accessToken, offset = 0, limit = 10, status) { try { // Validate limit (max 100) if (limit > 100) { throw new Error("Limit không được vượt quá 100"); } // Prepare query parameters const params = { offset, limit }; if (status !== undefined) { params.status = status; } const response = await this.client.apiGet(this.endpoints.template.list, accessToken, params); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get ZNS template list"); } } /** * Get ZNS template details * @param accessToken OA access token * @param templateId Template ID * @returns Template details * * Response data: * - templateId: ID của template * - templateName: Tên của template * - status: Trạng thái template (ENABLE, PENDING_REVIEW, DELETE, REJECT, DISABLE) * - reason: Lý do template có trạng thái hiện tại * - listParams: Danh sách các thuộc tính của template * - listButtons: Danh sách các buttons/CTAs của template * - timeout: Thời gian timeout của template * - previewUrl: Đường dẫn đến bản xem trước của template * - templateQuality: Chất lượng template (null từ 10/12) * - templateTag: Loại nội dung (TRANSACTION, CUSTOMER_CARE, PROMOTION) * - price: Đơn giá của template */ async getTemplateDetails(accessToken, templateId) { try { const response = await this.client.apiGet(this.endpoints.template.detail, accessToken, { template_id: templateId }); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get ZNS template details"); } } /** * Get ZNS template sample data * @param accessToken OA access token * @param templateId Template ID * @returns Template sample data * * Response data: * - Chứa tham số và dữ liệu mẫu của template * - Ví dụ: { "balance_debt": 2000, "due_date": "01/01/1970", "customer_name": "customer_name_sample" } */ async getTemplateSampleData(accessToken, templateId) { try { const response = await this.client.apiGet(this.endpoints.template.sampleData, accessToken, { template_id: templateId }); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get ZNS template sample data"); } } /** * Get customer rating information * @param accessToken OA access token * @param templateId Template ID * @param fromTime Start time (timestamp in milliseconds) * @param toTime End time (timestamp in milliseconds) * @param offset Position of first rating to return * @param limit Maximum number of ratings to return * @returns Customer rating information * * Lưu ý: * - Chỉ có thể lấy thông tin đánh giá từ template đánh giá dịch vụ được tạo bởi ứng dụng * - Access token phải ứng với template ID được tạo bởi app và OA * - Thời gian theo định dạng timestamp (millisecond) */ async getCustomerRating(accessToken, templateId, fromTime, toTime, offset, limit) { try { const response = await this.client.apiGet(this.endpoints.rating.get, accessToken, { template_id: templateId, from_time: fromTime, to_time: toTime, offset, limit, }); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get customer rating"); } } /** * Get OA ZNS sending quality information * @param accessToken OA access token * @returns OA quality information * * Response data: * - oaCurrentQuality: Chất lượng gửi ZNS trong 48 giờ gần nhất * - oa7dayQuality: Chất lượng gửi ZNS trong 7 ngày gần nhất * * Quality levels: * - HIGH: Mức độ chất lượng tốt * - MEDIUM: Mức độ chất lượng trung bình * - LOW: Mức độ chất lượng kém * - UNDEFINED: Chưa được xác định (OA không gửi ZNS trong khung thời gian đánh giá) */ async getOAQuality(accessToken) { try { const response = await this.client.apiGet(this.endpoints.quality.oa, accessToken); return response; } catch (error) { throw this.handleZNSError(error, "Failed to get OA quality information"); } } /** * Create ZNS template * @param accessToken OA access token * @param templateData Template creation data theo chuẩn Zalo API * @returns Created template response * * API: POST https://business.openapi.zalo.me/template/create */ async createTemplate(accessToken, templateData) { try { // Validate required fields if (!templateData.template_name) { throw new Error("template_name is required"); } if (!templateData.template_type) { throw new Error("template_type is required"); } if (!templateData.tag) { throw new Error("tag is required"); } if (!templateData.layout) { throw new Error("layout is required"); } if (!templateData.tracking_id) { throw new Error("tracking_id is required"); } const response = await this.client.apiPost(this.endpoints.template.create, accessToken, templateData); return response; } catch (error) { throw this.handleZNSError(error, "Failed to create ZNS template"); } } /** * Edit ZNS template (chỉnh sửa template có trạng thái REJECT) * @param accessToken OA access token * @param templateData Template edit data theo chuẩn Zalo API * @returns Edited template response * * Lưu ý: * - Chỉ có thể chỉnh sửa template có trạng thái REJECT * - Template sau khi chỉnh sửa sẽ chuyển về trạng thái PENDING_REVIEW * - Daily quota: 100 requests/ngày * - Cần quyền "Quản lý tài sản" * * API: POST https://business.openapi.zalo.me/template/edit */ async updateTemplate(accessToken, templateData) { try { // Validate required fields if (!templateData.template_id) { throw new Error("template_id is required"); } if (!templateData.template_name) { throw new Error("template_name is required"); } if (!templateData.template_type) { throw new Error("template_type is required"); } if (!templateData.tag) { throw new Error("tag is required"); } if (!templateData.layout) { throw new Error("layout is required"); } if (!templateData.tracking_id) { throw new Error("tracking_id is required"); } const response = await this.client.apiPost(this.endpoints.template.edit, accessToken, templateData); return response; } catch (error) { throw this.handleZNSError(error, "Failed to edit ZNS template"); } } /** * Upload image for ZNS template * @param accessToken OA access token * @param imageFile Image file (Buffer or ReadableStream) * @param filename Filename with extension * @returns Upload result with media_id * * Lưu ý: * - Định dạng hỗ trợ: JPG, PNG * - Dung lượng tối đa: 500KB * - Hạn mức: 5000 ảnh/tháng/app * - Logo: PNG, 400x96px * - Hình ảnh: JPG/PNG, tỉ lệ 16:9 * - Cần quyền "Quản lý tài sản" */ async uploadImage(accessToken, imageFile, filename = "image.jpg") { try { // Validate file extension const allowedExtensions = [".jpg", ".jpeg", ".png"]; const fileExtension = filename .toLowerCase() .substring(filename.lastIndexOf(".")); if (!allowedExtensions.includes(fileExtension)) { throw new Error("File phải có định dạng JPG hoặc PNG"); } // Validate file size if it's a Buffer (max 500KB) if (Buffer.isBuffer(imageFile)) { const maxSize = 500 * 1024; // 500KB if (imageFile.length > maxSize) { throw new Error("Dung lượng file không được vượt quá 500KB"); } } const response = await this.client.apiUploadFile(this.endpoints.template.uploadImage, accessToken, imageFile, filename); return response; } catch (error) { throw this.handleZNSError(error, "Failed to upload ZNS image"); } } handleZNSError(error, defaultMessage) { if (error.response?.data) { const errorData = error.response.data; return new Error(`${defaultMessage}: ${errorData.message || errorData.error || "Unknown error"}`); } return new Error(`${defaultMessage}: ${error.message || "Unknown error"}`); } } exports.ZNSService = ZNSService; //# sourceMappingURL=zns.service.js.map