@warriorteam/redai-zalo-sdk
Version:
Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account, ZNS, Consultation Service, Group Messaging, and Social APIs
564 lines • 22.4 kB
JavaScript
;
/**
* User management service for Zalo API
* Handles all user-related operations with specific endpoints
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserService = void 0;
const common_1 = require("../types/common");
/**
* User management service for handling user operations
* Each method uses specific Zalo API endpoints
*/
class UserService {
constructor(client) {
this.client = client;
// Zalo API endpoints - organized by functionality
this.endpoints = {
// User management endpoints
user: {
getList: "https://openapi.zalo.me/v3.0/oa/user/getlist",
postList: "https://openapi.zalo.me/v3.0/oa/user/getlist",
getDetail: "https://openapi.zalo.me/v3.0/oa/user/detail",
update: "https://openapi.zalo.me/v3.0/oa/user/update",
delete: "https://openapi.zalo.me/v2.0/oa/deletefollowerinfo",
getCustomInfo: "https://openapi.zalo.me/v3.0/oa/user/detail/custominfo",
updateCustomInfo: "https://openapi.zalo.me/v3.0/oa/user/update/custominfo",
},
// Tag management endpoints
tag: {
addToUser: "https://openapi.zalo.me/v2.0/oa/tag/tagfollower",
removeFromUser: "https://openapi.zalo.me/v2.0/oa/tag/rmfollowerfromtag",
getList: "https://openapi.zalo.me/v2.0/oa/tag/gettagsofoa",
delete: "https://openapi.zalo.me/v2.0/oa/tag/rmtag",
},
};
}
/**
* Truy xuất chi tiết người dùng
* Endpoint: https://openapi.zalo.me/v3.0/oa/user/detail
* Method: GET with data parameter as JSON string
*
* @param accessToken OA access token
* @param userId User ID to get details for
* @returns Promise<UserInfo> - Detailed user information
*/
async getUserInfo(accessToken, userId) {
try {
// Build data object according to API spec
const dataObject = { user_id: userId };
// Send data as JSON string parameter according to API spec
const params = {
data: JSON.stringify(dataObject),
};
const result = await this.client.apiGet(this.endpoints.user.getDetail, accessToken, params);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to get user information", result.error, result);
}
if (!result.data) {
throw new common_1.ZaloSDKError("No user data received", -1);
}
return result.data;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
this.handleError(error, "Failed to get user info");
}
}
/**
* Truy xuất danh sách người dùng
* Endpoint: https://openapi.zalo.me/v3.0/oa/user/getlist
* Method: GET with data parameter as JSON string
*
* @param accessToken OA access token
* @param request User list request parameters
* @returns Promise<UserListResponse> - List of users with pagination info
*/
async getUserList(accessToken, request) {
try {
// Build data object according to API spec
const dataObject = {
offset: request.offset,
count: Math.min(request.count, 50), // Max 50 users per request
...(request.tag_name && { tag_name: request.tag_name }),
...(request.last_interaction_period && {
last_interaction_period: request.last_interaction_period,
}),
...(request.is_follower !== undefined && {
is_follower: request.is_follower,
}),
};
// Send data as JSON string parameter according to API spec
const params = {
data: encodeURIComponent(JSON.stringify(dataObject)),
};
const result = await this.client.apiGet(this.endpoints.user.getList, accessToken, params);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to get user list", result.error, result);
}
if (!result.data) {
throw new common_1.ZaloSDKError("No user list data received", -1);
}
return result.data;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
this.handleError(error, "Failed to get user list");
}
}
/**
* Truy xuất danh sách người dùng (POST method)
* Endpoint: https://openapi.zalo.me/v3.0/oa/user/getlist
* Method: POST with data in request body
*
* @param accessToken OA access token
* @param request User list request parameters
* @returns Promise<UserListResponse> - List of users with pagination info
*/
async postUserList(accessToken, request) {
try {
// Build data object according to API spec
const dataObject = {
offset: request.offset,
count: Math.min(request.count, 50), // Max 50 users per request
...(request.tag_name && { tag_name: request.tag_name }),
...(request.last_interaction_period && {
last_interaction_period: request.last_interaction_period,
}),
...(request.is_follower !== undefined && {
is_follower: request.is_follower,
}),
};
const result = await this.client.apiPost(this.endpoints.user.postList, accessToken, dataObject);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to get user list", result.error, result);
}
if (!result.data) {
throw new common_1.ZaloSDKError("No user list data received", -1);
}
return result.data;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
this.handleError(error, "Failed to get user list");
}
}
/**
* Get all users with automatic pagination
* Lấy tất cả users với phân trang tự động, đảm bảo lấy 100%
*
* @param accessToken Access token của OA
* @param filters Bộ lọc tùy chọn
* @returns Promise<UserInfo[]> - Danh sách đầy đủ tất cả users
*/
async getAllUsers(accessToken, filters) {
const users = [];
let offset = 0;
const count = 50; // Max per request
let totalUsers = 0;
let fetchedUsers = 0;
try {
while (true) {
const userListResponse = await this.getUserList(accessToken, {
offset,
count,
...filters,
});
// Lần đầu tiên, lưu tổng số users
if (offset === 0) {
totalUsers = userListResponse.total;
console.log(`Total users to fetch: ${totalUsers}`);
// Cảnh báo nếu vượt quá giới hạn Zalo API
if (totalUsers > 10000) {
console.warn(`Warning: Total users (${totalUsers}) exceeds Zalo API limit (10000). ` +
`Only first 10000 users can be fetched due to max offset = 9951.`);
}
}
// Get detailed info for each user
const userIds = userListResponse.users.map((u) => u.user_id);
const userInfoPromises = userIds.map((userId) => this.getUserInfo(accessToken, userId));
const userInfos = await Promise.all(userInfoPromises);
users.push(...userInfos);
fetchedUsers += userInfos.length;
console.log(`Fetched ${fetchedUsers}/${totalUsers} users`);
// Điều kiện dừng ĐÚNG:
// 1. Không còn users trong response
// 2. Hoặc đã lấy đủ số lượng theo total
// 3. Hoặc số users trả về ít hơn count (page cuối)
if (userListResponse.users.length === 0 ||
fetchedUsers >= totalUsers ||
userListResponse.users.length < count) {
break;
}
offset += count;
// Protection: Tránh infinite loop
// Zalo giới hạn max offset = 9951 (tương ứng 10000 người dùng)
if (offset > 9951) {
console.warn("Reached Zalo maximum offset limit (9951), stopping pagination");
break;
}
}
console.log(`Successfully fetched all ${users.length} users`);
return users;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
this.handleError(error, "Failed to get all users");
}
}
/**
* Get all user IDs only (without detailed info) - Faster version
* Chỉ lấy danh sách user ID, không lấy thông tin chi tiết - Nhanh hơn
*
* @param accessToken Access token của OA
* @param filters Bộ lọc tùy chọn
* @returns Promise<string[]> - Danh sách user IDs
*/
async getAllUserIds(accessToken, filters) {
const userIds = [];
let offset = 0;
const count = 50; // Max per request
let totalUsers = 0;
try {
while (true) {
const userListResponse = await this.getUserList(accessToken, {
offset,
count,
...filters,
});
// Lần đầu tiên, lưu tổng số users
if (offset === 0) {
totalUsers = userListResponse.total;
console.log(`Total user IDs to fetch: ${totalUsers}`);
// Cảnh báo nếu vượt quá giới hạn Zalo API
if (totalUsers > 10000) {
console.warn(`Warning: Total users (${totalUsers}) exceeds Zalo API limit (10000). ` +
`Only first 10000 users can be fetched due to max offset = 9951.`);
}
}
// Chỉ lấy user IDs, không lấy detailed info
const currentUserIds = userListResponse.users.map((u) => u.user_id);
userIds.push(...currentUserIds);
console.log(`Fetched ${userIds.length}/${Math.min(totalUsers, 10000)} user IDs`);
// Điều kiện dừng
if (userListResponse.users.length === 0 ||
userIds.length >= Math.min(totalUsers, 10000) ||
userListResponse.users.length < count) {
break;
}
offset += count;
// Protection: Tránh infinite loop
if (offset > 9951) {
console.warn("Reached Zalo maximum offset limit (9951), stopping pagination");
break;
}
}
console.log(`Successfully fetched ${userIds.length} user IDs`);
return userIds;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
this.handleError(error, "Failed to get all user IDs");
}
}
/**
* Cập nhật chi tiết người dùng
* Endpoint: https://openapi.zalo.me/v3.0/oa/user/update
* Method: POST
*
* @param accessToken OA access token
* @param request Update user request with user_id and optional fields
* @returns Promise<boolean> - true if successful
*/
async updateUser(accessToken, request) {
try {
const result = await this.client.apiPost(this.endpoints.user.update, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to update user", result.error, result);
}
return true;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to update user: ${error.message}`, -1, error);
}
}
/**
* Xóa thông tin người dùng
* Endpoint: https://openapi.zalo.me/v2.0/oa/deletefollowerinfo
* Method: POST
*
* @param accessToken OA access token
* @param userId User ID to delete info for
* @returns Promise<boolean> - true if successful
*
* Note: Chỉ xóa thông tin lưu trữ ở phía OA, không thay đổi tài khoản Zalo
*/
async deleteUserInfo(accessToken, userId) {
try {
const request = { user_id: userId };
const result = await this.client.apiPost(this.endpoints.user.delete, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to delete user info", result.error, result);
}
return true;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to delete user info: ${error.message}`, -1, error);
}
}
/**
* Truy xuất thông tin tùy biến của người dùng
* Endpoint: https://openapi.zalo.me/v3.0/oa/user/detail/custominfo
* Method: GET with data parameter as JSON string
*
* @param accessToken OA access token
* @param request Get custom info request with user_id and optional fields_to_export
* @returns Promise<UserCustomInfoResponse> - Custom info data
*
* Note: Tất cả giá trị trong custom_info đều được trả về dưới dạng string
*/
async getUserCustomInfo(accessToken, request) {
try {
// Build data object according to API spec
const dataObject = {
user_id: request.user_id,
...(request.fields_to_export && {
fields_to_export: request.fields_to_export,
}),
};
// Send data as JSON string parameter according to API spec
const params = {
data: JSON.stringify(dataObject),
};
const result = await this.client.apiGet(this.endpoints.user.getCustomInfo, accessToken, params);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to get user custom info", result.error, result);
}
return result;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to get user custom info: ${error.message}`, -1, error);
}
}
/**
* Cập nhật thông tin tùy biến của người dùng
* Endpoint: https://openapi.zalo.me/v3.0/oa/user/update/custominfo
* Method: POST
*
* @param accessToken OA access token
* @param request Update custom info request with user_id and custom_info
* @returns Promise<boolean> - true if successful
*
* Note: Cấu trúc custom_info phụ thuộc vào thiết lập OA
*/
async updateUserCustomInfo(accessToken, request) {
try {
const result = await this.client.apiPost(this.endpoints.user.updateCustomInfo, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to update user custom info", result.error, result);
}
return true;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to update user custom info: ${error.message}`, -1, error);
}
}
/**
* Lấy danh sách nhãn
* Endpoint: https://openapi.zalo.me/v2.0/oa/tag/gettagsofoa
* Method: GET
*
* @param accessToken OA access token
* @returns Promise<string[]> - Array of tag names
*/
async getLabels(accessToken) {
try {
const result = await this.client.apiGet(this.endpoints.tag.getList, accessToken);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to get labels", result.error, result);
}
return result.data || [];
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to get labels: ${error.message}`, -1, error);
}
}
/**
* Gắn nhãn người dùng
* Endpoint: https://openapi.zalo.me/v2.0/oa/tag/tagfollower
*
* @param accessToken OA access token
* @param request Tag user request with user_id and tag_name
* @returns Promise<boolean> - true if successful
*
* Note: Nếu tag_name không tồn tại, API sẽ tự động tạo nhãn mới
*/
async addTagToUser(accessToken, request) {
try {
const result = await this.client.apiPost(this.endpoints.tag.addToUser, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to add tag to user", result.error, result);
}
return true;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to add tag to user: ${error.message}`, -1, error);
}
}
/**
* Gỡ nhãn người dùng
* Endpoint: https://openapi.zalo.me/v2.0/oa/tag/rmfollowerfromtag
*
* @param accessToken OA access token
* @param request Remove tag request with user_id and tag_name
* @returns Promise<boolean> - true if successful
*/
async removeTagFromUser(accessToken, request) {
try {
const result = await this.client.apiPost(this.endpoints.tag.removeFromUser, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to remove tag from user", result.error, result);
}
return true;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to remove tag from user: ${error.message}`, -1, error);
}
}
/**
* Xóa nhãn
* Endpoint: https://openapi.zalo.me/v2.0/oa/tag/rmtag
* Method: POST
*
* @param accessToken OA access token
* @param tagName Tag name to delete
* @returns Promise<boolean> - true if successful
*/
async deleteLabel(accessToken, tagName) {
try {
const request = { tag_name: tagName };
const result = await this.client.apiPost(this.endpoints.tag.delete, accessToken, request);
if (result.error !== 0) {
throw new common_1.ZaloSDKError(result.message || "Failed to delete label", result.error, result);
}
return true;
}
catch (error) {
if (error instanceof common_1.ZaloSDKError) {
throw error;
}
throw new common_1.ZaloSDKError(`Failed to delete label: ${error.message}`, -1, error);
}
}
/**
* Get users by tag
*/
async getUsersByTag(accessToken, tagName, offset = 0, count = 50) {
return this.getUserList(accessToken, {
offset,
count,
tag_name: tagName,
});
}
/**
* Get followers only
*/
async getFollowers(accessToken, offset = 0, count = 50) {
return this.getUserList(accessToken, {
offset,
count,
is_follower: true,
});
}
/**
* Get users by interaction period
*/
async getUsersByInteraction(accessToken, period, offset = 0, count = 50) {
return this.getUserList(accessToken, {
offset,
count,
last_interaction_period: period,
});
}
/**
* Bulk tag operations
*/
async bulkAddTag(accessToken, userIds, tagName) {
const results = {
success: [],
failed: [],
};
for (const userId of userIds) {
try {
await this.addTagToUser(accessToken, {
user_id: userId,
tag_name: tagName,
});
results.success.push(userId);
}
catch (error) {
results.failed.push(userId);
}
}
return results;
}
/**
* Bulk remove tag operations
*/
async bulkRemoveTag(accessToken, userIds, tagName) {
const results = {
success: [],
failed: [],
};
for (const userId of userIds) {
try {
await this.removeTagFromUser(accessToken, {
user_id: userId,
tag_name: tagName,
});
results.success.push(userId);
}
catch (error) {
results.failed.push(userId);
}
}
return results;
}
handleError(error, message) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
throw new common_1.ZaloSDKError(`${message}: ${errorMessage}`, -1, error);
}
}
exports.UserService = UserService;
//# sourceMappingURL=user.service.js.map