@tencent-weixin/openclaw-weixin 2.3.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api/api.js +374 -0
- package/dist/src/api/api.js.map +1 -0
- package/dist/src/api/config-cache.js +64 -0
- package/dist/src/api/config-cache.js.map +1 -0
- package/dist/src/api/session-guard.js +49 -0
- package/dist/src/api/session-guard.js.map +1 -0
- package/dist/src/api/types.js +35 -0
- package/dist/src/api/types.js.map +1 -0
- package/dist/src/auth/accounts.js +326 -0
- package/dist/src/auth/accounts.js.map +1 -0
- package/dist/src/auth/login-qr.js +332 -0
- package/dist/src/auth/login-qr.js.map +1 -0
- package/dist/src/auth/pairing.js +104 -0
- package/dist/src/auth/pairing.js.map +1 -0
- package/dist/src/cdn/aes-ecb.js +19 -0
- package/dist/src/cdn/aes-ecb.js.map +1 -0
- package/dist/src/cdn/cdn-upload.js +73 -0
- package/dist/src/cdn/cdn-upload.js.map +1 -0
- package/dist/src/cdn/cdn-url.js +14 -0
- package/dist/src/cdn/cdn-url.js.map +1 -0
- package/dist/src/cdn/pic-decrypt.js +89 -0
- package/dist/src/cdn/pic-decrypt.js.map +1 -0
- package/dist/src/cdn/upload.js +106 -0
- package/dist/src/cdn/upload.js.map +1 -0
- package/dist/src/channel.js +460 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/compat.js +67 -0
- package/dist/src/compat.js.map +1 -0
- package/dist/src/config/config-schema.js +19 -0
- package/dist/src/config/config-schema.js.map +1 -0
- package/dist/src/media/media-download.js +95 -0
- package/dist/src/media/media-download.js.map +1 -0
- package/dist/src/media/mime.js +73 -0
- package/dist/src/media/mime.js.map +1 -0
- package/dist/src/media/silk-transcode.js +64 -0
- package/dist/src/media/silk-transcode.js.map +1 -0
- package/dist/src/media/voice-outbound.js +177 -0
- package/dist/src/media/voice-outbound.js.map +1 -0
- package/dist/src/messaging/abort-fence.js +70 -0
- package/dist/src/messaging/abort-fence.js.map +1 -0
- package/dist/src/messaging/buttons.js +117 -0
- package/dist/src/messaging/buttons.js.map +1 -0
- package/dist/src/messaging/debug-mode.js +63 -0
- package/dist/src/messaging/debug-mode.js.map +1 -0
- package/dist/src/messaging/error-notice.js +24 -0
- package/dist/src/messaging/error-notice.js.map +1 -0
- package/dist/src/messaging/inbound.js +201 -0
- package/dist/src/messaging/inbound.js.map +1 -0
- package/dist/src/messaging/lane-key.js +66 -0
- package/dist/src/messaging/lane-key.js.map +1 -0
- package/dist/src/messaging/markdown-filter.js +368 -0
- package/dist/src/messaging/markdown-filter.js.map +1 -0
- package/dist/src/messaging/merged-record.js +149 -0
- package/dist/src/messaging/merged-record.js.map +1 -0
- package/dist/src/messaging/model-buttons.js +182 -0
- package/dist/src/messaging/model-buttons.js.map +1 -0
- package/dist/src/messaging/model-callback-handler.js +133 -0
- package/dist/src/messaging/model-callback-handler.js.map +1 -0
- package/dist/src/messaging/outbound-hooks.js +56 -0
- package/dist/src/messaging/outbound-hooks.js.map +1 -0
- package/dist/src/messaging/process-message.js +381 -0
- package/dist/src/messaging/process-message.js.map +1 -0
- package/dist/src/messaging/send-media.js +54 -0
- package/dist/src/messaging/send-media.js.map +1 -0
- package/dist/src/messaging/send.js +182 -0
- package/dist/src/messaging/send.js.map +1 -0
- package/dist/src/messaging/slash-commands.js +70 -0
- package/dist/src/messaging/slash-commands.js.map +1 -0
- package/dist/src/monitor/lane-scheduler.js +46 -0
- package/dist/src/monitor/lane-scheduler.js.map +1 -0
- package/dist/src/monitor/monitor.js +143 -0
- package/dist/src/monitor/monitor.js.map +1 -0
- package/dist/src/runtime.js +54 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/storage/state-dir.js +9 -0
- package/dist/src/storage/state-dir.js.map +1 -0
- package/dist/src/storage/sync-buf.js +64 -0
- package/dist/src/storage/sync-buf.js.map +1 -0
- package/dist/src/streaming/stream-pipeline.js +431 -0
- package/dist/src/streaming/stream-pipeline.js.map +1 -0
- package/dist/src/streaming/stream-session.js +260 -0
- package/dist/src/streaming/stream-session.js.map +1 -0
- package/dist/src/streaming/stream.js +239 -0
- package/dist/src/streaming/stream.js.map +1 -0
- package/dist/src/util/logger.js +120 -0
- package/dist/src/util/logger.js.map +1 -0
- package/dist/src/util/markdown-fences.js +54 -0
- package/dist/src/util/markdown-fences.js.map +1 -0
- package/dist/src/util/random.js +16 -0
- package/dist/src/util/random.js.map +1 -0
- package/dist/src/util/redact.js +54 -0
- package/dist/src/util/redact.js.map +1 -0
- package/index.ts +0 -5
- package/openclaw.plugin.json +11 -1
- package/package.json +9 -2
- package/src/api/api.ts +2 -3
- package/src/auth/accounts.ts +0 -1
- package/src/channel.ts +13 -1
- package/src/monitor/monitor.ts +11 -10
- package/src/runtime.ts +0 -70
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
|
|
3
|
+
import { resolveSenderCommandAuthorizationWithRuntime, resolveDirectDmAuthorizationOutcome, } from "openclaw/plugin-sdk/command-auth";
|
|
4
|
+
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/infra-runtime";
|
|
5
|
+
import { sendTyping } from "../api/api.js";
|
|
6
|
+
import { MessageItemType, TypingStatus } from "../api/types.js";
|
|
7
|
+
import { loadWeixinAccount } from "../auth/accounts.js";
|
|
8
|
+
import { readFrameworkAllowFromList } from "../auth/pairing.js";
|
|
9
|
+
import { downloadRemoteImageToTemp } from "../cdn/upload.js";
|
|
10
|
+
import { downloadMediaFromItem } from "../media/media-download.js";
|
|
11
|
+
import { logger } from "../util/logger.js";
|
|
12
|
+
import { redactBody, redactToken } from "../util/redact.js";
|
|
13
|
+
import { isDebugMode } from "./debug-mode.js";
|
|
14
|
+
import { sendWeixinErrorNotice } from "./error-notice.js";
|
|
15
|
+
import { applyWeixinMessageSendingHook, emitWeixinMessageSent } from "./outbound-hooks.js";
|
|
16
|
+
import { setContextToken, weixinMessageToMsgContext, getContextTokenFromMsgContext, isMediaItem, } from "./inbound.js";
|
|
17
|
+
import { sendWeixinMediaFile } from "./send-media.js";
|
|
18
|
+
import { StreamingMarkdownFilter } from "./markdown-filter.js";
|
|
19
|
+
import { sendMessageWeixin } from "./send.js";
|
|
20
|
+
import { handleSlashCommand } from "./slash-commands.js";
|
|
21
|
+
const MEDIA_OUTBOUND_TEMP_DIR = path.join(resolvePreferredOpenClawTmpDir(), "weixin/media/outbound-temp");
|
|
22
|
+
/** Extract text body from item_list (for slash command detection). */
|
|
23
|
+
function extractTextBody(itemList) {
|
|
24
|
+
if (!itemList?.length)
|
|
25
|
+
return "";
|
|
26
|
+
for (const item of itemList) {
|
|
27
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
|
|
28
|
+
return String(item.text_item.text);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Process a single inbound message: route → download media → dispatch reply.
|
|
35
|
+
* Extracted from the monitor loop to keep monitoring and message handling separate.
|
|
36
|
+
*/
|
|
37
|
+
export async function processOneMessage(full, deps) {
|
|
38
|
+
if (!deps?.channelRuntime) {
|
|
39
|
+
logger.error(`processOneMessage: channelRuntime is undefined, skipping message from=${full.from_user_id}`);
|
|
40
|
+
deps.errLog("processOneMessage: channelRuntime is undefined, skip");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const receivedAt = Date.now();
|
|
44
|
+
const debug = isDebugMode(deps.accountId);
|
|
45
|
+
const debugTrace = [];
|
|
46
|
+
const debugTs = { received: receivedAt };
|
|
47
|
+
const textBody = extractTextBody(full.item_list);
|
|
48
|
+
if (textBody.startsWith("/")) {
|
|
49
|
+
const slashResult = await handleSlashCommand(textBody, {
|
|
50
|
+
to: full.from_user_id ?? "",
|
|
51
|
+
contextToken: full.context_token,
|
|
52
|
+
baseUrl: deps.baseUrl,
|
|
53
|
+
token: deps.token,
|
|
54
|
+
accountId: deps.accountId,
|
|
55
|
+
log: deps.log,
|
|
56
|
+
errLog: deps.errLog,
|
|
57
|
+
}, receivedAt, full.create_time_ms);
|
|
58
|
+
if (slashResult.handled) {
|
|
59
|
+
logger.info(`[weixin] Slash command handled, skipping AI pipeline`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (debug) {
|
|
64
|
+
const itemTypes = full.item_list?.map((i) => i.type).join(",") ?? "none";
|
|
65
|
+
debugTrace.push("── 收消息 ──", `│ seq=${full.seq ?? "?"} msgId=${full.message_id ?? "?"} from=${full.from_user_id ?? "?"}`, `│ body="${textBody.slice(0, 40)}${textBody.length > 40 ? "…" : ""}" (len=${textBody.length}) itemTypes=[${itemTypes}]`, `│ sessionId=${full.session_id ?? "?"} contextToken=${full.context_token ? "present" : "none"}`);
|
|
66
|
+
}
|
|
67
|
+
const mediaOpts = {};
|
|
68
|
+
// Find the first downloadable media item (priority: IMAGE > VIDEO > FILE > VOICE).
|
|
69
|
+
// When none found in the main item_list, fall back to media referenced via a quoted message.
|
|
70
|
+
const hasDownloadableMedia = (m) => m?.encrypt_query_param || m?.full_url;
|
|
71
|
+
const mainMediaItem = full.item_list?.find((i) => i.type === MessageItemType.IMAGE && hasDownloadableMedia(i.image_item?.media)) ??
|
|
72
|
+
full.item_list?.find((i) => i.type === MessageItemType.VIDEO && hasDownloadableMedia(i.video_item?.media)) ??
|
|
73
|
+
full.item_list?.find((i) => i.type === MessageItemType.FILE && hasDownloadableMedia(i.file_item?.media)) ??
|
|
74
|
+
full.item_list?.find((i) => i.type === MessageItemType.VOICE &&
|
|
75
|
+
hasDownloadableMedia(i.voice_item?.media) &&
|
|
76
|
+
!i.voice_item?.text);
|
|
77
|
+
const refMediaItem = !mainMediaItem
|
|
78
|
+
? full.item_list?.find((i) => i.type === MessageItemType.TEXT &&
|
|
79
|
+
i.ref_msg?.message_item &&
|
|
80
|
+
isMediaItem(i.ref_msg.message_item))?.ref_msg?.message_item
|
|
81
|
+
: undefined;
|
|
82
|
+
const mediaDownloadStart = Date.now();
|
|
83
|
+
const mediaItem = mainMediaItem ?? refMediaItem;
|
|
84
|
+
if (mediaItem) {
|
|
85
|
+
const label = refMediaItem ? "ref" : "inbound";
|
|
86
|
+
const downloaded = await downloadMediaFromItem(mediaItem, {
|
|
87
|
+
cdnBaseUrl: deps.cdnBaseUrl,
|
|
88
|
+
saveMedia: deps.channelRuntime.media.saveMediaBuffer,
|
|
89
|
+
log: deps.log,
|
|
90
|
+
errLog: deps.errLog,
|
|
91
|
+
label,
|
|
92
|
+
});
|
|
93
|
+
Object.assign(mediaOpts, downloaded);
|
|
94
|
+
}
|
|
95
|
+
const mediaDownloadMs = Date.now() - mediaDownloadStart;
|
|
96
|
+
if (debug) {
|
|
97
|
+
debugTrace.push(mediaItem
|
|
98
|
+
? `│ mediaDownload: type=${mediaItem.type} cost=${mediaDownloadMs}ms`
|
|
99
|
+
: "│ mediaDownload: none");
|
|
100
|
+
}
|
|
101
|
+
const ctx = weixinMessageToMsgContext(full, deps.accountId, mediaOpts);
|
|
102
|
+
// --- Framework command authorization ---
|
|
103
|
+
const rawBody = ctx.Body?.trim() ?? "";
|
|
104
|
+
ctx.CommandBody = rawBody;
|
|
105
|
+
const senderId = full.from_user_id ?? "";
|
|
106
|
+
const { senderAllowedForCommands, commandAuthorized } = await resolveSenderCommandAuthorizationWithRuntime({
|
|
107
|
+
cfg: deps.config,
|
|
108
|
+
rawBody,
|
|
109
|
+
isGroup: false,
|
|
110
|
+
dmPolicy: "pairing",
|
|
111
|
+
configuredAllowFrom: [],
|
|
112
|
+
configuredGroupAllowFrom: [],
|
|
113
|
+
senderId,
|
|
114
|
+
isSenderAllowed: (id, list) => list.length === 0 || list.includes(id),
|
|
115
|
+
/** Pairing: framework credentials `*-allowFrom.json`, with account `userId` fallback for legacy installs. */
|
|
116
|
+
readAllowFromStore: async () => {
|
|
117
|
+
const fromStore = readFrameworkAllowFromList(deps.accountId);
|
|
118
|
+
if (fromStore.length > 0)
|
|
119
|
+
return fromStore;
|
|
120
|
+
const uid = loadWeixinAccount(deps.accountId)?.userId?.trim();
|
|
121
|
+
return uid ? [uid] : [];
|
|
122
|
+
},
|
|
123
|
+
runtime: deps.channelRuntime.commands,
|
|
124
|
+
});
|
|
125
|
+
const directDmOutcome = resolveDirectDmAuthorizationOutcome({
|
|
126
|
+
isGroup: false,
|
|
127
|
+
dmPolicy: "pairing",
|
|
128
|
+
senderAllowedForCommands,
|
|
129
|
+
});
|
|
130
|
+
if (directDmOutcome === "disabled" || directDmOutcome === "unauthorized") {
|
|
131
|
+
logger.info(`authorization: dropping message from=${senderId} outcome=${directDmOutcome}`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
ctx.CommandAuthorized = commandAuthorized;
|
|
135
|
+
logger.debug(`authorization: senderId=${senderId} commandAuthorized=${String(commandAuthorized)} senderAllowed=${String(senderAllowedForCommands)}`);
|
|
136
|
+
if (debug) {
|
|
137
|
+
debugTrace.push("── 鉴权 & 路由 ──", `│ auth: cmdAuthorized=${String(commandAuthorized)} senderAllowed=${String(senderAllowedForCommands)}`);
|
|
138
|
+
}
|
|
139
|
+
const route = deps.channelRuntime.routing.resolveAgentRoute({
|
|
140
|
+
cfg: deps.config,
|
|
141
|
+
channel: "openclaw-weixin",
|
|
142
|
+
accountId: deps.accountId,
|
|
143
|
+
peer: { kind: "direct", id: ctx.To },
|
|
144
|
+
});
|
|
145
|
+
logger.debug(`resolveAgentRoute: agentId=${route.agentId ?? "(none)"} sessionKey=${route.sessionKey ?? "(none)"} mainSessionKey=${route.mainSessionKey ?? "(none)"}`);
|
|
146
|
+
if (!route.agentId) {
|
|
147
|
+
logger.error(`resolveAgentRoute: no agentId resolved for peer=${ctx.To} accountId=${deps.accountId} — message will not be dispatched`);
|
|
148
|
+
}
|
|
149
|
+
if (debug) {
|
|
150
|
+
debugTrace.push(`│ route: agent=${route.agentId ?? "none"} session=${route.sessionKey ?? "none"}`);
|
|
151
|
+
debugTs.preDispatch = Date.now();
|
|
152
|
+
}
|
|
153
|
+
// Propagate the resolved session key into ctx so dispatchReplyFromConfig uses
|
|
154
|
+
// the correct session (matching the dmScope from config) instead of falling back
|
|
155
|
+
// to agent:main:main.
|
|
156
|
+
ctx.SessionKey = route.sessionKey;
|
|
157
|
+
const storePath = deps.channelRuntime.session.resolveStorePath(deps.config.session?.store, {
|
|
158
|
+
agentId: route.agentId,
|
|
159
|
+
});
|
|
160
|
+
const finalized = deps.channelRuntime.reply.finalizeInboundContext(ctx);
|
|
161
|
+
logger.info(`inbound: from=${finalized.From} to=${finalized.To} bodyLen=${(finalized.Body ?? "").length} hasMedia=${Boolean(finalized.MediaPath ?? finalized.MediaUrl)}`);
|
|
162
|
+
logger.debug(`inbound context: ${redactBody(JSON.stringify(finalized))}`);
|
|
163
|
+
await deps.channelRuntime.session.recordInboundSession({
|
|
164
|
+
storePath,
|
|
165
|
+
sessionKey: route.sessionKey,
|
|
166
|
+
ctx: finalized,
|
|
167
|
+
updateLastRoute: {
|
|
168
|
+
sessionKey: route.mainSessionKey,
|
|
169
|
+
channel: "openclaw-weixin",
|
|
170
|
+
to: ctx.To,
|
|
171
|
+
accountId: deps.accountId,
|
|
172
|
+
},
|
|
173
|
+
onRecordError: (err) => deps.errLog(`recordInboundSession: ${String(err)}`),
|
|
174
|
+
});
|
|
175
|
+
logger.debug(`recordInboundSession: done storePath=${storePath} sessionKey=${route.sessionKey ?? "(none)"}`);
|
|
176
|
+
const contextToken = getContextTokenFromMsgContext(ctx);
|
|
177
|
+
if (contextToken) {
|
|
178
|
+
setContextToken(deps.accountId, full.from_user_id ?? "", contextToken);
|
|
179
|
+
}
|
|
180
|
+
const humanDelay = deps.channelRuntime.reply.resolveHumanDelayConfig(deps.config, route.agentId);
|
|
181
|
+
const hasTypingTicket = Boolean(deps.typingTicket);
|
|
182
|
+
const typingCallbacks = createTypingCallbacks({
|
|
183
|
+
start: hasTypingTicket
|
|
184
|
+
? () => sendTyping({
|
|
185
|
+
baseUrl: deps.baseUrl,
|
|
186
|
+
token: deps.token,
|
|
187
|
+
body: {
|
|
188
|
+
ilink_user_id: ctx.To,
|
|
189
|
+
typing_ticket: deps.typingTicket,
|
|
190
|
+
status: TypingStatus.TYPING,
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
: async () => { },
|
|
194
|
+
stop: hasTypingTicket
|
|
195
|
+
? () => sendTyping({
|
|
196
|
+
baseUrl: deps.baseUrl,
|
|
197
|
+
token: deps.token,
|
|
198
|
+
body: {
|
|
199
|
+
ilink_user_id: ctx.To,
|
|
200
|
+
typing_ticket: deps.typingTicket,
|
|
201
|
+
status: TypingStatus.CANCEL,
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
: async () => { },
|
|
205
|
+
onStartError: (err) => deps.log(`[weixin] typing send error: ${String(err)}`),
|
|
206
|
+
onStopError: (err) => deps.log(`[weixin] typing cancel error: ${String(err)}`),
|
|
207
|
+
keepaliveIntervalMs: 5000,
|
|
208
|
+
});
|
|
209
|
+
/** Delivery records populated synchronously at deliver() entry, safe to read in finally. */
|
|
210
|
+
const debugDeliveries = [];
|
|
211
|
+
const { dispatcher, replyOptions, markDispatchIdle } = deps.channelRuntime.reply.createReplyDispatcherWithTyping({
|
|
212
|
+
humanDelay,
|
|
213
|
+
typingCallbacks,
|
|
214
|
+
deliver: async (payload) => {
|
|
215
|
+
const rawText = payload.text ?? "";
|
|
216
|
+
let text = (() => {
|
|
217
|
+
const f = new StreamingMarkdownFilter();
|
|
218
|
+
return f.feed(rawText) + f.flush();
|
|
219
|
+
})();
|
|
220
|
+
const mediaUrl = payload.mediaUrl ?? payload.mediaUrls?.[0];
|
|
221
|
+
logger.debug(`outbound payload: ${redactBody(JSON.stringify(payload))}`);
|
|
222
|
+
logger.info(`outbound: to=${ctx.To} contextToken=${redactToken(contextToken)} textLen=${text.length} mediaUrl=${mediaUrl ? "present" : "none"}`);
|
|
223
|
+
if (debug) {
|
|
224
|
+
debugDeliveries.push({
|
|
225
|
+
textLen: text.length,
|
|
226
|
+
media: mediaUrl ? "present" : "none",
|
|
227
|
+
preview: `${text.slice(0, 60)}${text.length > 60 ? "…" : ""}`,
|
|
228
|
+
ts: Date.now(),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const sendingResult = await applyWeixinMessageSendingHook({
|
|
232
|
+
to: ctx.To,
|
|
233
|
+
text,
|
|
234
|
+
accountId: deps.accountId,
|
|
235
|
+
mediaUrl,
|
|
236
|
+
});
|
|
237
|
+
if (sendingResult.cancelled) {
|
|
238
|
+
logger.info(`outbound: cancelled by message_sending hook to=${ctx.To}`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
text = sendingResult.text;
|
|
242
|
+
try {
|
|
243
|
+
if (mediaUrl) {
|
|
244
|
+
let filePath;
|
|
245
|
+
if (!mediaUrl.includes("://") || mediaUrl.startsWith("file://")) {
|
|
246
|
+
if (mediaUrl.startsWith("file://")) {
|
|
247
|
+
filePath = new URL(mediaUrl).pathname;
|
|
248
|
+
}
|
|
249
|
+
else if (!path.isAbsolute(mediaUrl)) {
|
|
250
|
+
filePath = path.resolve(mediaUrl);
|
|
251
|
+
logger.debug(`outbound: resolved relative path ${mediaUrl} -> ${filePath}`);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
filePath = mediaUrl;
|
|
255
|
+
}
|
|
256
|
+
logger.debug(`outbound: local file path resolved filePath=${filePath}`);
|
|
257
|
+
}
|
|
258
|
+
else if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
|
|
259
|
+
logger.debug(`outbound: downloading remote mediaUrl=${mediaUrl.slice(0, 80)}...`);
|
|
260
|
+
filePath = await downloadRemoteImageToTemp(mediaUrl, MEDIA_OUTBOUND_TEMP_DIR);
|
|
261
|
+
logger.debug(`outbound: remote image downloaded to filePath=${filePath}`);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
logger.warn(`outbound: unrecognized mediaUrl scheme, sending text only mediaUrl=${mediaUrl.slice(0, 80)}`);
|
|
265
|
+
await sendMessageWeixin({ to: ctx.To, text, opts: {
|
|
266
|
+
baseUrl: deps.baseUrl,
|
|
267
|
+
token: deps.token,
|
|
268
|
+
contextToken,
|
|
269
|
+
} });
|
|
270
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId });
|
|
271
|
+
logger.info(`outbound: text sent to=${ctx.To}`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
await sendWeixinMediaFile({
|
|
275
|
+
filePath,
|
|
276
|
+
to: ctx.To,
|
|
277
|
+
text,
|
|
278
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken },
|
|
279
|
+
cdnBaseUrl: deps.cdnBaseUrl,
|
|
280
|
+
});
|
|
281
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId });
|
|
282
|
+
logger.info(`outbound: media sent OK to=${ctx.To}`);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
logger.debug(`outbound: sending text message to=${ctx.To}`);
|
|
286
|
+
await sendMessageWeixin({ to: ctx.To, text, opts: {
|
|
287
|
+
baseUrl: deps.baseUrl,
|
|
288
|
+
token: deps.token,
|
|
289
|
+
contextToken,
|
|
290
|
+
} });
|
|
291
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId });
|
|
292
|
+
logger.info(`outbound: text sent OK to=${ctx.To}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: false, error: String(err), accountId: deps.accountId });
|
|
297
|
+
logger.error(`outbound: FAILED to=${ctx.To} mediaUrl=${mediaUrl ?? "none"} err=${String(err)} stack=${err.stack ?? ""}`);
|
|
298
|
+
throw err;
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
onError: (err, info) => {
|
|
302
|
+
deps.errLog(`weixin reply ${info.kind}: ${String(err)}`);
|
|
303
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
304
|
+
let notice;
|
|
305
|
+
if (errMsg.includes("remote media download failed") || errMsg.includes("fetch")) {
|
|
306
|
+
notice = `⚠️ 媒体文件下载失败,请检查链接是否可访问。`;
|
|
307
|
+
}
|
|
308
|
+
else if (errMsg.includes("getUploadUrl") ||
|
|
309
|
+
errMsg.includes("CDN upload") ||
|
|
310
|
+
errMsg.includes("upload_param")) {
|
|
311
|
+
notice = `⚠️ 媒体文件上传失败,请稍后重试。`;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
notice = `⚠️ 消息发送失败:${errMsg}`;
|
|
315
|
+
}
|
|
316
|
+
void sendWeixinErrorNotice({
|
|
317
|
+
to: ctx.To,
|
|
318
|
+
contextToken,
|
|
319
|
+
message: notice,
|
|
320
|
+
baseUrl: deps.baseUrl,
|
|
321
|
+
token: deps.token,
|
|
322
|
+
errLog: deps.errLog,
|
|
323
|
+
});
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
logger.debug(`dispatchReplyFromConfig: starting agentId=${route.agentId ?? "(none)"}`);
|
|
327
|
+
try {
|
|
328
|
+
await deps.channelRuntime.reply.withReplyDispatcher({
|
|
329
|
+
dispatcher,
|
|
330
|
+
run: () => deps.channelRuntime.reply.dispatchReplyFromConfig({
|
|
331
|
+
ctx: finalized,
|
|
332
|
+
cfg: deps.config,
|
|
333
|
+
dispatcher,
|
|
334
|
+
replyOptions: { ...replyOptions, disableBlockStreaming: true },
|
|
335
|
+
}),
|
|
336
|
+
});
|
|
337
|
+
logger.debug(`dispatchReplyFromConfig: done agentId=${route.agentId ?? "(none)"}`);
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
logger.error(`dispatchReplyFromConfig: error agentId=${route.agentId ?? "(none)"} err=${String(err)}`);
|
|
341
|
+
throw err;
|
|
342
|
+
}
|
|
343
|
+
finally {
|
|
344
|
+
markDispatchIdle();
|
|
345
|
+
logger.info(`debug-check: accountId=${deps.accountId} debug=${String(debug)} hasContextToken=${Boolean(contextToken)}`);
|
|
346
|
+
if (debug && contextToken) {
|
|
347
|
+
const dispatchDoneAt = Date.now();
|
|
348
|
+
const eventTs = full.create_time_ms ?? 0;
|
|
349
|
+
const platformDelay = eventTs > 0 ? `${receivedAt - eventTs}ms` : "N/A";
|
|
350
|
+
const inboundProcessMs = (debugTs.preDispatch ?? receivedAt) - receivedAt;
|
|
351
|
+
const aiMs = dispatchDoneAt - (debugTs.preDispatch ?? receivedAt);
|
|
352
|
+
const totalTime = eventTs > 0 ? `${dispatchDoneAt - eventTs}ms` : `${dispatchDoneAt - receivedAt}ms`;
|
|
353
|
+
if (debugDeliveries.length > 0) {
|
|
354
|
+
debugTrace.push("── 回复 ──");
|
|
355
|
+
for (const d of debugDeliveries) {
|
|
356
|
+
debugTrace.push(`│ textLen=${d.textLen} media=${d.media}`, `│ text="${d.preview}"`);
|
|
357
|
+
}
|
|
358
|
+
const firstTs = debugDeliveries[0].ts;
|
|
359
|
+
debugTrace.push(`│ deliver耗时: ${dispatchDoneAt - firstTs}ms`);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
debugTrace.push("── 回复 ──", "│ (deliver未捕获)");
|
|
363
|
+
}
|
|
364
|
+
debugTrace.push("── 耗时 ──", `├ 平台→插件: ${platformDelay}`, `├ 入站处理(auth+route+media): ${inboundProcessMs}ms (mediaDownload: ${mediaDownloadMs}ms)`, `├ AI生成+回复: ${aiMs}ms`, `├ 总耗时: ${totalTime}`, `└ eventTime: ${eventTs > 0 ? new Date(eventTs).toISOString() : "N/A"}`);
|
|
365
|
+
const timingText = `⏱ Debug 全链路\n${debugTrace.join("\n")}`;
|
|
366
|
+
logger.info(`debug-timing: sending to=${ctx.To}`);
|
|
367
|
+
try {
|
|
368
|
+
await sendMessageWeixin({
|
|
369
|
+
to: ctx.To,
|
|
370
|
+
text: timingText,
|
|
371
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken },
|
|
372
|
+
});
|
|
373
|
+
logger.info(`debug-timing: sent OK`);
|
|
374
|
+
}
|
|
375
|
+
catch (debugErr) {
|
|
376
|
+
logger.error(`debug-timing: send FAILED err=${String(debugErr)}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=process-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-message.js","sourceRoot":"","sources":["../../../src/messaging/process-message.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,182 @@
|
|
|
1
|
+
import { sendMessage as sendMessageApi } from "../api/api.js";
|
|
2
|
+
import { logger } from "../util/logger.js";
|
|
3
|
+
import { generateId } from "../util/random.js";
|
|
4
|
+
import { MessageItemType, MessageState, MessageType } from "../api/types.js";
|
|
5
|
+
export { StreamingMarkdownFilter } from "./markdown-filter.js";
|
|
6
|
+
function generateClientId() {
|
|
7
|
+
return generateId("openclaw-weixin");
|
|
8
|
+
}
|
|
9
|
+
/** Build a SendMessageReq containing a single text message. */
|
|
10
|
+
function buildTextMessageReq(params) {
|
|
11
|
+
const { to, text, contextToken, clientId } = params;
|
|
12
|
+
const item_list = text
|
|
13
|
+
? [{ type: MessageItemType.TEXT, text_item: { text } }]
|
|
14
|
+
: [];
|
|
15
|
+
return {
|
|
16
|
+
msg: {
|
|
17
|
+
from_user_id: "",
|
|
18
|
+
to_user_id: to,
|
|
19
|
+
client_id: clientId,
|
|
20
|
+
message_type: MessageType.BOT,
|
|
21
|
+
message_state: MessageState.FINISH,
|
|
22
|
+
item_list: item_list.length ? item_list : undefined,
|
|
23
|
+
context_token: contextToken ?? undefined,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Build a SendMessageReq from a reply payload (text only; image send uses sendImageMessageWeixin). */
|
|
28
|
+
function buildSendMessageReq(params) {
|
|
29
|
+
const { to, contextToken, payload, clientId } = params;
|
|
30
|
+
return buildTextMessageReq({
|
|
31
|
+
to,
|
|
32
|
+
text: payload.text ?? "",
|
|
33
|
+
contextToken,
|
|
34
|
+
clientId,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Send a plain text message downstream.
|
|
39
|
+
*/
|
|
40
|
+
export async function sendMessageWeixin(params) {
|
|
41
|
+
const { to, text, opts } = params;
|
|
42
|
+
if (!opts.contextToken) {
|
|
43
|
+
logger.warn(`sendMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
44
|
+
}
|
|
45
|
+
const clientId = generateClientId();
|
|
46
|
+
const req = buildSendMessageReq({
|
|
47
|
+
to,
|
|
48
|
+
contextToken: opts.contextToken,
|
|
49
|
+
payload: { text },
|
|
50
|
+
clientId,
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
await sendMessageApi({
|
|
54
|
+
baseUrl: opts.baseUrl,
|
|
55
|
+
token: opts.token,
|
|
56
|
+
timeoutMs: opts.timeoutMs,
|
|
57
|
+
body: req,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
logger.error(`sendMessageWeixin: failed to=${to} clientId=${clientId} err=${String(err)}`);
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
return { messageId: clientId };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Send one or more MessageItems (optionally preceded by a text caption) downstream.
|
|
68
|
+
* Each item is sent as its own request so that item_list always has exactly one entry.
|
|
69
|
+
*/
|
|
70
|
+
async function sendMediaItems(params) {
|
|
71
|
+
const { to, text, mediaItem, opts, label } = params;
|
|
72
|
+
const items = [];
|
|
73
|
+
if (text) {
|
|
74
|
+
items.push({ type: MessageItemType.TEXT, text_item: { text } });
|
|
75
|
+
}
|
|
76
|
+
items.push(mediaItem);
|
|
77
|
+
let lastClientId = "";
|
|
78
|
+
for (const item of items) {
|
|
79
|
+
lastClientId = generateClientId();
|
|
80
|
+
const req = {
|
|
81
|
+
msg: {
|
|
82
|
+
from_user_id: "",
|
|
83
|
+
to_user_id: to,
|
|
84
|
+
client_id: lastClientId,
|
|
85
|
+
message_type: MessageType.BOT,
|
|
86
|
+
message_state: MessageState.FINISH,
|
|
87
|
+
item_list: [item],
|
|
88
|
+
context_token: opts.contextToken ?? undefined,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
await sendMessageApi({
|
|
93
|
+
baseUrl: opts.baseUrl,
|
|
94
|
+
token: opts.token,
|
|
95
|
+
timeoutMs: opts.timeoutMs,
|
|
96
|
+
body: req,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
logger.error(`${label}: failed to=${to} clientId=${lastClientId} err=${String(err)}`);
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
logger.info(`${label}: success to=${to} clientId=${lastClientId}`);
|
|
105
|
+
return { messageId: lastClientId };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Send an image message downstream using a previously uploaded file.
|
|
109
|
+
* Optionally include a text caption as a separate TEXT item before the image.
|
|
110
|
+
*
|
|
111
|
+
* ImageItem fields:
|
|
112
|
+
* - media.encrypt_query_param: CDN download param
|
|
113
|
+
* - media.aes_key: AES key, base64-encoded
|
|
114
|
+
* - mid_size: original ciphertext file size
|
|
115
|
+
*/
|
|
116
|
+
export async function sendImageMessageWeixin(params) {
|
|
117
|
+
const { to, text, uploaded, opts } = params;
|
|
118
|
+
if (!opts.contextToken) {
|
|
119
|
+
logger.warn(`sendImageMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
120
|
+
}
|
|
121
|
+
logger.info(`sendImageMessageWeixin: to=${to} filekey=${uploaded.filekey} fileSize=${uploaded.fileSize} aeskey=present`);
|
|
122
|
+
const imageItem = {
|
|
123
|
+
type: MessageItemType.IMAGE,
|
|
124
|
+
image_item: {
|
|
125
|
+
media: {
|
|
126
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
127
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
128
|
+
encrypt_type: 1,
|
|
129
|
+
},
|
|
130
|
+
mid_size: uploaded.fileSizeCiphertext,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
return sendMediaItems({ to, text, mediaItem: imageItem, opts, label: "sendImageMessageWeixin" });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Send a video message downstream using a previously uploaded file.
|
|
137
|
+
* VideoItem: media (CDN ref), video_size (ciphertext bytes).
|
|
138
|
+
* Includes an optional text caption sent as a separate TEXT item first.
|
|
139
|
+
*/
|
|
140
|
+
export async function sendVideoMessageWeixin(params) {
|
|
141
|
+
const { to, text, uploaded, opts } = params;
|
|
142
|
+
if (!opts.contextToken) {
|
|
143
|
+
logger.warn(`sendVideoMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
144
|
+
}
|
|
145
|
+
const videoItem = {
|
|
146
|
+
type: MessageItemType.VIDEO,
|
|
147
|
+
video_item: {
|
|
148
|
+
media: {
|
|
149
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
150
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
151
|
+
encrypt_type: 1,
|
|
152
|
+
},
|
|
153
|
+
video_size: uploaded.fileSizeCiphertext,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
return sendMediaItems({ to, text, mediaItem: videoItem, opts, label: "sendVideoMessageWeixin" });
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Send a file attachment downstream using a previously uploaded file.
|
|
160
|
+
* FileItem: media (CDN ref), file_name, len (plaintext bytes as string).
|
|
161
|
+
* Includes an optional text caption sent as a separate TEXT item first.
|
|
162
|
+
*/
|
|
163
|
+
export async function sendFileMessageWeixin(params) {
|
|
164
|
+
const { to, text, fileName, uploaded, opts } = params;
|
|
165
|
+
if (!opts.contextToken) {
|
|
166
|
+
logger.warn(`sendFileMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
167
|
+
}
|
|
168
|
+
const fileItem = {
|
|
169
|
+
type: MessageItemType.FILE,
|
|
170
|
+
file_item: {
|
|
171
|
+
media: {
|
|
172
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
173
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
174
|
+
encrypt_type: 1,
|
|
175
|
+
},
|
|
176
|
+
file_name: fileName,
|
|
177
|
+
len: String(uploaded.fileSize),
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
return sendMediaItems({ to, text, mediaItem: fileItem, opts, label: "sendFileMessageWeixin" });
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=send.js.map
|