zalo-agent-cli 1.0.18 → 1.0.19

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
@@ -80,13 +80,32 @@ A QR code PNG will be saved and a local HTTP server starts automatically. Open t
80
80
 
81
81
  > **Important:** Use Zalo's built-in QR scanner (not regular phone camera). The QR expires in ~60 seconds.
82
82
 
83
- #### 2. Send a message
83
+ #### 2. Find a thread ID
84
+
85
+ A `thread_id` is a user ID (for DMs) or group ID (for group chats). You need it for most messaging commands.
86
+
87
+ ```bash
88
+ # Search friends by name → get their thread_id
89
+ zalo-agent friend search "Phúc"
90
+
91
+ # List recent conversations (friends + groups) with thread_id
92
+ zalo-agent conv recent
93
+
94
+ # List only groups
95
+ zalo-agent conv recent --groups-only
96
+ ```
97
+
98
+ #### 3. Send a message
84
99
 
85
100
  ```bash
101
+ # Send to a user (type 0, default)
86
102
  zalo-agent msg send <THREAD_ID> "Hello from zalo-agent!"
103
+
104
+ # Send to a group (type 1)
105
+ zalo-agent msg send <THREAD_ID> "Hello group!" -t 1
87
106
  ```
88
107
 
89
- #### 3. List friends
108
+ #### 4. List friends
90
109
 
91
110
  ```bash
92
111
  zalo-agent friend list
@@ -140,6 +159,7 @@ zalo-agent whoami
140
159
  | Command | Description |
141
160
  |---------|-------------|
142
161
  | `friend list` | List all friends |
162
+ | `friend search <name>` | Search friends by name (get thread_id) |
143
163
  | `friend online` | List online friends |
144
164
  | `friend find <query>` | Find by phone or ID |
145
165
  | `friend info <userId>` | Get user profile |
@@ -168,6 +188,7 @@ zalo-agent whoami
168
188
 
169
189
  | Command | Description |
170
190
  |---------|-------------|
191
+ | `conv recent [-n limit] [--friends-only] [--groups-only]` | List recent conversations with thread_id |
171
192
  | `conv pinned` | List pinned |
172
193
  | `conv archived` | List archived |
173
194
  | `conv mute <threadId> [-t 0\|1] [-d secs]` | Mute (-1 = forever) |
@@ -365,13 +386,32 @@ zalo-agent login
365
386
 
366
387
  HTTP server tự khởi động và hiện URL (ví dụ `http://ip:18927/qr`). Mở URL trên trình duyệt, quét bằng **Zalo app > Quét mã QR** (không dùng camera thường). Thông tin đăng nhập tự động lưu tại `~/.zalo-agent-cli/`.
367
388
 
368
- #### 2. Gửi tin nhắn
389
+ #### 2. Tìm thread ID
390
+
391
+ `thread_id` là ID của người dùng (tin nhắn riêng) hoặc ID nhóm (nhóm chat). Bạn cần nó cho hầu hết các lệnh gửi tin.
392
+
393
+ ```bash
394
+ # Tìm bạn bè theo tên → lấy thread_id
395
+ zalo-agent friend search "Phúc"
396
+
397
+ # Xem danh sách hội thoại gần đây (bạn bè + nhóm) kèm thread_id
398
+ zalo-agent conv recent
399
+
400
+ # Chỉ xem nhóm
401
+ zalo-agent conv recent --groups-only
402
+ ```
403
+
404
+ #### 3. Gửi tin nhắn
369
405
 
370
406
  ```bash
407
+ # Gửi cho cá nhân (type 0, mặc định)
371
408
  zalo-agent msg send <THREAD_ID> "Xin chào!"
409
+
410
+ # Gửi vào nhóm (type 1)
411
+ zalo-agent msg send <THREAD_ID> "Xin chào nhóm!" -t 1
372
412
  ```
373
413
 
374
- #### 3. Xem danh sách bạn bè
414
+ #### 4. Xem danh sách bạn bè
375
415
 
376
416
  ```bash
377
417
  zalo-agent friend list
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zalo-agent-cli",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "CLI tool for Zalo automation — multi-account, proxy support, bank transfers, QR payments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,11 +3,95 @@
3
3
  */
4
4
 
5
5
  import { getApi } from "../core/zalo-client.js";
6
- import { success, error, output } from "../utils/output.js";
6
+ import { success, error, info, output } from "../utils/output.js";
7
7
 
8
8
  export function registerConvCommands(program) {
9
9
  const conv = program.command("conv").description("Manage conversations");
10
10
 
11
+ conv.command("recent")
12
+ .description("List recent conversations with thread_id (friends + groups)")
13
+ .option("-n, --limit <n>", "Max results per type", "20")
14
+ .option("--friends-only", "Show only friend conversations")
15
+ .option("--groups-only", "Show only group conversations")
16
+ .action(async (opts) => {
17
+ try {
18
+ const api = getApi();
19
+ const limit = Number(opts.limit);
20
+ const conversations = [];
21
+
22
+ // Fetch friends (sorted by lastActionTime = most recent interaction)
23
+ if (!opts.groupsOnly) {
24
+ const friends = await api.getAllFriends();
25
+ const list = Array.isArray(friends) ? friends : [];
26
+ const sorted = list
27
+ .filter((f) => f.lastActionTime > 0)
28
+ .sort((a, b) => b.lastActionTime - a.lastActionTime)
29
+ .slice(0, limit);
30
+ for (const f of sorted) {
31
+ conversations.push({
32
+ threadId: f.userId,
33
+ name: f.displayName || f.zaloName || "?",
34
+ type: "User",
35
+ typeFlag: 0,
36
+ lastActive: new Date(f.lastActionTime * 1000).toLocaleString(),
37
+ });
38
+ }
39
+ }
40
+
41
+ // Fetch groups
42
+ if (!opts.friendsOnly) {
43
+ const groupsResult = await api.getAllGroups();
44
+ const groupIds = Object.keys(groupsResult?.gridVerMap || {});
45
+ if (groupIds.length > 0) {
46
+ const batchSize = 50;
47
+ const batches = [];
48
+ for (let i = 0; i < Math.min(groupIds.length, limit); i += batchSize) {
49
+ batches.push(groupIds.slice(i, i + batchSize));
50
+ }
51
+ for (const batch of batches) {
52
+ try {
53
+ const groupInfo = await api.getGroupInfo(batch);
54
+ const map = groupInfo?.gridInfoMap || {};
55
+ for (const [gid, g] of Object.entries(map)) {
56
+ conversations.push({
57
+ threadId: gid,
58
+ name: g.name || "?",
59
+ type: "Group",
60
+ typeFlag: 1,
61
+ memberCount: g.totalMember || 0,
62
+ });
63
+ }
64
+ } catch {
65
+ // Skip failed batch
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ output(conversations, program.opts().json, () => {
72
+ if (conversations.length === 0) {
73
+ error("No conversations found.");
74
+ return;
75
+ }
76
+ info(`${conversations.length} conversation(s):`);
77
+ console.log();
78
+ console.log(" THREAD_ID TYPE NAME");
79
+ console.log(" " + "-".repeat(60));
80
+ for (const c of conversations) {
81
+ const typeLabel = c.type === "Group" ? `Group(${c.memberCount})` : "User";
82
+ const id = c.threadId.padEnd(22);
83
+ console.log(` ${id} ${typeLabel.padEnd(12)} ${c.name}`);
84
+ }
85
+ console.log();
86
+ info("Use thread_id with messaging commands:");
87
+ info(' zalo-agent msg send <thread_id> "Hello" (User)');
88
+ info(' zalo-agent msg send <thread_id> "Hello" -t 1 (Group)');
89
+ });
90
+ } catch (e) {
91
+ error(e.message);
92
+ }
93
+ });
94
+
11
95
  conv.command("pinned")
12
96
  .description("List pinned conversations")
13
97
  .action(async () => {
@@ -45,6 +45,39 @@ export function registerFriendCommands(program) {
45
45
  }
46
46
  });
47
47
 
48
+ friend
49
+ .command("search <name>")
50
+ .description("Search friends by name (returns thread_id for messaging)")
51
+ .action(async (name) => {
52
+ try {
53
+ const result = await getApi().getAllFriends();
54
+ const friends = Array.isArray(result) ? result : [];
55
+ const query = name.toLowerCase();
56
+ const matches = friends.filter((f) => {
57
+ const dn = (f.displayName || "").toLowerCase();
58
+ const zn = (f.zaloName || "").toLowerCase();
59
+ return dn.includes(query) || zn.includes(query);
60
+ });
61
+ output(matches, program.opts().json, () => {
62
+ if (matches.length === 0) {
63
+ error(`No friends matching "${name}". Use "friend list" to see all.`);
64
+ return;
65
+ }
66
+ info(`${matches.length} friend(s) matching "${name}":`);
67
+ console.log();
68
+ for (const f of matches) {
69
+ const display = f.displayName || f.zaloName || "?";
70
+ console.log(` ${f.userId} ${display}`);
71
+ }
72
+ console.log();
73
+ info("Use the ID above as thread_id for messaging commands.");
74
+ info('Example: zalo-agent msg send <thread_id> "Hello"');
75
+ });
76
+ } catch (e) {
77
+ error(e.message);
78
+ }
79
+ });
80
+
48
81
  friend
49
82
  .command("find <query>")
50
83
  .description("Find user by phone number or Zalo ID")