This commit is contained in:
2026-05-10 22:03:32 +07:00
parent b857b73132
commit 530b39c749
246 changed files with 27965 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

@@ -0,0 +1,5 @@
{
"imei": "",
"userAgent": "",
"cookie": {}
}
+176
View File
@@ -0,0 +1,176 @@
/**
* Xem hướng dẫn sử dụng tại:
* @tutorial https://drive.google.com/file/d/1xuGHYCu-5H28er4bXZEFjTCSGKcOekhi/view?usp=sharing
*/
import fs from "node:fs";
import { createInterface } from "node:readline";
import { Zalo, type Credentials } from "../src/index.js";
import { decodeAES, decodeEventData } from "../src/utils.js";
const CREDENTIALS_PATH = "./test/credentials.json";
const SECRET_PATH = "./test/secret.test.txt";
let gotFresh = false;
const credentials: Credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8"));
let secretKey = await getSecretKey(),
cipher_key = "";
const rl = createInterface(process.stdin, process.stdout);
const cannotDecodeAlert = () =>
console.log(
"\n\x1b[31mKhông thể giải mã, đảm bảo bạn lấy dữ liệu từ đúng tài khoản và trình duyệt của tài khoản đã đăng nhập\x1b[0m\n",
);
const exit = () => {
console.log();
process.exit();
};
main();
async function main() {
console.log();
if (secretKey && !gotFresh) {
const shouldGetFreshKey = await promptForFreshKey();
if (shouldGetFreshKey) {
secretKey = await getFreshKey();
}
}
prompt();
}
function prompt() {
console.log();
rl.question(
"Bạn muốn giải mã hóa loại nào? \x1b[32m[1] HTTP(s)\x1b[0m, \x1b[38;5;165m[2] Websocket\x1b[0m, \x1b[38;5;15m[3] thoát?\x1b[0m: ",
(answer) => {
if (answer == "1") {
console.clear();
console.log("\x1b[32m[ HTTP(s) ] Bắt đầu khởi động!\x1b[0m");
decodeHTTP();
} else if (answer == "2") {
console.clear();
console.log("\x1b[38;5;165m[ WEBSOCKET ] Bắt đầu khởi động!\x1b[0m");
decodeWebsocket();
} else if (answer == "3") exit();
else {
prompt();
}
},
);
}
function decodeHTTP() {
rl.question("\n\x1b[32m[ HTTP(s) ] Dán đoạn dữ liệu bị mã hóa hoặc gõ \x1b[37mexit\x1b[0m \x1b[32mđể thoát:\x1b[0m\n\n", (encoded) => {
if (encoded.length == 0) return decodeHTTP();
if (encoded == "exit") return prompt();
if (encoded.startsWith('"') && encoded.endsWith('"')) encoded = JSON.parse(encoded);
try {
const decoded = decodeAES(secretKey, encoded);
if (decoded == null) {
cannotDecodeAlert();
return decodeHTTP();
}
const parsed = JSON.stringify(JSON.parse(decoded), null, 2);
console.log();
console.log("\x1b[32m=========== HTTP(s) ===========\x1b[0m");
console.log(parsed);
console.log("\x1b[32m===============================\x1b[0m");
console.log();
return decodeHTTP();
} catch {
cannotDecodeAlert();
return decodeHTTP();
}
});
}
async function decodeWebsocket() {
if (!cipher_key) {
console.log(
"Mở devtools trên trình duyệt tại https://chat.zalo.me/, vào tab Network -> (WS/Socket)\nMở tin nhắn đầu tiên, đổi sang định dạng UTF-8",
);
cipher_key = await promptForCipherKey();
}
rl.question(
"\n\x1b[38;5;165m[ WEBSOCKET ] Dán đoạn dữ liệu bị mã hóa hoặc gõ \x1b[37mexit\x1b[0m \x1b[38;5;165mđể thoát:\x1b[0m\n\n",
async (encoded) => {
if (encoded.length == 0) return decodeWebsocket();
if (encoded == "exit") return prompt();
try {
const decoded = await decodeEventData(JSON.parse(encoded), cipher_key);
if (decoded == null) {
cannotDecodeAlert();
return decodeWebsocket();
}
const parsed = JSON.stringify(decoded, null, 2);
console.log();
console.log("\x1b[38;5;165m=========== WEBSOCKET ===========\x1b[0m");
console.log(parsed);
console.log("\x1b[38;5;165m=================================\x1b[0m");
console.log();
return decodeWebsocket();
} catch {
cannotDecodeAlert();
return decodeWebsocket();
}
},
);
}
function promptForFreshKey() {
return new Promise<boolean>((resolve) => {
rl.question("\x1b[38;5;204m[ !? ] Tìm thấy khóa bí mật, bạn có muốn tái sử dụng? (y/n):\x1b[0m ", (answer) => {
if (answer.toLowerCase() == "y") resolve(false);
else if (answer.toLowerCase() == "n") resolve(true);
else resolve(promptForFreshKey());
});
});
}
async function getSecretKey(): Promise<string> {
if (!fs.existsSync(SECRET_PATH)) {
return await getFreshKey();
} else {
const localKey = fs.readFileSync(SECRET_PATH, "utf-8").trim();
if (!localKey) return await getFreshKey();
return localKey;
}
}
async function getFreshKey() {
gotFresh = true;
const zalo = new Zalo({
selfListen: true,
logging: true,
});
console.log();
const api = await zalo.login(credentials);
fs.writeFileSync(SECRET_PATH, api.getContext().secretKey, "utf-8");
return api.getContext().secretKey;
}
async function promptForCipherKey() {
return new Promise<string>((resolve) => {
rl.question("\n\x1b[38;5;81mSao chép khóa và dán vào đây:\x1b[0m ", (answer) => {
if (answer.length == 0) resolve(promptForCipherKey());
resolve(answer);
});
});
}
+100
View File
@@ -0,0 +1,100 @@
import path from "node:path";
import fs from "node:fs";
import { Reactions, Zalo } from "../src/index.js";
import { ThreadType } from "../src/models/index.js";
const zalo = new Zalo({
selfListen: true,
logging: true,
});
const api = await zalo.login(JSON.parse(fs.readFileSync(path.resolve("./test/credentials.json"), "utf-8")));
const { listener } = api;
listener.on("connected", () => {
console.log("Connected");
});
listener.on("closed", (code, reason) => {
console.log("Closed:", code, reason);
});
listener.on("error", (error) => {
console.error("Error:", error);
});
listener.on("message", (message) => {
console.log("Message:", message.threadId, message.data.content);
switch (message.type) {
case ThreadType.User:
api.addReaction(Reactions.HAHA, message).then(console.log);
if (!message.data.content || typeof message.data.content != "string") return;
if (!message.isSelf) {
switch (message.data.content) {
case "reply": {
api.sendMessage(
{
msg: "reply",
quote: message.data,
},
message.threadId,
message.type,
).then(console.log);
break;
}
case "ping": {
api.sendMessage("pong", message.threadId).then(console.log);
break;
}
default: {
const args = message.data.content.split(/\s+/);
if (args[0] == "sticker" && args[1]) {
api.getStickers(args[1]).then(async (stickerIds) => {
const random = stickerIds[Math.floor(Math.random() * stickerIds.length)];
const sticker = await api.getStickersDetail(random);
console.log("Sending sticker:", sticker[0]);
if (random) api.sendSticker(sticker[0], message.threadId).then(console.log);
else api.sendMessage("No sticker found", message.threadId).then(console.log);
});
}
break;
}
}
} else {
const args = message.data.content.split(/\s+/);
if (args[0] == "find" && args[1]) {
api.findUser(args[1]).then(console.log);
} else if (args[0] == "get") {
api.sendMessage(
{
msg: "hi",
attachments: [path.resolve("./test/a.png")],
},
message.threadId,
message.type,
).then(console.log);
}
}
break;
case ThreadType.Group:
if (!message.isSelf) {
switch (message.data.content) {
case "ping": {
api.sendMessage("pong", message.threadId, message.type).then(console.log);
break;
}
default:
break;
}
}
break;
default:
break;
}
});
listener.start();