zalo-agent-cli 1.0.24 → 1.0.25

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
@@ -260,6 +260,37 @@ zalo-agent poll vote <pollId> 123 456
260
260
  zalo-agent poll lock <pollId>
261
261
  ```
262
262
 
263
+ #### Reminders (`reminder`)
264
+
265
+ | Command | Description |
266
+ |---------|-------------|
267
+ | `reminder create <threadId> <title> [-t 0\|1] [--time "YYYY-MM-DD HH:mm"] [--repeat mode] [--emoji]` | Create a reminder |
268
+ | `reminder list <threadId> [-t 0\|1] [-n count]` | List reminders |
269
+ | `reminder info <reminderId>` | View reminder details (group only) |
270
+ | `reminder responses <reminderId>` | View who accepted/rejected (group only) |
271
+ | `reminder edit <reminderId> <threadId> <title> [-t 0\|1] [--time] [--repeat] [--emoji]` | Edit a reminder |
272
+ | `reminder remove <reminderId> <threadId> [-t 0\|1]` | Remove a reminder |
273
+
274
+ **Repeat modes:** `none`, `daily`, `weekly`, `monthly`
275
+
276
+ **Example:**
277
+ ```bash
278
+ # Create a daily reminder in a group at 9:00 AM tomorrow
279
+ zalo-agent reminder create <groupId> "Standup meeting" -t 1 --time "2026-03-16 09:00" --repeat daily
280
+
281
+ # List reminders in a group
282
+ zalo-agent reminder list <groupId> -t 1
283
+
284
+ # View who responded
285
+ zalo-agent reminder responses <reminderId>
286
+
287
+ # Edit reminder title and time
288
+ zalo-agent reminder edit <reminderId> <groupId> "New title" -t 1 --time "2026-03-17 10:00"
289
+
290
+ # Remove a reminder
291
+ zalo-agent reminder remove <reminderId> <groupId> -t 1
292
+ ```
293
+
263
294
  #### Accounts (`account`)
264
295
 
265
296
  | Command | Description |
@@ -515,6 +546,27 @@ zalo-agent poll vote <pollId> 123 456
515
546
  zalo-agent poll lock <pollId>
516
547
  ```
517
548
 
549
+ #### 7. Nhắc nhở (Reminder)
550
+
551
+ ```bash
552
+ # Tạo nhắc nhở hàng ngày trong nhóm lúc 9h sáng
553
+ zalo-agent reminder create <groupId> "Họp standup" -t 1 --time "2026-03-16 09:00" --repeat daily
554
+
555
+ # Xem danh sách nhắc nhở
556
+ zalo-agent reminder list <groupId> -t 1
557
+
558
+ # Xem ai đã chấp nhận/từ chối
559
+ zalo-agent reminder responses <reminderId>
560
+
561
+ # Sửa nhắc nhở
562
+ zalo-agent reminder edit <reminderId> <groupId> "Tiêu đề mới" -t 1 --time "2026-03-17 10:00"
563
+
564
+ # Xóa nhắc nhở
565
+ zalo-agent reminder remove <reminderId> <groupId> -t 1
566
+ ```
567
+
568
+ Chế độ lặp: `none` (không lặp), `daily` (hàng ngày), `weekly` (hàng tuần), `monthly` (hàng tháng)
569
+
518
570
  ### Danh sách lệnh
519
571
 
520
572
  Xem đầy đủ tại [phần tiếng Anh](#command-reference) phía trên. Tất cả lệnh đều giống nhau.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zalo-agent-cli",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
4
4
  "description": "CLI tool for Zalo automation — multi-account, proxy support, bank transfers, QR payments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Reminder commands — create, list, info, edit, remove reminders in users/groups.
3
+ */
4
+
5
+ import { getApi } from "../core/zalo-client.js";
6
+ import { success, error, info, output } from "../utils/output.js";
7
+
8
+ /** Repeat mode labels matching zca-js ReminderRepeatMode enum. */
9
+ const REPEAT_MODES = { none: 0, daily: 1, weekly: 2, monthly: 3 };
10
+ const REPEAT_LABELS = { 0: "None", 1: "Daily", 2: "Weekly", 3: "Monthly" };
11
+
12
+ /** Parse a datetime string into Unix timestamp (ms). Accepts ISO or "YYYY-MM-DD HH:mm". */
13
+ function parseTime(str) {
14
+ // Try "YYYY-MM-DD HH:mm" format
15
+ const match = str.match(/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})$/);
16
+ if (match) {
17
+ const [, y, mo, d, h, mi] = match;
18
+ return new Date(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi)).getTime();
19
+ }
20
+ // Fallback to Date.parse (ISO, etc.)
21
+ const ts = Date.parse(str);
22
+ if (isNaN(ts)) return null;
23
+ return ts;
24
+ }
25
+
26
+ export function registerReminderCommands(program) {
27
+ const reminder = program.command("reminder").description("Create and manage reminders in users/groups");
28
+
29
+ reminder
30
+ .command("create <threadId> <title>")
31
+ .description("Create a reminder")
32
+ .option("-t, --type <n>", "Thread type: 0=User, 1=Group", "0")
33
+ .option("--time <datetime>", 'Reminder time: "YYYY-MM-DD HH:mm" (default: now)')
34
+ .option("--emoji <emoji>", "Emoji icon", "⏰")
35
+ .option("--repeat <mode>", "Repeat: none, daily, weekly, monthly", "none")
36
+ .action(async (threadId, title, opts) => {
37
+ try {
38
+ const repeatMode = REPEAT_MODES[opts.repeat];
39
+ if (repeatMode === undefined) {
40
+ error(`Invalid repeat mode: "${opts.repeat}". Valid: none, daily, weekly, monthly`);
41
+ return;
42
+ }
43
+ let startTime = Date.now();
44
+ if (opts.time) {
45
+ startTime = parseTime(opts.time);
46
+ if (!startTime) {
47
+ error(`Invalid time format: "${opts.time}". Use "YYYY-MM-DD HH:mm" or ISO format.`);
48
+ return;
49
+ }
50
+ }
51
+ const result = await getApi().createReminder(
52
+ { title, emoji: opts.emoji, startTime, repeat: repeatMode },
53
+ threadId,
54
+ Number(opts.type),
55
+ );
56
+ output(result, program.opts().json, () => {
57
+ success(`Reminder created: "${title}"`);
58
+ const id = result.reminderId || result.id || "?";
59
+ info(`Reminder ID: ${id}`);
60
+ info(`Time: ${new Date(startTime).toLocaleString()}`);
61
+ if (repeatMode > 0) info(`Repeat: ${REPEAT_LABELS[repeatMode]}`);
62
+ });
63
+ } catch (e) {
64
+ error(`Create reminder failed: ${e.message}`);
65
+ }
66
+ });
67
+
68
+ reminder
69
+ .command("list <threadId>")
70
+ .description("List reminders in a thread")
71
+ .option("-t, --type <n>", "Thread type: 0=User, 1=Group", "0")
72
+ .option("-n, --count <n>", "Max results", "20")
73
+ .action(async (threadId, opts) => {
74
+ try {
75
+ let result;
76
+ try {
77
+ result = await getApi().getListReminder({ count: Number(opts.count) }, threadId, Number(opts.type));
78
+ } catch {
79
+ result = null;
80
+ }
81
+ const items = Array.isArray(result) ? result : [];
82
+ output(items, program.opts().json, () => {
83
+ if (items.length === 0) {
84
+ info("No reminders found.");
85
+ return;
86
+ }
87
+ info(`${items.length} reminder(s):`);
88
+ console.log();
89
+ for (const r of items) {
90
+ const id = r.reminderId || r.id || "?";
91
+ const title = r.params?.title || "?";
92
+ const time = new Date(r.startTime).toLocaleString();
93
+ const repeat = REPEAT_LABELS[r.repeat] || "None";
94
+ const emoji = r.emoji || "";
95
+ console.log(` ${emoji} [${id}] ${title}`);
96
+ console.log(` Time: ${time} | Repeat: ${repeat}`);
97
+ }
98
+ });
99
+ } catch (e) {
100
+ error(`List reminders failed: ${e.message}`);
101
+ }
102
+ });
103
+
104
+ reminder
105
+ .command("info <reminderId>")
106
+ .description("View reminder details (group reminders only)")
107
+ .action(async (reminderId) => {
108
+ try {
109
+ const result = await getApi().getReminder(reminderId);
110
+ output(result, program.opts().json, () => {
111
+ const title = result.params?.title || "?";
112
+ const id = result.id || reminderId;
113
+ info(`Title: ${result.emoji || ""} ${title}`);
114
+ info(`Reminder ID: ${id}`);
115
+ info(`Created: ${new Date(result.createTime).toLocaleString()}`);
116
+ info(`Time: ${new Date(result.startTime).toLocaleString()}`);
117
+ info(`Repeat: ${REPEAT_LABELS[result.repeat] || "None"}`);
118
+ info(`Creator: ${result.creatorId}`);
119
+ if (result.responseMem) {
120
+ info(
121
+ `Responses: ${result.responseMem.acceptMember} accepted, ${result.responseMem.rejectMember} rejected`,
122
+ );
123
+ }
124
+ });
125
+ } catch (e) {
126
+ error(`Get reminder failed: ${e.message}`);
127
+ }
128
+ });
129
+
130
+ reminder
131
+ .command("responses <reminderId>")
132
+ .description("View who accepted/rejected a reminder (group only)")
133
+ .action(async (reminderId) => {
134
+ try {
135
+ const result = await getApi().getReminderResponses(reminderId);
136
+ output(result, program.opts().json, () => {
137
+ const accepted = result.acceptMember || [];
138
+ const rejected = result.rejectMember || [];
139
+ info(`Accepted (${accepted.length}):`);
140
+ accepted.forEach((uid) => console.log(` ✓ ${uid}`));
141
+ info(`Rejected (${rejected.length}):`);
142
+ rejected.forEach((uid) => console.log(` ✗ ${uid}`));
143
+ });
144
+ } catch (e) {
145
+ error(`Get responses failed: ${e.message}`);
146
+ }
147
+ });
148
+
149
+ reminder
150
+ .command("edit <reminderId> <threadId> <title>")
151
+ .description("Edit a reminder")
152
+ .option("-t, --type <n>", "Thread type: 0=User, 1=Group", "0")
153
+ .option("--time <datetime>", 'New time: "YYYY-MM-DD HH:mm"')
154
+ .option("--emoji <emoji>", "New emoji icon")
155
+ .option("--repeat <mode>", "Repeat: none, daily, weekly, monthly")
156
+ .action(async (reminderId, threadId, title, opts) => {
157
+ try {
158
+ const editOpts = { title, topicId: reminderId };
159
+ if (opts.emoji) editOpts.emoji = opts.emoji;
160
+ if (opts.time) {
161
+ const ts = parseTime(opts.time);
162
+ if (!ts) {
163
+ error(`Invalid time format: "${opts.time}". Use "YYYY-MM-DD HH:mm".`);
164
+ return;
165
+ }
166
+ editOpts.startTime = ts;
167
+ }
168
+ if (opts.repeat) {
169
+ const mode = REPEAT_MODES[opts.repeat];
170
+ if (mode === undefined) {
171
+ error(`Invalid repeat mode: "${opts.repeat}". Valid: none, daily, weekly, monthly`);
172
+ return;
173
+ }
174
+ editOpts.repeat = mode;
175
+ }
176
+ const result = await getApi().editReminder(editOpts, threadId, Number(opts.type));
177
+ output(result, program.opts().json, () => success(`Reminder ${reminderId} updated`));
178
+ } catch (e) {
179
+ error(`Edit reminder failed: ${e.message}`);
180
+ }
181
+ });
182
+
183
+ reminder
184
+ .command("remove <reminderId> <threadId>")
185
+ .description("Remove a reminder")
186
+ .option("-t, --type <n>", "Thread type: 0=User, 1=Group", "0")
187
+ .action(async (reminderId, threadId, opts) => {
188
+ try {
189
+ const result = await getApi().removeReminder(reminderId, threadId, Number(opts.type));
190
+ output(result, program.opts().json, () => success(`Reminder ${reminderId} removed`));
191
+ } catch (e) {
192
+ error(`Remove reminder failed: ${e.message}`);
193
+ }
194
+ });
195
+ }
package/src/index.js CHANGED
@@ -20,6 +20,7 @@ import { registerConvCommands } from "./commands/conv.js";
20
20
  import { registerAccountCommands } from "./commands/account.js";
21
21
  import { registerProfileCommands } from "./commands/profile.js";
22
22
  import { registerPollCommands } from "./commands/poll.js";
23
+ import { registerReminderCommands } from "./commands/reminder.js";
23
24
  import { registerListenCommand } from "./commands/listen.js";
24
25
  import { autoLogin } from "./core/zalo-client.js";
25
26
  import { checkForUpdates, selfUpdate } from "./utils/update-check.js";
@@ -74,6 +75,7 @@ registerConvCommands(program);
74
75
  registerAccountCommands(program);
75
76
  registerProfileCommands(program);
76
77
  registerPollCommands(program);
78
+ registerReminderCommands(program);
77
79
  registerListenCommand(program);
78
80
 
79
81
  program.parse();