UNPKG

@warriorteam/zalo-personal

Version:

Unofficial Zalo Personal API for JavaScript - A powerful library for interacting with Zalo personal accounts with URL attachment support

883 lines (693 loc) 25.4 kB
# Quản Lý Nhóm (Group Management) ## Tổng Quan SDK cung cấp API đầy đủ để quản lý nhóm Zalo: - Tạo và xóa nhóm - Quản lý thành viên - Thay đổi thông tin nhóm - Quản lý quyền admin - Tạo và quản lý polls, notes - Cài đặt nhóm nâng cao ## Lấy Thông Tin Nhóm ### 1. Danh Sách Tất Cả Nhóm ```typescript try { const groups = await api.getAllGroups(); console.log(`Bạn tham gia ${groups.data.length} nhóm:`); groups.data.forEach(group => { console.log(`- ${group.name} (ID: ${group.id})`); console.log(` Thành viên: ${group.totalMember}`); console.log(` Loại: ${group.type}`); }); } catch (error) { console.error('Lỗi lấy danh sách nhóm:', error.message); } ``` ### 2. Thông Tin Chi Tiết Nhóm ```typescript const groupInfo = await api.getGroupInfo('group-id'); console.log('📋 Thông tin nhóm:'); console.log(` Tên: ${groupInfo.data.name}`); console.log(` ID: ${groupInfo.data.id}`); console.log(` Người tạo: ${groupInfo.data.creatorId}`); console.log(` Tổng thành viên: ${groupInfo.data.totalMember}`); console.log(` Admins: ${groupInfo.data.adminIds.join(', ')}`); console.log(` Link tham gia: ${groupInfo.data.linkJoin || 'Chưa có'}`); console.log(` Mô tả: ${groupInfo.data.description || 'Không có'}`); ``` ### 3. Thông Tin Thành Viên Nhóm ```typescript const members = await api.getGroupMembersInfo('group-id'); console.log(`👥 Danh sách ${members.data.length} thành viên:`); members.data.forEach(member => { const role = member.role === 'admin' ? '👑 Admin' : '👤 Thành viên'; const joinDate = new Date(member.joinTime).toLocaleDateString(); console.log(` ${role} ${member.displayName} (${member.userId})`); console.log(` Tham gia: ${joinDate}`); console.log(` Avatar: ${member.avatar ? '✅' : '❌'}`); }); ``` ## Tạo Nhóm ### 1. Tạo Nhóm Cơ Bản ```typescript import { CreateGroupOptions } from 'zalo-personal-sdk'; const groupOptions: CreateGroupOptions = { name: 'Nhóm Test SDK', members: ['friend-id-1', 'friend-id-2', 'friend-id-3'] }; try { const newGroup = await api.createGroup(groupOptions); console.log('✅ Tạo nhóm thành công!'); console.log(` ID: ${newGroup.data.groupId}`); console.log(` Tên: ${groupOptions.name}`); console.log(` Thành viên: ${groupOptions.members.length + 1} người`); } catch (error) { console.error('❌ Lỗi tạo nhóm:', error.message); } ``` ### 2. Tạo Nhóm Với Avatar ```typescript async function createGroupWithAvatar(name: string, members: string[], avatarPath?: string) { try { // Tạo nhóm trước const newGroup = await api.createGroup({ name, members }); const groupId = newGroup.data.groupId; // Thêm avatar nếu có if (avatarPath) { await api.changeGroupAvatar(groupId, avatarPath); console.log('✅ Đã thêm avatar cho nhóm'); } return groupId; } catch (error) { console.error('❌ Lỗi tạo nhóm với avatar:', error.message); throw error; } } // Sử dụng const groupId = await createGroupWithAvatar( 'Nhóm Có Avatar', ['friend-1', 'friend-2'], './group-avatar.jpg' ); ``` ### 3. Tạo Nhóm Từ Danh Sách Bạn Bè ```typescript async function createGroupFromFriends( groupName: string, friendQuery: string, maxMembers: number = 10 ) { try { // Lấy danh sách bạn bè const friends = await api.getAllFriends(); // Lọc bạn bè theo tên const matchedFriends = friends.data.friends .filter(friend => friend.displayName.toLowerCase().includes(friendQuery.toLowerCase()) ) .slice(0, maxMembers) .map(friend => friend.userId); if (matchedFriends.length === 0) { throw new Error('Không tìm thấy bạn bè phù hợp'); } console.log(`🔍 Tìm thấy ${matchedFriends.length} bạn bè phù hợp`); const newGroup = await api.createGroup({ name: groupName, members: matchedFriends }); console.log(`✅ Đã tạo nhóm "${groupName}" với ${matchedFriends.length} thành viên`); return newGroup.data.groupId; } catch (error) { console.error('❌ Lỗi tạo nhóm từ danh sách bạn bè:', error.message); throw error; } } // Sử dụng await createGroupFromFriends('Nhóm Developers', 'dev', 5); ``` ## Quản Lý Thành Viên ### 1. Thêm Thành Viên ```typescript // Thêm một thành viên await api.addUserToGroup('group-id', ['user-id']); console.log('✅ Đã thêm thành viên vào nhóm'); // Thêm nhiều thành viên await api.addUserToGroup('group-id', ['user-1', 'user-2', 'user-3']); console.log('✅ Đã thêm nhiều thành viên vào nhóm'); ``` ### 2. Xóa Thành Viên ```typescript await api.removeUserFromGroup('group-id', 'user-id'); console.log('✅ Đã xóa thành viên khỏi nhóm'); ``` ### 3. Rời Nhóm ```typescript await api.leaveGroup('group-id'); console.log('✅ Đã rời nhóm'); ``` ### 4. Quản Lý Hàng Loạt ```typescript async function bulkMemberManagement( groupId: string, operations: Array<{ action: 'add' | 'remove'; userId: string; reason?: string; }> ) { const results = []; const errors = []; for (const op of operations) { try { console.log(`${op.action === 'add' ? '➕' : '➖'} ${op.action} ${op.userId}...`); if (op.action === 'add') { await api.addUserToGroup(groupId, [op.userId]); } else { await api.removeUserFromGroup(groupId, op.userId); } results.push({ ...op, success: true }); // Delay giữa các thao tác await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { errors.push({ ...op, error: error.message }); } } console.log(`✅ Thành công: ${results.length}`); console.log(`❌ Thất bại: ${errors.length}`); return { results, errors }; } // Sử dụng await bulkMemberManagement('group-id', [ { action: 'add', userId: 'new-member-1' }, { action: 'add', userId: 'new-member-2' }, { action: 'remove', userId: 'old-member-1', reason: 'Spam' } ]); ``` ### 5. Mời Vào Nhiều Nhóm ```typescript await api.inviteUserToGroups('user-id', ['group-1', 'group-2', 'group-3']); console.log('✅ Đã mời user tham gia nhiều nhóm'); ``` ## Quản Lý Quyền Admin ### 1. Thêm Deputy (Phó Admin) ```typescript await api.addGroupDeputy('group-id', 'user-id'); console.log('👑 Đã thêm deputy cho nhóm'); ``` ### 2. Xóa Deputy ```typescript await api.removeGroupDeputy('group-id', 'user-id'); console.log('✅ Đã xóa deputy khỏi nhóm'); ``` ### 3. Chuyển Quyền Owner ```typescript await api.changeGroupOwner('group-id', 'new-owner-id'); console.log('👑 Đã chuyển quyền owner nhóm'); ``` ### 4. Quản Lý Quyền Nâng Cao ```typescript class GroupPermissionManager { private api: any; constructor(api: any) { this.api = api; } async promoteMembers(groupId: string, userIds: string[]) { const results = []; for (const userId of userIds) { try { await this.api.addGroupDeputy(groupId, userId); results.push({ userId, success: true }); console.log(`👑 Đã thăng ${userId} làm deputy`); } catch (error) { results.push({ userId, success: false, error: error.message }); } await new Promise(resolve => setTimeout(resolve, 1000)); } return results; } async demoteMembers(groupId: string, userIds: string[]) { const results = []; for (const userId of userIds) { try { await this.api.removeGroupDeputy(groupId, userId); results.push({ userId, success: true }); console.log(`👤 Đã hạ ${userId} xuống thành viên`); } catch (error) { results.push({ userId, success: false, error: error.message }); } await new Promise(resolve => setTimeout(resolve, 1000)); } return results; } async getAdminList(groupId: string) { const groupInfo = await this.api.getGroupInfo(groupId); const members = await this.api.getGroupMembersInfo(groupId); const admins = members.data.filter((m: any) => m.role === 'admin'); const owner = members.data.find((m: any) => m.userId === groupInfo.data.creatorId); return { owner, admins }; } } // Sử dụng const permManager = new GroupPermissionManager(api); await permManager.promoteMembers('group-id', ['user-1', 'user-2']); ``` ## Thay Đổi Thông Tin Nhóm ### 1. Đổi Tên Nhóm ```typescript await api.changeGroupName('group-id', 'Tên Nhóm Mới'); console.log('✅ Đã đổi tên nhóm'); ``` ### 2. Thay Avatar Nhóm ```typescript // Từ file local await api.changeGroupAvatar('group-id', '/path/to/avatar.jpg'); // Từ URL (cần download trước) async function changeGroupAvatarFromUrl(groupId: string, imageUrl: string) { try { // Download image const response = await fetch(imageUrl); const buffer = await response.buffer(); // Lưu tạm const tempPath = './temp-avatar.jpg'; require('fs').writeFileSync(tempPath, buffer); // Thay avatar await api.changeGroupAvatar(groupId, tempPath); // Xóa file tạm require('fs').unlinkSync(tempPath); console.log('✅ Đã thay avatar nhóm từ URL'); } catch (error) { console.error('❌ Lỗi thay avatar từ URL:', error.message); } } ``` ### 3. Xóa Avatar ```typescript await api.deleteAvatarList('group-id'); console.log('🗑️ Đã xóa avatar nhóm'); ``` ## Cài Đặt Nhóm ### 1. Cài Đặt Chung ```typescript import { UpdateGroupSettingsOptions } from 'zalo-personal-sdk'; const settings: UpdateGroupSettingsOptions = { allowMemberInvite: true, // Cho phép thành viên mời người khác allowMemberCreatePoll: true, // Cho phép thành viên tạo poll allowMemberPin: false, // Không cho phép thành viên pin tin nhắn requireAdminApprove: true // Cần admin duyệt thành viên mới }; await api.updateGroupSettings('group-id', settings); console.log('⚙️ Đã cập nhật cài đặt nhóm'); ``` ### 2. Link Tham Gia Nhóm ```typescript // Bật link tham gia await api.enableGroupLink('group-id'); console.log('🔗 Đã bật link tham gia nhóm'); // Lấy thông tin link const linkInfo = await api.getGroupLinkInfo('group-id'); console.log(`Link tham gia: ${linkInfo.data.linkJoin}`); // Tắt link tham gia await api.disableGroupLink('group-id'); console.log('🔗 Đã tắt link tham gia nhóm'); ``` ### 3. Tự Động Xóa Tin Nhắn ```typescript import { ChatTTL } from 'zalo-personal-sdk'; // Cài đặt tự động xóa tin nhắn sau 1 ngày await api.updateAutoDeleteChat('group-id', ChatTTL.OneDay); console.log('🗑️ Đã bật tự động xóa tin nhắn sau 1 ngày'); // Tắt tự động xóa await api.updateAutoDeleteChat('group-id', ChatTTL.Off); console.log('✅ Đã tắt tự động xóa tin nhắn'); ``` ## Tạo Nội Dung Nhóm ### 1. Tạo Poll/Bình Chọn ```typescript import { CreatePollOptions } from 'zalo-personal-sdk'; const pollOptions: CreatePollOptions = { question: 'Chúng ta nên họp vào thời gian nào?', options: [ '9:00 AM', '2:00 PM', '7:00 PM', 'Thứ 7 tuần sau' ], multipleChoice: false, // Chỉ cho chọn 1 đáp án anonymous: false // Không ẩn danh }; const poll = await api.createPoll(pollOptions, 'group-id'); console.log('📊 Đã tạo poll:', poll.data.pollId); // Khóa poll await api.lockPoll(poll.data.pollId); console.log('🔒 Đã khóa poll'); // Lấy chi tiết poll const pollDetail = await api.getPollDetail(poll.data.pollId); console.log('Poll results:', pollDetail.data); ``` ### 2. Tạo Note Nhóm ```typescript import { CreateNoteGroupOptions } from 'zalo-personal-sdk'; const noteOptions: CreateNoteGroupOptions = { title: 'Thông Báo Quan Trọng', content: 'Nội dung ghi chú cho nhóm...', color: '#FF5722' // Màu nền }; const note = await api.createNoteGroup(noteOptions, 'group-id'); console.log('📝 Đã tạo note nhóm'); // Chỉnh sửa note const editOptions: EditNoteGroupOptions = { title: 'Thông Báo Đã Cập Nhật', content: 'Nội dung mới...' }; await api.editNoteGroup(note.data.noteId, editOptions); console.log('✅ Đã cập nhật note'); ``` ### 3. Tạo Reminder Nhóm ```typescript import { CreateReminderOptions, ReminderRepeatMode } from 'zalo-personal-sdk'; const reminderOptions: CreateReminderOptions = { title: 'Họp Team Weekly', content: 'Họp review công việc tuần', time: new Date('2024-12-31T15:00:00'), repeatMode: ReminderRepeatMode.Weekly, members: ['member-1', 'member-2'] // Reminder cho những thành viên này }; const reminder = await api.createReminder(reminderOptions, 'group-id'); console.log('⏰ Đã tạo reminder'); // Lấy danh sách reminders const reminders = await api.getListReminder('group-id', { limit: 10, offset: 0 }); console.log(`📅 Có ${reminders.data.length} reminders:`); reminders.data.forEach(r => { console.log(` - ${r.title}: ${new Date(r.time).toLocaleString()}`); }); ``` ## Quản Lý Nội Dung ### 1. Lấy Danh Sách Board Items ```typescript import { BoardType } from 'zalo-personal-sdk'; // Lấy tất cả board items const boards = await api.getListBoard('group-id', { type: BoardType.Note, // Chỉ lấy notes limit: 20 }); console.log(`📋 Có ${boards.data.length} board items:`); boards.data.forEach(item => { console.log(` - ${item.title} (${item.type})`); }); ``` ### 2. Quản Lý Tin Nhắn Ghim ```typescript // Pin tin nhắn await api.setPinnedConversations(['message-id-1', 'message-id-2']); console.log('📌 Đã pin tin nhắn'); // Lấy danh sách tin nhắn đã pin const pinned = await api.getPinConversations(); console.log('📌 Tin nhắn đã pin:', pinned.data); ``` ## Xóa Nhóm ### 1. Giải Tán Nhóm (Owner Only) ```typescript try { await api.disperseGroup('group-id'); console.log('💥 Đã giải tán nhóm'); } catch (error) { console.error('❌ Lỗi giải tán nhóm:', error.message); // Chỉ owner mới có thể giải tán nhóm } ``` ### 2. Rời Nhóm An Toàn ```typescript async function leaveGroupSafely(groupId: string) { try { // Kiểm tra xem mình có phải owner không const groupInfo = await api.getGroupInfo(groupId); const myId = await api.getOwnId(); if (groupInfo.data.creatorId === myId) { console.log('⚠️ Bạn là owner nhóm này.'); // Lấy danh sách admin khác const members = await api.getGroupMembersInfo(groupId); const otherAdmins = members.data.filter(m => m.role === 'admin' && m.userId !== myId ); if (otherAdmins.length > 0) { // Chuyển quyền owner cho admin khác await api.changeGroupOwner(groupId, otherAdmins[0].userId); console.log(`👑 Đã chuyển quyền owner cho ${otherAdmins[0].displayName}`); } else { console.log('⚠️ Không có admin khác, nhóm sẽ bị giải tán nếu bạn rời.'); return false; } } // Rời nhóm await api.leaveGroup(groupId); console.log('✅ Đã rời nhóm an toàn'); return true; } catch (error) { console.error('❌ Lỗi rời nhóm:', error.message); return false; } } ``` ## Thống Kê và Báo Cáo ### 1. Thống Kê Nhóm ```typescript async function getGroupStats(groupId: string) { try { const groupInfo = await api.getGroupInfo(groupId); const members = await api.getGroupMembersInfo(groupId); const boards = await api.getListBoard(groupId); const stats = { name: groupInfo.data.name, totalMembers: members.data.length, admins: members.data.filter(m => m.role === 'admin').length, regularMembers: members.data.filter(m => m.role !== 'admin').length, notes: boards.data.filter(b => b.type === BoardType.Note).length, polls: boards.data.filter(b => b.type === BoardType.Poll).length, pinnedMessages: boards.data.filter(b => b.type === BoardType.PinnedMessage).length, hasLink: !!groupInfo.data.linkJoin, createdDate: new Date(groupInfo.data.createdTime).toLocaleDateString() }; console.log('📊 Thống kê nhóm:'); console.log(` Tên: ${stats.name}`); console.log(` Tổng thành viên: ${stats.totalMembers}`); console.log(` Admin: ${stats.admins} | Thành viên: ${stats.regularMembers}`); console.log(` Nội dung: ${stats.notes} notes, ${stats.polls} polls`); console.log(` Link tham gia: ${stats.hasLink ? 'Có' : 'Không'}`); console.log(` Ngày tạo: ${stats.createdDate}`); return stats; } catch (error) { console.error('❌ Lỗi lấy thống kê:', error.message); return null; } } ``` ### 2. Backup Thông Tin Nhóm ```typescript import fs from 'fs/promises'; async function backupGroup(groupId: string, backupDir: string = './backup') { try { console.log('💾 Đang backup nhóm...'); const groupInfo = await api.getGroupInfo(groupId); const members = await api.getGroupMembersInfo(groupId); const boards = await api.getListBoard(groupId); const reminders = await api.getListReminder(groupId); const backup = { timestamp: new Date().toISOString(), groupInfo: groupInfo.data, members: members.data, boards: boards.data, reminders: reminders.data }; // Tạo thư mục backup nếu chưa có await fs.mkdir(backupDir, { recursive: true }); const filename = `${groupInfo.data.name.replace(/[^a-zA-Z0-9]/g, '_')}_${Date.now()}.json`; const filepath = `${backupDir}/${filename}`; await fs.writeFile(filepath, JSON.stringify(backup, null, 2)); console.log(`✅ Đã backup nhóm "${groupInfo.data.name}" vào ${filepath}`); return filepath; } catch (error) { console.error('❌ Lỗi backup nhóm:', error.message); throw error; } } ``` ## Utility Classes ### 1. Group Manager ```typescript class GroupManager { private api: any; constructor(api: any) { this.api = api; } async createGroupFromTemplate(template: { name: string; description?: string; members: string[]; settings?: any; avatar?: string; initialNote?: string; }) { try { // Tạo nhóm const group = await this.api.createGroup({ name: template.name, members: template.members }); const groupId = group.data.groupId; console.log(`✅ Đã tạo nhóm "${template.name}"`); // Thêm avatar nếu có if (template.avatar) { await this.api.changeGroupAvatar(groupId, template.avatar); console.log('🖼️ Đã thêm avatar'); } // Cài đặt nhóm nếu có if (template.settings) { await this.api.updateGroupSettings(groupId, template.settings); console.log('⚙️ Đã cập nhật cài đặt'); } // Tạo note đầu tiên nếu có if (template.initialNote) { await this.api.createNoteGroup({ title: 'Chào mừng!', content: template.initialNote }, groupId); console.log('📝 Đã tạo note chào mừng'); } return groupId; } catch (error) { console.error('❌ Lỗi tạo nhóm từ template:', error.message); throw error; } } async cloneGroup(sourceGroupId: string, newName: string, includeMembers: boolean = true) { try { const sourceInfo = await this.api.getGroupInfo(sourceGroupId); let members = []; if (includeMembers) { const sourceMembers = await this.api.getGroupMembersInfo(sourceGroupId); members = sourceMembers.data .filter((m: any) => m.role !== 'owner') .map((m: any) => m.userId); } const newGroup = await this.api.createGroup({ name: newName, members }); console.log(`✅ Đã clone nhóm "${sourceInfo.data.name}" thành "${newName}"`); return newGroup.data.groupId; } catch (error) { console.error('❌ Lỗi clone nhóm:', error.message); throw error; } } async massInvite(groupId: string, userIds: string[], batchSize: number = 5) { const batches = []; for (let i = 0; i < userIds.length; i += batchSize) { batches.push(userIds.slice(i, i + batchSize)); } for (const [index, batch] of batches.entries()) { try { console.log(`Mời batch ${index + 1}/${batches.length} (${batch.length} người)...`); await this.api.addUserToGroup(groupId, batch); // Delay giữa các batch if (index < batches.length - 1) { await new Promise(resolve => setTimeout(resolve, 3000)); } } catch (error) { console.error(`❌ Lỗi mời batch ${index + 1}:`, error.message); } } } } // Sử dụng const groupManager = new GroupManager(api); const groupId = await groupManager.createGroupFromTemplate({ name: 'Nhóm Dự Án ABC', members: ['dev-1', 'dev-2', 'pm-1'], avatar: './project-avatar.jpg', initialNote: 'Chào mừng các bạn tham gia dự án ABC! 🎉', settings: { allowMemberInvite: false, requireAdminApprove: true } }); ``` ## Best Practices ### 1. Rate Limiting cho Group Operations ```typescript class GroupOperationLimiter { private operations: Array<() => Promise<any>> = []; private isProcessing = false; private readonly delay = 2000; // 2 giây delay async addOperation<T>(operation: () => Promise<T>): Promise<T> { return new Promise((resolve, reject) => { this.operations.push(async () => { try { const result = await operation(); resolve(result); } catch (error) { reject(error); } }); this.processQueue(); }); } private async processQueue() { if (this.isProcessing) return; this.isProcessing = true; while (this.operations.length > 0) { const operation = this.operations.shift()!; await operation(); if (this.operations.length > 0) { await new Promise(resolve => setTimeout(resolve, this.delay)); } } this.isProcessing = false; } } ``` ### 2. Group Health Check ```typescript async function healthCheckGroup(groupId: string) { const issues = []; try { const groupInfo = await api.getGroupInfo(groupId); const members = await api.getGroupMembersInfo(groupId); // Kiểm tra số lượng admin const admins = members.data.filter(m => m.role === 'admin'); if (admins.length < 2) { issues.push('⚠️ Nhóm nên có ít nhất 2 admin'); } // Kiểm tra owner còn trong nhóm không const owner = members.data.find(m => m.userId === groupInfo.data.creatorId); if (!owner) { issues.push('❌ Owner không còn trong nhóm'); } // Kiểm tra link tham gia if (!groupInfo.data.linkJoin) { issues.push('💡 Có thể bật link tham gia để dễ mời thêm thành viên'); } console.log(`🔍 Health check nhóm "${groupInfo.data.name}":`) if (issues.length === 0) { console.log('✅ Nhóm hoạt động tốt!'); } else { issues.forEach(issue => console.log(issue)); } return issues; } catch (error) { console.error('❌ Lỗi health check:', error.message); return ['❌ Không thể kiểm tra trạng thái nhóm']; } } ``` ## Lưu Ý Quan Trọng 1. **Quyền Hạn**: Một số thao tác chỉ admin/owner mới thực hiện được 2. **Rate Limiting**: Tránh thực hiện quá nhiều thao tác liên tiếp 3. **Member Limits**: Zalo có giới hạn số lượng thành viên trong nhóm 4. **Data Backup**: Thường xuyên backup thông tin nhóm quan trọng 5. **Privacy**: Respectful với privacy của thành viên 6. **Spam Prevention**: Không spam invite hoặc tạo quá nhiều nhóm 7. **Error Handling**: Luôn xử lý lỗi và có fallback strategy 8. **Monitoring**: Monitor health và activity của các nhóm quan trọng