zca-js is an unofficial JavaScript/TypeScript library designed to interact with the Zalo platform for personal accounts by simulating a web browser's interaction with Zalo Web. The current stable version is 2.1.2, and the project maintains an active release cadence, frequently publishing patches and minor updates to address bugs and introduce new API functionalities. It ships with TypeScript types, facilitating robust development. A key differentiator is its approach of simulating browser behavior, which allows access to features typically unavailable through official APIs. However, this method comes with a significant warning: using the library carries a risk of account suspension or banning, and the maintainers explicitly disclaim responsibility for such outcomes. Developers are advised to proceed with caution and consider using dedicated test accounts.
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
The Zalo class is the primary entry point for the API. ESM import is recommended given the package's `node` engine requirement and TypeScript types.
import { Zalo } from 'zca-js'
ThreadType is an enum used to distinguish between different conversation types (e.g., User, Group) when handling messages.
import { ThreadType } from 'zca-js'
Specific error classes like ZaloApiLoginQRAborted were introduced in v2.0.3 to provide clearer error handling for login operations.
import { ZaloApiLoginQRAborted } from 'zca-js'
Demonstrates how to log in with a QR code, set up a listener for incoming messages, and implement a basic echo bot that replies to direct and group messages received.
import { Zalo, ThreadType } from 'zca-js';
const zalo = new Zalo();
// For advanced image sending by file path, you might need to provide an imageMetadataGetter:
// import sharp from 'sharp';
// import fs from 'node:fs';
// async function imageMetadataGetter(filePath) {
// const data = await fs.promises.readFile(filePath);
// const metadata = await sharp(data).metadata();
// return {
// height: metadata.height,
// width: metadata.width,
// size: metadata.size || data.length,
// };
// }
// const zalo = new Zalo({ imageMetadataGetter });
async function runBot() {
const api = await zalo.loginQR(); // Authenticates via QR code displayed in the console.
console.log('Logged in successfully!');
api.listener.on('message', (message) => {
const isPlainText = typeof message.data.content === 'string';
if (message.isSelf || !isPlainText) return; // Ignore messages from self or non-plain text.
console.log(`Received message from ${message.threadId} (${message.type}): ${message.data.content}`);
const replyMessage = `echo: ${message.data.content}`;
const quoteData = message.data; // Optional: quote the original message.
switch (message.type) {
case ThreadType.User: {
api.sendMessage(
{ msg: replyMessage, quote: quoteData },
message.threadId,
message.type
);
console.log(`Sent echo to user ${message.threadId}`);
break;
}
case ThreadType.Group: {
api.sendMessage(
{ msg: replyMessage, quote: quoteData },
message.threadId,
message.type
);
console.log(`Sent echo to group ${message.threadId}`);
break;
}
}
});
api.listener.on('disconnected', (reason) => {
console.warn(`Listener disconnected: ${reason}. Attempting to restart...`);
// In a production environment, you might want to implement a robust retry mechanism.
// For this example, we just log.
});
api.listener.start();
console.log('Listener started. Waiting for messages...');
}
runBot().catch(console.error);
Upgrade
Version history
Breaking-change detection hasn't run for this library yet.
Audit
Security & dependencies
CVE tracking and dependency tree are planned for a later release.