zalo-agent-cli 1.0.27 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/package.json +1 -1
- package/src/commands/group.js +58 -0
- package/src/commands/listen.js +26 -1
package/README.md
CHANGED
|
@@ -221,6 +221,7 @@ zalo-agent msg send <threadId> "Hello World" --style 0:5:bold 6:5:italic
|
|
|
221
221
|
|---------|-------------|
|
|
222
222
|
| `group list` | List all groups |
|
|
223
223
|
| `group create <name> <memberIds...>` | Create group |
|
|
224
|
+
| `group history <groupId> [-n count]` | Get chat history (normalized JSON) |
|
|
224
225
|
| `group info <groupId>` | Group details |
|
|
225
226
|
| `group members <groupId>` | List members |
|
|
226
227
|
| `group add-member <groupId> <userIds...>` | Add members |
|
|
@@ -327,6 +328,29 @@ zalo-agent reminder remove <reminderId> <groupId> -t 1
|
|
|
327
328
|
| `account info` | Show active account |
|
|
328
329
|
| `account export [ownerId] [-o path]` | Export credentials for transfer |
|
|
329
330
|
|
|
331
|
+
#### Listener (`listen`)
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Listen for messages (default: message + friend events)
|
|
335
|
+
zalo-agent listen
|
|
336
|
+
|
|
337
|
+
# Filter: DM only, with webhook
|
|
338
|
+
zalo-agent listen -f user -w https://your-n8n.com/webhook/zalo
|
|
339
|
+
|
|
340
|
+
# Save messages locally as JSONL (one file per thread)
|
|
341
|
+
zalo-agent listen --save ./zalo-logs
|
|
342
|
+
|
|
343
|
+
# JSON mode for piping + save locally + webhook
|
|
344
|
+
zalo-agent --json listen --save ./zalo-logs -w https://webhook.url
|
|
345
|
+
|
|
346
|
+
# Auto-accept friend requests
|
|
347
|
+
zalo-agent listen --auto-accept
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Flags:** `-e` events (message,friend,group,reaction), `-f` filter (user,group,all), `-w` webhook URL, `--save <dir>` local JSONL storage, `--no-self` exclude own messages, `--auto-accept` auto-accept friend requests
|
|
351
|
+
|
|
352
|
+
**JSONL storage format:** One file per thread (`<threadId>.jsonl`), each line is a JSON object with event data + `savedAt` timestamp. Useful as workaround for chat history — Zalo API only returns ~20 recent messages.
|
|
353
|
+
|
|
330
354
|
### Multi-Account & Proxy
|
|
331
355
|
|
|
332
356
|
Each Zalo account can be bound to its own dedicated proxy (1:1 mapping).
|
package/package.json
CHANGED
package/src/commands/group.js
CHANGED
|
@@ -46,6 +46,64 @@ export function registerGroupCommands(program) {
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
group
|
|
50
|
+
.command("history <groupId>")
|
|
51
|
+
.description("Get group chat history (recent messages)")
|
|
52
|
+
.option("-n, --count <n>", "Number of messages to fetch", "20")
|
|
53
|
+
.action(async (groupId, opts) => {
|
|
54
|
+
try {
|
|
55
|
+
const result = await getApi().getGroupChatHistory(groupId, Number(opts.count));
|
|
56
|
+
const msgs = result?.groupMsgs || [];
|
|
57
|
+
|
|
58
|
+
// Normalize messages into clean, storage-friendly JSON
|
|
59
|
+
const normalized = msgs.map((m) => {
|
|
60
|
+
const d = m.data || m;
|
|
61
|
+
const content = typeof d.content === "string" ? d.content : d.content;
|
|
62
|
+
return {
|
|
63
|
+
msgId: d.msgId,
|
|
64
|
+
cliMsgId: d.cliMsgId,
|
|
65
|
+
fromUid: d.uidFrom,
|
|
66
|
+
groupId: d.idTo,
|
|
67
|
+
msgType: d.msgType,
|
|
68
|
+
content: typeof content === "string" ? content : content,
|
|
69
|
+
timestamp: Number(d.ts),
|
|
70
|
+
isoTime: new Date(Number(d.ts)).toISOString(),
|
|
71
|
+
isSelf: m.isSelf ?? false,
|
|
72
|
+
...(d.mentions && { mentions: d.mentions }),
|
|
73
|
+
...(d.quote && {
|
|
74
|
+
quote: {
|
|
75
|
+
ownerId: d.quote.ownerId,
|
|
76
|
+
msg: d.quote.msg,
|
|
77
|
+
msgId: d.quote.globalMsgId,
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const payload = {
|
|
84
|
+
groupId,
|
|
85
|
+
count: normalized.length,
|
|
86
|
+
hasMore: result?.more === 1,
|
|
87
|
+
messages: normalized,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
output(payload, program.opts().json, () => {
|
|
91
|
+
info(`${normalized.length} message(s) in group ${groupId}`);
|
|
92
|
+
if (result?.more === 1) info("(more messages available)");
|
|
93
|
+
console.log();
|
|
94
|
+
for (const m of normalized) {
|
|
95
|
+
const time = new Date(m.timestamp).toLocaleTimeString();
|
|
96
|
+
const dir = m.isSelf ? "→" : "←";
|
|
97
|
+
const text =
|
|
98
|
+
typeof m.content === "string" ? m.content.slice(0, 80) : `[${m.msgType || "attachment"}]`;
|
|
99
|
+
console.log(` ${time} ${dir} ${m.fromUid}: ${text}`);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch (e) {
|
|
103
|
+
error(`Get history failed: ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
49
107
|
group
|
|
50
108
|
.command("members <groupId>")
|
|
51
109
|
.description("List group members")
|
package/src/commands/listen.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Production-ready with auto-reconnect and re-login.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { appendFileSync, mkdirSync, existsSync } from "fs";
|
|
7
|
+
import { resolve, join } from "path";
|
|
6
8
|
import { getApi, autoLogin, clearSession } from "../core/zalo-client.js";
|
|
7
9
|
import { success, error, info, warning } from "../utils/output.js";
|
|
8
10
|
|
|
@@ -41,6 +43,7 @@ export function registerListenCommand(program) {
|
|
|
41
43
|
.option("-w, --webhook <url>", "POST each event as JSON to this URL (for n8n, Make, etc.)")
|
|
42
44
|
.option("--no-self", "Exclude self-sent messages")
|
|
43
45
|
.option("--auto-accept", "Auto-accept incoming friend requests")
|
|
46
|
+
.option("--save <dir>", "Save messages locally as JSONL files (one file per thread, e.g. --save ./zalo-logs)")
|
|
44
47
|
.action(async (opts) => {
|
|
45
48
|
const jsonMode = program.opts().json;
|
|
46
49
|
const startTime = Date.now();
|
|
@@ -65,7 +68,26 @@ export function registerListenCommand(program) {
|
|
|
65
68
|
}).catch(() => {});
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
// Setup save directory if --save flag provided
|
|
72
|
+
let saveDir = null;
|
|
73
|
+
if (opts.save) {
|
|
74
|
+
saveDir = resolve(opts.save);
|
|
75
|
+
if (!existsSync(saveDir)) mkdirSync(saveDir, { recursive: true });
|
|
76
|
+
info(`Saving messages to: ${saveDir}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Append event to JSONL file (one file per threadId) */
|
|
80
|
+
function saveEvent(data) {
|
|
81
|
+
if (!saveDir || !data.threadId) return;
|
|
82
|
+
const filename = `${data.threadId}.jsonl`;
|
|
83
|
+
const filepath = join(saveDir, filename);
|
|
84
|
+
const line = JSON.stringify({ ...data, savedAt: new Date().toISOString() }) + "\n";
|
|
85
|
+
try {
|
|
86
|
+
appendFileSync(filepath, line, "utf-8");
|
|
87
|
+
} catch {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Output event as JSON or human-readable, save locally, then post to webhook */
|
|
69
91
|
function emitEvent(data, humanMsg) {
|
|
70
92
|
eventCount++;
|
|
71
93
|
if (jsonMode) {
|
|
@@ -73,6 +95,7 @@ export function registerListenCommand(program) {
|
|
|
73
95
|
} else {
|
|
74
96
|
info(humanMsg);
|
|
75
97
|
}
|
|
98
|
+
saveEvent(data);
|
|
76
99
|
postWebhook(data);
|
|
77
100
|
}
|
|
78
101
|
|
|
@@ -224,6 +247,7 @@ export function registerListenCommand(program) {
|
|
|
224
247
|
info("Auto-reconnect enabled.");
|
|
225
248
|
if (opts.filter !== "all") info(`Message filter: ${opts.filter}`);
|
|
226
249
|
if (opts.webhook) info(`Webhook: ${opts.webhook}`);
|
|
250
|
+
if (saveDir) info(`Save dir: ${saveDir} (JSONL per thread)`);
|
|
227
251
|
if (opts.autoAccept) info("Auto-accept friend requests: ON");
|
|
228
252
|
} catch (e) {
|
|
229
253
|
error(`Listen failed: ${e.message}`);
|
|
@@ -237,6 +261,7 @@ export function registerListenCommand(program) {
|
|
|
237
261
|
getApi().listener.stop();
|
|
238
262
|
} catch {}
|
|
239
263
|
info(`Stopped. Uptime: ${uptime()}, events: ${eventCount}, reconnects: ${reconnectCount}`);
|
|
264
|
+
if (saveDir) info(`Messages saved to: ${saveDir}`);
|
|
240
265
|
resolve();
|
|
241
266
|
});
|
|
242
267
|
});
|