zalo-agent-cli 1.0.28 → 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 CHANGED
@@ -328,6 +328,29 @@ zalo-agent reminder remove <reminderId> <groupId> -t 1
328
328
  | `account info` | Show active account |
329
329
  | `account export [ownerId] [-o path]` | Export credentials for transfer |
330
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
+
331
354
  ### Multi-Account & Proxy
332
355
 
333
356
  Each Zalo account can be bound to its own dedicated proxy (1:1 mapping).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zalo-agent-cli",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
4
4
  "description": "CLI tool for Zalo automation — multi-account, proxy support, bank transfers, QR payments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- /** Output event as JSON or human-readable, then post to webhook */
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
  });