zalo-agent-cli 1.0.22 → 1.0.24

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
@@ -201,6 +201,7 @@ zalo-agent whoami
201
201
  | `group add-member <groupId> <userIds...>` | Add members |
202
202
  | `group remove-member <groupId> <userIds...>` | Remove members |
203
203
  | `group rename <groupId> <name>` | Rename |
204
+ | `group upgrade-community <groupId>` | Upgrade group to Zalo Community |
204
205
  | `group leave <groupId>` | Leave group |
205
206
  | `group join <link>` | Join via invite link |
206
207
 
@@ -230,6 +231,35 @@ zalo-agent whoami
230
231
 
231
232
  **Privacy settings:** `online-status`, `seen-status`, `birthday`, `receive-msg`, `accept-call`, `add-by-phone`, `add-by-qr`, `add-by-group`, `recommend`
232
233
 
234
+ #### Polls (`poll`)
235
+
236
+ | Command | Description |
237
+ |---------|-------------|
238
+ | `poll create <groupId> <question> <options...>` | Create a poll (see flags below) |
239
+ | `poll info <pollId>` | View poll details and vote results |
240
+ | `poll vote <pollId> <optionIds...>` | Vote on a poll (option IDs from `poll info`) |
241
+ | `poll unvote <pollId>` | Remove your vote |
242
+ | `poll add-option <pollId> <options...> [--vote]` | Add new options to a poll |
243
+ | `poll lock <pollId>` | Close a poll (no more votes) |
244
+ | `poll share <pollId>` | Share a poll |
245
+
246
+ **Poll create flags:** `--multi` (multiple choices), `--add-options` (members can add options), `--anonymous` (hide voters), `--hide-preview` (hide results until voted), `--expire <minutes>` (auto-close)
247
+
248
+ **Example:**
249
+ ```bash
250
+ # Create a multi-choice poll with 3 options, auto-close after 60 minutes
251
+ zalo-agent poll create <groupId> "Chọn ngày họp" "Thứ 2" "Thứ 4" "Thứ 6" --multi --expire 60
252
+
253
+ # View results
254
+ zalo-agent poll info <pollId>
255
+
256
+ # Vote for option IDs 123 and 456
257
+ zalo-agent poll vote <pollId> 123 456
258
+
259
+ # Close the poll
260
+ zalo-agent poll lock <pollId>
261
+ ```
262
+
233
263
  #### Accounts (`account`)
234
264
 
235
265
  | Command | Description |
@@ -469,6 +499,22 @@ zalo-agent profile settings
469
499
  zalo-agent profile set online-status 0
470
500
  ```
471
501
 
502
+ #### 6. Tạo khảo sát (Poll) trong nhóm
503
+
504
+ ```bash
505
+ # Tạo poll multi-choice, tự đóng sau 60 phút
506
+ zalo-agent poll create <groupId> "Chọn ngày họp" "Thứ 2" "Thứ 4" "Thứ 6" --multi --expire 60
507
+
508
+ # Xem kết quả
509
+ zalo-agent poll info <pollId>
510
+
511
+ # Bỏ phiếu (dùng option ID từ poll info)
512
+ zalo-agent poll vote <pollId> 123 456
513
+
514
+ # Đóng poll
515
+ zalo-agent poll lock <pollId>
516
+ ```
517
+
472
518
  ### Danh sách lệnh
473
519
 
474
520
  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.22",
3
+ "version": "1.0.24",
4
4
  "description": "CLI tool for Zalo automation — multi-account, proxy support, bank transfers, QR payments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -171,6 +171,18 @@ export function registerGroupCommands(program) {
171
171
  }
172
172
  });
173
173
 
174
+ group
175
+ .command("upgrade-community <groupId>")
176
+ .description("Upgrade a group to Zalo Community (requires verified 18+ account)")
177
+ .action(async (groupId) => {
178
+ try {
179
+ const result = await getApi().upgradeGroupToCommunity(groupId);
180
+ output(result, program.opts().json, () => success("Group upgraded to community"));
181
+ } catch (e) {
182
+ error(`Upgrade failed: ${e.message}`);
183
+ }
184
+ });
185
+
174
186
  group
175
187
  .command("leave <groupId>")
176
188
  .description("Leave a group")
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Poll commands — create, view, vote, add options, lock, and share polls in groups.
3
+ */
4
+
5
+ import { getApi } from "../core/zalo-client.js";
6
+ import { success, error, info, output } from "../utils/output.js";
7
+
8
+ export function registerPollCommands(program) {
9
+ const poll = program.command("poll").description("Create and manage polls in groups");
10
+
11
+ poll.command("create <groupId> <question> <options...>")
12
+ .description("Create a poll in a group (options separated by spaces, quote multi-word options)")
13
+ .option("--multi", "Allow multiple choices")
14
+ .option("--add-options", "Allow members to add new options")
15
+ .option("--anonymous", "Hide voter identities")
16
+ .option("--hide-preview", "Hide results until voted")
17
+ .option("--expire <minutes>", "Auto-close after N minutes", parseInt)
18
+ .action(async (groupId, question, options, opts) => {
19
+ try {
20
+ if (options.length < 2) {
21
+ error("A poll requires at least 2 options.");
22
+ return;
23
+ }
24
+ const expiredTime = opts.expire ? opts.expire * 60 * 1000 : 0;
25
+ const result = await getApi().createPoll(
26
+ {
27
+ question,
28
+ options,
29
+ allowMultiChoices: !!opts.multi,
30
+ allowAddNewOption: !!opts.addOptions,
31
+ isAnonymous: !!opts.anonymous,
32
+ hideVotePreview: !!opts.hidePreview,
33
+ expiredTime,
34
+ },
35
+ groupId,
36
+ );
37
+ output(result, program.opts().json, () => {
38
+ success(`Poll created: "${question}"`);
39
+ info(`Poll ID: ${result.poll_id}`);
40
+ if (result.options) {
41
+ result.options.forEach((o, i) => console.log(` [${o.option_id}] ${o.content}`));
42
+ }
43
+ });
44
+ } catch (e) {
45
+ error(`Create poll failed: ${e.message}`);
46
+ }
47
+ });
48
+
49
+ poll.command("info <pollId>")
50
+ .description("View poll details and results")
51
+ .action(async (pollId) => {
52
+ try {
53
+ const result = await getApi().getPollDetail(Number(pollId));
54
+ output(result, program.opts().json, () => {
55
+ info(`Question: ${result.question}`);
56
+ info(`Poll ID: ${result.poll_id}`);
57
+ info(`Status: ${result.closed ? "Closed" : "Open"}`);
58
+ info(`Total votes: ${result.num_vote}`);
59
+ info(`Multi-choice: ${result.allow_multi_choices ? "Yes" : "No"}`);
60
+ info(`Anonymous: ${result.is_anonymous ? "Yes" : "No"}`);
61
+ if (result.expired_time > 0) {
62
+ const expDate = new Date(result.expired_time);
63
+ info(`Expires: ${expDate.toLocaleString()}`);
64
+ }
65
+ console.log();
66
+ console.log(" ID VOTES OPTION");
67
+ console.log(" " + "-".repeat(50));
68
+ for (const o of result.options || []) {
69
+ const voted = o.voted ? " ✓" : "";
70
+ console.log(
71
+ ` ${String(o.option_id).padEnd(6)} ${String(o.votes).padEnd(6)} ${o.content}${voted}`,
72
+ );
73
+ }
74
+ });
75
+ } catch (e) {
76
+ error(`Get poll failed: ${e.message}`);
77
+ }
78
+ });
79
+
80
+ poll.command("vote <pollId> <optionIds...>")
81
+ .description("Vote on a poll (use option IDs from 'poll info')")
82
+ .action(async (pollId, optionIds) => {
83
+ try {
84
+ const ids = optionIds.map(Number);
85
+ const result = await getApi().votePoll(Number(pollId), ids);
86
+ output(result, program.opts().json, () => {
87
+ success(`Voted on poll ${pollId}`);
88
+ if (result.options) {
89
+ for (const o of result.options) {
90
+ const voted = o.voted ? " ✓" : "";
91
+ console.log(` [${o.option_id}] ${o.content}: ${o.votes} vote(s)${voted}`);
92
+ }
93
+ }
94
+ });
95
+ } catch (e) {
96
+ error(`Vote failed: ${e.message}`);
97
+ }
98
+ });
99
+
100
+ poll.command("unvote <pollId>")
101
+ .description("Remove your vote from a poll")
102
+ .action(async (pollId) => {
103
+ try {
104
+ const result = await getApi().votePoll(Number(pollId), []);
105
+ output(result, program.opts().json, () => success(`Removed vote from poll ${pollId}`));
106
+ } catch (e) {
107
+ error(`Unvote failed: ${e.message}`);
108
+ }
109
+ });
110
+
111
+ poll.command("add-option <pollId> <options...>")
112
+ .description("Add new options to an existing poll")
113
+ .option("--vote", "Also vote for the new options")
114
+ .action(async (pollId, options, opts) => {
115
+ try {
116
+ const newOptions = options.map((content) => ({
117
+ content,
118
+ voted: !!opts.vote,
119
+ }));
120
+ const result = await getApi().addPollOptions({
121
+ pollId: Number(pollId),
122
+ options: newOptions,
123
+ votedOptionIds: [],
124
+ });
125
+ output(result, program.opts().json, () => {
126
+ success(`Added ${options.length} option(s) to poll ${pollId}`);
127
+ if (result.options) {
128
+ for (const o of result.options) {
129
+ console.log(` [${o.option_id}] ${o.content}: ${o.votes} vote(s)`);
130
+ }
131
+ }
132
+ });
133
+ } catch (e) {
134
+ error(`Add option failed: ${e.message}`);
135
+ }
136
+ });
137
+
138
+ poll.command("lock <pollId>")
139
+ .description("Lock/close a poll (no more votes)")
140
+ .action(async (pollId) => {
141
+ try {
142
+ const result = await getApi().lockPoll(Number(pollId));
143
+ output(result, program.opts().json, () => success(`Poll ${pollId} locked`));
144
+ } catch (e) {
145
+ error(`Lock poll failed: ${e.message}`);
146
+ }
147
+ });
148
+
149
+ poll.command("share <pollId>")
150
+ .description("Share a poll")
151
+ .action(async (pollId) => {
152
+ try {
153
+ const result = await getApi().sharePoll(Number(pollId));
154
+ output(result, program.opts().json, () => success(`Poll ${pollId} shared`));
155
+ } catch (e) {
156
+ error(`Share poll failed: ${e.message}`);
157
+ }
158
+ });
159
+ }
@@ -18,7 +18,7 @@ const SETTING_MAP = {
18
18
  label: "Display seen status",
19
19
  values: { 0: "hidden", 1: "visible" },
20
20
  },
21
- "birthday": {
21
+ birthday: {
22
22
  key: "view_birthday",
23
23
  label: "Birthday visibility",
24
24
  values: { 0: "hidden", 1: "full (day/month/year)", 2: "partial (day/month)" },
@@ -48,7 +48,7 @@ const SETTING_MAP = {
48
48
  label: "Find via group",
49
49
  values: { 0: "disabled", 1: "enabled" },
50
50
  },
51
- "recommend": {
51
+ recommend: {
52
52
  key: "display_on_recommend_friend",
53
53
  label: "Show in friend recommendations",
54
54
  values: { 0: "disabled", 1: "enabled" },
@@ -111,7 +111,9 @@ export function registerProfileCommands(program) {
111
111
  } else {
112
112
  output({ bio: newBio, requested: text }, program.opts().json, () => {
113
113
  success(`Bio update request sent: "${text}"`);
114
- info("Note: Zalo may take time to reflect changes, or bio may not be supported for your account type.");
114
+ info(
115
+ "Note: Zalo may take time to reflect changes, or bio may not be supported for your account type.",
116
+ );
115
117
  });
116
118
  }
117
119
  }
@@ -145,7 +147,7 @@ export function registerProfileCommands(program) {
145
147
  profile: {
146
148
  name: opts.name || p.displayName || p.zaloName,
147
149
  dob: opts.dob || currentDob,
148
- gender: opts.gender !== undefined ? Number(opts.gender) : p.gender ?? 0,
150
+ gender: opts.gender !== undefined ? Number(opts.gender) : (p.gender ?? 0),
149
151
  },
150
152
  };
151
153
  const result = await getApi().updateProfile(payload);
@@ -193,14 +195,14 @@ export function registerProfileCommands(program) {
193
195
  const numVal = Number(value);
194
196
  if (!(numVal in meta.values)) {
195
197
  error(
196
- `Invalid value for ${setting}. Valid: ${Object.entries(meta.values).map(([k, v]) => `${k}=${v}`).join(", ")}`,
198
+ `Invalid value for ${setting}. Valid: ${Object.entries(meta.values)
199
+ .map(([k, v]) => `${k}=${v}`)
200
+ .join(", ")}`,
197
201
  );
198
202
  return;
199
203
  }
200
204
  const result = await getApi().updateSettings(meta.key, numVal);
201
- output(result, program.opts().json, () =>
202
- success(`${meta.label}: ${meta.values[numVal]} (${numVal})`),
203
- );
205
+ output(result, program.opts().json, () => success(`${meta.label}: ${meta.values[numVal]} (${numVal})`));
204
206
  } catch (e) {
205
207
  error(`Setting update failed: ${e.message}`);
206
208
  }
package/src/index.js CHANGED
@@ -19,12 +19,14 @@ import { registerGroupCommands } from "./commands/group.js";
19
19
  import { registerConvCommands } from "./commands/conv.js";
20
20
  import { registerAccountCommands } from "./commands/account.js";
21
21
  import { registerProfileCommands } from "./commands/profile.js";
22
+ import { registerPollCommands } from "./commands/poll.js";
22
23
  import { registerListenCommand } from "./commands/listen.js";
23
24
  import { autoLogin } from "./core/zalo-client.js";
24
25
  import { checkForUpdates, selfUpdate } from "./utils/update-check.js";
25
26
  import { success, error, warning } from "./utils/output.js";
26
27
 
27
- const DISCLAIMER = "This tool uses unofficial Zalo APIs (zca-js) — your account may be banned. Use at your own risk. | Tool này dùng API Zalo không chính thức (zca-js) — account có thể bị ban. Tự chịu trách nhiệm.";
28
+ const DISCLAIMER =
29
+ "This tool uses unofficial Zalo APIs (zca-js) — your account may be banned. Use at your own risk. | Tool này dùng API Zalo không chính thức (zca-js) — account có thể bị ban. Tự chịu trách nhiệm.";
28
30
 
29
31
  const program = new Command();
30
32
 
@@ -71,6 +73,7 @@ registerGroupCommands(program);
71
73
  registerConvCommands(program);
72
74
  registerAccountCommands(program);
73
75
  registerProfileCommands(program);
76
+ registerPollCommands(program);
74
77
  registerListenCommand(program);
75
78
 
76
79
  program.parse();