UNPKG

@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
# 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 (${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 :`); 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