UNPKG

@warriorteam/redai-zalo-sdk

Version:

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

722 lines 30.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GroupManagementService = void 0; const common_1 = require("../types/common"); /** * Service for handling Zalo Official Account Group Management Framework (GMF) APIs * * CONDITIONS FOR USING ZALO GMF GROUP MANAGEMENT: * * 1. GENERAL CONDITIONS: * - OA must be granted permission to use GMF (Group Message Framework) feature * - Access token must have "manage_group" and "group_message" scopes * - OA must have active status and be verified * - Must comply with limits on number of groups and members * * 2. CREATE NEW GROUP: * - Group name: required, max 100 characters, no special characters * - Description: optional, max 500 characters * - Avatar: optional, JPG/PNG format, max 5MB * - Initial members: max 200 people, must be users who have interacted with OA * - OA automatically becomes admin of the group * * 3. MEMBER MANAGEMENT: * - Only admins can invite/remove members * - Invite members: max 50 people per time, users must have interacted with OA * - Remove members: cannot remove other admins, must have at least 1 admin * - Members can leave group themselves * * 4. ADMIN MANAGEMENT: * - Only current admins can add/remove other admins * - Must have at least 1 admin in group * - OA always has admin rights and cannot be removed * * 5. LIMITS AND CONSTRAINTS: * - Maximum groups: according to service package (usually 10-100 groups) * - Maximum members per group: 200 people * - Group creation frequency: max 10 groups/day * - Member invitation frequency: max 500 invitations/day */ class GroupManagementService { constructor(client) { this.client = client; // Zalo API endpoints - organized by functionality this.endpoints = { group: { create: "https://openapi.zalo.me/v3.0/oa/group/creategroupwithoa", get: "https://openapi.zalo.me/v3.0/oa/group/getgroup", updateInfo: "https://openapi.zalo.me/v3.0/oa/group/updateinfo", updateAsset: "https://openapi.zalo.me/v3.0/oa/group/updateasset", invite: "https://openapi.zalo.me/v3.0/oa/group/invite", listPendingInvite: "https://openapi.zalo.me/v3.0/oa/group/listpendinginvite", acceptPendingInvite: "https://openapi.zalo.me/v3.0/oa/group/acceptpendinginvite", rejectPendingInvite: "https://openapi.zalo.me/v3.0/oa/group/rejectpendinginvite", removeMembers: "https://openapi.zalo.me/v3.0/oa/group/removemembers", getGroupsOfOA: "https://openapi.zalo.me/v3.0/oa/group/getgroupsofoa", recent: "https://openapi.zalo.me/v3.0/oa/group/listrecentchat", conversation: "https://openapi.zalo.me/v3.0/oa/group/conversation", addAdmins: "https://openapi.zalo.me/v3.0/oa/group/addadmins", removeAdmins: "https://openapi.zalo.me/v3.0/oa/group/removeadmins", conversationByGroupId: (groupId) => `https://openapi.zalo.me/v3.0/oa/group/${groupId}/conversation`, members: "https://openapi.zalo.me/v3.0/oa/group/listmember", delete: "https://openapi.zalo.me/v3.0/oa/group/delete", quota: "https://openapi.zalo.me/v3.0/oa/quota/group", }, }; } /** * Create new group chat with asset_id * @param accessToken OA access token * @param groupData Group information to create * @returns Created group information * * API: POST https://openapi.zalo.me/v3.0/oa/group/creategroupwithoa */ async createGroup(accessToken, groupData) { try { // Validate input if (!groupData.group_name || groupData.group_name.trim().length === 0) { throw new common_1.ZaloSDKError("Group name cannot be empty", -1); } if (groupData.group_name.length > 100) { throw new common_1.ZaloSDKError("Group name cannot exceed 100 characters", -1); } if (!groupData.asset_id || groupData.asset_id.trim().length === 0) { throw new common_1.ZaloSDKError("Asset ID cannot be empty", -1); } if (groupData.member_user_ids.length > 99) { throw new common_1.ZaloSDKError("Initial member count cannot exceed 99 people", -1); } if (groupData.member_user_ids.length === 0) { throw new common_1.ZaloSDKError("Member list cannot be empty", -1); } const requestData = { group_name: groupData.group_name.trim(), ...(groupData.group_description && { group_description: groupData.group_description.trim(), }), asset_id: groupData.asset_id, member_user_ids: groupData.member_user_ids, }; const response = await this.client.apiPost(this.endpoints.group.create, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to create group"); } } /** * Helper method to extract group data from create response * @param response Full API response * @returns Group data only */ extractGroupData(response) { return response.data; } /** * Helper method to extract group info from update response * @param response Full API response * @returns Group info only */ extractGroupInfo(response) { return response.data.group_info; } /** * Helper method to extract group settings from update response * @param response Full API response * @returns Group settings only */ extractGroupSettings(response) { return response.data.group_setting; } /** * Helper method to extract asset info from update response * @param response Full API response * @returns Asset info only */ extractAssetInfo(response) { return response.data.asset_info; } async updateGroupAsset(accessToken, groupIdOrUpdateData, assetId) { try { let requestData; // Handle overloaded parameters if (typeof groupIdOrUpdateData === 'string') { // First overload: (accessToken, groupId, assetId) if (!assetId) { throw new common_1.ZaloSDKError("Asset ID is required when using separate parameters", -1); } requestData = { group_id: groupIdOrUpdateData, asset_id: assetId, }; } else { // Second overload: (accessToken, updateData) requestData = groupIdOrUpdateData; } // Validate input if (!requestData.group_id || requestData.group_id.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } if (!requestData.asset_id || requestData.asset_id.trim().length === 0) { throw new common_1.ZaloSDKError("Asset ID cannot be empty", -1); } const response = await this.client.apiPost(this.endpoints.group.updateAsset, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to update group asset"); } } /** * Get detailed group information * @param accessToken OA access token * @param groupId Group ID * @returns Detailed group information including group_info, asset_info and group_setting * * API: GET https://openapi.zalo.me/v3.0/oa/group/getgroup */ async getGroupInfo(accessToken, groupId) { try { // Validate access token if (!accessToken || accessToken.trim().length === 0) { throw new common_1.ZaloSDKError("Access token cannot be empty", -1); } // Validate group ID if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } const response = await this.client.apiGet(this.endpoints.group.get, accessToken, { group_id: groupId.trim(), }); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get group information"); } } /** * Update group information * @param accessToken OA access token * @param groupId Group ID * @param updateData Information to update * @returns Update result with full group information * * API: POST https://openapi.zalo.me/v3.0/oa/group/updateinfo */ async updateGroupInfo(accessToken, groupId, updateData) { try { // Validate input if (updateData.group_name && updateData.group_name.length > 100) { throw new common_1.ZaloSDKError("Group name cannot exceed 100 characters", -1); } if (updateData.group_description && updateData.group_description.length > 500) { throw new common_1.ZaloSDKError("Group description cannot exceed 500 characters", -1); } // Build request data according to Zalo API const requestData = { group_id: groupId, ...(updateData.group_name && { group_name: updateData.group_name.trim(), }), ...(updateData.group_avatar && { group_avatar: updateData.group_avatar, }), ...(updateData.group_description && { group_description: updateData.group_description.trim(), }), ...(updateData.lock_send_msg !== undefined && { lock_send_msg: updateData.lock_send_msg, }), ...(updateData.join_appr !== undefined && { join_appr: updateData.join_appr, }), ...(updateData.enable_msg_history !== undefined && { enable_msg_history: updateData.enable_msg_history, }), ...(updateData.enable_link_join !== undefined && { enable_link_join: updateData.enable_link_join, }), }; const response = await this.client.apiPost(this.endpoints.group.updateInfo, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to update group information"); } } /** * Update group avatar * @param accessToken OA access token * @param groupId Group ID * @param avatarData New avatar information * @returns Update result * @deprecated Use updateGroupInfo() with group_avatar field instead */ async updateGroupAvatar(accessToken, groupId, avatarData) { try { const response = await this.client.apiPost(this.endpoints.group.updateInfo, accessToken, { group_id: groupId, ...avatarData, }); return { success: response.error === 0 }; } catch (error) { throw this.handleGroupManagementError(error, "Failed to update group avatar"); } } async inviteMembers(accessToken, groupId, inviteDataOrUserIds) { try { let memberUserIds; // Handle overloaded parameters if (Array.isArray(inviteDataOrUserIds)) { // Second overload: (accessToken, groupId, memberUserIds) memberUserIds = inviteDataOrUserIds; } else { // First overload: (accessToken, groupId, inviteData) memberUserIds = inviteDataOrUserIds.member_user_ids; } // Validate input if (memberUserIds.length === 0) { throw new common_1.ZaloSDKError("Member list cannot be empty", -1); } if (memberUserIds.length > 50) { throw new common_1.ZaloSDKError("Cannot invite more than 50 people at once", -1); } const requestData = { group_id: groupId, member_user_ids: memberUserIds, }; const response = await this.client.apiPost(this.endpoints.group.invite, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to invite members"); } } /** * Get list of pending members * @param accessToken OA access token * @param groupId Group ID * @param offset Offset for pagination (default: 0) * @param count Maximum number to return (default: 20, max: 50) * @returns List of pending members */ async getPendingMembers(accessToken, groupId, offset = 0, count = 5) { try { // Validate input if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } if (offset < 0) { throw new common_1.ZaloSDKError("Offset must be >= 0", -1); } if (count <= 0 || count > 50) { throw new common_1.ZaloSDKError("Count must be from 1 to 50", -1); } const response = await this.client.apiGet(this.endpoints.group.listPendingInvite, accessToken, { group_id: groupId.trim(), offset: offset, count: count, }); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get pending members"); } } /** * Accept pending members to group * @param accessToken OA access token * @param groupId Group ID * @param memberUserIds List of user IDs to accept * @returns Accept result */ async acceptPendingMembers(accessToken, groupId, memberUserIds) { try { // Validate input if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } if (!memberUserIds || memberUserIds.length === 0) { throw new common_1.ZaloSDKError("Member user IDs list cannot be empty", -1); } if (memberUserIds.length > 100) { throw new common_1.ZaloSDKError("Cannot accept more than 100 members at once", -1); } // Validate user IDs const validUserIds = memberUserIds.filter((id) => id && id.trim().length > 0); if (validUserIds.length === 0) { throw new common_1.ZaloSDKError("No valid user IDs found", -1); } const requestBody = { group_id: groupId.trim(), member_user_ids: validUserIds.map((id) => id.trim()), }; const response = await this.client.apiPost(this.endpoints.group.acceptPendingInvite, accessToken, requestBody); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to accept pending members"); } } /** * Reject pending members from group * @param accessToken OA access token * @param groupId Group ID * @param memberUserIds List of user IDs to reject * @returns Reject result */ async rejectPendingMembers(accessToken, groupId, memberUserIds) { try { // Validate input if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } if (!memberUserIds || memberUserIds.length === 0) { throw new common_1.ZaloSDKError("Member user IDs list cannot be empty", -1); } if (memberUserIds.length > 100) { throw new common_1.ZaloSDKError("Cannot reject more than 100 members at once", -1); } // Validate user IDs const validUserIds = memberUserIds.filter((id) => id && id.trim().length > 0); if (validUserIds.length === 0) { throw new common_1.ZaloSDKError("No valid user IDs found", -1); } const requestBody = { group_id: groupId.trim(), member_user_ids: validUserIds.map((id) => id.trim()), }; const response = await this.client.apiPost(this.endpoints.group.rejectPendingInvite, accessToken, requestBody); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to reject pending members"); } } /** * Remove members from group * @param accessToken OA access token * @param groupId Group ID * @param memberUserIds List of user IDs to remove * @returns Remove result */ async removeMembers(accessToken, groupId, memberUserIds) { try { // Validate input if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } if (!memberUserIds || memberUserIds.length === 0) { throw new common_1.ZaloSDKError("Member user IDs list cannot be empty", -1); } if (memberUserIds.length > 100) { throw new common_1.ZaloSDKError("Cannot remove more than 100 members at once", -1); } // Validate user IDs const validUserIds = memberUserIds.filter((id) => id && id.trim().length > 0); if (validUserIds.length === 0) { throw new common_1.ZaloSDKError("No valid user IDs found", -1); } const requestBody = { group_id: groupId.trim(), member_user_ids: validUserIds.map((id) => id.trim()), }; const response = await this.client.apiPost(this.endpoints.group.removeMembers, accessToken, requestBody); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to remove members"); } } async addAdmins(accessToken, groupId, adminDataOrUserIds) { try { let memberUserIds; // Handle overloaded parameters if (Array.isArray(adminDataOrUserIds)) { // Second overload: (accessToken, groupId, memberUserIds) memberUserIds = adminDataOrUserIds; } else { // First overload: (accessToken, groupId, adminData) memberUserIds = adminDataOrUserIds.member_user_ids; } // Validate input if (!memberUserIds || memberUserIds.length === 0) { throw new common_1.ZaloSDKError("Member user IDs list cannot be empty", -1); } const requestData = { group_id: groupId, member_user_ids: memberUserIds, }; const response = await this.client.apiPost(this.endpoints.group.addAdmins, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to add admins"); } } async removeAdmins(accessToken, groupId, adminDataOrUserIds) { try { let memberUserIds; // Handle overloaded parameters if (Array.isArray(adminDataOrUserIds)) { // Second overload: (accessToken, groupId, memberUserIds) memberUserIds = adminDataOrUserIds; } else { // First overload: (accessToken, groupId, adminData) memberUserIds = adminDataOrUserIds.member_user_ids; } // Validate input if (!memberUserIds || memberUserIds.length === 0) { throw new common_1.ZaloSDKError("Member user IDs list cannot be empty", -1); } const requestData = { group_id: groupId, member_user_ids: memberUserIds, }; const response = await this.client.apiPost(this.endpoints.group.removeAdmins, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to remove admins"); } } /** * Delete group chat (Disband group) * @param accessToken OA access token * @param groupId Group ID to delete * @returns Delete result */ async deleteGroup(accessToken, groupId) { try { const requestData = { group_id: groupId, }; const response = await this.client.apiPost(this.endpoints.group.delete, accessToken, requestData); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to delete group"); } } /** * Get list of OA groups * @param accessToken OA access token * @param offset Offset for pagination (default: 0) * @param count Maximum number to return (default: 5, max: 50) * @returns List of OA groups * * API: GET https://openapi.zalo.me/v3.0/oa/group/getgroupsofoa */ async getGroupsOfOA(accessToken, offset = 0, count = 5) { try { // Validate access token if (!accessToken || accessToken.trim().length === 0) { throw new common_1.ZaloSDKError("Access token cannot be empty", -1); } // Validate parameters if (offset < 0) { throw new common_1.ZaloSDKError("Offset must be >= 0", -1); } if (count <= 0 || count > 50) { throw new common_1.ZaloSDKError("Count must be from 1 to 50", -1); } const response = await this.client.apiGet(this.endpoints.group.getGroupsOfOA, accessToken, { offset: offset, count: count, }); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get groups of OA"); } } /** * Get group quota information and asset_id * @param accessToken OA access token * @param productType Product type (optional) * @param quotaType Quota type (optional) * @returns Group quota information including asset_id * * API: POST https://openapi.zalo.me/v3.0/oa/quota/group */ async getGroupQuota(accessToken, productType, quotaType) { try { // Validate access token if (!accessToken || accessToken.trim().length === 0) { throw new common_1.ZaloSDKError("Access token cannot be empty", -1); } const quotaRequest = { quota_owner: "OA", ...(productType && { product_type: productType }), ...(quotaType && { quota_type: quotaType }), }; const response = await this.client.apiPost(this.endpoints.group.quota, accessToken, quotaRequest); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get group quota"); } } /** * Get asset_id for creating GMF group * @param accessToken OA access token * @returns Asset_id for group creation */ async getAssetId(accessToken) { try { const quotaRequest = { quota_owner: "OA", }; const response = await this.client.apiPost(this.endpoints.group.quota, accessToken, quotaRequest); if (response.error !== 0) { throw new common_1.ZaloSDKError(response.message || "Failed to get asset_id", response.error); } if (response.data && response.data.length > 0) { const activeAsset = response.data.find((asset) => asset.status === "available"); if (activeAsset) { return activeAsset.asset_id; } } throw new common_1.ZaloSDKError("No valid asset_id found for group creation. Please check your GMF package.", -1); } catch (error) { throw this.handleGroupManagementError(error, "Failed to get asset_id for group creation"); } } /** * Get list of asset_ids available for creating GMF groups * @param accessToken OA access token * @returns List of asset_ids and quota information */ async getAssetIds(accessToken) { try { const quotaRequest = { quota_owner: "OA", }; const response = await this.client.apiPost(this.endpoints.group.quota, accessToken, quotaRequest); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get asset_ids list"); } } /** * Get list of recent chats * @param accessToken OA access token * @param offset Offset for pagination (default: 0) * @param count Maximum number to return (default: 5, max: 50) * @returns List of recent chats * * API: GET https://openapi.zalo.me/v3.0/oa/group/listrecentchat */ async getRecentChats(accessToken, offset = 0, count = 5) { try { // Validate access token if (!accessToken || accessToken.trim().length === 0) { throw new common_1.ZaloSDKError("Access token cannot be empty", -1); } // Validate parameters if (offset < 0) { throw new common_1.ZaloSDKError("Offset must be >= 0", -1); } if (count <= 0 || count > 50) { throw new common_1.ZaloSDKError("Count must be from 1 to 50", -1); } const response = await this.client.apiGet(this.endpoints.group.recent, accessToken, { offset: offset, count: count, }); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get recent chats"); } } /** * Get group conversation history * @param accessToken OA access token * @param groupId Group ID * @param offset Offset for pagination (default: 0) * @param count Maximum number of messages to return (default: 5, max: 100) * @returns Group conversation history * * API: GET https://openapi.zalo.me/v3.0/oa/group/conversation */ async getGroupConversation(accessToken, groupId, offset = 0, count = 5) { try { // Validate access token if (!accessToken || accessToken.trim().length === 0) { throw new common_1.ZaloSDKError("Access token cannot be empty", -1); } // Validate group ID if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } // Validate parameters if (offset < 0) { throw new common_1.ZaloSDKError("Offset must be >= 0", -1); } if (count <= 0 || count > 100) { throw new common_1.ZaloSDKError("Count must be from 1 to 100", -1); } const response = await this.client.apiGet(this.endpoints.group.conversation, accessToken, { group_id: groupId.trim(), offset: offset, count: count, }); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get group conversation"); } } /** * Get group members list from Zalo API * @param accessToken OA access token * @param groupId Group ID * @param offset Offset for pagination (default: 0) * @param count Maximum number to return (default: 5, max: 50) * @returns Group members list */ async getGroupMembers(accessToken, groupId, offset = 0, count = 5) { try { // Validate input if (!groupId || groupId.trim().length === 0) { throw new common_1.ZaloSDKError("Group ID cannot be empty", -1); } if (offset < 0) { throw new common_1.ZaloSDKError("Offset must be >= 0", -1); } if (count <= 0 || count > 50) { throw new common_1.ZaloSDKError("Count must be from 1 to 50", -1); } const response = await this.client.apiGet(this.endpoints.group.members, accessToken, { group_id: groupId.trim(), offset: offset, count: count, }); return response; } catch (error) { throw this.handleGroupManagementError(error, "Failed to get group members"); } } handleGroupManagementError(error, defaultMessage) { if (error instanceof common_1.ZaloSDKError) { return error; } if (error.response?.data) { const errorData = error.response.data; return new common_1.ZaloSDKError(`${defaultMessage}: ${errorData.message || errorData.error || "Unknown error"}`, errorData.error || -1, errorData); } return new common_1.ZaloSDKError(`${defaultMessage}: ${error.message || "Unknown error"}`, -1, error); } } exports.GroupManagementService = GroupManagementService; //# sourceMappingURL=group-management.service.js.map