@xuanyue202/wechat-mp 2026.3.21

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 (95) hide show
  1. package/README.md +74 -0
  2. package/dist/index.d.ts +20 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +59 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/api.d.ts +101 -0
  7. package/dist/src/api.d.ts.map +1 -0
  8. package/dist/src/api.js +142 -0
  9. package/dist/src/api.js.map +1 -0
  10. package/dist/src/channel.d.ts +296 -0
  11. package/dist/src/channel.d.ts.map +1 -0
  12. package/dist/src/channel.js +341 -0
  13. package/dist/src/channel.js.map +1 -0
  14. package/dist/src/config.d.ts +69 -0
  15. package/dist/src/config.d.ts.map +1 -0
  16. package/dist/src/config.js +167 -0
  17. package/dist/src/config.js.map +1 -0
  18. package/dist/src/crypto.d.ts +117 -0
  19. package/dist/src/crypto.d.ts.map +1 -0
  20. package/dist/src/crypto.js +270 -0
  21. package/dist/src/crypto.js.map +1 -0
  22. package/dist/src/crypto.test.d.ts +2 -0
  23. package/dist/src/crypto.test.d.ts.map +1 -0
  24. package/dist/src/crypto.test.js +76 -0
  25. package/dist/src/crypto.test.js.map +1 -0
  26. package/dist/src/dispatch.d.ts +15 -0
  27. package/dist/src/dispatch.d.ts.map +1 -0
  28. package/dist/src/dispatch.js +193 -0
  29. package/dist/src/dispatch.js.map +1 -0
  30. package/dist/src/dispatch.test.d.ts +2 -0
  31. package/dist/src/dispatch.test.d.ts.map +1 -0
  32. package/dist/src/dispatch.test.js +231 -0
  33. package/dist/src/dispatch.test.js.map +1 -0
  34. package/dist/src/inbound.d.ts +7 -0
  35. package/dist/src/inbound.d.ts.map +1 -0
  36. package/dist/src/inbound.js +82 -0
  37. package/dist/src/inbound.js.map +1 -0
  38. package/dist/src/onboarding.d.ts +25 -0
  39. package/dist/src/onboarding.d.ts.map +1 -0
  40. package/dist/src/onboarding.js +49 -0
  41. package/dist/src/onboarding.js.map +1 -0
  42. package/dist/src/outbound.d.ts +17 -0
  43. package/dist/src/outbound.d.ts.map +1 -0
  44. package/dist/src/outbound.js +55 -0
  45. package/dist/src/outbound.js.map +1 -0
  46. package/dist/src/outbound.test.d.ts +2 -0
  47. package/dist/src/outbound.test.d.ts.map +1 -0
  48. package/dist/src/outbound.test.js +175 -0
  49. package/dist/src/outbound.test.js.map +1 -0
  50. package/dist/src/probe.d.ts +15 -0
  51. package/dist/src/probe.d.ts.map +1 -0
  52. package/dist/src/probe.js +55 -0
  53. package/dist/src/probe.js.map +1 -0
  54. package/dist/src/runtime.d.ts +22 -0
  55. package/dist/src/runtime.d.ts.map +1 -0
  56. package/dist/src/runtime.js +33 -0
  57. package/dist/src/runtime.js.map +1 -0
  58. package/dist/src/send.d.ts +27 -0
  59. package/dist/src/send.d.ts.map +1 -0
  60. package/dist/src/send.js +103 -0
  61. package/dist/src/send.js.map +1 -0
  62. package/dist/src/state.d.ts +7 -0
  63. package/dist/src/state.d.ts.map +1 -0
  64. package/dist/src/state.js +109 -0
  65. package/dist/src/state.js.map +1 -0
  66. package/dist/src/text.d.ts +46 -0
  67. package/dist/src/text.d.ts.map +1 -0
  68. package/dist/src/text.js +192 -0
  69. package/dist/src/text.js.map +1 -0
  70. package/dist/src/text.test.d.ts +2 -0
  71. package/dist/src/text.test.d.ts.map +1 -0
  72. package/dist/src/text.test.js +110 -0
  73. package/dist/src/text.test.js.map +1 -0
  74. package/dist/src/token.d.ts +40 -0
  75. package/dist/src/token.d.ts.map +1 -0
  76. package/dist/src/token.js +154 -0
  77. package/dist/src/token.js.map +1 -0
  78. package/dist/src/token.test.d.ts +2 -0
  79. package/dist/src/token.test.d.ts.map +1 -0
  80. package/dist/src/token.test.js +74 -0
  81. package/dist/src/token.test.js.map +1 -0
  82. package/dist/src/types.d.ts +320 -0
  83. package/dist/src/types.d.ts.map +1 -0
  84. package/dist/src/types.js +2 -0
  85. package/dist/src/types.js.map +1 -0
  86. package/dist/src/webhook.d.ts +6 -0
  87. package/dist/src/webhook.d.ts.map +1 -0
  88. package/dist/src/webhook.js +381 -0
  89. package/dist/src/webhook.js.map +1 -0
  90. package/dist/src/webhook.test.d.ts +2 -0
  91. package/dist/src/webhook.test.d.ts.map +1 -0
  92. package/dist/src/webhook.test.js +737 -0
  93. package/dist/src/webhook.test.js.map +1 -0
  94. package/openclaw.plugin.json +83 -0
  95. package/package.json +103 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../src/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { PluginConfig } from "./types.js";
2
+ export interface WizardPrompter {
3
+ note: (message: string, title?: string) => Promise<void>;
4
+ }
5
+ export declare const wechatMpOnboardingAdapter: {
6
+ channel: "wechat-mp";
7
+ getStatus: (params: {
8
+ cfg: PluginConfig;
9
+ }) => Promise<{
10
+ channel: "wechat-mp";
11
+ configured: boolean;
12
+ statusLines: string[];
13
+ selectionHint: string;
14
+ quickstartScore: number;
15
+ }>;
16
+ configure: (params: {
17
+ cfg: PluginConfig;
18
+ prompter: WizardPrompter;
19
+ }) => Promise<{
20
+ cfg: PluginConfig;
21
+ accountId: string;
22
+ }>;
23
+ disable: (cfg: PluginConfig) => PluginConfig;
24
+ };
25
+ //# sourceMappingURL=onboarding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../src/onboarding.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,YAAY,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,eAAO,MAAM,yBAAyB;;wBAGV;QAAE,GAAG,EAAE,YAAY,CAAA;KAAE;;;;;;;wBAsBrB;QAAE,GAAG,EAAE,YAAY,CAAC;QAAC,QAAQ,EAAE,cAAc,CAAA;KAAE;;;;mBAiB1D,YAAY,KAAG,YAAY;CAU3C,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { DEFAULT_ACCOUNT_ID, resolveDefaultWechatMpAccountId, resolveWechatMpAccount } from "./config.js";
2
+ import { getAccountState } from "./state.js";
3
+ export const wechatMpOnboardingAdapter = {
4
+ channel: "wechat-mp",
5
+ getStatus: async (params) => {
6
+ const accountId = resolveDefaultWechatMpAccountId(params.cfg);
7
+ const account = resolveWechatMpAccount({ cfg: params.cfg, accountId });
8
+ const state = await getAccountState(accountId);
9
+ const configured = account.configured;
10
+ return {
11
+ channel: "wechat-mp",
12
+ configured,
13
+ statusLines: [
14
+ configured
15
+ ? `WeChat MP: 已配置${accountId !== DEFAULT_ACCOUNT_ID ? ` (${accountId})` : ""}`
16
+ : "WeChat MP: 需要 appId / token / (safe模式需要 encodingAESKey)",
17
+ `Webhook: ${(account.config.webhookPath ?? "/wechat-mp").trim() || "/wechat-mp"}`,
18
+ `ReplyMode: ${account.config.replyMode ?? "passive"}`,
19
+ state.lastError ? `最近错误: ${state.lastError}` : "最近错误: 无",
20
+ ],
21
+ selectionHint: configured ? "已配置" : "需要基础凭证",
22
+ quickstartScore: configured ? 2 : 0,
23
+ };
24
+ },
25
+ configure: async (params) => {
26
+ await params.prompter.note([
27
+ "1) 在公众号后台配置服务器地址",
28
+ "2) 准备 AppID / AppSecret / Token",
29
+ "3) safe/compat 模式下额外准备 EncodingAESKey",
30
+ "4) 确认 webhookPath 与宿主网关路由一致",
31
+ "5) P0 默认使用 passive reply,active send 依赖 appSecret",
32
+ ].join("\n"), "WeChat MP 配置");
33
+ return {
34
+ cfg: params.cfg,
35
+ accountId: resolveDefaultWechatMpAccountId(params.cfg),
36
+ };
37
+ },
38
+ disable: (cfg) => ({
39
+ ...cfg,
40
+ channels: {
41
+ ...cfg.channels,
42
+ "wechat-mp": {
43
+ ...cfg.channels?.["wechat-mp"],
44
+ enabled: false,
45
+ },
46
+ },
47
+ }),
48
+ };
49
+ //# sourceMappingURL=onboarding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/onboarding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAO7C,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,WAAoB;IAE7B,SAAS,EAAE,KAAK,EAAE,MAA6B,EAAE,EAAE;QACjD,MAAM,SAAS,GAAG,+BAA+B,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,sBAAsB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAEtC,OAAO;YACL,OAAO,EAAE,WAAoB;YAC7B,UAAU;YACV,WAAW,EAAE;gBACX,UAAU;oBACR,CAAC,CAAC,iBAAiB,SAAS,KAAK,kBAAkB,CAAC,CAAC,CAAC,KAAK,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBAC9E,CAAC,CAAC,yDAAyD;gBAC7D,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,YAAY,CAAC,CAAC,IAAI,EAAE,IAAI,YAAY,EAAE;gBACjF,cAAc,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,SAAS,EAAE;gBACrD,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;aACzD;YACD,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;YAC5C,eAAe,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACpC,CAAC;IACJ,CAAC;IAED,SAAS,EAAE,KAAK,EAAE,MAAuD,EAAE,EAAE;QAC3E,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CACxB;YACE,kBAAkB;YAClB,iCAAiC;YACjC,uCAAuC;YACvC,6BAA6B;YAC7B,mDAAmD;SACpD,CAAC,IAAI,CAAC,IAAI,CAAC,EACZ,cAAc,CACf,CAAC;QACF,OAAO;YACL,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,SAAS,EAAE,+BAA+B,CAAC,MAAM,CAAC,GAAG,CAAC;SACvD,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,CAAC,GAAiB,EAAgB,EAAE,CAAC,CAAC;QAC7C,GAAG,GAAG;QACN,QAAQ,EAAE;YACR,GAAG,GAAG,CAAC,QAAQ;YACf,WAAW,EAAE;gBACX,GAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAgC;gBAC9D,OAAO,EAAE,KAAK;aACf;SACF;KACF,CAAC;CACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { PluginConfig } from "./types.js";
2
+ export declare const wechatMpOutbound: {
3
+ deliveryMode: "direct";
4
+ textChunkLimit: number;
5
+ sendText: (params: {
6
+ cfg: PluginConfig;
7
+ accountId?: string;
8
+ to: string;
9
+ text: string;
10
+ }) => Promise<{
11
+ channel: string;
12
+ ok: boolean;
13
+ messageId: string;
14
+ error: Error | undefined;
15
+ }>;
16
+ };
17
+ //# sourceMappingURL=outbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.d.ts","sourceRoot":"","sources":["../../src/outbound.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqB/C,eAAO,MAAM,gBAAgB;;;uBAIF;QACvB,GAAG,EAAE,YAAY,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd;;;;;;CA+BF,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { resolveWechatMpAccount } from "./config.js";
2
+ import { sendWechatMpActiveText } from "./send.js";
3
+ import { normalizeWechatMpText, resolveRenderMarkdown } from "./text.js";
4
+ function parseTarget(rawTarget) {
5
+ let raw = String(rawTarget ?? "").trim();
6
+ if (!raw)
7
+ return null;
8
+ if (/^wechat-mp:/i.test(raw)) {
9
+ raw = raw.slice("wechat-mp:".length);
10
+ }
11
+ let accountId;
12
+ const atIndex = raw.lastIndexOf("@");
13
+ if (atIndex > 0 && atIndex < raw.length - 1) {
14
+ accountId = raw.slice(atIndex + 1).trim();
15
+ raw = raw.slice(0, atIndex);
16
+ }
17
+ if (/^user:/i.test(raw)) {
18
+ raw = raw.slice("user:".length);
19
+ }
20
+ const openId = raw.trim();
21
+ return openId ? { accountId, openId } : null;
22
+ }
23
+ export const wechatMpOutbound = {
24
+ deliveryMode: "direct",
25
+ textChunkLimit: 600,
26
+ sendText: async (params) => {
27
+ const parsed = parseTarget(params.to);
28
+ if (!parsed) {
29
+ return {
30
+ channel: "wechat-mp",
31
+ ok: false,
32
+ messageId: "",
33
+ error: new Error(`Unsupported target for WeChat MP: ${params.to}`),
34
+ };
35
+ }
36
+ const account = resolveWechatMpAccount({
37
+ cfg: params.cfg,
38
+ accountId: parsed.accountId ?? params.accountId,
39
+ });
40
+ const renderMarkdown = resolveRenderMarkdown(account.config);
41
+ const normalizedText = normalizeWechatMpText(params.text, renderMarkdown);
42
+ const result = await sendWechatMpActiveText({
43
+ account,
44
+ toUserName: parsed.openId,
45
+ text: normalizedText,
46
+ });
47
+ return {
48
+ channel: "wechat-mp",
49
+ ok: result.ok,
50
+ messageId: result.msgid ?? "",
51
+ error: result.ok ? undefined : new Error(result.error ?? "send failed"),
52
+ };
53
+ },
54
+ };
55
+ //# sourceMappingURL=outbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.js","sourceRoot":"","sources":["../../src/outbound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAGzE,SAAS,WAAW,CAAC,SAAiB;IACpC,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,SAA6B,CAAC;IAClC,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC1B,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,YAAY,EAAE,QAAiB;IAC/B,cAAc,EAAE,GAAG;IAEnB,QAAQ,EAAE,KAAK,EAAE,MAKhB,EAAE,EAAE;QACH,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE,WAAW;gBACpB,EAAE,EAAE,KAAK;gBACT,SAAS,EAAE,EAAE;gBACb,KAAK,EAAE,IAAI,KAAK,CAAC,qCAAqC,MAAM,CAAC,EAAE,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,sBAAsB,CAAC;YACrC,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS;SAChD,CAAC,CAAC;QAEH,MAAM,cAAc,GAAG,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,cAAc,GAAG,qBAAqB,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAE1E,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC;YAC1C,OAAO;YACP,UAAU,EAAE,MAAM,CAAC,MAAM;YACzB,IAAI,EAAE,cAAc;SACrB,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,SAAS,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE;YAC7B,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC;SACxE,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=outbound.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.test.d.ts","sourceRoot":"","sources":["../../src/outbound.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,175 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ const mocks = vi.hoisted(() => ({
3
+ resolveWechatMpAccount: vi.fn(),
4
+ sendWechatMpActiveText: vi.fn(),
5
+ }));
6
+ vi.mock("./config.js", () => ({
7
+ resolveWechatMpAccount: mocks.resolveWechatMpAccount,
8
+ }));
9
+ vi.mock("./send.js", () => ({
10
+ sendWechatMpActiveText: mocks.sendWechatMpActiveText,
11
+ }));
12
+ import { wechatMpOutbound } from "./outbound.js";
13
+ function createAccount(overrides) {
14
+ return {
15
+ accountId: "default",
16
+ name: "WeChat MP (default)",
17
+ enabled: true,
18
+ configured: true,
19
+ canSendActive: true,
20
+ config: {
21
+ appId: "wx-test-appid",
22
+ appSecret: "secret",
23
+ token: "token",
24
+ encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
25
+ webhookPath: "/wechat-mp",
26
+ messageMode: "safe",
27
+ replyMode: "passive",
28
+ },
29
+ ...overrides,
30
+ };
31
+ }
32
+ describe("wechatMpOutbound sendText", () => {
33
+ beforeEach(() => {
34
+ vi.clearAllMocks();
35
+ mocks.resolveWechatMpAccount.mockReturnValue(createAccount());
36
+ mocks.sendWechatMpActiveText.mockResolvedValue({ ok: true, msgid: "msg-1" });
37
+ });
38
+ it("normalizes markdown text by default", async () => {
39
+ const result = await wechatMpOutbound.sendText({
40
+ cfg: {},
41
+ to: "user:openid-123",
42
+ text: "**bold** and `code`",
43
+ });
44
+ expect(result.ok).toBe(true);
45
+ expect(result.channel).toBe("wechat-mp");
46
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
47
+ toUserName: "openid-123",
48
+ text: "bold and code",
49
+ }));
50
+ });
51
+ it("preserves markdown when renderMarkdown is false", async () => {
52
+ mocks.resolveWechatMpAccount.mockReturnValue(createAccount({
53
+ config: {
54
+ appId: "wx-test-appid",
55
+ appSecret: "secret",
56
+ token: "token",
57
+ encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
58
+ webhookPath: "/wechat-mp",
59
+ messageMode: "safe",
60
+ replyMode: "passive",
61
+ renderMarkdown: false,
62
+ },
63
+ }));
64
+ const result = await wechatMpOutbound.sendText({
65
+ cfg: {},
66
+ to: "user:openid-123",
67
+ text: "**bold** and `code`",
68
+ });
69
+ expect(result.ok).toBe(true);
70
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
71
+ toUserName: "openid-123",
72
+ text: "**bold** and `code`",
73
+ }));
74
+ });
75
+ it("normalizes headings to bracketed format", async () => {
76
+ const result = await wechatMpOutbound.sendText({
77
+ cfg: {},
78
+ to: "user:openid-123",
79
+ text: "# Main Title\n## Subtitle",
80
+ });
81
+ expect(result.ok).toBe(true);
82
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
83
+ toUserName: "openid-123",
84
+ text: "[Main Title]\n[Subtitle]",
85
+ }));
86
+ });
87
+ it("parses user prefix with account suffix", async () => {
88
+ mocks.resolveWechatMpAccount.mockReturnValue(createAccount({
89
+ accountId: "account-2",
90
+ config: {
91
+ appId: "wx-app-2",
92
+ appSecret: "secret-2",
93
+ token: "token-2",
94
+ webhookPath: "/wechat-mp-2",
95
+ messageMode: "safe",
96
+ replyMode: "active",
97
+ },
98
+ }));
99
+ const result = await wechatMpOutbound.sendText({
100
+ cfg: {},
101
+ accountId: "account-2",
102
+ to: "user:openid-456@account-2",
103
+ text: "hello",
104
+ });
105
+ expect(result.ok).toBe(true);
106
+ expect(mocks.resolveWechatMpAccount).toHaveBeenCalledWith(expect.objectContaining({
107
+ accountId: "account-2",
108
+ }));
109
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
110
+ toUserName: "openid-456",
111
+ text: "hello",
112
+ }));
113
+ });
114
+ it("parses wechat-mp prefix", async () => {
115
+ const result = await wechatMpOutbound.sendText({
116
+ cfg: {},
117
+ to: "wechat-mp:user:openid-789",
118
+ text: "test message",
119
+ });
120
+ expect(result.ok).toBe(true);
121
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
122
+ toUserName: "openid-789",
123
+ text: "test message",
124
+ }));
125
+ });
126
+ it("returns error for unsupported target format", async () => {
127
+ const result = await wechatMpOutbound.sendText({
128
+ cfg: {},
129
+ to: "", // Empty target is invalid
130
+ text: "hello",
131
+ });
132
+ expect(result.ok).toBe(false);
133
+ expect(result.error).toBeInstanceOf(Error);
134
+ expect(result.error?.message).toContain("Unsupported target");
135
+ expect(mocks.sendWechatMpActiveText).not.toHaveBeenCalled();
136
+ });
137
+ it("returns error when send fails", async () => {
138
+ mocks.sendWechatMpActiveText.mockResolvedValue({
139
+ ok: false,
140
+ error: "API error",
141
+ });
142
+ const result = await wechatMpOutbound.sendText({
143
+ cfg: {},
144
+ to: "user:openid-123",
145
+ text: "hello",
146
+ });
147
+ expect(result.ok).toBe(false);
148
+ expect(result.error).toBeInstanceOf(Error);
149
+ expect(result.error?.message).toBe("API error");
150
+ });
151
+ it("applies normalization parity with reply path", async () => {
152
+ // This test verifies that outbound and reply path have the same normalization behavior
153
+ const markdownInput = `# Heading
154
+ **bold** and *italic*
155
+ - list item
156
+ \`inline code\``;
157
+ const result = await wechatMpOutbound.sendText({
158
+ cfg: {},
159
+ to: "user:openid-123",
160
+ text: markdownInput,
161
+ });
162
+ expect(result.ok).toBe(true);
163
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
164
+ toUserName: "openid-123",
165
+ text: expect.stringContaining("[Heading]"),
166
+ }));
167
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
168
+ text: expect.not.stringContaining("**bold**"),
169
+ }));
170
+ expect(mocks.sendWechatMpActiveText).toHaveBeenCalledWith(expect.objectContaining({
171
+ text: expect.stringContaining("- list item"),
172
+ }));
173
+ });
174
+ });
175
+ //# sourceMappingURL=outbound.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.test.js","sourceRoot":"","sources":["../../src/outbound.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9D,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9B,sBAAsB,EAAE,EAAE,CAAC,EAAE,EAAE;IAC/B,sBAAsB,EAAE,EAAE,CAAC,EAAE,EAAE;CAChC,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5B,sBAAsB,EAAE,KAAK,CAAC,sBAAsB;CACrD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1B,sBAAsB,EAAE,KAAK,CAAC,sBAAsB;CACrD,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGjD,SAAS,aAAa,CAAC,SAA4C;IACjE,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,IAAI;QAChB,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE;YACN,KAAK,EAAE,eAAe;YACtB,SAAS,EAAE,QAAQ;YACnB,KAAK,EAAE,OAAO;YACd,cAAc,EAAE,6CAA6C;YAC7D,WAAW,EAAE,YAAY;YACzB,WAAW,EAAE,MAAM;YACnB,SAAS,EAAE,SAAS;SACrB;QACD,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,KAAK,CAAC,sBAAsB,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC;QAC9D,KAAK,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,iBAAiB;YACrB,IAAI,EAAE,qBAAqB;SAC5B,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,eAAe;SACtB,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,KAAK,CAAC,sBAAsB,CAAC,eAAe,CAC1C,aAAa,CAAC;YACZ,MAAM,EAAE;gBACN,KAAK,EAAE,eAAe;gBACtB,SAAS,EAAE,QAAQ;gBACnB,KAAK,EAAE,OAAO;gBACd,cAAc,EAAE,6CAA6C;gBAC7D,WAAW,EAAE,YAAY;gBACzB,WAAW,EAAE,MAAM;gBACnB,SAAS,EAAE,SAAS;gBACpB,cAAc,EAAE,KAAK;aACtB;SACF,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,iBAAiB;YACrB,IAAI,EAAE,qBAAqB;SAC5B,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,qBAAqB;SAC5B,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,iBAAiB;YACrB,IAAI,EAAE,2BAA2B;SAClC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,0BAA0B;SACjC,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,KAAK,CAAC,sBAAsB,CAAC,eAAe,CAC1C,aAAa,CAAC;YACZ,SAAS,EAAE,WAAW;YACtB,MAAM,EAAE;gBACN,KAAK,EAAE,UAAU;gBACjB,SAAS,EAAE,UAAU;gBACrB,KAAK,EAAE,SAAS;gBAChB,WAAW,EAAE,cAAc;gBAC3B,WAAW,EAAE,MAAM;gBACnB,SAAS,EAAE,QAAQ;aACpB;SACF,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,SAAS,EAAE,WAAW;YACtB,EAAE,EAAE,2BAA2B;YAC/B,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,SAAS,EAAE,WAAW;SACvB,CAAC,CACH,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,OAAO;SACd,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,2BAA2B;YAC/B,IAAI,EAAE,cAAc;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,cAAc;SACrB,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,EAAE,EAAE,0BAA0B;YAClC,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC9D,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC7C,KAAK,CAAC,sBAAsB,CAAC,iBAAiB,CAAC;YAC7C,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,iBAAiB;YACrB,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,uFAAuF;QACvF,MAAM,aAAa,GAAG;;;gBAGV,CAAC;QAEb,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC;YAC7C,GAAG,EAAE,EAAkB;YACvB,EAAE,EAAE,iBAAiB;YACrB,IAAI,EAAE,aAAa;SACpB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,UAAU,EAAE,YAAY;YACxB,IAAI,EAAE,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC;SAC3C,CAAC,CACH,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,UAAU,CAAC;SAC9C,CAAC,CACH,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CACvD,MAAM,CAAC,gBAAgB,CAAC;YACtB,IAAI,EAAE,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC;SAC7C,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { PluginConfig } from "./types.js";
2
+ export declare function probeWechatMpAccount(params: {
3
+ cfg: PluginConfig;
4
+ accountId?: string;
5
+ }): Promise<{
6
+ channel: "wechat-mp";
7
+ accountId: string;
8
+ configured: boolean;
9
+ canSendActive: boolean;
10
+ webhookPath?: string;
11
+ authOk: boolean;
12
+ lastInboundAt?: number;
13
+ error?: string;
14
+ }>;
15
+ //# sourceMappingURL=probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/probe.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,GAAG,EAAE,YAAY,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC;IACV,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAoDD"}
@@ -0,0 +1,55 @@
1
+ import { resolveWechatMpAccount } from "./config.js";
2
+ import { getAccessToken } from "./token.js";
3
+ import { getAccountState } from "./state.js";
4
+ export async function probeWechatMpAccount(params) {
5
+ const account = resolveWechatMpAccount({ cfg: params.cfg, accountId: params.accountId });
6
+ const state = await getAccountState(account.accountId);
7
+ if (!account.configured) {
8
+ return {
9
+ channel: "wechat-mp",
10
+ accountId: account.accountId,
11
+ configured: false,
12
+ canSendActive: account.canSendActive,
13
+ webhookPath: account.config.webhookPath ?? "/wechat-mp",
14
+ authOk: false,
15
+ lastInboundAt: state.lastInboundAt,
16
+ error: "missing appId/token/(encodingAESKey for safe/compat)",
17
+ };
18
+ }
19
+ if (!account.canSendActive) {
20
+ return {
21
+ channel: "wechat-mp",
22
+ accountId: account.accountId,
23
+ configured: true,
24
+ canSendActive: false,
25
+ webhookPath: account.config.webhookPath ?? "/wechat-mp",
26
+ authOk: true,
27
+ lastInboundAt: state.lastInboundAt,
28
+ };
29
+ }
30
+ try {
31
+ await getAccessToken(account);
32
+ return {
33
+ channel: "wechat-mp",
34
+ accountId: account.accountId,
35
+ configured: true,
36
+ canSendActive: true,
37
+ webhookPath: account.config.webhookPath ?? "/wechat-mp",
38
+ authOk: true,
39
+ lastInboundAt: state.lastInboundAt,
40
+ };
41
+ }
42
+ catch (error) {
43
+ return {
44
+ channel: "wechat-mp",
45
+ accountId: account.accountId,
46
+ configured: true,
47
+ canSendActive: true,
48
+ webhookPath: account.config.webhookPath ?? "/wechat-mp",
49
+ authOk: false,
50
+ lastInboundAt: state.lastInboundAt,
51
+ error: error instanceof Error ? error.message : String(error),
52
+ };
53
+ }
54
+ }
55
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/probe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAG1C;IAUC,MAAM,OAAO,GAAG,sBAAsB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACzF,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,KAAK;YACjB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,YAAY;YACvD,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,KAAK,EAAE,sDAAsD;SAC9D,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,IAAI;YAChB,aAAa,EAAE,KAAK;YACpB,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,YAAY;YACvD,MAAM,EAAE,IAAI;YACZ,aAAa,EAAE,KAAK,CAAC,aAAa;SACnC,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,IAAI;YAChB,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,YAAY;YACvD,MAAM,EAAE,IAAI;YACZ,aAAa,EAAE,KAAK,CAAC,aAAa;SACnC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,IAAI;YAChB,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,YAAY;YACvD,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { PluginRuntime } from "./types.js";
2
+ /**
3
+ * Set the WeChat MP plugin runtime.
4
+ * Called during plugin registration to inject host runtime capabilities.
5
+ */
6
+ export declare function setWechatMpRuntime(next: PluginRuntime): void;
7
+ /**
8
+ * Get the WeChat MP plugin runtime.
9
+ * Throws if runtime is not initialized.
10
+ */
11
+ export declare function getWechatMpRuntime(): PluginRuntime;
12
+ /**
13
+ * Try to get the WeChat MP plugin runtime.
14
+ * Returns null if not initialized instead of throwing.
15
+ */
16
+ export declare function tryGetWechatMpRuntime(): PluginRuntime | null;
17
+ /**
18
+ * Clear the WeChat MP plugin runtime.
19
+ * Used for cleanup and testing.
20
+ */
21
+ export declare function clearWechatMpRuntime(): void;
22
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAE5D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,CAKlD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,aAAa,GAAG,IAAI,CAE5D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -0,0 +1,33 @@
1
+ let runtime = null;
2
+ /**
3
+ * Set the WeChat MP plugin runtime.
4
+ * Called during plugin registration to inject host runtime capabilities.
5
+ */
6
+ export function setWechatMpRuntime(next) {
7
+ runtime = next;
8
+ }
9
+ /**
10
+ * Get the WeChat MP plugin runtime.
11
+ * Throws if runtime is not initialized.
12
+ */
13
+ export function getWechatMpRuntime() {
14
+ if (!runtime) {
15
+ throw new Error("WeChat MP runtime not initialized.");
16
+ }
17
+ return runtime;
18
+ }
19
+ /**
20
+ * Try to get the WeChat MP plugin runtime.
21
+ * Returns null if not initialized instead of throwing.
22
+ */
23
+ export function tryGetWechatMpRuntime() {
24
+ return runtime;
25
+ }
26
+ /**
27
+ * Clear the WeChat MP plugin runtime.
28
+ * Used for cleanup and testing.
29
+ */
30
+ export function clearWechatMpRuntime() {
31
+ runtime = null;
32
+ }
33
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"AAEA,IAAI,OAAO,GAAyB,IAAI,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAmB;IACpD,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { ResolvedWechatMpAccount, WechatMpActiveDeliveryMode, WechatMpReplyMode } from "./types.js";
2
+ export type PassiveReplyResult = {
3
+ ok: boolean;
4
+ body?: string;
5
+ error?: string;
6
+ };
7
+ export type ActiveSendResult = {
8
+ ok: boolean;
9
+ msgid?: string;
10
+ error?: string;
11
+ };
12
+ export declare function buildPassiveTextReply(params: {
13
+ account: ResolvedWechatMpAccount;
14
+ toUserName: string;
15
+ fromUserName: string;
16
+ content: string;
17
+ timestamp?: string;
18
+ nonce?: string;
19
+ }): PassiveReplyResult;
20
+ export declare function sendWechatMpActiveText(params: {
21
+ account: ResolvedWechatMpAccount;
22
+ toUserName: string;
23
+ text: string;
24
+ }): Promise<ActiveSendResult>;
25
+ export declare function resolveReplyMode(account: ResolvedWechatMpAccount): WechatMpReplyMode;
26
+ export declare function resolveActiveDeliveryMode(account: ResolvedWechatMpAccount): WechatMpActiveDeliveryMode;
27
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/send.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,uBAAuB,EACvB,0BAA0B,EAC1B,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,OAAO,EAAE,uBAAuB,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,kBAAkB,CAoDrB;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,OAAO,EAAE,uBAAuB,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA2B5B;AA0BD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,iBAAiB,CAEpF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,uBAAuB,GAAG,0BAA0B,CAEtG"}
@@ -0,0 +1,103 @@
1
+ import { buildEncryptedReplyXml, buildPlainReplyXml, computeMsgSignature, encryptWechatMpMessage, } from "./crypto.js";
2
+ import { sendWechatMpMessage } from "./api.js";
3
+ import { WECHAT_TEXT_BYTE_LIMIT, getUtf8ByteLength, splitTextByByteLimit, } from "./text.js";
4
+ export function buildPassiveTextReply(params) {
5
+ const content = params.content.trim();
6
+ if (!content) {
7
+ return { ok: false, error: "empty passive reply content" };
8
+ }
9
+ const createTime = Number(params.timestamp ?? Math.floor(Date.now() / 1000));
10
+ const plainXml = buildPlainReplyXml({
11
+ toUserName: params.toUserName,
12
+ fromUserName: params.fromUserName,
13
+ createTime,
14
+ msgType: "text",
15
+ content,
16
+ });
17
+ if (params.account.config.messageMode === "plain" || !params.account.config.encodingAESKey) {
18
+ return { ok: true, body: plainXml };
19
+ }
20
+ if (!params.account.config.appId || !params.account.config.token) {
21
+ return { ok: false, error: "missing appId or token for encrypted passive reply" };
22
+ }
23
+ try {
24
+ const timestamp = params.timestamp ?? String(Math.floor(Date.now() / 1000));
25
+ const nonce = params.nonce ?? Math.random().toString(36).slice(2, 10);
26
+ const encrypted = encryptWechatMpMessage({
27
+ encodingAESKey: params.account.config.encodingAESKey,
28
+ appId: params.account.config.appId,
29
+ plaintext: plainXml,
30
+ }).encrypt;
31
+ const signature = computeMsgSignature({
32
+ token: params.account.config.token,
33
+ timestamp,
34
+ nonce,
35
+ encrypt: encrypted,
36
+ });
37
+ return {
38
+ ok: true,
39
+ body: buildEncryptedReplyXml({
40
+ encrypt: encrypted,
41
+ signature,
42
+ timestamp,
43
+ nonce,
44
+ }),
45
+ };
46
+ }
47
+ catch (error) {
48
+ return {
49
+ ok: false,
50
+ error: error instanceof Error ? error.message : String(error),
51
+ };
52
+ }
53
+ }
54
+ export async function sendWechatMpActiveText(params) {
55
+ if (!params.account.canSendActive) {
56
+ return {
57
+ ok: false,
58
+ error: "Account not configured for active sending (missing appId/appSecret)",
59
+ };
60
+ }
61
+ const byteLength = getUtf8ByteLength(params.text);
62
+ // Single message within limit, send directly
63
+ if (byteLength <= WECHAT_TEXT_BYTE_LIMIT) {
64
+ return sendSingleMessage(params.account, params.toUserName, params.text);
65
+ }
66
+ // Exceeds limit, split and send chunks
67
+ const chunks = splitTextByByteLimit(params.text, WECHAT_TEXT_BYTE_LIMIT);
68
+ let lastResult = { ok: true };
69
+ for (const chunk of chunks) {
70
+ lastResult = await sendSingleMessage(params.account, params.toUserName, chunk);
71
+ if (!lastResult.ok) {
72
+ return lastResult; // Stop on failure
73
+ }
74
+ }
75
+ return lastResult;
76
+ }
77
+ async function sendSingleMessage(account, toUserName, text) {
78
+ try {
79
+ const result = await sendWechatMpMessage(account, {
80
+ touser: toUserName,
81
+ msgtype: "text",
82
+ text: { content: text },
83
+ });
84
+ return {
85
+ ok: result.errcode === 0,
86
+ msgid: result.msgid ? String(result.msgid) : undefined,
87
+ error: result.errcode === 0 ? undefined : result.errmsg,
88
+ };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ ok: false,
93
+ error: error instanceof Error ? error.message : String(error),
94
+ };
95
+ }
96
+ }
97
+ export function resolveReplyMode(account) {
98
+ return account.config.replyMode ?? "passive";
99
+ }
100
+ export function resolveActiveDeliveryMode(account) {
101
+ return account.config.activeDeliveryMode ?? "split";
102
+ }
103
+ //# sourceMappingURL=send.js.map