UNPKG

@warriorteam/redai-zalo-sdk

Version:

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

489 lines 20.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConsultationService = void 0; const common_1 = require("../types/common"); /** * Service xử lý các API tin nhắn tư vấn của Zalo Official Account * * Tin nhắn tư vấn (CS - Customer Service) là loại tin nhắn đặc biệt * cho phép OA gửi tin nhắn chủ động đến người dùng trong khung thời gian nhất định * * ĐIỀU KIỆN GỬI TIN TƯ VẤN: * * 1. THỜI GIAN GỬI: * - Chỉ được gửi trong vòng 48 giờ kể từ khi người dùng tương tác cuối cùng với OA * - Tương tác bao gồm: gửi tin nhắn, nhấn button, gọi điện, truy cập website từ OA * * 2. NỘI DUNG TIN NHẮN: * - Phải liên quan đến tư vấn, hỗ trợ khách hàng * - Bao gồm: trả lời câu hỏi, hướng dẫn sử dụng, hỗ trợ kỹ thuật * - Không được chứa nội dung quảng cáo trực tiếp * * 3. TẦN SUẤT GỬI: * - Không giới hạn số lượng tin nhắn tư vấn trong ngày * - Tuy nhiên cần tuân thủ nguyên tắc không spam * * 4. NGƯỜI DÙNG: * - Người dùng phải đã follow OA * - Người dùng không được block OA * - Người dùng phải có tương tác gần đây với OA */ class ConsultationService { constructor(client) { this.client = client; // Zalo API endpoint for consultation messages this.endpoint = "https://openapi.zalo.me/v3.0/oa/message/cs"; } /** * Gửi tin nhắn tư vấn văn bản * @param accessToken Access token của Official Account * @param recipient Thông tin người nhận * @param message Nội dung tin nhắn văn bản * @returns Thông tin tin nhắn đã gửi */ async sendTextMessage(accessToken, recipient, message) { try { // Validate text length theo quy định của Zalo if (!message.text || message.text.trim().length === 0) { throw new common_1.ZaloSDKError("Nội dung tin nhắn không được để trống", -1); } if (message.text.length > 2000) { throw new common_1.ZaloSDKError("Nội dung tin nhắn không được vượt quá 2000 ký tự", -1); } // Request structure theo API spec const request = { recipient: { user_id: recipient.user_id, }, message: { text: message.text, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation text message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation text message: ${error.message}`, -1, error); } } /** * Gửi tin nhắn tư vấn trích dẫn (quote message) * @param accessToken Access token của Official Account * @param recipient Thông tin người nhận * @param text Nội dung tin nhắn trả lời (tối đa 2000 ký tự) * @param quoteMessageId ID của tin nhắn muốn trích dẫn * @returns Thông tin tin nhắn đã gửi với quota information */ async sendQuoteMessage(accessToken, recipient, text, quoteMessageId) { try { // Validate input parameters if (!text || text.trim().length === 0) { throw new common_1.ZaloSDKError("Nội dung tin nhắn không được để trống", -1); } if (text.length > 2000) { throw new common_1.ZaloSDKError("Nội dung tin nhắn không được vượt quá 2000 ký tự", -1); } if (!quoteMessageId || quoteMessageId.trim().length === 0) { throw new common_1.ZaloSDKError("Quote message ID không được để trống", -1); } if (!recipient.user_id || recipient.user_id.trim().length === 0) { throw new common_1.ZaloSDKError("User ID không được để trống", -1); } // Request structure theo API spec const request = { recipient: { user_id: recipient.user_id, }, message: { text: text.trim(), quote_message_id: quoteMessageId.trim(), }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation quote message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation quote message: ${error.message}`, -1, error); } } /** * Tạo ConsultationQuoteMessage object * @param text Nội dung tin nhắn trả lời * @param quoteMessageId ID của tin nhắn muốn trích dẫn * @param quoteContent Nội dung của tin nhắn được trích dẫn (optional) * @returns ConsultationQuoteMessage object */ createQuoteMessage(text, quoteMessageId, quoteContent) { return { type: "consultation_quote", text: text.trim(), quote: { message_id: quoteMessageId.trim(), content: quoteContent || "", }, }; } /** * Gửi tin nhắn tư vấn hình ảnh * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs * Method: POST * * @param accessToken Access token của Official Account * @param userId ID người nhận (user_id) * @param imageUrl URL hình ảnh (jpg, png, tối đa 1MB) * @param text Tiêu đề ảnh (optional, tối đa 2000 ký tự) * @returns Thông tin tin nhắn đã gửi */ async sendImageMessage(accessToken, userId, imageUrl, text) { try { if (!imageUrl || imageUrl.trim().length === 0) { throw new common_1.ZaloSDKError("URL hình ảnh không được để trống", -1); } if (text && text.length > 2000) { throw new common_1.ZaloSDKError("Tiêu đề ảnh không được vượt quá 2000 ký tự", -1); } // Request structure theo API spec const request = { recipient: { user_id: userId, }, message: { ...(text && { text: text }), attachment: { type: "template", payload: { template_type: "media", elements: [ { media_type: "image", url: imageUrl, }, ], }, }, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation image message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation image message: ${error.message}`, -1, error); } } /** * Gửi tin nhắn tư vấn hình ảnh bằng attachment_id * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs * Method: POST * * @param accessToken Access token của Official Account * @param userId ID người nhận (user_id) * @param attachmentId ID của ảnh đã upload (từ API upload ảnh) * @param text Tiêu đề ảnh (optional, tối đa 2000 ký tự) * @returns Thông tin tin nhắn đã gửi */ async sendImageByAttachmentId(accessToken, userId, attachmentId, text) { try { if (!attachmentId || attachmentId.trim().length === 0) { throw new common_1.ZaloSDKError("Attachment ID không được để trống", -1); } if (text && text.length > 2000) { throw new common_1.ZaloSDKError("Tiêu đề ảnh không được vượt quá 2000 ký tự", -1); } // Request structure theo API spec const request = { recipient: { user_id: userId, }, message: { ...(text && { text: text }), attachment: { type: "template", payload: { template_type: "media", elements: [ { media_type: "image", attachment_id: attachmentId, }, ], }, }, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation image message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation image message: ${error.message}`, -1, error); } } /** * Gửi tin nhắn tư vấn GIF * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs * Method: POST * * @param accessToken Access token của Official Account * @param userId ID người nhận (user_id) * @param gifUrl URL ảnh GIF * @param width Chiều rộng của ảnh (bắt buộc cho GIF) * @param height Chiều cao của ảnh (bắt buộc cho GIF) * @param text Tiêu đề ảnh (optional, tối đa 2000 ký tự) * @returns Thông tin tin nhắn đã gửi */ async sendGifMessage(accessToken, userId, gifUrl, width, height, text) { try { if (!gifUrl || gifUrl.trim().length === 0) { throw new common_1.ZaloSDKError("URL GIF không được để trống", -1); } if (width <= 0 || height <= 0) { throw new common_1.ZaloSDKError("Chiều rộng và chiều cao phải lớn hơn 0", -1); } if (text && text.length > 2000) { throw new common_1.ZaloSDKError("Tiêu đề ảnh không được vượt quá 2000 ký tự", -1); } // Request structure theo API spec const request = { recipient: { user_id: userId, }, message: { ...(text && { text: text }), attachment: { type: "template", payload: { template_type: "media", elements: [ { media_type: "gif", url: gifUrl, width: width, height: height, }, ], }, }, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation gif message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation gif message: ${error.message}`, -1, error); } } /** * Gửi tin nhắn tư vấn đính kèm file * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs * Method: POST * * @param accessToken Access token của Official Account * @param userId ID người nhận (user_id) * @param fileToken Token của file đã upload (từ API upload file) * @returns Thông tin tin nhắn đã gửi * * Note: Cần sử dụng API upload file trước để lấy token */ async sendFileMessage(accessToken, userId, fileToken) { try { if (!fileToken || fileToken.trim().length === 0) { throw new common_1.ZaloSDKError("File token không được để trống", -1); } // Request structure theo API spec const request = { recipient: { user_id: userId, }, message: { attachment: { type: "file", payload: { token: fileToken, }, }, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation file message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation file message: ${error.message}`, -1, error); } } /** * Gửi tin nhắn tư vấn theo mẫu yêu cầu thông tin người dùng * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs * Method: POST * * @param accessToken Access token của Official Account * @param userId ID người nhận (user_id) * @param title Tiêu đề hiển thị của template (tối đa 100 ký tự) * @param subtitle Tiêu đề phụ của template (tối đa 500 ký tự) * @param imageUrl Đường dẫn đến ảnh * @returns Thông tin tin nhắn đã gửi */ async sendRequestUserInfoMessage(accessToken, userId, title, subtitle, imageUrl) { try { // Validation theo API spec if (!title || title.trim().length === 0) { throw new common_1.ZaloSDKError("Tiêu đề không được để trống", -1); } if (title.length > 100) { throw new common_1.ZaloSDKError("Tiêu đề không được vượt quá 100 ký tự", -1); } if (!subtitle || subtitle.trim().length === 0) { throw new common_1.ZaloSDKError("Tiêu đề phụ không được để trống", -1); } if (subtitle.length > 500) { throw new common_1.ZaloSDKError("Tiêu đề phụ không được vượt quá 500 ký tự", -1); } if (!imageUrl || imageUrl.trim().length === 0) { throw new common_1.ZaloSDKError("URL hình ảnh không được để trống", -1); } // Request structure theo API spec const request = { recipient: { user_id: userId, }, message: { attachment: { type: "template", payload: { template_type: "request_user_info", elements: [ { title: title, subtitle: subtitle, image_url: imageUrl, }, ], }, }, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send request user info message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 request user info message: ${error.message}`, -1, error); } } /** * Gửi tin nhắn tư vấn kèm Sticker * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs * Method: POST * * @param accessToken Access token của Official Account * @param userId ID người nhận (user_id) * @param stickerAttachmentId ID của sticker (lấy từ https://stickers.zaloapp.com/) * @returns Thông tin tin nhắn đã gửi * * Note: Sticker ID lấy từ nguồn https://stickers.zaloapp.com/ * Xem video hướng dẫn: https://vimeo.com/649330161 */ async sendStickerMessage(accessToken, userId, stickerAttachmentId) { try { if (!stickerAttachmentId || stickerAttachmentId.trim().length === 0) { throw new common_1.ZaloSDKError("Sticker attachment ID không được để trống", -1); } // Request structure theo API spec const request = { recipient: { user_id: userId, }, message: { attachment: { type: "template", payload: { template_type: "media", elements: [ { media_type: "sticker", attachment_id: stickerAttachmentId, }, ], }, }, }, }; const result = await this.client.apiPost(this.endpoint, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to send consultation sticker message", result.error, result); } if (!result.data) { throw new common_1.ZaloSDKError("No 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 consultation sticker message: ${error.message}`, -1, error); } } } exports.ConsultationService = ConsultationService; //# sourceMappingURL=consultation.service.js.map