@ardennguyen/zalo-agent-cli 2.5.1 → 2.5.3

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.
Files changed (35) hide show
  1. package/README.md +41 -33
  2. package/node_modules/zca-js/dist/apis.js +0 -8
  3. package/node_modules/zca-js/dist/cjs/apis.cjs +0 -8
  4. package/package.json +3 -3
  5. package/patches/zca-js+2.2.0.patch +4 -236
  6. package/src/commands/bank.js +126 -0
  7. package/src/commands/catalog.js +14 -0
  8. package/src/commands/conv.js +19 -0
  9. package/src/commands/friend.js +66 -21
  10. package/src/commands/group.js +38 -2
  11. package/src/commands/listen.js +51 -2
  12. package/src/commands/mcp.js +232 -2
  13. package/src/commands/msg.js +60 -0
  14. package/src/commands/profile.js +39 -0
  15. package/src/commands/sync.js +1 -68
  16. package/src/core/bank-accounts.js +52 -0
  17. package/src/core/daemon-channel.js +191 -2
  18. package/src/core/friend-requests.js +142 -0
  19. package/src/core/friend-search.js +41 -0
  20. package/src/core/group-admin.js +116 -0
  21. package/src/core/group-invite.js +53 -0
  22. package/src/core/sync.js +4 -235
  23. package/src/index.js +2 -0
  24. package/src/mcp/mcp-config.js +1 -0
  25. package/src/mcp/mcp-tools.js +302 -2
  26. package/src/utils/cli-args.js +84 -0
  27. package/src/utils/secrets.js +69 -0
  28. package/node_modules/zca-js/dist/apis/deleteSnapshotMobileMsg.js +0 -18
  29. package/node_modules/zca-js/dist/apis/getCrossDB.js +0 -18
  30. package/node_modules/zca-js/dist/apis/pullMobileMsg.js +0 -22
  31. package/node_modules/zca-js/dist/apis/requestSyncMessage.js +0 -18
  32. package/node_modules/zca-js/dist/cjs/apis/deleteSnapshotMobileMsg.cjs +0 -22
  33. package/node_modules/zca-js/dist/cjs/apis/getCrossDB.cjs +0 -22
  34. package/node_modules/zca-js/dist/cjs/apis/pullMobileMsg.cjs +0 -26
  35. package/node_modules/zca-js/dist/cjs/apis/requestSyncMessage.cjs +0 -22
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Small argument parsers for the commands that wrap a zca-js call taking an
3
+ * enum or a boolean: typing indicator, report, active status, language.
4
+ *
5
+ * Each throws an Error whose message says what was accepted, so the command's
6
+ * catch block can print it as the usage error. None touches Zalo.
7
+ */
8
+ import { ReportReason, UpdateLangAvailableLanguages } from "zca-js";
9
+
10
+ /**
11
+ * `--type <n>`: 0 = user, 1 = group.
12
+ *
13
+ * @param {string|number} value
14
+ * @returns {0|1}
15
+ * @throws {Error} for anything else
16
+ */
17
+ export function parseThreadTypeArg(value) {
18
+ const s = String(value).trim();
19
+ if (s === "0") return 0;
20
+ if (s === "1") return 1;
21
+ throw new Error(`--type must be 0 (user) or 1 (group), got "${value}"`);
22
+ }
23
+
24
+ /**
25
+ * `on` / `off` (also true/false, 1/0, yes/no, enable/disable).
26
+ *
27
+ * @param {string} value
28
+ * @param {string} [what] - the setting's name, for the error message
29
+ * @returns {boolean}
30
+ * @throws {Error} for anything else
31
+ */
32
+ export function parseOnOff(value, what = "value") {
33
+ const s = String(value).trim().toLowerCase();
34
+ if (["on", "true", "1", "yes", "enable", "enabled"].includes(s)) return true;
35
+ if (["off", "false", "0", "no", "disable", "disabled"].includes(s)) return false;
36
+ throw new Error(`${what} must be "on" or "off", got "${value}"`);
37
+ }
38
+
39
+ /**
40
+ * `vi` / `en`, as zca-js's `updateLang` takes them (it sends `VI` / `EN`).
41
+ *
42
+ * @param {string} value
43
+ * @returns {string} an UpdateLangAvailableLanguages value
44
+ * @throws {Error} for any other language
45
+ */
46
+ export function parseLanguage(value) {
47
+ const key = String(value).trim().toUpperCase();
48
+ if (key in UpdateLangAvailableLanguages) return UpdateLangAvailableLanguages[key];
49
+ throw new Error(`language must be one of: ${Object.keys(UpdateLangAvailableLanguages).join(", ").toLowerCase()}`);
50
+ }
51
+
52
+ /** The reasons `msg report` accepts, by name. */
53
+ export const REPORT_REASONS = Object.freeze({
54
+ sensitive: ReportReason.Sensitive,
55
+ annoy: ReportReason.Annoy,
56
+ fraud: ReportReason.Fraud,
57
+ other: ReportReason.Other,
58
+ });
59
+
60
+ /**
61
+ * The options object zca-js's `sendReport` takes.
62
+ *
63
+ * `other` is the only reason that carries text, and zca-js sends it only for
64
+ * that reason, so a text given with another reason would be dropped without a
65
+ * word: refuse it instead of pretending it was reported.
66
+ *
67
+ * @param {string} reason - sensitive | annoy | fraud | other
68
+ * @param {string} [content] - required with `other`, refused otherwise
69
+ * @returns {{reason: number, content?: string}}
70
+ * @throws {Error} for an unknown reason, `other` without text, or text with any other reason
71
+ */
72
+ export function parseReportReason(reason, content) {
73
+ const key = String(reason).trim().toLowerCase();
74
+ if (!(key in REPORT_REASONS)) {
75
+ throw new Error(`--reason must be one of: ${Object.keys(REPORT_REASONS).join(", ")}, got "${reason}"`);
76
+ }
77
+ const text = content === undefined ? "" : String(content).trim();
78
+ if (key === "other") {
79
+ if (!text) throw new Error('--reason other needs the details: add --content "<what happened>"');
80
+ return { reason: REPORT_REASONS.other, content: text };
81
+ }
82
+ if (text) throw new Error(`--content is only sent with --reason other; "${key}" carries no text`);
83
+ return { reason: REPORT_REASONS[key] };
84
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Secrets that arrive as command-line flags: masking them for logs, and
3
+ * resolving the webhook signing secret from its three sources.
4
+ */
5
+
6
+ /** Flags whose value is a secret and must never be written to a log. */
7
+ export const SECRET_FLAGS = Object.freeze(["--auth", "--client-secret", "--webhook-secret"]);
8
+
9
+ /**
10
+ * A copy of an argument list with every secret flag's value replaced by `***`.
11
+ *
12
+ * Handles both `--auth value` and `--auth=value`. The launcher for `-d` writes
13
+ * its argument list into `daemon.log` ("starting daemon: ..."), and a bearer
14
+ * token or signing secret has no business in a log that gets shipped or
15
+ * attached to an issue.
16
+ *
17
+ * @param {string[]} args
18
+ * @returns {string[]} a new array; the input is not modified
19
+ */
20
+ export function maskSecretArgs(args) {
21
+ const out = [];
22
+ for (let i = 0; i < args.length; i++) {
23
+ const a = String(args[i]);
24
+ const eq = a.indexOf("=");
25
+ if (eq > 0 && SECRET_FLAGS.includes(a.slice(0, eq))) {
26
+ out.push(`${a.slice(0, eq)}=***`);
27
+ } else if (SECRET_FLAGS.includes(a) && i + 1 < args.length) {
28
+ out.push(a, "***");
29
+ i++;
30
+ } else {
31
+ out.push(a);
32
+ }
33
+ }
34
+ return out;
35
+ }
36
+
37
+ /**
38
+ * The webhook signing secret: the `--webhook-secret` flag, else the
39
+ * `ZALO_WEBHOOK_SECRET[_<ownId>]` environment variable, else the MCP config's
40
+ * `webhookSecret`. Null when none is set, which means unsigned webhooks.
41
+ *
42
+ * A flag that is present but empty throws instead of falling through: an
43
+ * unset shell variable (`--webhook-secret "$SECRET"`) is how an empty value
44
+ * arrives, and falling back to "no secret" would silently send unsigned
45
+ * webhooks to a receiver that believes it is verifying signatures.
46
+ *
47
+ * @param {object} sources
48
+ * @param {string|undefined} [sources.flag] - the parsed `--webhook-secret` value
49
+ * @param {string|null|undefined} [sources.env] - the environment variable's value
50
+ * @param {string|null|undefined} [sources.config] - `webhookSecret` from mcp-config.json
51
+ * @returns {string|null}
52
+ * @throws {Error} when the flag is given empty
53
+ */
54
+ export function resolveWebhookSecret({ flag, env, config } = {}) {
55
+ if (flag !== undefined) {
56
+ const s = String(flag).trim();
57
+ if (!s) {
58
+ throw new Error(
59
+ "--webhook-secret was given an empty secret. Refusing to start: webhooks would go out unsigned " +
60
+ "while the receiver expects signatures.",
61
+ );
62
+ }
63
+ return s;
64
+ }
65
+ for (const v of [env, config]) {
66
+ if (typeof v === "string" && v.trim()) return v.trim();
67
+ }
68
+ return null;
69
+ }
@@ -1,18 +0,0 @@
1
- import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
- import { apiFactory } from "../utils.js";
3
-
4
- export const deleteSnapshotMobileMsgFactory = apiFactory()((api, ctx, utils) => {
5
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/delete_snapshot_mobile_msg`);
6
- return async function deleteSnapshotMobileMsg(publicKey) {
7
- const params = {
8
- public_key: publicKey,
9
- imei: ctx.imei
10
- };
11
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
12
- if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message");
13
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
14
- method: "GET"
15
- });
16
- return utils.resolve(response);
17
- };
18
- });
@@ -1,18 +0,0 @@
1
- import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
- import { apiFactory } from "../utils.js";
3
-
4
- export const getCrossDBFactory = apiFactory()((api, _, utils) => {
5
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/get_crossdb`);
6
- return async function getCrossDB(syncSession) {
7
- const params = {
8
- pc_name: "Web",
9
- sync_session: syncSession
10
- };
11
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
12
- if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message");
13
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
14
- method: "GET"
15
- });
16
- return utils.resolve(response);
17
- };
18
- });
@@ -1,22 +0,0 @@
1
- import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
- import { apiFactory } from "../utils.js";
3
-
4
- export const pullMobileMsgFactory = apiFactory()((api, _, utils) => {
5
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/pull_mobile_msg`);
6
- return async function pullMobileMsg(publicKey, fromSeqId = 0, isRetry = 0, tempKey = "") {
7
- const params = {
8
- pc_name: "Web",
9
- public_key: publicKey,
10
- from_seq_id: fromSeqId,
11
- is_retry: isRetry,
12
- min_seq_id: 0,
13
- temp_key: tempKey
14
- };
15
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
16
- if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message");
17
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
18
- method: "GET"
19
- });
20
- return utils.resolve(response);
21
- };
22
- });
@@ -1,18 +0,0 @@
1
- import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
- import { apiFactory } from "../utils.js";
3
-
4
- export const requestSyncMessageFactory = apiFactory()((api, _, utils) => {
5
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/transfer-sync-v2/request-sync`);
6
- return async function requestSyncMessage(reqId, data) {
7
- const params = {
8
- reqId: String(reqId),
9
- data: JSON.stringify(data)
10
- };
11
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
12
- if (!encryptedParams) throw new ZaloApiError("Failed to encrypt message");
13
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
14
- method: "GET"
15
- });
16
- return utils.resolve(response);
17
- };
18
- });
@@ -1,22 +0,0 @@
1
- 'use strict';
2
-
3
- var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
- var utils = require('../utils.cjs');
5
-
6
- const deleteSnapshotMobileMsgFactory = utils.apiFactory()((api, ctx, utils) => {
7
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/delete_snapshot_mobile_msg`);
8
- return async function deleteSnapshotMobileMsg(publicKey) {
9
- const params = {
10
- public_key: publicKey,
11
- imei: ctx.imei
12
- };
13
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
14
- if (!encryptedParams) throw new ZaloApiError.ZaloApiError("Failed to encrypt message");
15
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
16
- method: "GET"
17
- });
18
- return utils.resolve(response);
19
- };
20
- });
21
-
22
- exports.deleteSnapshotMobileMsgFactory = deleteSnapshotMobileMsgFactory;
@@ -1,22 +0,0 @@
1
- 'use strict';
2
-
3
- var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
- var utils = require('../utils.cjs');
5
-
6
- const getCrossDBFactory = utils.apiFactory()((api, _, utils) => {
7
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/get_crossdb`);
8
- return async function getCrossDB(syncSession) {
9
- const params = {
10
- pc_name: "Web",
11
- sync_session: syncSession
12
- };
13
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
14
- if (!encryptedParams) throw new ZaloApiError.ZaloApiError("Failed to encrypt message");
15
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
16
- method: "GET"
17
- });
18
- return utils.resolve(response);
19
- };
20
- });
21
-
22
- exports.getCrossDBFactory = getCrossDBFactory;
@@ -1,26 +0,0 @@
1
- 'use strict';
2
-
3
- var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
- var utils = require('../utils.cjs');
5
-
6
- const pullMobileMsgFactory = utils.apiFactory()((api, _, utils) => {
7
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/message/pull_mobile_msg`);
8
- return async function pullMobileMsg(publicKey, fromSeqId = 0, isRetry = 0, tempKey = "") {
9
- const params = {
10
- pc_name: "Web",
11
- public_key: publicKey,
12
- from_seq_id: fromSeqId,
13
- is_retry: isRetry,
14
- min_seq_id: 0,
15
- temp_key: tempKey
16
- };
17
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
18
- if (!encryptedParams) throw new ZaloApiError.ZaloApiError("Failed to encrypt message");
19
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
20
- method: "GET"
21
- });
22
- return utils.resolve(response);
23
- };
24
- });
25
-
26
- exports.pullMobileMsgFactory = pullMobileMsgFactory;
@@ -1,22 +0,0 @@
1
- 'use strict';
2
-
3
- var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
- var utils = require('../utils.cjs');
5
-
6
- const requestSyncMessageFactory = utils.apiFactory()((api, _, utils) => {
7
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.file[0]}/api/transfer-sync-v2/request-sync`);
8
- return async function requestSyncMessage(reqId, data) {
9
- const params = {
10
- reqId: String(reqId),
11
- data: JSON.stringify(data)
12
- };
13
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
14
- if (!encryptedParams) throw new ZaloApiError.ZaloApiError("Failed to encrypt message");
15
- const response = await utils.request(`${serviceURL}&params=${encodeURIComponent(encryptedParams)}`, {
16
- method: "GET"
17
- });
18
- return utils.resolve(response);
19
- };
20
- });
21
-
22
- exports.requestSyncMessageFactory = requestSyncMessageFactory;