UNPKG

@warriorteam/zalo-personal

Version:

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

827 lines (655 loc) 23.5 kB
# Tài Liệu Hướng Dẫn Sử Dụng Zalo Personal API ## Giới Thiệu **Zalo Personal API** là một thư viện JavaScript/TypeScript không chính thức cho phép tương tác với tài khoản Zalo cá nhân thông qua việc mô phỏng trình duyệt web. Thư viện cung cấp giao diện lập trình toàn diện để gửi tin nhắn, lắng nghe sự kiện, và quản lý các tính năng Zalo. > ⚠️ **CẢNH BÁO**: Đây là API không chính thức. Việc sử dụng có thể dẫn đến tài khoản bị khóa hoặc cấm. Sử dụng với trách nhiệm của bạn. ## Cài Đặt ```bash npm install zalo-personal # hoặc yarn add zalo-personal # hoặc bun install zalo-personal ``` ## Yêu Cầu Hệ Thống - Node.js >= 18.0.0 - TypeScript (khuyến nghị) ## Đăng Nhập - Hướng Dẫn Chi Tiết ### 1. Đăng Nhập Bằng QR Code (Khuyến Nghị) Đây là phương pháp đăng nhập đơn giản và an toàn nhất: ```typescript import { Zalo, LoginQRCallbackEventType } from "zalo-personal"; const zalo = new Zalo({ selfListen: true, // Lắng nghe tin nhắn từ chính mình logging: true // Bật log để debug }); // Đăng nhập đơn giản const api = await zalo.loginQR(); // Hoặc đăng nhập với tùy chọn nâng cao const api = await zalo.loginQR( { userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", language: "vi", qrPath: "./qr-code.png" // Đường dẫn lưu QR code }, (event) => { switch (event.type) { case LoginQRCallbackEventType.QRCodeGenerated: console.log("QR Code đã được tạo!"); console.log("Mã QR:", event.data.code); // Tự động lưu QR code event.actions.saveToFile("./my-qr.png"); break; case LoginQRCallbackEventType.QRCodeScanned: console.log("QR Code đã được quét bởi:", event.data.display_name); break; case LoginQRCallbackEventType.QRCodeExpired: console.log("QR Code đã hết hạn, đang tạo mới..."); event.actions.retry(); break; case LoginQRCallbackEventType.QRCodeDeclined: console.log("Đăng nhập bị từ chối"); break; case LoginQRCallbackEventType.GotLoginInfo: console.log("Đã lấy được thông tin đăng nhập!"); console.log("IMEI:", event.data.imei); console.log("User Agent:", event.data.userAgent); // Lưu thông tin này để đăng nhập lần sau break; } } ); console.log("Đăng nhập thành công!"); ``` ### 2. Đăng Nhập Bằng Credentials (Nâng Cao) Sử dụng thông tin đã lưu từ lần đăng nhập QR trước đó: ```typescript import { Zalo, type Credentials } from "zalo-personal"; // Thông tin credentials từ lần đăng nhập QR trước const credentials: Credentials = { imei: "your-imei-here", userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", language: "vi", cookie: [ // Mảng cookie từ lần đăng nhập trước { domain: "chat.zalo.me", name: "cookie_name", value: "cookie_value", // ... các thuộc tính khác } ] }; const zalo = new Zalo({ selfListen: true, logging: true }); try { const api = await zalo.login(credentials); console.log("Đăng nhập thành công với credentials!"); } catch (error) { console.error("Đăng nhập thất bại:", error); // Nếu credentials hết hạn, sử dụng QR code const api = await zalo.loginQR(); } ``` ### 3. Lưu Và Tái Sử Dụng Credentials ```typescript import fs from 'fs/promises'; // Lưu credentials sau khi đăng nhập QR thành công const api = await zalo.loginQR({}, (event) => { if (event.type === LoginQRCallbackEventType.GotLoginInfo) { const credentials = { imei: event.data.imei, userAgent: event.data.userAgent, cookie: event.data.cookie, language: "vi" }; // Lưu vào file fs.writeFile('credentials.json', JSON.stringify(credentials, null, 2)); console.log("Đã lưu credentials vào credentials.json"); } }); // Đọc và sử dụng credentials đã lưu async function loginWithSavedCredentials() { try { const credentialsData = await fs.readFile('credentials.json', 'utf8'); const credentials = JSON.parse(credentialsData); const api = await zalo.login(credentials); return api; } catch (error) { console.log("Không thể đăng nhập với credentials đã lưu, sử dụng QR code..."); return await zalo.loginQR(); } } ``` ## Cấu Hình Tùy Chọn ```typescript const zalo = new Zalo({ selfListen: true, // Lắng nghe tin nhắn từ chính mình logging: true, // Bật logging apiType: "web", // Loại API (mặc định: "web") apiVersion: "1.0", // Phiên bản API agent: proxyAgent // Proxy agent (tùy chọn) }); ``` ## Xử Lý Lỗi Đăng Nhập ```typescript import { ZaloApiError } from "zalo-personal"; async function safeLogin() { try { const api = await zalo.loginQR(); return api; } catch (error) { if (error instanceof ZaloApiError) { switch (error.message) { case "Đăng nhập thất bại": console.error("Lỗi xác thực, kiểm tra lại thông tin"); break; case "Unable to login with QRCode": console.error("Không thể đăng nhập bằng QR code"); break; default: console.error("Lỗi API:", error.message); } } else { console.error("Lỗi không xác định:", error); } throw error; } } ``` ## Lắng Nghe Sự Kiện Sau khi đăng nhập thành công, bạn có thể lắng nghe các sự kiện: ```typescript const { listener } = api; // Sự kiện kết nối listener.on("connected", () => { console.log("Đã kết nối WebSocket"); }); // Sự kiện ngắt kết nối listener.on("disconnected", (reason) => { console.log("Ngắt kết nối:", reason); }); // Sự kiện lỗi listener.on("error", (error) => { console.error("Lỗi listener:", error); }); // Bắt đầu lắng nghe listener.start(); ``` ## Gửi Tin Nhắn Cơ Bản ```typescript import { ThreadType } from "zalo-personal"; // Gửi tin nhắn văn bản đơn giản await api.sendMessage("Xin chào!", "user_id_or_group_id", ThreadType.User); // Gửi tin nhắn với tùy chọn nâng cao await api.sendMessage({ msg: "Tin nhắn với nhiều tính năng", quote: previousMessage, // Trả lời tin nhắn attachments: ["./image.jpg"], // Đính kèm file mentions: [{ uid: "user_id", pos: 0, len: 5 }] // Tag người dùng }, "thread_id", ThreadType.Group); ``` ## Lưu Ý Quan Trọng 1. **Giới hạn kết nối**: Chỉ có thể có một kết nối WebSocket cho mỗi tài khoản 2. **Bảo mật**: Không chia sẻ credentials với người khác 3. **Rate limiting**: Tránh gửi quá nhiều tin nhắn trong thời gian ngắn 4. **Cập nhật**: Thư viện có thể cần cập nhật khi Zalo thay đổi API ## Lắng Nghe Tin Nhắn ### Lắng Nghe Tin Nhắn Mới ```typescript import { ThreadType } from "zalo-personal"; api.listener.on("message", (message) => { const isPlainText = typeof message.data.content === "string"; console.log("Nhận tin nhắn từ:", message.data.dName); console.log("Nội dung:", message.data.content); console.log("Thread ID:", message.threadId); console.log("Là tin nhắn của mình:", message.isSelf); switch (message.type) { case ThreadType.User: if (isPlainText && !message.isSelf) { console.log("Tin nhắn riêng:", message.data.content); // Tự động trả lời api.sendMessage("Đã nhận tin nhắn của bạn!", message.threadId, ThreadType.User); } break; case ThreadType.Group: if (isPlainText && !message.isSelf) { console.log("Tin nhắn nhóm:", message.data.content); // Chỉ trả lời khi được tag if (message.data.content.includes("@bot")) { api.sendMessage("Xin chào nhóm!", message.threadId, ThreadType.Group); } } break; } }); ``` ### Lắng Nghe Các Sự Kiện Khác ```typescript // Lắng nghe reaction api.listener.on("reaction", (reaction) => { console.log("Có người react:", reaction.data.react); console.log("Tin nhắn được react:", reaction.data.msgId); }); // Lắng nghe typing api.listener.on("typing", (typing) => { console.log("Có người đang gõ:", typing.data.userId); }); // Lắng nghe tin nhắn đã xem api.listener.on("seen_messages", (seenMessages) => { seenMessages.forEach(seen => { console.log("Tin nhắn đã được xem:", seen.data.msgId); }); }); // Lắng nghe sự kiện nhóm api.listener.on("group_event", (groupEvent) => { console.log("Sự kiện nhóm:", groupEvent.type); console.log("Dữ liệu:", groupEvent.data); }); // Lắng nghe sự kiện bạn bè api.listener.on("friend_event", (friendEvent) => { console.log("Sự kiện bạn bè:", friendEvent.type); console.log("Dữ liệu:", friendEvent.data); }); ``` ## Gửi Tin Nhắn ### Gửi Tin Nhắn Văn Bản ```typescript // Gửi tin nhắn đơn giản await api.sendMessage("Xin chào!", "user_id", ThreadType.User); // Gửi tin nhắn với định dạng await api.sendMessage({ msg: "Tin nhắn có định dạng", styles: [ { type: "bold", start: 0, end: 8 }, // In đậm { type: "italic", start: 9, end: 15 } // In nghiêng ] }, "user_id", ThreadType.User); // Gửi tin nhắn khẩn cấp await api.sendMessage({ msg: "Tin nhắn khẩn cấp!", urgency: "high" }, "user_id", ThreadType.User); ``` ### Trả Lời Tin Nhắn ```typescript api.listener.on("message", async (message) => { if (!message.isSelf && typeof message.data.content === "string") { // Trả lời tin nhắn await api.sendMessage({ msg: "Đây là tin nhắn trả lời", quote: message // Trích dẫn tin nhắn gốc }, message.threadId, message.type); } }); ``` ### Gửi File Đính Kèm ```typescript // Gửi hình ảnh await api.sendMessage({ msg: "Gửi hình ảnh", attachments: ["./image.jpg", "./image2.png"] }, "user_id", ThreadType.User); // Gửi video await api.sendVideo({ filePath: "./video.mp4", message: "Video hay nhé!" }, "user_id", ThreadType.User); // Gửi voice message await api.sendVoice({ filePath: "./audio.mp3" }, "user_id", ThreadType.User); ``` ### Gửi Sticker ```typescript // Tìm sticker theo từ khóa const stickerIds = await api.getStickers("hello"); if (stickerIds.length > 0) { const stickerDetails = await api.getStickersDetail(stickerIds[0]); await api.sendSticker(stickerDetails[0], "user_id", ThreadType.User); } ``` ### Mention Người Dùng (Trong Nhóm) ```typescript await api.sendMessage({ msg: "@user Xin chào bạn!", mentions: [ { uid: "user_id", pos: 0, // Vị trí bắt đầu mention len: 5, // Độ dài mention type: 0 // Loại mention } ] }, "group_id", ThreadType.Group); ``` ## Quản Lý Bạn Bè và Nhóm ### Quản Lý Bạn Bè ```typescript // Lấy danh sách bạn bè const friends = await api.getAllFriends(); console.log("Danh sách bạn bè:", friends); // Tìm kiếm người dùng const searchResult = await api.findUser("tên hoặc số điện thoại"); console.log("Kết quả tìm kiếm:", searchResult); // Gửi lời mời kết bạn await api.sendFriendRequest("user_id", "Xin chào, kết bạn nhé!"); // Chấp nhận lời mời kết bạn await api.acceptFriendRequest("user_id"); // Xóa bạn await api.removeFriend("user_id"); // Chặn người dùng await api.blockUser("user_id"); // Bỏ chặn người dùng await api.unblockUser("user_id"); ``` ### Quản Lý Nhóm ```typescript // Lấy danh sách nhóm const groups = await api.getAllGroups(); console.log("Danh sách nhóm:", groups); // Tạo nhóm mới const newGroup = await api.createGroup({ name: "Tên nhóm mới", members: ["user_id_1", "user_id_2"] }); // Lấy thông tin nhóm const groupInfo = await api.getGroupInfo("group_id"); console.log("Thông tin nhóm:", groupInfo); // Thêm thành viên vào nhóm await api.addUserToGroup("user_id", "group_id"); // Xóa thành viên khỏi nhóm await api.removeUserFromGroup("user_id", "group_id"); // Đổi tên nhóm await api.changeGroupName("group_id", "Tên mới"); // Đổi avatar nhóm await api.changeGroupAvatar("group_id", "./avatar.jpg"); // Rời nhóm await api.leaveGroup("group_id"); // Giải tán nhóm (chỉ admin) await api.disperseGroup("group_id"); ``` ## Tính Năng Nâng Cao ### Reaction ```typescript import { Reactions } from "zalo-personal"; // Thêm reaction vào tin nhắn await api.addReaction(Reactions.LIKE, message); await api.addReaction(Reactions.LOVE, message); await api.addReaction(Reactions.HAHA, message); await api.addReaction(Reactions.WOW, message); await api.addReaction(Reactions.SAD, message); await api.addReaction(Reactions.ANGRY, message); // Reaction tùy chỉnh await api.addReaction({ icon: "👍", message: message }); ``` ### Quản Lý Tin Nhắn ```typescript // Xóa tin nhắn await api.deleteMessage("message_id", "thread_id", ThreadType.User); // Chuyển tiếp tin nhắn await api.forwardMessage({ message: message, targets: [ { threadId: "user_id_1", type: ThreadType.User }, { threadId: "group_id_1", type: ThreadType.Group } ] }); // Đánh dấu tin nhắn chưa đọc await api.addUnreadMark("thread_id", ThreadType.User); // Xóa đánh dấu chưa đọc await api.removeUnreadMark("thread_id", ThreadType.User); ``` ### Cài Đặt Tài Khoản ```typescript // Cập nhật thông tin cá nhân await api.updateProfile({ displayName: "Tên hiển thị mới", birthday: "1990-01-01", gender: 1 // 1: Nam, 2: Nữ }); // Đổi avatar await api.changeAccountAvatar("./new_avatar.jpg"); // Cập nhật ngôn ngữ await api.updateLang("en"); // "vi", "en", etc. ``` ### Tính Năng Khác ```typescript // Gửi typing indicator await api.sendTypingEvent("user_id", ThreadType.User); // Đánh dấu đã xem tin nhắn await api.sendSeenEvent("message_id", "thread_id", ThreadType.User); // Tạo poll trong nhóm await api.createPoll({ question: "Câu hỏi poll", options: ["Tùy chọn 1", "Tùy chọn 2", "Tùy chọn 3"], allowMultipleChoice: false }, "group_id"); // Tạo reminder await api.createReminder({ message: "Nhắc nhở", time: new Date(Date.now() + 3600000), // 1 giờ sau targets: ["user_id_1", "user_id_2"] }); ``` ## Xử Lý Lỗi và Debug ```typescript import { ZaloApiError } from "zalo-personal"; try { await api.sendMessage("Test", "invalid_user_id"); } catch (error) { if (error instanceof ZaloApiError) { console.error("Lỗi API Zalo:", error.message); // Xử lý các loại lỗi cụ thể switch (error.message) { case "Missing threadId": console.error("Thiếu ID thread"); break; case "Failed to encrypt message": console.error("Lỗi mã hóa tin nhắn"); break; default: console.error("Lỗi không xác định:", error.message); } } else { console.error("Lỗi hệ thống:", error); } } // Bật logging để debug const zalo = new Zalo({ logging: true, selfListen: true }); ``` ## Best Practices ### 1. Quản Lý Kết Nối ```typescript // Tự động kết nối lại khi bị ngắt api.listener.on("disconnected", (reason) => { console.log("Ngắt kết nối:", reason); // Kết nối lại sau 5 giây setTimeout(() => { api.listener.start({ retryOnClose: true }); }, 5000); }); // Giữ kết nối hoạt động setInterval(async () => { try { await api.keepAlive(); } catch (error) { console.error("Lỗi keep alive:", error); } }, 300000); // 5 phút ``` ### 2. Rate Limiting ```typescript class MessageQueue { private queue: Array<() => Promise<any>> = []; private processing = false; private delay = 1000; // 1 giây giữa các tin nhắn async add(fn: () => Promise<any>) { this.queue.push(fn); if (!this.processing) { this.process(); } } private async process() { this.processing = true; while (this.queue.length > 0) { const fn = this.queue.shift()!; try { await fn(); } catch (error) { console.error("Lỗi gửi tin nhắn:", error); } await new Promise(resolve => setTimeout(resolve, this.delay)); } this.processing = false; } } const messageQueue = new MessageQueue(); // Sử dụng queue để gửi tin nhắn messageQueue.add(() => api.sendMessage("Tin nhắn 1", "user_id")); messageQueue.add(() => api.sendMessage("Tin nhắn 2", "user_id")); ``` ### 3. Lưu Trữ Dữ Liệu ```typescript import fs from 'fs/promises'; class DataManager { private dataFile = './bot_data.json'; private data: any = {}; async load() { try { const content = await fs.readFile(this.dataFile, 'utf8'); this.data = JSON.parse(content); } catch (error) { this.data = {}; } } async save() { await fs.writeFile(this.dataFile, JSON.stringify(this.data, null, 2)); } get(key: string) { return this.data[key]; } set(key: string, value: any) { this.data[key] = value; this.save(); // Tự động lưu } } const dataManager = new DataManager(); await dataManager.load(); // Lưu thông tin người dùng api.listener.on("message", (message) => { const userData = dataManager.get(`user_${message.data.uidFrom}`) || {}; userData.lastMessage = message.data.content; userData.lastSeen = new Date().toISOString(); dataManager.set(`user_${message.data.uidFrom}`, userData); }); ``` ## Ví Dụ Chatbot Hoàn Chỉnh ```typescript import { Zalo, ThreadType, LoginQRCallbackEventType } from "zalo-personal"; class ZaloChatBot { private api: any; private commands: Map<string, (message: any, args: string[]) => Promise<void>>; constructor() { this.commands = new Map(); this.setupCommands(); } private setupCommands() { this.commands.set("ping", this.pingCommand.bind(this)); this.commands.set("time", this.timeCommand.bind(this)); this.commands.set("weather", this.weatherCommand.bind(this)); this.commands.set("help", this.helpCommand.bind(this)); } async start() { const zalo = new Zalo({ selfListen: false, logging: true }); // Đăng nhập this.api = await zalo.loginQR({}, (event) => { if (event.type === LoginQRCallbackEventType.QRCodeGenerated) { console.log("Quét QR code để đăng nhập bot"); event.actions.saveToFile("./bot-qr.png"); } }); // Thiết lập listener this.setupListeners(); console.log("Bot đã sẵn sàng!"); } private setupListeners() { this.api.listener.on("message", this.handleMessage.bind(this)); this.api.listener.on("connected", () => { console.log("Bot đã kết nối"); }); this.api.listener.on("error", (error: any) => { console.error("Lỗi bot:", error); }); this.api.listener.start({ retryOnClose: true }); } private async handleMessage(message: any) { if (message.isSelf || typeof message.data.content !== "string") return; const content = message.data.content.trim(); if (!content.startsWith("/")) return; const [command, ...args] = content.slice(1).split(" "); const commandHandler = this.commands.get(command.toLowerCase()); if (commandHandler) { try { await commandHandler(message, args); } catch (error) { console.error(`Lỗi command ${command}:`, error); await this.api.sendMessage( "Có lỗi xảy ra khi xử lý lệnh.", message.threadId, message.type ); } } } private async pingCommand(message: any) { await this.api.sendMessage("Pong! 🏓", message.threadId, message.type); } private async timeCommand(message: any) { const now = new Date().toLocaleString("vi-VN"); await this.api.sendMessage(`Thời gian hiện tại: ${now}`, message.threadId, message.type); } private async weatherCommand(message: any, args: string[]) { const city = args.join(" ") || "Hà Nội"; // Tích hợp API thời tiết thực tế ở đây await this.api.sendMessage( `Thời tiết tại ${city}: Nắng, 25°C ☀️`, message.threadId, message.type ); } private async helpCommand(message: any) { const helpText = ` 🤖 Danh sách lệnh: /ping - Kiểm tra bot /time - Xem thời gian /weather [thành phố] - Xem thời tiết /help - Hiển thị trợ giúp `.trim(); await this.api.sendMessage(helpText, message.threadId, message.type); } } // Khởi chạy bot const bot = new ZaloChatBot(); bot.start().catch(console.error); ``` ## Kết Luận Thư viện **Zalo Personal API** cung cấp một bộ công cụ mạnh mẽ để tương tác với Zalo. Với hướng dẫn chi tiết này, bạn có thể: 1. **Đăng nhập an toàn** bằng QR code hoặc credentials 2. **Lắng nghe và xử lý** tin nhắn, sự kiện 3. **Gửi tin nhắn đa dạng** với nhiều định dạng 4. **Quản lý bạn bè và nhóm** hiệu quả 5. **Xây dựng chatbot** hoàn chỉnh Hãy nhớ sử dụng thư viện một cách có trách nhiệm và tuân thủ các quy định của Zalo để tránh bị khóa tài khoản. --- 📚 **Tài liệu này được cập nhật thường xuyên. Hãy kiểm tra phiên bản mới nhất trên GitHub.**