@ecat/weixin-bot-cli 0.1.0

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 (81) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +77 -0
  3. package/dist/api/api.d.ts +47 -0
  4. package/dist/api/api.js +233 -0
  5. package/dist/api/api.js.map +1 -0
  6. package/dist/api/config-cache.d.ts +18 -0
  7. package/dist/api/config-cache.js +64 -0
  8. package/dist/api/config-cache.js.map +1 -0
  9. package/dist/api/session-guard.d.ts +15 -0
  10. package/dist/api/session-guard.js +49 -0
  11. package/dist/api/session-guard.js.map +1 -0
  12. package/dist/api/types.d.ts +201 -0
  13. package/dist/api/types.js +35 -0
  14. package/dist/api/types.js.map +1 -0
  15. package/dist/auth/accounts.d.ts +30 -0
  16. package/dist/auth/accounts.js +158 -0
  17. package/dist/auth/accounts.js.map +1 -0
  18. package/dist/auth/login-qr.d.ts +31 -0
  19. package/dist/auth/login-qr.js +235 -0
  20. package/dist/auth/login-qr.js.map +1 -0
  21. package/dist/cdn/aes-ecb.d.ts +6 -0
  22. package/dist/cdn/aes-ecb.js +19 -0
  23. package/dist/cdn/aes-ecb.js.map +1 -0
  24. package/dist/cdn/cdn-upload.d.ts +17 -0
  25. package/dist/cdn/cdn-upload.js +73 -0
  26. package/dist/cdn/cdn-upload.js.map +1 -0
  27. package/dist/cdn/cdn-url.d.ts +13 -0
  28. package/dist/cdn/cdn-url.js +14 -0
  29. package/dist/cdn/cdn-url.js.map +1 -0
  30. package/dist/cdn/pic-decrypt.d.ts +9 -0
  31. package/dist/cdn/pic-decrypt.js +89 -0
  32. package/dist/cdn/pic-decrypt.js.map +1 -0
  33. package/dist/cdn/upload.d.ts +42 -0
  34. package/dist/cdn/upload.js +106 -0
  35. package/dist/cdn/upload.js.map +1 -0
  36. package/dist/cli.d.ts +2 -0
  37. package/dist/cli.js +127 -0
  38. package/dist/cli.js.map +1 -0
  39. package/dist/config/config-schema.d.ts +16 -0
  40. package/dist/config/config-schema.js +17 -0
  41. package/dist/config/config-schema.js.map +1 -0
  42. package/dist/media/media-download.d.ts +18 -0
  43. package/dist/media/media-download.js +95 -0
  44. package/dist/media/media-download.js.map +1 -0
  45. package/dist/media/mime.d.ts +6 -0
  46. package/dist/media/mime.js +73 -0
  47. package/dist/media/mime.js.map +1 -0
  48. package/dist/media/silk-transcode.d.ts +8 -0
  49. package/dist/media/silk-transcode.js +64 -0
  50. package/dist/media/silk-transcode.js.map +1 -0
  51. package/dist/messaging/debug-mode.d.ts +9 -0
  52. package/dist/messaging/debug-mode.js +63 -0
  53. package/dist/messaging/debug-mode.js.map +1 -0
  54. package/dist/messaging/inbound.d.ts +69 -0
  55. package/dist/messaging/inbound.js +201 -0
  56. package/dist/messaging/inbound.js.map +1 -0
  57. package/dist/messaging/send-media.d.ts +21 -0
  58. package/dist/messaging/send-media.js +54 -0
  59. package/dist/messaging/send-media.js.map +1 -0
  60. package/dist/messaging/send.d.ts +70 -0
  61. package/dist/messaging/send.js +203 -0
  62. package/dist/messaging/send.js.map +1 -0
  63. package/dist/monitor/monitor.d.ts +12 -0
  64. package/dist/monitor/monitor.js +145 -0
  65. package/dist/monitor/monitor.js.map +1 -0
  66. package/dist/storage/state-dir.d.ts +2 -0
  67. package/dist/storage/state-dir.js +8 -0
  68. package/dist/storage/state-dir.js.map +1 -0
  69. package/dist/storage/sync-buf.d.ts +20 -0
  70. package/dist/storage/sync-buf.js +64 -0
  71. package/dist/storage/sync-buf.js.map +1 -0
  72. package/dist/util/logger.d.ts +14 -0
  73. package/dist/util/logger.js +119 -0
  74. package/dist/util/logger.js.map +1 -0
  75. package/dist/util/random.d.ts +10 -0
  76. package/dist/util/random.js +16 -0
  77. package/dist/util/random.js.map +1 -0
  78. package/dist/util/redact.d.ts +20 -0
  79. package/dist/util/redact.js +54 -0
  80. package/dist/util/redact.js.map +1 -0
  81. package/package.json +38 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Per-bot debug mode toggle, persisted to disk so it survives gateway restarts.
3
+ *
4
+ * State file: `<stateDir>/openclaw-weixin/debug-mode.json`
5
+ * Format: `{ "accounts": { "<accountId>": true, ... } }`
6
+ *
7
+ * When enabled, processOneMessage appends a timing summary after each
8
+ * AI reply is delivered to the user.
9
+ */
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { resolveStateDir } from "../storage/state-dir.js";
13
+ import { logger } from "../util/logger.js";
14
+ function resolveDebugModePath() {
15
+ return path.join(resolveStateDir(), "openclaw-weixin", "debug-mode.json");
16
+ }
17
+ function loadState() {
18
+ try {
19
+ const raw = fs.readFileSync(resolveDebugModePath(), "utf-8");
20
+ const parsed = JSON.parse(raw);
21
+ if (parsed && typeof parsed.accounts === "object")
22
+ return parsed;
23
+ }
24
+ catch {
25
+ // missing or corrupt — start fresh
26
+ }
27
+ return { accounts: {} };
28
+ }
29
+ function saveState(state) {
30
+ const filePath = resolveDebugModePath();
31
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
32
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
33
+ }
34
+ /** Toggle debug mode for a bot account. Returns the new state. */
35
+ export function toggleDebugMode(accountId) {
36
+ const state = loadState();
37
+ const next = !state.accounts[accountId];
38
+ state.accounts[accountId] = next;
39
+ try {
40
+ saveState(state);
41
+ }
42
+ catch (err) {
43
+ logger.error(`debug-mode: failed to persist state: ${String(err)}`);
44
+ }
45
+ return next;
46
+ }
47
+ /** Check whether debug mode is active for a bot account. */
48
+ export function isDebugMode(accountId) {
49
+ return loadState().accounts[accountId] === true;
50
+ }
51
+ /**
52
+ * Reset internal state — only for tests.
53
+ * @internal
54
+ */
55
+ export function _resetForTest() {
56
+ try {
57
+ fs.unlinkSync(resolveDebugModePath());
58
+ }
59
+ catch {
60
+ // ignore if not present
61
+ }
62
+ }
63
+ //# sourceMappingURL=debug-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-mode.js","sourceRoot":"","sources":["../../src/messaging/debug-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAM3C,SAAS,oBAAoB;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,EAAE,OAAO,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QACjD,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAqB;IACtC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACxC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,SAAS,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,69 @@
1
+ import type { WeixinMessage, MessageItem } from "../api/types.js";
2
+ /**
3
+ * Restore persisted context tokens for an account into the in-memory map.
4
+ * Called once during gateway startAccount to survive restarts.
5
+ */
6
+ export declare function restoreContextTokens(accountId: string): void;
7
+ /** Remove all context tokens for a given account (memory + disk). */
8
+ export declare function clearContextTokensForAccount(accountId: string): void;
9
+ /** Store a context token for a given account+user pair (memory + disk). */
10
+ export declare function setContextToken(accountId: string, userId: string, token: string): void;
11
+ /** Retrieve the cached context token for a given account+user pair. */
12
+ export declare function getContextToken(accountId: string, userId: string): string | undefined;
13
+ /**
14
+ * Find all accountIds that have an active contextToken for the given userId.
15
+ * Used to infer the sending bot account from the recipient address when
16
+ * accountId is not explicitly provided (e.g. cron delivery).
17
+ *
18
+ * Returns all matching accountIds (not just the first) so the caller can
19
+ * detect ambiguity when multiple accounts have sessions with the same user.
20
+ */
21
+ export declare function findAccountIdsByContextToken(accountIds: string[], userId: string): string[];
22
+ /** Inbound context passed to the OpenClaw core pipeline (matches MsgContext shape). */
23
+ export type WeixinMsgContext = {
24
+ Body: string;
25
+ From: string;
26
+ To: string;
27
+ AccountId: string;
28
+ OriginatingChannel: "openclaw-weixin";
29
+ OriginatingTo: string;
30
+ MessageSid: string;
31
+ Timestamp?: number;
32
+ Provider: "openclaw-weixin";
33
+ ChatType: "direct";
34
+ /** Set by monitor after resolveAgentRoute so dispatchReplyFromConfig uses the correct session. */
35
+ SessionKey?: string;
36
+ context_token?: string;
37
+ MediaUrl?: string;
38
+ MediaPath?: string;
39
+ MediaType?: string;
40
+ /** Raw message body for framework command authorization. */
41
+ CommandBody?: string;
42
+ /** Whether the sender is authorized to execute slash commands. */
43
+ CommandAuthorized?: boolean;
44
+ };
45
+ /** Returns true if the message item is a media type (image, video, file, or voice). */
46
+ export declare function isMediaItem(item: MessageItem): boolean;
47
+ export type WeixinInboundMediaOpts = {
48
+ /** Local path to decrypted image file. */
49
+ decryptedPicPath?: string;
50
+ /** Local path to transcoded/raw voice file (.wav or .silk). */
51
+ decryptedVoicePath?: string;
52
+ /** MIME type for the voice file (e.g. "audio/wav" or "audio/silk"). */
53
+ voiceMediaType?: string;
54
+ /** Local path to decrypted file attachment. */
55
+ decryptedFilePath?: string;
56
+ /** MIME type for the file attachment (guessed from file_name). */
57
+ fileMediaType?: string;
58
+ /** Local path to decrypted video file. */
59
+ decryptedVideoPath?: string;
60
+ };
61
+ /**
62
+ * Convert a WeixinMessage from getUpdates to the inbound MsgContext for the core pipeline.
63
+ * Media: only pass MediaPath (local file, after CDN download + decrypt).
64
+ * We never pass MediaUrl — the upstream CDN URL is encrypted/auth-only.
65
+ * Priority when multiple media types present: image > video > file > voice.
66
+ */
67
+ export declare function weixinMessageToMsgContext(msg: WeixinMessage, accountId: string, opts?: WeixinInboundMediaOpts): WeixinMsgContext;
68
+ /** Extract the context_token from an inbound WeixinMsgContext. */
69
+ export declare function getContextTokenFromMsgContext(ctx: WeixinMsgContext): string | undefined;
@@ -0,0 +1,201 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { logger } from "../util/logger.js";
4
+ import { generateId } from "../util/random.js";
5
+ import { MessageItemType } from "../api/types.js";
6
+ import { resolveStateDir } from "../storage/state-dir.js";
7
+ // ---------------------------------------------------------------------------
8
+ // Context token store (in-process cache + disk persistence)
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * contextToken is issued per-message by the Weixin getupdates API and must
12
+ * be echoed verbatim in every outbound send. The in-memory map is the primary
13
+ * lookup; a disk-backed file per account ensures tokens survive gateway restarts.
14
+ */
15
+ const contextTokenStore = new Map();
16
+ function contextTokenKey(accountId, userId) {
17
+ return `${accountId}:${userId}`;
18
+ }
19
+ // ---------------------------------------------------------------------------
20
+ // Disk persistence helpers
21
+ // ---------------------------------------------------------------------------
22
+ function resolveContextTokenFilePath(accountId) {
23
+ return path.join(resolveStateDir(), "openclaw-weixin", "accounts", `${accountId}.context-tokens.json`);
24
+ }
25
+ /** Persist all context tokens for a given account to disk. */
26
+ function persistContextTokens(accountId) {
27
+ const prefix = `${accountId}:`;
28
+ const tokens = {};
29
+ for (const [k, v] of contextTokenStore) {
30
+ if (k.startsWith(prefix)) {
31
+ tokens[k.slice(prefix.length)] = v;
32
+ }
33
+ }
34
+ const filePath = resolveContextTokenFilePath(accountId);
35
+ try {
36
+ const dir = path.dirname(filePath);
37
+ fs.mkdirSync(dir, { recursive: true });
38
+ fs.writeFileSync(filePath, JSON.stringify(tokens, null, 0), "utf-8");
39
+ }
40
+ catch (err) {
41
+ logger.warn(`persistContextTokens: failed to write ${filePath}: ${String(err)}`);
42
+ }
43
+ }
44
+ /**
45
+ * Restore persisted context tokens for an account into the in-memory map.
46
+ * Called once during gateway startAccount to survive restarts.
47
+ */
48
+ export function restoreContextTokens(accountId) {
49
+ const filePath = resolveContextTokenFilePath(accountId);
50
+ try {
51
+ if (!fs.existsSync(filePath))
52
+ return;
53
+ const raw = fs.readFileSync(filePath, "utf-8");
54
+ const tokens = JSON.parse(raw);
55
+ let count = 0;
56
+ for (const [userId, token] of Object.entries(tokens)) {
57
+ if (typeof token === "string" && token) {
58
+ contextTokenStore.set(contextTokenKey(accountId, userId), token);
59
+ count++;
60
+ }
61
+ }
62
+ logger.info(`restoreContextTokens: restored ${count} tokens for account=${accountId}`);
63
+ }
64
+ catch (err) {
65
+ logger.warn(`restoreContextTokens: failed to read ${filePath}: ${String(err)}`);
66
+ }
67
+ }
68
+ /** Remove all context tokens for a given account (memory + disk). */
69
+ export function clearContextTokensForAccount(accountId) {
70
+ const prefix = `${accountId}:`;
71
+ for (const k of [...contextTokenStore.keys()]) {
72
+ if (k.startsWith(prefix)) {
73
+ contextTokenStore.delete(k);
74
+ }
75
+ }
76
+ const filePath = resolveContextTokenFilePath(accountId);
77
+ try {
78
+ if (fs.existsSync(filePath))
79
+ fs.unlinkSync(filePath);
80
+ }
81
+ catch (err) {
82
+ logger.warn(`clearContextTokensForAccount: failed to remove ${filePath}: ${String(err)}`);
83
+ }
84
+ logger.info(`clearContextTokensForAccount: cleared tokens for account=${accountId}`);
85
+ }
86
+ /** Store a context token for a given account+user pair (memory + disk). */
87
+ export function setContextToken(accountId, userId, token) {
88
+ const k = contextTokenKey(accountId, userId);
89
+ logger.debug(`setContextToken: key=${k}`);
90
+ contextTokenStore.set(k, token);
91
+ persistContextTokens(accountId);
92
+ }
93
+ /** Retrieve the cached context token for a given account+user pair. */
94
+ export function getContextToken(accountId, userId) {
95
+ const k = contextTokenKey(accountId, userId);
96
+ const val = contextTokenStore.get(k);
97
+ logger.debug(`getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`);
98
+ return val;
99
+ }
100
+ /**
101
+ * Find all accountIds that have an active contextToken for the given userId.
102
+ * Used to infer the sending bot account from the recipient address when
103
+ * accountId is not explicitly provided (e.g. cron delivery).
104
+ *
105
+ * Returns all matching accountIds (not just the first) so the caller can
106
+ * detect ambiguity when multiple accounts have sessions with the same user.
107
+ */
108
+ export function findAccountIdsByContextToken(accountIds, userId) {
109
+ return accountIds.filter((id) => contextTokenStore.has(contextTokenKey(id, userId)));
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Message ID generation
113
+ // ---------------------------------------------------------------------------
114
+ function generateMessageSid() {
115
+ return generateId("openclaw-weixin");
116
+ }
117
+ /** Returns true if the message item is a media type (image, video, file, or voice). */
118
+ export function isMediaItem(item) {
119
+ return (item.type === MessageItemType.IMAGE ||
120
+ item.type === MessageItemType.VIDEO ||
121
+ item.type === MessageItemType.FILE ||
122
+ item.type === MessageItemType.VOICE);
123
+ }
124
+ function bodyFromItemList(itemList) {
125
+ if (!itemList?.length)
126
+ return "";
127
+ for (const item of itemList) {
128
+ if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
129
+ const text = String(item.text_item.text);
130
+ const ref = item.ref_msg;
131
+ if (!ref)
132
+ return text;
133
+ // Quoted media is passed as MediaPath; only include the current text as body.
134
+ if (ref.message_item && isMediaItem(ref.message_item))
135
+ return text;
136
+ // Build quoted context from both title and message_item content.
137
+ const parts = [];
138
+ if (ref.title)
139
+ parts.push(ref.title);
140
+ if (ref.message_item) {
141
+ const refBody = bodyFromItemList([ref.message_item]);
142
+ if (refBody)
143
+ parts.push(refBody);
144
+ }
145
+ if (!parts.length)
146
+ return text;
147
+ return `[引用: ${parts.join(" | ")}]\n${text}`;
148
+ }
149
+ // 语音转文字:如果语音消息有 text 字段,直接使用文字内容
150
+ if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
151
+ return item.voice_item.text;
152
+ }
153
+ }
154
+ return "";
155
+ }
156
+ /**
157
+ * Convert a WeixinMessage from getUpdates to the inbound MsgContext for the core pipeline.
158
+ * Media: only pass MediaPath (local file, after CDN download + decrypt).
159
+ * We never pass MediaUrl — the upstream CDN URL is encrypted/auth-only.
160
+ * Priority when multiple media types present: image > video > file > voice.
161
+ */
162
+ export function weixinMessageToMsgContext(msg, accountId, opts) {
163
+ const from_user_id = msg.from_user_id ?? "";
164
+ const ctx = {
165
+ Body: bodyFromItemList(msg.item_list),
166
+ From: from_user_id,
167
+ To: from_user_id,
168
+ AccountId: accountId,
169
+ OriginatingChannel: "openclaw-weixin",
170
+ OriginatingTo: from_user_id,
171
+ MessageSid: generateMessageSid(),
172
+ Timestamp: msg.create_time_ms,
173
+ Provider: "openclaw-weixin",
174
+ ChatType: "direct",
175
+ };
176
+ if (msg.context_token) {
177
+ ctx.context_token = msg.context_token;
178
+ }
179
+ if (opts?.decryptedPicPath) {
180
+ ctx.MediaPath = opts.decryptedPicPath;
181
+ ctx.MediaType = "image/*";
182
+ }
183
+ else if (opts?.decryptedVideoPath) {
184
+ ctx.MediaPath = opts.decryptedVideoPath;
185
+ ctx.MediaType = "video/mp4";
186
+ }
187
+ else if (opts?.decryptedFilePath) {
188
+ ctx.MediaPath = opts.decryptedFilePath;
189
+ ctx.MediaType = opts.fileMediaType ?? "application/octet-stream";
190
+ }
191
+ else if (opts?.decryptedVoicePath) {
192
+ ctx.MediaPath = opts.decryptedVoicePath;
193
+ ctx.MediaType = opts.voiceMediaType ?? "audio/wav";
194
+ }
195
+ return ctx;
196
+ }
197
+ /** Extract the context_token from an inbound WeixinMsgContext. */
198
+ export function getContextTokenFromMsgContext(ctx) {
199
+ return ctx.context_token;
200
+ }
201
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../src/messaging/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { WeixinApiOptions } from "../api/api.js";
2
+ /**
3
+ * Upload a local file and send it as a weixin message, routing by MIME type:
4
+ * video/* → uploadVideoToWeixin + sendVideoMessageWeixin
5
+ * image/* → uploadFileToWeixin + sendImageMessageWeixin
6
+ * else → uploadFileAttachmentToWeixin + sendFileMessageWeixin
7
+ *
8
+ * Used by both the auto-reply deliver path (monitor.ts) and the outbound
9
+ * sendMedia path (channel.ts) so they stay in sync.
10
+ */
11
+ export declare function sendWeixinMediaFile(params: {
12
+ filePath: string;
13
+ to: string;
14
+ text: string;
15
+ opts: WeixinApiOptions & {
16
+ contextToken?: string;
17
+ };
18
+ cdnBaseUrl: string;
19
+ }): Promise<{
20
+ messageId: string;
21
+ }>;
@@ -0,0 +1,54 @@
1
+ import path from "node:path";
2
+ import { logger } from "../util/logger.js";
3
+ import { getMimeFromFilename } from "../media/mime.js";
4
+ import { sendFileMessageWeixin, sendImageMessageWeixin, sendVideoMessageWeixin } from "./send.js";
5
+ import { uploadFileAttachmentToWeixin, uploadFileToWeixin, uploadVideoToWeixin } from "../cdn/upload.js";
6
+ /**
7
+ * Upload a local file and send it as a weixin message, routing by MIME type:
8
+ * video/* → uploadVideoToWeixin + sendVideoMessageWeixin
9
+ * image/* → uploadFileToWeixin + sendImageMessageWeixin
10
+ * else → uploadFileAttachmentToWeixin + sendFileMessageWeixin
11
+ *
12
+ * Used by both the auto-reply deliver path (monitor.ts) and the outbound
13
+ * sendMedia path (channel.ts) so they stay in sync.
14
+ */
15
+ export async function sendWeixinMediaFile(params) {
16
+ const { filePath, to, text, opts, cdnBaseUrl } = params;
17
+ const mime = getMimeFromFilename(filePath);
18
+ const uploadOpts = { baseUrl: opts.baseUrl, token: opts.token };
19
+ if (mime.startsWith("video/")) {
20
+ logger.info(`[weixin] sendWeixinMediaFile: uploading video filePath=${filePath} to=${to}`);
21
+ const uploaded = await uploadVideoToWeixin({
22
+ filePath,
23
+ toUserId: to,
24
+ opts: uploadOpts,
25
+ cdnBaseUrl,
26
+ });
27
+ logger.info(`[weixin] sendWeixinMediaFile: video upload done filekey=${uploaded.filekey} size=${uploaded.fileSize}`);
28
+ return sendVideoMessageWeixin({ to, text, uploaded, opts });
29
+ }
30
+ if (mime.startsWith("image/")) {
31
+ logger.info(`[weixin] sendWeixinMediaFile: uploading image filePath=${filePath} to=${to}`);
32
+ const uploaded = await uploadFileToWeixin({
33
+ filePath,
34
+ toUserId: to,
35
+ opts: uploadOpts,
36
+ cdnBaseUrl,
37
+ });
38
+ logger.info(`[weixin] sendWeixinMediaFile: image upload done filekey=${uploaded.filekey} size=${uploaded.fileSize}`);
39
+ return sendImageMessageWeixin({ to, text, uploaded, opts });
40
+ }
41
+ // File attachment: pdf, doc, zip, etc.
42
+ const fileName = path.basename(filePath);
43
+ logger.info(`[weixin] sendWeixinMediaFile: uploading file attachment filePath=${filePath} name=${fileName} to=${to}`);
44
+ const uploaded = await uploadFileAttachmentToWeixin({
45
+ filePath,
46
+ fileName,
47
+ toUserId: to,
48
+ opts: uploadOpts,
49
+ cdnBaseUrl,
50
+ });
51
+ logger.info(`[weixin] sendWeixinMediaFile: file upload done filekey=${uploaded.filekey} size=${uploaded.fileSize}`);
52
+ return sendFileMessageWeixin({ to, text, fileName, uploaded, opts });
53
+ }
54
+ //# sourceMappingURL=send-media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-media.js","sourceRoot":"","sources":["../../src/messaging/send-media.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEzG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAMzC;IACC,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IACxD,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAqB,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAElF,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,0DAA0D,QAAQ,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3F,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC;YACzC,QAAQ;YACR,QAAQ,EAAE,EAAE;YACZ,IAAI,EAAE,UAAU;YAChB,UAAU;SACX,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CACT,2DAA2D,QAAQ,CAAC,OAAO,SAAS,QAAQ,CAAC,QAAQ,EAAE,CACxG,CAAC;QACF,OAAO,sBAAsB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,0DAA0D,QAAQ,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3F,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC;YACxC,QAAQ;YACR,QAAQ,EAAE,EAAE;YACZ,IAAI,EAAE,UAAU;YAChB,UAAU;SACX,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CACT,2DAA2D,QAAQ,CAAC,OAAO,SAAS,QAAQ,CAAC,QAAQ,EAAE,CACxG,CAAC;QACF,OAAO,sBAAsB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,uCAAuC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,CAAC,IAAI,CACT,oEAAoE,QAAQ,SAAS,QAAQ,OAAO,EAAE,EAAE,CACzG,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,4BAA4B,CAAC;QAClD,QAAQ;QACR,QAAQ;QACR,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,UAAU;QAChB,UAAU;KACX,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CACT,0DAA0D,QAAQ,CAAC,OAAO,SAAS,QAAQ,CAAC,QAAQ,EAAE,CACvG,CAAC;IACF,OAAO,qBAAqB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,70 @@
1
+ export declare function stripMarkdown(str: string): string;
2
+ import type { WeixinApiOptions } from "../api/api.js";
3
+ import type { UploadedFileInfo } from "../cdn/upload.js";
4
+ /**
5
+ * Convert markdown-formatted model reply to plain text for Weixin delivery.
6
+ * Preserves newlines; strips markdown syntax.
7
+ */
8
+ export declare function markdownToPlainText(text: string): string;
9
+ /**
10
+ * Send a plain text message downstream.
11
+ */
12
+ export declare function sendMessageWeixin(params: {
13
+ to: string;
14
+ text: string;
15
+ opts: WeixinApiOptions & {
16
+ contextToken?: string;
17
+ };
18
+ }): Promise<{
19
+ messageId: string;
20
+ }>;
21
+ /**
22
+ * Send an image message downstream using a previously uploaded file.
23
+ * Optionally include a text caption as a separate TEXT item before the image.
24
+ *
25
+ * ImageItem fields:
26
+ * - media.encrypt_query_param: CDN download param
27
+ * - media.aes_key: AES key, base64-encoded
28
+ * - mid_size: original ciphertext file size
29
+ */
30
+ export declare function sendImageMessageWeixin(params: {
31
+ to: string;
32
+ text: string;
33
+ uploaded: UploadedFileInfo;
34
+ opts: WeixinApiOptions & {
35
+ contextToken?: string;
36
+ };
37
+ }): Promise<{
38
+ messageId: string;
39
+ }>;
40
+ /**
41
+ * Send a video message downstream using a previously uploaded file.
42
+ * VideoItem: media (CDN ref), video_size (ciphertext bytes).
43
+ * Includes an optional text caption sent as a separate TEXT item first.
44
+ */
45
+ export declare function sendVideoMessageWeixin(params: {
46
+ to: string;
47
+ text: string;
48
+ uploaded: UploadedFileInfo;
49
+ opts: WeixinApiOptions & {
50
+ contextToken?: string;
51
+ };
52
+ }): Promise<{
53
+ messageId: string;
54
+ }>;
55
+ /**
56
+ * Send a file attachment downstream using a previously uploaded file.
57
+ * FileItem: media (CDN ref), file_name, len (plaintext bytes as string).
58
+ * Includes an optional text caption sent as a separate TEXT item first.
59
+ */
60
+ export declare function sendFileMessageWeixin(params: {
61
+ to: string;
62
+ text: string;
63
+ fileName: string;
64
+ uploaded: UploadedFileInfo;
65
+ opts: WeixinApiOptions & {
66
+ contextToken?: string;
67
+ };
68
+ }): Promise<{
69
+ messageId: string;
70
+ }>;