@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.
- package/README.md +41 -33
- package/node_modules/zca-js/dist/apis.js +0 -8
- package/node_modules/zca-js/dist/cjs/apis.cjs +0 -8
- package/package.json +3 -3
- package/patches/zca-js+2.2.0.patch +4 -236
- package/src/commands/bank.js +126 -0
- package/src/commands/catalog.js +14 -0
- package/src/commands/conv.js +19 -0
- package/src/commands/friend.js +66 -21
- package/src/commands/group.js +38 -2
- package/src/commands/listen.js +51 -2
- package/src/commands/mcp.js +232 -2
- package/src/commands/msg.js +60 -0
- package/src/commands/profile.js +39 -0
- package/src/commands/sync.js +1 -68
- package/src/core/bank-accounts.js +52 -0
- package/src/core/daemon-channel.js +191 -2
- package/src/core/friend-requests.js +142 -0
- package/src/core/friend-search.js +41 -0
- package/src/core/group-admin.js +116 -0
- package/src/core/group-invite.js +53 -0
- package/src/core/sync.js +4 -235
- package/src/index.js +2 -0
- package/src/mcp/mcp-config.js +1 -0
- package/src/mcp/mcp-tools.js +302 -2
- package/src/utils/cli-args.js +84 -0
- package/src/utils/secrets.js +69 -0
- package/node_modules/zca-js/dist/apis/deleteSnapshotMobileMsg.js +0 -18
- package/node_modules/zca-js/dist/apis/getCrossDB.js +0 -18
- package/node_modules/zca-js/dist/apis/pullMobileMsg.js +0 -22
- package/node_modules/zca-js/dist/apis/requestSyncMessage.js +0 -18
- package/node_modules/zca-js/dist/cjs/apis/deleteSnapshotMobileMsg.cjs +0 -22
- package/node_modules/zca-js/dist/cjs/apis/getCrossDB.cjs +0 -22
- package/node_modules/zca-js/dist/cjs/apis/pullMobileMsg.cjs +0 -26
- 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}¶ms=${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}¶ms=${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}¶ms=${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}¶ms=${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}¶ms=${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}¶ms=${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}¶ms=${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}¶ms=${encodeURIComponent(encryptedParams)}`, {
|
|
16
|
-
method: "GET"
|
|
17
|
-
});
|
|
18
|
-
return utils.resolve(response);
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
exports.requestSyncMessageFactory = requestSyncMessageFactory;
|