@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
Markdown
# 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