@ardennguyen/zalo-agent-cli 2.5.1 → 2.5.4

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 (41) 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 +71 -4
  12. package/src/commands/mcp.js +252 -4
  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/current-user-names.js +27 -7
  18. package/src/core/daemon-channel.js +191 -2
  19. package/src/core/daemon-log.js +250 -0
  20. package/src/core/daemon-sync.js +140 -9
  21. package/src/core/friend-requests.js +142 -0
  22. package/src/core/friend-search.js +41 -0
  23. package/src/core/group-admin.js +116 -0
  24. package/src/core/group-invite.js +53 -0
  25. package/src/core/sync-v2/gid.js +20 -20
  26. package/src/core/sync-v2/index.js +77 -11
  27. package/src/core/sync.js +4 -235
  28. package/src/core/user-info.js +170 -0
  29. package/src/index.js +2 -0
  30. package/src/mcp/mcp-config.js +1 -0
  31. package/src/mcp/mcp-tools.js +302 -2
  32. package/src/utils/cli-args.js +84 -0
  33. package/src/utils/secrets.js +69 -0
  34. package/node_modules/zca-js/dist/apis/deleteSnapshotMobileMsg.js +0 -18
  35. package/node_modules/zca-js/dist/apis/getCrossDB.js +0 -18
  36. package/node_modules/zca-js/dist/apis/pullMobileMsg.js +0 -22
  37. package/node_modules/zca-js/dist/apis/requestSyncMessage.js +0 -18
  38. package/node_modules/zca-js/dist/cjs/apis/deleteSnapshotMobileMsg.cjs +0 -22
  39. package/node_modules/zca-js/dist/cjs/apis/getCrossDB.cjs +0 -22
  40. package/node_modules/zca-js/dist/cjs/apis/pullMobileMsg.cjs +0 -26
  41. package/node_modules/zca-js/dist/cjs/apis/requestSyncMessage.cjs +0 -22
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * MCP tool registrations for Zalo message access, sending, user lookup, and group productivity.
3
- * Registers 23 tools: zalo_get_messages, zalo_get_history, zalo_send_message, zalo_list_threads,
3
+ * Registers 25 tools: zalo_get_messages, zalo_get_history, zalo_send_message, zalo_list_threads,
4
4
  * zalo_search_threads, zalo_mark_read, zalo_view_media, zalo_react, zalo_undo, zalo_get_group_members,
5
5
  * zalo_list_conversations, zalo_coverage, zalo_sync, zalo_sync_status, zalo_search_messages,
6
6
  * zalo_lookup_user, zalo_forward_message, zalo_delete_message, zalo_reminder, zalo_note, zalo_poll,
7
- * zalo_wait_for_message, zalo_label.
7
+ * zalo_wait_for_message, zalo_label, zalo_friend, zalo_group.
8
8
  *
9
9
  * A tool that mirrors a CLI command calls the same code the command does (src/core/cached-message.js,
10
10
  * forward.js, pin.js, group-members.js, recent-conversations.js, src/utils/my-documents.js, urgency.js),
@@ -47,6 +47,23 @@ import { expandMentions, ALL_MENTION_UID } from "../utils/mentions.js";
47
47
  import { resolveSelfThread } from "../utils/my-documents.js";
48
48
  import { buildQuote, resolveQuoteSender } from "../utils/quote.js";
49
49
  import { urgencyLevel } from "../utils/urgency.js";
50
+ import {
51
+ describeFriendAddError,
52
+ extractErrorCode,
53
+ friendStatusFrom,
54
+ friendsFrom,
55
+ receivedRequestsFrom,
56
+ sentRequestsFrom,
57
+ } from "../core/friend-requests.js";
58
+ import {
59
+ CONFIRMED_ACTIONS,
60
+ NOT_A_MEMBER_CODES,
61
+ REMOVE_NOT_IN_GROUP,
62
+ createdGroupFrom,
63
+ groupConfirmRequired,
64
+ groupInfoFrom,
65
+ memberChangeFrom,
66
+ } from "../core/group-admin.js";
50
67
  import { resolveCurrentUserNames, withCurrentSenderNames } from "../core/current-user-names.js";
51
68
 
52
69
  /** Repeat mode codes matching zca-js ReminderRepeatMode enum and src/commands/reminder.js. */
@@ -2549,4 +2566,287 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2549
2566
  }
2550
2567
  },
2551
2568
  );
2569
+
2570
+ // --- zalo_friend ---
2571
+ server.registerTool(
2572
+ "zalo_friend",
2573
+ {
2574
+ title: "Manage Zalo Friends and Friend Requests",
2575
+ description:
2576
+ "Check, list and answer friend requests, and list friends. Actions: " +
2577
+ "'status' (the relationship with userId: is_friend, and is_requested / is_requesting, Zalo's two flags " +
2578
+ "for a request pending between you and them; to tell which direction a request goes, use the two lists), " +
2579
+ "'sent_requests' (requests you sent that are still pending), " +
2580
+ "'received_requests' (requests others sent you, newest first, with their message), " +
2581
+ "'send' (friend request to userId, with an optional message), " +
2582
+ "'accept' / 'reject' (the request from userId), " +
2583
+ "'undo' (cancel the request you sent to userId), " +
2584
+ "'list' (your friends, most recently active first, capped by limit). " +
2585
+ "userId is the numeric Zalo id (zalo_lookup_user finds one from a phone number). " +
2586
+ "Zalo restricts accounts that send many friend requests, so send sparingly.",
2587
+ inputSchema: z.object({
2588
+ action: z
2589
+ .enum(["status", "sent_requests", "received_requests", "send", "accept", "reject", "undo", "list"])
2590
+ .describe("Friend operation to perform"),
2591
+ userId: z
2592
+ .string()
2593
+ .min(1)
2594
+ .optional()
2595
+ .describe("Numeric Zalo user id (required for status, send, accept, reject and undo)"),
2596
+ message: z
2597
+ .string()
2598
+ .optional()
2599
+ .describe("Message sent with the friend request (only for 'send'; default: none)"),
2600
+ limit: z
2601
+ .number()
2602
+ .int()
2603
+ .min(1)
2604
+ .max(1000)
2605
+ .optional()
2606
+ .describe(
2607
+ "Most friends to return for 'list' (default 200; the reply says how many there are in all)",
2608
+ ),
2609
+ }),
2610
+ },
2611
+ async ({ action, userId, message, limit }) => {
2612
+ const needsUser = ["status", "send", "accept", "reject", "undo"].includes(action);
2613
+ const target = userId === undefined ? "" : String(userId).trim();
2614
+ if (needsUser) {
2615
+ if (!target) return err(`userId is required for action='${action}'.`);
2616
+ if (!/^\d{1,30}$/.test(target)) {
2617
+ return err(
2618
+ `userId must be a numeric Zalo id (got "${target}"). zalo_lookup_user finds one from a phone number.`,
2619
+ );
2620
+ }
2621
+ }
2622
+ try {
2623
+ switch (action) {
2624
+ case "status":
2625
+ return ok({ action, ...friendStatusFrom(await api.getFriendRequestStatus(target), target) });
2626
+ case "sent_requests": {
2627
+ let res = null;
2628
+ try {
2629
+ res = await api.getSentFriendRequest();
2630
+ } catch (e) {
2631
+ // Zalo answers code 112 when there are none, which is an empty list, not a failure.
2632
+ if (!/\b112\b/.test(String(e?.message))) throw e;
2633
+ }
2634
+ const requests = sentRequestsFrom(res);
2635
+ return ok({ action, count: requests.length, requests });
2636
+ }
2637
+ case "received_requests": {
2638
+ const requests = receivedRequestsFrom(await api.getFriendRecommendations());
2639
+ return ok({ action, count: requests.length, requests });
2640
+ }
2641
+ case "send":
2642
+ await api.sendFriendRequest(message ?? "", target);
2643
+ return ok({ action, success: true, userId: target });
2644
+ case "accept":
2645
+ await api.acceptFriendRequest(target);
2646
+ return ok({ action, success: true, userId: target });
2647
+ case "reject":
2648
+ await api.rejectFriendRequest(target);
2649
+ return ok({ action, success: true, userId: target });
2650
+ case "undo":
2651
+ await api.undoFriendRequest(target);
2652
+ return ok({ action, success: true, userId: target });
2653
+ case "list": {
2654
+ const { total, friends } = friendsFrom(await api.getAllFriends(), limit ?? 200);
2655
+ return ok({ action, total, count: friends.length, truncated: friends.length < total, friends });
2656
+ }
2657
+ default:
2658
+ return err(`Unsupported friend action: ${action}`);
2659
+ }
2660
+ } catch (e) {
2661
+ console.error("[mcp-tools] zalo_friend error:", e.message);
2662
+ // 'send' keeps the CLI's wording for Zalo's refusal codes (already friends, blocked, ...).
2663
+ const known =
2664
+ action === "send" ? describeFriendAddError(e.code ?? extractErrorCode(e.message), target) : null;
2665
+ return err(known || e.message);
2666
+ }
2667
+ },
2668
+ );
2669
+
2670
+ // --- zalo_group ---
2671
+ server.registerTool(
2672
+ "zalo_group",
2673
+ {
2674
+ title: "Manage Zalo Groups",
2675
+ description:
2676
+ "Leave, inspect, create and change the membership of a group (zalo_get_group_members lists who is " +
2677
+ "in one). Actions: " +
2678
+ "'info' (the group's name, description, creator, admins, member count and limit, creation time and " +
2679
+ "settings), " +
2680
+ "'create' (a new group named `name` with the members in userIds), " +
2681
+ "'add_members' (add userIds to the group), " +
2682
+ "'remove_member' (remove the one member userId from the group), " +
2683
+ "'leave' (leave the group; `silent` hides the usual notice from ordinary members; the owner still sees it). " +
2684
+ "groupId is the group's thread id (zalo_search_threads or zalo_list_conversations finds it); " +
2685
+ "user ids are numeric Zalo ids (zalo_lookup_user finds one from a phone number). " +
2686
+ "'leave' and 'remove_member' cannot be undone from here, so they need confirm: true (a plain " +
2687
+ "parameter, no prompt; a deployment can turn the check off with groupConfirm: false in " +
2688
+ "mcp-config.json or ZALO_MCP_GROUP_CONFIRM=0). Zalo can refuse some members " +
2689
+ "of a create or add_members: the reply lists the members it did and did not take.",
2690
+ inputSchema: z.object({
2691
+ action: z
2692
+ .enum(["leave", "info", "create", "add_members", "remove_member"])
2693
+ .describe("Group operation to perform"),
2694
+ groupId: z
2695
+ .string()
2696
+ .min(1)
2697
+ .optional()
2698
+ .describe("The group's id (required for leave, info, add_members and remove_member)"),
2699
+ name: z.string().optional().describe("The new group's name (required for 'create')"),
2700
+ userIds: z
2701
+ .array(z.string().min(1))
2702
+ .min(1)
2703
+ .max(100)
2704
+ .optional()
2705
+ .describe("Numeric Zalo user ids (required for 'create' and 'add_members')"),
2706
+ userId: z.string().min(1).optional().describe("Numeric Zalo user id (required for 'remove_member')"),
2707
+ silent: z
2708
+ .boolean()
2709
+ .optional()
2710
+ .describe(
2711
+ "Only for 'leave': hide the usual notice from ordinary members; the owner still sees it (default false)",
2712
+ ),
2713
+ confirm: z
2714
+ .boolean()
2715
+ .optional()
2716
+ .describe(
2717
+ "Must be true for 'leave' and 'remove_member', which cannot be undone from here " +
2718
+ "(ignored when the deployment has switched the check off)",
2719
+ ),
2720
+ }),
2721
+ },
2722
+ async ({ action, groupId, name, userIds, userId, silent, confirm }) => {
2723
+ const gid = groupId === undefined ? "" : String(groupId).trim();
2724
+ if (["leave", "info", "add_members", "remove_member"].includes(action)) {
2725
+ if (!gid) return err(`groupId is required for action='${action}'.`);
2726
+ if (!/^\d{1,30}$/.test(gid)) {
2727
+ return err(
2728
+ `groupId must be a numeric Zalo group id (got "${gid}"). zalo_search_threads finds one by name.`,
2729
+ );
2730
+ }
2731
+ }
2732
+ const badId = (id) => !/^\d{1,30}$/.test(id);
2733
+ let members = [];
2734
+ if (action === "create" || action === "add_members") {
2735
+ members = [...new Set((userIds ?? []).map((u) => String(u).trim()))];
2736
+ if (members.length === 0) return err(`userIds is required for action='${action}'.`);
2737
+ const bad = members.find(badId);
2738
+ if (bad !== undefined) {
2739
+ return err(
2740
+ `userIds must be numeric Zalo ids (got "${bad}"). zalo_lookup_user finds one from a phone number.`,
2741
+ );
2742
+ }
2743
+ }
2744
+ const target = userId === undefined ? "" : String(userId).trim();
2745
+ if (action === "remove_member") {
2746
+ if (!target) return err("userId is required for action='remove_member'.");
2747
+ if (badId(target)) {
2748
+ return err(
2749
+ `userId must be a numeric Zalo id (got "${target}"). zalo_get_group_members lists a group's members.`,
2750
+ );
2751
+ }
2752
+ }
2753
+ const groupName = name === undefined ? "" : String(name).trim();
2754
+ if (action === "create" && !groupName) return err("name is required for action='create'.");
2755
+
2756
+ // After validation, before Zalo: a refused call changes nothing and says exactly how to retry.
2757
+ if (CONFIRMED_ACTIONS.includes(action) && confirm !== true && groupConfirmRequired({ config })) {
2758
+ const what = action === "leave" ? `leave group ${gid}` : `remove ${target} from group ${gid}`;
2759
+ return err(
2760
+ `Not done: this would ${what}, which cannot be undone from here. Repeat the call with ` +
2761
+ "confirm: true to proceed. (An automatic or headless deployment can turn this check off " +
2762
+ "with groupConfirm: false in mcp-config.json, or ZALO_MCP_GROUP_CONFIRM=0 in the environment.)",
2763
+ );
2764
+ }
2765
+
2766
+ try {
2767
+ switch (action) {
2768
+ case "info": {
2769
+ const info = groupInfoFrom(await api.getGroupInfo([gid]), gid);
2770
+ if (!info) {
2771
+ return err(
2772
+ `Zalo's answer does not include group ${gid}: this account is not in it, or it is not ` +
2773
+ "a group id (a 1-1 conversation's id is the other person's uid).",
2774
+ );
2775
+ }
2776
+ return ok({ action, ...info });
2777
+ }
2778
+ case "create": {
2779
+ const made = createdGroupFrom(await api.createGroup({ name: groupName, members }), members);
2780
+ if (!made.groupId)
2781
+ return err("Zalo did not return a group id; the group may not have been created.");
2782
+ return ok({
2783
+ action,
2784
+ groupId: made.groupId,
2785
+ name: groupName,
2786
+ added: made.added,
2787
+ failed: made.failed,
2788
+ });
2789
+ }
2790
+ case "add_members": {
2791
+ const change = memberChangeFrom(await api.addUserToGroup(members, gid), members);
2792
+ if (change.done.length === 0) {
2793
+ const why = change.errorData
2794
+ ? ` (Zalo's error codes: ${JSON.stringify(change.errorData)})`
2795
+ : "";
2796
+ return err(
2797
+ `Zalo did not add any of the ${members.length} member(s) to group ${gid}${why}.`,
2798
+ );
2799
+ }
2800
+ return ok({
2801
+ action,
2802
+ groupId: gid,
2803
+ added: change.done,
2804
+ failed: change.failed,
2805
+ ...(change.errorData && { errorData: change.errorData }),
2806
+ });
2807
+ }
2808
+ case "remove_member": {
2809
+ const change = memberChangeFrom(await api.removeUserFromGroup([target], gid), [target]);
2810
+ if (change.failed.length > 0) {
2811
+ return err(
2812
+ `Zalo did not remove ${target} from group ${gid}: not a member, or this account is not ` +
2813
+ "allowed to remove members (a group's owner or an admin can).",
2814
+ );
2815
+ }
2816
+ return ok({ action, success: true, groupId: gid, userId: target });
2817
+ }
2818
+ case "leave": {
2819
+ const quiet = silent === true;
2820
+ const res = await api.leaveGroup(gid, quiet);
2821
+ const memberError = Array.isArray(res?.memberError) ? res.memberError : [];
2822
+ return ok({
2823
+ action,
2824
+ success: true,
2825
+ groupId: gid,
2826
+ silent: quiet,
2827
+ ...(memberError.length > 0 && { memberError }),
2828
+ });
2829
+ }
2830
+ default:
2831
+ return err(`Unsupported group action: ${action}`);
2832
+ }
2833
+ } catch (e) {
2834
+ console.error("[mcp-tools] zalo_group error:", e.message);
2835
+ if (action === "leave" && NOT_A_MEMBER_CODES.includes(Number(e.code ?? extractErrorCode(e.message)))) {
2836
+ return err(
2837
+ `This account is not in group ${gid} (already left, or never joined), so there is nothing to leave.`,
2838
+ );
2839
+ }
2840
+ if (
2841
+ action === "remove_member" &&
2842
+ Number(e.code ?? extractErrorCode(e.message)) === REMOVE_NOT_IN_GROUP
2843
+ ) {
2844
+ return err(
2845
+ `Zalo says ${target} is not in group ${gid} ("${e.message}", code ${REMOVE_NOT_IN_GROUP}).`,
2846
+ );
2847
+ }
2848
+ return err(e.message);
2849
+ }
2850
+ },
2851
+ );
2552
2852
  }
@@ -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;