@warriorteam/zalo-personal
Version:
Unofficial Zalo Personal API for JavaScript - A powerful library for interacting with Zalo personal accounts with URL attachment support
711 lines (568 loc) • 21.7 kB
Markdown
# Quản Lý Bạn Bè (Friend Management)
## Tổng Quan
SDK cung cấp đầy đủ API để quản lý bạn bè trên Zalo:
- Lấy danh sách bạn bè
- Tìm kiếm người dùng
- Gửi lời mời kết bạn
- Chấp nhận/từ chối lời mời
- Xóa bạn bè
- Quản lý biệt danh
- Chặn/bỏ chặn người dùng
## Lấy Thông Tin Bạn Bè
### 1. Danh Sách Tất Cả Bạn Bè
```typescript
try {
const friends = await api.getAllFriends();
console.log('Tổng số bạn bè:', friends.data.friends.length);
friends.data.friends.forEach(friend => {
console.log(`- ${friend.displayName} (${friend.userId})`);
});
} catch (error) {
console.error('Lỗi lấy danh sách bạn bè:', error.message);
}
```
### 2. Thông Tin Chi Tiết Người Dùng
```typescript
// Lấy thông tin một hoặc nhiều user
const userIds = ['user-id-1', 'user-id-2'];
const userInfo = await api.getUserInfo(userIds);
userInfo.data.forEach(user => {
console.log(`Tên: ${user.displayName}`);
console.log(`Avatar: ${user.avatar}`);
console.log(`Giới tính: ${user.gender === 0 ? 'Nam' : 'Nữ'}`);
console.log(`Trạng thái: ${user.isOnline ? 'Online' : 'Offline'}`);
});
```
### 3. Thông Tin Tài Khoản Của Mình
```typescript
// Lấy ID của chính mình
const myId = await api.getOwnId();
console.log('ID của tôi:', myId);
// Lấy thông tin chi tiết tài khoản
const accountInfo = await api.fetchAccountInfo();
console.log('Thông tin tài khoản:', accountInfo);
```
## Tìm Kiếm Người Dùng
### 1. Tìm Kiếm Cơ Bản
```typescript
const searchResults = await api.findUser('Nguyễn Văn A');
console.log(`Tìm thấy ${searchResults.data.length} kết quả:`);
searchResults.data.forEach(user => {
console.log(`- ${user.displayName} (ID: ${user.userId})`);
console.log(` Avatar: ${user.avatar}`);
console.log(` Có thể kết bạn: ${user.canAddFriend ? 'Có' : 'Không'}`);
});
```
### 2. Tìm Kiếm Nâng Cao
```typescript
async function advancedUserSearch(query: string, options: {
maxResults?: number;
filterOnline?: boolean;
filterCanAddFriend?: boolean;
} = {}) {
try {
const results = await api.findUser(query);
let filteredResults = results.data;
// Lọc theo trạng thái online
if (options.filterOnline) {
filteredResults = filteredResults.filter(user => user.isOnline);
}
// Lọc theo khả năng kết bạn
if (options.filterCanAddFriend) {
filteredResults = filteredResults.filter(user => user.canAddFriend);
}
// Giới hạn số kết quả
if (options.maxResults) {
filteredResults = filteredResults.slice(0, options.maxResults);
}
return filteredResults;
} catch (error) {
console.error('Lỗi tìm kiếm:', error.message);
return [];
}
}
// Sử dụng
const users = await advancedUserSearch('John', {
maxResults: 10,
filterCanAddFriend: true
});
```
## Gửi Lời Mời Kết Bạn
### 1. Gửi Lời Mời Cơ Bản
```typescript
try {
const result = await api.sendFriendRequest('user-id', 'Xin chào! Kết bạn nhé!');
console.log('✅ Đã gửi lời mời kết bạn');
} catch (error) {
console.error('❌ Lỗi gửi lời mời:', error.message);
}
```
### 2. Gửi Lời Mời Hàng Loạt
```typescript
async function sendBulkFriendRequests(userIds: string[], message: string = 'Xin chào!') {
const results = [];
const errors = [];
for (const [index, userId] of userIds.entries()) {
try {
console.log(`Gửi lời mời ${index + 1}/${userIds.length} cho ${userId}...`);
const result = await api.sendFriendRequest(userId, message);
results.push({ userId, success: true });
// Delay giữa các requests để tránh spam
if (index < userIds.length - 1) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
} catch (error) {
errors.push({ userId, error: error.message });
}
}
console.log(`✅ Gửi thành công: ${results.length}`);
console.log(`❌ Gửi thất bại: ${errors.length}`);
return { results, errors };
}
```
### 3. Hủy Lời Mời Đã Gửi
```typescript
// Lấy danh sách lời mời đã gửi
const sentRequests = await api.getSentFriendRequest();
console.log('Lời mời đã gửi:', sentRequests.data.length);
// Hủy lời mời cụ thể
await api.undoFriendRequest('user-id');
console.log('✅ Đã hủy lời mời kết bạn');
```
## Xử Lý Lời Mời Nhận Được
### 1. Lấy Danh Sách Lời Mời
```typescript
const receivedRequests = await api.getReceivedFriendRequests();
console.log(`Có ${receivedRequests.data.length} lời mời kết bạn:`);
receivedRequests.data.forEach(request => {
console.log(`- Từ: ${request.displayName} (${request.userId})`);
console.log(` Lời nhắn: ${request.message || 'Không có'}`);
console.log(` Thời gian: ${new Date(request.timestamp).toLocaleString()}`);
});
```
### 2. Chấp Nhận Lời Mời
```typescript
// Chấp nhận lời mời cụ thể
await api.acceptFriendRequest('user-id');
console.log('✅ Đã chấp nhận lời mời kết bạn');
```
### 3. Xử Lý Lời Mời Tự Động
```typescript
async function autoProcessFriendRequests(options: {
autoAccept?: boolean;
blacklist?: string[];
whitelist?: string[];
maxRequestsPerDay?: number;
}) {
try {
const requests = await api.getReceivedFriendRequests();
let processedCount = 0;
for (const request of requests.data) {
// Kiểm tra blacklist
if (options.blacklist?.includes(request.userId)) {
console.log(`🚫 Bỏ qua ${request.displayName} (trong blacklist)`);
continue;
}
// Kiểm tra whitelist
if (options.whitelist && !options.whitelist.includes(request.userId)) {
console.log(`⏭️ Bỏ qua ${request.displayName} (không trong whitelist)`);
continue;
}
// Kiểm tra giới hạn số lượng
if (options.maxRequestsPerDay && processedCount >= options.maxRequestsPerDay) {
console.log('⏹️ Đã đạt giới hạn xử lý cho ngày hôm nay');
break;
}
if (options.autoAccept) {
await api.acceptFriendRequest(request.userId);
console.log(`✅ Đã chấp nhận kết bạn với ${request.displayName}`);
processedCount++;
// Delay để tránh spam
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
console.log(`🎉 Đã xử lý ${processedCount} lời mời kết bạn`);
} catch (error) {
console.error('❌ Lỗi xử lý lời mời:', error.message);
}
}
// Sử dụng
await autoProcessFriendRequests({
autoAccept: true,
maxRequestsPerDay: 10,
blacklist: ['spam-user-id-1', 'spam-user-id-2']
});
```
## Quản Lý Bạn Bè
### 1. Xóa Bạn Bè
```typescript
try {
await api.removeFriend('user-id');
console.log('✅ Đã xóa bạn bè');
} catch (error) {
console.error('❌ Lỗi xóa bạn bè:', error.message);
}
```
### 2. Quản Lý Biệt Danh
```typescript
// Đặt biệt danh cho bạn bè
await api.changeFriendAlias('user-id', 'Biệt danh mới');
console.log('✅ Đã đổi biệt danh');
// Xóa biệt danh
await api.removeFriendAlias('user-id');
console.log('✅ Đã xóa biệt danh');
// Lấy danh sách biệt danh
const aliasList = await api.getAliasList();
aliasList.data.forEach(alias => {
console.log(`${alias.displayName}: ${alias.alias}`);
});
```
### 3. Chặn và Bỏ Chặn
```typescript
// Chặn người dùng
await api.blockUser('user-id');
console.log('🚫 Đã chặn người dùng');
// Bỏ chặn
await api.unblockUser('user-id');
console.log('✅ Đã bỏ chặn người dùng');
// Chặn xem feed (không hiển thị bài viết của họ)
await api.blockViewFeed('user-id');
console.log('🚫 Đã chặn xem feed');
```
## Trạng Thái Online
### 1. Kiểm Tra Trạng Thái
```typescript
// Kiểm tra lần cuối online
const lastOnline = await api.lastOnline(['user-id-1', 'user-id-2']);
lastOnline.data.forEach(user => {
if (user.isOnline) {
console.log(`${user.userId}: Đang online`);
} else {
const lastSeen = new Date(user.lastOnlineTime);
console.log(`${user.userId}: Lần cuối ${lastSeen.toLocaleString()}`);
}
});
```
### 2. Monitor Online Status
```typescript
class OnlineStatusMonitor {
private monitoredUsers: Set<string> = new Set();
private statusCache: Map<string, boolean> = new Map();
private interval: NodeJS.Timeout | null = null;
addUser(userId: string) {
this.monitoredUsers.add(userId);
}
removeUser(userId: string) {
this.monitoredUsers.delete(userId);
this.statusCache.delete(userId);
}
async startMonitoring(intervalMs: number = 60000) {
this.interval = setInterval(async () => {
try {
const userIds = Array.from(this.monitoredUsers);
if (userIds.length === 0) return;
const statusData = await api.lastOnline(userIds);
statusData.data.forEach(user => {
const wasOnline = this.statusCache.get(user.userId);
const isOnline = user.isOnline;
if (wasOnline !== isOnline) {
console.log(`📱 ${user.userId} ${isOnline ? 'đã online' : 'đã offline'}`);
this.statusCache.set(user.userId, isOnline);
}
});
} catch (error) {
console.error('❌ Lỗi monitor online status:', error.message);
}
}, intervalMs);
}
stopMonitoring() {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
}
// Sử dụng
const monitor = new OnlineStatusMonitor();
monitor.addUser('friend-id-1');
monitor.addUser('friend-id-2');
monitor.startMonitoring(30000); // Check mỗi 30 giây
```
## Báo Cáo và Thống Kê
### 1. Thống Kê Bạn Bè
```typescript
async function getFriendStats() {
try {
const friends = await api.getAllFriends();
const friendList = friends.data.friends;
const stats = {
total: friendList.length,
online: 0,
male: 0,
female: 0,
withAvatar: 0,
canMessage: 0
};
// Lấy thông tin chi tiết để thống kê
const userIds = friendList.map(f => f.userId);
const detailInfo = await api.getUserInfo(userIds);
const onlineStatus = await api.lastOnline(userIds);
detailInfo.data.forEach(user => {
if (user.gender === 0) stats.male++;
else stats.female++;
if (user.avatar) stats.withAvatar++;
if (user.canMessage) stats.canMessage++;
});
onlineStatus.data.forEach(user => {
if (user.isOnline) stats.online++;
});
console.log('📊 Thống kê bạn bè:');
console.log(` Tổng số: ${stats.total}`);
console.log(` Online: ${stats.online} (${(stats.online/stats.total*100).toFixed(1)}%)`);
console.log(` Nam: ${stats.male} (${(stats.male/stats.total*100).toFixed(1)}%)`);
console.log(` Nữ: ${stats.female} (${(stats.female/stats.total*100).toFixed(1)}%)`);
console.log(` Có avatar: ${stats.withAvatar} (${(stats.withAvatar/stats.total*100).toFixed(1)}%)`);
console.log(` Có thể nhắn tin: ${stats.canMessage} (${(stats.canMessage/stats.total*100).toFixed(1)}%)`);
return stats;
} catch (error) {
console.error('❌ Lỗi lấy thống kê:', error.message);
return null;
}
}
await getFriendStats();
```
### 2. Tìm Bạn Chung
```typescript
async function findMutualFriends(userId: string) {
try {
// Lấy danh sách bạn bè của mình
const myFriends = await api.getAllFriends();
const myFriendIds = new Set(myFriends.data.friends.map(f => f.userId));
// Lấy thông tin user target (cần phương pháp khác vì API không public)
// Đây là ví dụ logic, cần implement phù hợp với API có sẵn
console.log(`🤝 Tìm bạn chung với user ${userId}`);
// Logic tìm bạn chung tùy thuộc vào API có sẵn
} catch (error) {
console.error('❌ Lỗi tìm bạn chung:', error.message);
}
}
```
## Backup và Restore
### 1. Backup Danh Sách Bạn Bè
```typescript
import fs from 'fs/promises';
async function backupFriends(filePath: string = './friends-backup.json') {
try {
console.log('💾 Đang backup danh sách bạn bè...');
const friends = await api.getAllFriends();
const friendIds = friends.data.friends.map(f => f.userId);
// Lấy thông tin chi tiết
const detailInfo = await api.getUserInfo(friendIds);
const aliases = await api.getAliasList();
const backup = {
timestamp: new Date().toISOString(),
totalFriends: friends.data.friends.length,
friends: detailInfo.data,
aliases: aliases.data
};
await fs.writeFile(filePath, JSON.stringify(backup, null, 2));
console.log(`✅ Đã backup ${backup.totalFriends} bạn bè vào ${filePath}`);
return backup;
} catch (error) {
console.error('❌ Lỗi backup:', error.message);
throw error;
}
}
// Sử dụng
await backupFriends('./backup/friends-' + new Date().toISOString().split('T')[0] + '.json');
```
### 2. So Sánh Thay Đổi
```typescript
async function compareFriendsList(backupPath: string) {
try {
const backupData = JSON.parse(await fs.readFile(backupPath, 'utf8'));
const currentFriends = await api.getAllFriends();
const backupIds = new Set(backupData.friends.map((f: any) => f.userId));
const currentIds = new Set(currentFriends.data.friends.map(f => f.userId));
const newFriends = [...currentIds].filter(id => !backupIds.has(id));
const removedFriends = [...backupIds].filter(id => !currentIds.has(id));
console.log('📊 So sánh thay đổi danh sách bạn bè:');
console.log(` Backup: ${backupData.totalFriends} bạn bè (${backupData.timestamp})`);
console.log(` Hiện tại: ${currentFriends.data.friends.length} bạn bè`);
console.log(` Bạn mới: ${newFriends.length}`);
console.log(` Bạn đã xóa: ${removedFriends.length}`);
if (newFriends.length > 0) {
console.log(' 👥 Bạn bè mới:');
const newFriendsInfo = await api.getUserInfo(newFriends);
newFriendsInfo.data.forEach(friend => {
console.log(` - ${friend.displayName} (${friend.userId})`);
});
}
if (removedFriends.length > 0) {
console.log(' 👋 Bạn bè đã xóa:');
removedFriends.forEach(userId => {
const friend = backupData.friends.find((f: any) => f.userId === userId);
console.log(` - ${friend?.displayName || 'Unknown'} (${userId})`);
});
}
return { newFriends, removedFriends };
} catch (error) {
console.error('❌ Lỗi so sánh:', error.message);
return null;
}
}
```
## Utility Classes
### 1. Friend Manager
```typescript
class FriendManager {
private api: any;
constructor(api: any) {
this.api = api;
}
async searchAndAdd(query: string, message: string = 'Xin chào!', maxResults: number = 5) {
try {
const results = await this.api.findUser(query);
const candidates = results.data
.filter((user: any) => user.canAddFriend)
.slice(0, maxResults);
console.log(`🔍 Tìm thấy ${candidates.length} ứng viên có thể kết bạn:`);
for (const user of candidates) {
console.log(` - ${user.displayName} (${user.userId})`);
try {
await this.api.sendFriendRequest(user.userId, message);
console.log(` ✅ Đã gửi lời mời`);
} catch (error) {
console.log(` ❌ Lỗi: ${error.message}`);
}
// Delay giữa các request
await new Promise(resolve => setTimeout(resolve, 2000));
}
} catch (error) {
console.error('❌ Lỗi tìm kiếm và kết bạn:', error.message);
}
}
async cleanupFriends(options: {
removeInactive?: boolean;
inactiveDays?: number;
removeNoAvatar?: boolean;
dryRun?: boolean;
} = {}) {
const friends = await this.api.getAllFriends();
const friendIds = friends.data.friends.map((f: any) => f.userId);
const detailInfo = await this.api.getUserInfo(friendIds);
const onlineStatus = await this.api.lastOnline(friendIds);
const toRemove = [];
for (const friend of detailInfo.data) {
const online = onlineStatus.data.find((u: any) => u.userId === friend.userId);
// Kiểm tra không hoạt động
if (options.removeInactive && online && !online.isOnline) {
const daysSinceOnline = (Date.now() - online.lastOnlineTime) / (1000 * 60 * 60 * 24);
if (daysSinceOnline > (options.inactiveDays || 30)) {
toRemove.push({ user: friend, reason: `Không online ${daysSinceOnline.toFixed(0)} ngày` });
}
}
// Kiểm tra không có avatar
if (options.removeNoAvatar && !friend.avatar) {
toRemove.push({ user: friend, reason: 'Không có avatar' });
}
}
console.log(`🧹 Sẽ xóa ${toRemove.length} bạn bè:`);
toRemove.forEach(item => {
console.log(` - ${item.user.displayName}: ${item.reason}`);
});
if (!options.dryRun) {
for (const item of toRemove) {
try {
await this.api.removeFriend(item.user.userId);
console.log(` ✅ Đã xóa ${item.user.displayName}`);
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.log(` ❌ Lỗi xóa ${item.user.displayName}: ${error.message}`);
}
}
} else {
console.log('🔍 Chế độ dry run - không thực hiện xóa');
}
}
}
// Sử dụng
const friendManager = new FriendManager(api);
await friendManager.searchAndAdd('developer', 'Chào bạn! Mình cũng là developer');
await friendManager.cleanupFriends({
removeInactive: true,
inactiveDays: 60,
dryRun: true
});
```
## Best Practices
### 1. Rate Limiting
```typescript
class FriendRequestLimiter {
private requestCount = 0;
private lastResetTime = Date.now();
private readonly maxRequestsPerHour = 10;
private readonly resetInterval = 60 * 60 * 1000; // 1 hour
canSendRequest(): boolean {
const now = Date.now();
// Reset counter nếu đã qua 1 giờ
if (now - this.lastResetTime >= this.resetInterval) {
this.requestCount = 0;
this.lastResetTime = now;
}
return this.requestCount < this.maxRequestsPerHour;
}
async sendFriendRequest(userId: string, message: string) {
if (!this.canSendRequest()) {
const timeUntilReset = this.resetInterval - (Date.now() - this.lastResetTime);
const minutesLeft = Math.ceil(timeUntilReset / (60 * 1000));
throw new Error(`Đã đạt giới hạn gửi lời mời. Thử lại sau ${minutesLeft} phút.`);
}
try {
await api.sendFriendRequest(userId, message);
this.requestCount++;
console.log(`✅ Đã gửi lời mời (${this.requestCount}/${this.maxRequestsPerHour})`);
} catch (error) {
throw error;
}
}
}
```
### 2. Error Handling
```typescript
async function safeFriendOperation<T>(
operation: () => Promise<T>,
operationName: string,
retries: number = 3
): Promise<T | null> {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const result = await operation();
return result;
} catch (error) {
console.error(`❌ ${operationName} thất bại (lần ${attempt}):`, error.message);
if (attempt === retries) {
console.error(`💥 ${operationName} thất bại sau ${retries} lần thử`);
return null;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
return null;
}
// Sử dụng
const friends = await safeFriendOperation(
() => api.getAllFriends(),
'Lấy danh sách bạn bè'
);
```
## Lưu Ý Quan Trọng
1. **Rate Limiting**: Giới hạn số lượng request để tránh bị chặn
2. **Privacy**: Một số thông tin có thể không accessible tùy thuộc privacy settings
3. **Permissions**: Cần quyền thích hợp để thực hiện các thao tác
4. **Spam Prevention**: Tránh gửi quá nhiều lời mời trong thời gian ngắn
5. **Data Backup**: Định kỳ backup danh sách bạn bè quan trọng
6. **Error Handling**: Luôn xử lý lỗi và có phương án retry
7. **User Experience**: Thông báo rõ ràng cho user về các thao tác đang thực hiện