@tencent-weixin/openclaw-weixin 2.4.2 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/CHANGELOG.zh_CN.md +45 -0
- package/dist/src/api/api.js +84 -8
- package/dist/src/api/api.js.map +1 -1
- package/dist/src/api/types.js +2 -0
- package/dist/src/api/types.js.map +1 -1
- package/dist/src/auth/login-qr.js +1 -0
- package/dist/src/auth/login-qr.js.map +1 -1
- package/dist/src/channel.js +15 -1
- package/dist/src/channel.js.map +1 -1
- package/dist/src/config/config-schema.js +1 -0
- package/dist/src/config/config-schema.js.map +1 -1
- package/dist/src/config/reply-progress.js +5 -0
- package/dist/src/config/reply-progress.js.map +1 -0
- package/dist/src/media/voice-outbound.js +98 -1
- package/dist/src/media/voice-outbound.js.map +1 -1
- package/dist/src/messaging/batch-session.js +271 -0
- package/dist/src/messaging/batch-session.js.map +1 -0
- package/dist/src/messaging/error-notice.js +1 -0
- package/dist/src/messaging/error-notice.js.map +1 -1
- package/dist/src/messaging/outbound-hooks.js +2 -0
- package/dist/src/messaging/outbound-hooks.js.map +1 -1
- package/dist/src/messaging/process-message.js +32 -7
- package/dist/src/messaging/process-message.js.map +1 -1
- package/dist/src/messaging/reply-progress-sender.js +93 -0
- package/dist/src/messaging/reply-progress-sender.js.map +1 -0
- package/dist/src/messaging/run-context.js +9 -0
- package/dist/src/messaging/run-context.js.map +1 -0
- package/dist/src/messaging/run-report-session.js +123 -0
- package/dist/src/messaging/run-report-session.js.map +1 -0
- package/dist/src/messaging/send.js +40 -2
- package/dist/src/messaging/send.js.map +1 -1
- package/dist/src/monitor/monitor.js +3 -0
- package/dist/src/monitor/monitor.js.map +1 -1
- package/dist/src/streaming/batch-session.js +271 -0
- package/dist/src/streaming/batch-session.js.map +1 -0
- package/dist/src/streaming/stream-piece.js +54 -0
- package/dist/src/streaming/stream-piece.js.map +1 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/api/api.ts +92 -8
- package/src/api/types.ts +16 -0
- package/src/auth/login-qr.ts +8 -0
- package/src/channel.ts +16 -1
- package/src/config/config-schema.ts +1 -0
- package/src/config/reply-progress.ts +10 -0
- package/src/messaging/error-notice.ts +2 -0
- package/src/messaging/outbound-hooks.ts +4 -0
- package/src/messaging/process-message.ts +32 -7
- package/src/messaging/reply-progress-sender.ts +122 -0
- package/src/messaging/send-media.ts +1 -1
- package/src/messaging/send.ts +60 -7
- package/src/monitor/monitor.ts +3 -0
|
@@ -20,6 +20,7 @@ export async function applyWeixinMessageSendingHook(params: {
|
|
|
20
20
|
text: string;
|
|
21
21
|
accountId?: string;
|
|
22
22
|
mediaUrl?: string;
|
|
23
|
+
runId?: string;
|
|
23
24
|
}): Promise<{ cancelled: boolean; text: string }> {
|
|
24
25
|
const hookRunner = getGlobalHookRunner();
|
|
25
26
|
if (!hookRunner?.hasHooks("message_sending")) {
|
|
@@ -33,6 +34,7 @@ export async function applyWeixinMessageSendingHook(params: {
|
|
|
33
34
|
metadata: {
|
|
34
35
|
channel: CHANNEL_ID,
|
|
35
36
|
accountId: params.accountId,
|
|
37
|
+
runId: params.runId,
|
|
36
38
|
...(params.mediaUrl ? { mediaUrls: [params.mediaUrl] } : {}),
|
|
37
39
|
},
|
|
38
40
|
},
|
|
@@ -60,6 +62,7 @@ export function emitWeixinMessageSent(params: {
|
|
|
60
62
|
success: boolean;
|
|
61
63
|
error?: string;
|
|
62
64
|
accountId?: string;
|
|
65
|
+
runId?: string;
|
|
63
66
|
}): void {
|
|
64
67
|
const hookRunner = getGlobalHookRunner();
|
|
65
68
|
if (!hookRunner?.hasHooks("message_sent")) return;
|
|
@@ -71,6 +74,7 @@ export function emitWeixinMessageSent(params: {
|
|
|
71
74
|
channelId: CHANNEL_ID,
|
|
72
75
|
accountId: params.accountId,
|
|
73
76
|
conversationId: params.to,
|
|
77
|
+
runId: params.runId,
|
|
74
78
|
});
|
|
75
79
|
fireAndForgetHook(
|
|
76
80
|
Promise.resolve(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
|
|
3
4
|
import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
|
|
4
5
|
import {
|
|
@@ -14,6 +15,7 @@ import { MessageItemType, TypingStatus } from "../api/types.js";
|
|
|
14
15
|
import { loadWeixinAccount } from "../auth/accounts.js";
|
|
15
16
|
import { readFrameworkAllowFromList } from "../auth/pairing.js";
|
|
16
17
|
import { downloadRemoteImageToTemp } from "../cdn/upload.js";
|
|
18
|
+
import { resolveReplyProgressMessagesEnabled } from "../config/reply-progress.js";
|
|
17
19
|
import { downloadMediaFromItem } from "../media/media-download.js";
|
|
18
20
|
import { logger } from "../util/logger.js";
|
|
19
21
|
import { redactBody, redactToken } from "../util/redact.js";
|
|
@@ -31,6 +33,7 @@ import type { WeixinInboundMediaOpts } from "./inbound.js";
|
|
|
31
33
|
import { sendWeixinMediaFile } from "./send-media.js";
|
|
32
34
|
import { StreamingMarkdownFilter } from "./markdown-filter.js";
|
|
33
35
|
import { sendMessageWeixin } from "./send.js";
|
|
36
|
+
import { WeixinReplyProgressSender } from "./reply-progress-sender.js";
|
|
34
37
|
import { handleSlashCommand } from "./slash-commands.js";
|
|
35
38
|
|
|
36
39
|
const MEDIA_OUTBOUND_TEMP_DIR = path.join(resolvePreferredOpenClawTmpDir(), "weixin/media/outbound-temp");
|
|
@@ -270,6 +273,19 @@ export async function processOneMessage(
|
|
|
270
273
|
if (contextToken) {
|
|
271
274
|
setContextToken(deps.accountId, full.from_user_id ?? "", contextToken);
|
|
272
275
|
}
|
|
276
|
+
const runId = randomUUID();
|
|
277
|
+
const replyProgressSender = resolveReplyProgressMessagesEnabled(deps.config)
|
|
278
|
+
? new WeixinReplyProgressSender({
|
|
279
|
+
runId,
|
|
280
|
+
to: ctx.To,
|
|
281
|
+
accountId: deps.accountId,
|
|
282
|
+
opts: {
|
|
283
|
+
baseUrl: deps.baseUrl,
|
|
284
|
+
token: deps.token,
|
|
285
|
+
contextToken,
|
|
286
|
+
},
|
|
287
|
+
})
|
|
288
|
+
: undefined;
|
|
273
289
|
const humanDelay = deps.channelRuntime.reply.resolveHumanDelayConfig(deps.config, route.agentId);
|
|
274
290
|
|
|
275
291
|
const hasTypingTicket = Boolean(deps.typingTicket);
|
|
@@ -336,6 +352,7 @@ export async function processOneMessage(
|
|
|
336
352
|
text,
|
|
337
353
|
accountId: deps.accountId,
|
|
338
354
|
mediaUrl,
|
|
355
|
+
runId,
|
|
339
356
|
});
|
|
340
357
|
if (sendingResult.cancelled) {
|
|
341
358
|
logger.info(`outbound: cancelled by message_sending hook to=${ctx.To}`);
|
|
@@ -368,8 +385,9 @@ export async function processOneMessage(
|
|
|
368
385
|
baseUrl: deps.baseUrl,
|
|
369
386
|
token: deps.token,
|
|
370
387
|
contextToken,
|
|
388
|
+
runId,
|
|
371
389
|
}});
|
|
372
|
-
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId });
|
|
390
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId, runId });
|
|
373
391
|
logger.info(`outbound: text sent to=${ctx.To}`);
|
|
374
392
|
return;
|
|
375
393
|
}
|
|
@@ -377,10 +395,10 @@ export async function processOneMessage(
|
|
|
377
395
|
filePath,
|
|
378
396
|
to: ctx.To,
|
|
379
397
|
text,
|
|
380
|
-
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken },
|
|
398
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken, runId },
|
|
381
399
|
cdnBaseUrl: deps.cdnBaseUrl,
|
|
382
400
|
});
|
|
383
|
-
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId });
|
|
401
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId, runId });
|
|
384
402
|
logger.info(`outbound: media sent OK to=${ctx.To}`);
|
|
385
403
|
} else {
|
|
386
404
|
logger.debug(`outbound: sending text message to=${ctx.To}`);
|
|
@@ -388,12 +406,13 @@ export async function processOneMessage(
|
|
|
388
406
|
baseUrl: deps.baseUrl,
|
|
389
407
|
token: deps.token,
|
|
390
408
|
contextToken,
|
|
409
|
+
runId,
|
|
391
410
|
}});
|
|
392
|
-
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId });
|
|
411
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: true, accountId: deps.accountId, runId });
|
|
393
412
|
logger.info(`outbound: text sent OK to=${ctx.To}`);
|
|
394
413
|
}
|
|
395
414
|
} catch (err) {
|
|
396
|
-
emitWeixinMessageSent({ to: ctx.To, content: text, success: false, error: String(err), accountId: deps.accountId });
|
|
415
|
+
emitWeixinMessageSent({ to: ctx.To, content: text, success: false, error: String(err), accountId: deps.accountId, runId });
|
|
397
416
|
logger.error(
|
|
398
417
|
`outbound: FAILED to=${ctx.To} mediaUrl=${mediaUrl ?? "none"} err=${String(err)} stack=${(err as Error).stack ?? ""}`,
|
|
399
418
|
);
|
|
@@ -421,6 +440,7 @@ export async function processOneMessage(
|
|
|
421
440
|
message: notice,
|
|
422
441
|
baseUrl: deps.baseUrl,
|
|
423
442
|
token: deps.token,
|
|
443
|
+
runId,
|
|
424
444
|
errLog: deps.errLog,
|
|
425
445
|
});
|
|
426
446
|
},
|
|
@@ -435,7 +455,11 @@ export async function processOneMessage(
|
|
|
435
455
|
ctx: finalized,
|
|
436
456
|
cfg: deps.config,
|
|
437
457
|
dispatcher,
|
|
438
|
-
replyOptions: {
|
|
458
|
+
replyOptions: {
|
|
459
|
+
...replyOptions,
|
|
460
|
+
...(replyProgressSender?.replyOptions ?? {}),
|
|
461
|
+
disableBlockStreaming: true,
|
|
462
|
+
},
|
|
439
463
|
}),
|
|
440
464
|
});
|
|
441
465
|
logger.debug(`dispatchReplyFromConfig: done agentId=${route.agentId ?? "(none)"}`);
|
|
@@ -446,6 +470,7 @@ export async function processOneMessage(
|
|
|
446
470
|
throw err;
|
|
447
471
|
} finally {
|
|
448
472
|
markDispatchIdle();
|
|
473
|
+
await replyProgressSender?.finalize();
|
|
449
474
|
|
|
450
475
|
logger.info(
|
|
451
476
|
`debug-check: accountId=${deps.accountId} debug=${String(debug)} hasContextToken=${Boolean(contextToken)}`,
|
|
@@ -489,7 +514,7 @@ export async function processOneMessage(
|
|
|
489
514
|
await sendMessageWeixin({
|
|
490
515
|
to: ctx.To,
|
|
491
516
|
text: timingText,
|
|
492
|
-
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken },
|
|
517
|
+
opts: { baseUrl: deps.baseUrl, token: deps.token, contextToken, runId },
|
|
493
518
|
});
|
|
494
519
|
logger.info(`debug-timing: sent OK`);
|
|
495
520
|
} catch (debugErr) {
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { WeixinApiOptions } from "../api/api.js";
|
|
2
|
+
import type { MessageItem } from "../api/types.js";
|
|
3
|
+
import { MessageItemType } from "../api/types.js";
|
|
4
|
+
import { logger } from "../util/logger.js";
|
|
5
|
+
|
|
6
|
+
import { sendMessageItemWeixin } from "./send.js";
|
|
7
|
+
|
|
8
|
+
export type WeixinReplyProgressSenderDeps = {
|
|
9
|
+
runId: string;
|
|
10
|
+
to: string;
|
|
11
|
+
accountId: string;
|
|
12
|
+
opts: WeixinApiOptions & {
|
|
13
|
+
contextToken?: string;
|
|
14
|
+
runId?: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type ToolItemEventPayload = {
|
|
19
|
+
itemId?: string;
|
|
20
|
+
kind?: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
phase?: string;
|
|
24
|
+
status?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function normalizeToolStatus(status?: string): string {
|
|
28
|
+
if (status === "completed") return "completed";
|
|
29
|
+
if (status === "failed") return "failed";
|
|
30
|
+
if (status === "blocked") return "blocked";
|
|
31
|
+
return "unknown";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class WeixinReplyProgressSender {
|
|
35
|
+
readonly runId: string;
|
|
36
|
+
|
|
37
|
+
private readonly to: string;
|
|
38
|
+
private readonly accountId: string;
|
|
39
|
+
private readonly opts: WeixinReplyProgressSenderDeps["opts"];
|
|
40
|
+
private finalized = false;
|
|
41
|
+
private sendChain: Promise<void> = Promise.resolve();
|
|
42
|
+
|
|
43
|
+
constructor(deps: WeixinReplyProgressSenderDeps) {
|
|
44
|
+
this.runId = deps.runId;
|
|
45
|
+
this.to = deps.to;
|
|
46
|
+
this.accountId = deps.accountId;
|
|
47
|
+
this.opts = { ...deps.opts, runId: deps.runId };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get replyOptions() {
|
|
51
|
+
return {
|
|
52
|
+
runId: this.runId,
|
|
53
|
+
onItemEvent: (payload: ToolItemEventPayload) => this.handleToolItemEvent(payload),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private enqueueMessage(item: MessageItem, label: string): void {
|
|
58
|
+
if (this.finalized) return;
|
|
59
|
+
this.sendChain = this.sendChain
|
|
60
|
+
.then(async () => {
|
|
61
|
+
await sendMessageItemWeixin({
|
|
62
|
+
to: this.to,
|
|
63
|
+
item,
|
|
64
|
+
opts: this.opts,
|
|
65
|
+
label,
|
|
66
|
+
});
|
|
67
|
+
})
|
|
68
|
+
.catch((err) => {
|
|
69
|
+
logger.warn(`${label}: failed to=${this.to} accountId=${this.accountId} runId=${this.runId} err=${String(err)}`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private handleToolItemEvent(payload: ToolItemEventPayload): void {
|
|
74
|
+
if (this.finalized) return;
|
|
75
|
+
if (payload.kind !== "tool") return;
|
|
76
|
+
if (payload.phase !== "start" && payload.phase !== "end") return;
|
|
77
|
+
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
const toolName = payload.name?.trim() || payload.title?.trim() || "tool";
|
|
80
|
+
const toolCallId = payload.itemId?.trim() || undefined;
|
|
81
|
+
|
|
82
|
+
if (payload.phase === "start") {
|
|
83
|
+
this.enqueueMessage(
|
|
84
|
+
{
|
|
85
|
+
type: MessageItemType.TOOL_CALL_START,
|
|
86
|
+
create_time_ms: now,
|
|
87
|
+
is_completed: false,
|
|
88
|
+
tool_call_start_item: {
|
|
89
|
+
tool_name: toolName,
|
|
90
|
+
tool_call_id: toolCallId,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
"sendToolCallStartMessage",
|
|
94
|
+
);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.enqueueMessage(
|
|
99
|
+
{
|
|
100
|
+
type: MessageItemType.TOOL_CALL_RESULT,
|
|
101
|
+
create_time_ms: now,
|
|
102
|
+
is_completed: true,
|
|
103
|
+
tool_call_result_item: {
|
|
104
|
+
tool_name: toolName,
|
|
105
|
+
tool_call_id: toolCallId,
|
|
106
|
+
status: normalizeToolStatus(payload.status),
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
"sendToolCallResultMessage",
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async finalize(): Promise<void> {
|
|
114
|
+
if (this.finalized) return;
|
|
115
|
+
this.finalized = true;
|
|
116
|
+
try {
|
|
117
|
+
await this.sendChain;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
logger.warn(`WeixinReplyProgressSender.finalize: send drain failed runId=${this.runId} err=${String(err)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -18,7 +18,7 @@ export async function sendWeixinMediaFile(params: {
|
|
|
18
18
|
filePath: string;
|
|
19
19
|
to: string;
|
|
20
20
|
text: string;
|
|
21
|
-
opts: WeixinApiOptions & { contextToken?: string };
|
|
21
|
+
opts: WeixinApiOptions & { contextToken?: string; runId?: string };
|
|
22
22
|
cdnBaseUrl: string;
|
|
23
23
|
}): Promise<{ messageId: string }> {
|
|
24
24
|
const { filePath, to, text, opts, cdnBaseUrl } = params;
|
package/src/messaging/send.ts
CHANGED
|
@@ -10,6 +10,11 @@ import type { UploadedFileInfo } from "../cdn/upload.js";
|
|
|
10
10
|
|
|
11
11
|
export { StreamingMarkdownFilter } from "./markdown-filter.js";
|
|
12
12
|
|
|
13
|
+
type WeixinMessageSendOptions = WeixinApiOptions & {
|
|
14
|
+
contextToken?: string;
|
|
15
|
+
runId?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
13
18
|
function generateClientId(): string {
|
|
14
19
|
return generateId("openclaw-weixin");
|
|
15
20
|
}
|
|
@@ -19,9 +24,10 @@ function buildTextMessageReq(params: {
|
|
|
19
24
|
to: string;
|
|
20
25
|
text: string;
|
|
21
26
|
contextToken?: string;
|
|
27
|
+
runId?: string;
|
|
22
28
|
clientId: string;
|
|
23
29
|
}): SendMessageReq {
|
|
24
|
-
const { to, text, contextToken, clientId } = params;
|
|
30
|
+
const { to, text, contextToken, runId, clientId } = params;
|
|
25
31
|
const item_list: MessageItem[] = text
|
|
26
32
|
? [{ type: MessageItemType.TEXT, text_item: { text } }]
|
|
27
33
|
: [];
|
|
@@ -34,6 +40,7 @@ function buildTextMessageReq(params: {
|
|
|
34
40
|
message_state: MessageState.FINISH,
|
|
35
41
|
item_list: item_list.length ? item_list : undefined,
|
|
36
42
|
context_token: contextToken ?? undefined,
|
|
43
|
+
run_id: runId ?? undefined,
|
|
37
44
|
},
|
|
38
45
|
};
|
|
39
46
|
}
|
|
@@ -42,14 +49,16 @@ function buildTextMessageReq(params: {
|
|
|
42
49
|
function buildSendMessageReq(params: {
|
|
43
50
|
to: string;
|
|
44
51
|
contextToken?: string;
|
|
52
|
+
runId?: string;
|
|
45
53
|
payload: ReplyPayload;
|
|
46
54
|
clientId: string;
|
|
47
55
|
}): SendMessageReq {
|
|
48
|
-
const { to, contextToken, payload, clientId } = params;
|
|
56
|
+
const { to, contextToken, runId, payload, clientId } = params;
|
|
49
57
|
return buildTextMessageReq({
|
|
50
58
|
to,
|
|
51
59
|
text: payload.text ?? "",
|
|
52
60
|
contextToken,
|
|
61
|
+
runId,
|
|
53
62
|
clientId,
|
|
54
63
|
});
|
|
55
64
|
}
|
|
@@ -60,7 +69,7 @@ function buildSendMessageReq(params: {
|
|
|
60
69
|
export async function sendMessageWeixin(params: {
|
|
61
70
|
to: string;
|
|
62
71
|
text: string;
|
|
63
|
-
opts:
|
|
72
|
+
opts: WeixinMessageSendOptions;
|
|
64
73
|
}): Promise<{ messageId: string }> {
|
|
65
74
|
const { to, text, opts } = params;
|
|
66
75
|
if (!opts.contextToken) {
|
|
@@ -70,6 +79,7 @@ export async function sendMessageWeixin(params: {
|
|
|
70
79
|
const req = buildSendMessageReq({
|
|
71
80
|
to,
|
|
72
81
|
contextToken: opts.contextToken,
|
|
82
|
+
runId: opts.runId,
|
|
73
83
|
payload: { text },
|
|
74
84
|
clientId,
|
|
75
85
|
});
|
|
@@ -87,6 +97,47 @@ export async function sendMessageWeixin(params: {
|
|
|
87
97
|
return { messageId: clientId };
|
|
88
98
|
}
|
|
89
99
|
|
|
100
|
+
/** Send a single structured MessageItem downstream. */
|
|
101
|
+
export async function sendMessageItemWeixin(params: {
|
|
102
|
+
to: string;
|
|
103
|
+
item: MessageItem;
|
|
104
|
+
opts: WeixinMessageSendOptions;
|
|
105
|
+
clientId?: string;
|
|
106
|
+
label?: string;
|
|
107
|
+
}): Promise<{ messageId: string }> {
|
|
108
|
+
const { to, item, opts } = params;
|
|
109
|
+
if (!opts.contextToken) {
|
|
110
|
+
logger.warn(`sendMessageItemWeixin: contextToken missing for to=${to}, sending without context`);
|
|
111
|
+
}
|
|
112
|
+
const clientId = params.clientId ?? generateClientId();
|
|
113
|
+
const req: SendMessageReq = {
|
|
114
|
+
msg: {
|
|
115
|
+
from_user_id: "",
|
|
116
|
+
to_user_id: to,
|
|
117
|
+
client_id: clientId,
|
|
118
|
+
message_type: MessageType.BOT,
|
|
119
|
+
message_state: MessageState.FINISH,
|
|
120
|
+
item_list: [item],
|
|
121
|
+
context_token: opts.contextToken ?? undefined,
|
|
122
|
+
run_id: opts.runId,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
try {
|
|
126
|
+
await sendMessageApi({
|
|
127
|
+
baseUrl: opts.baseUrl,
|
|
128
|
+
token: opts.token,
|
|
129
|
+
timeoutMs: opts.timeoutMs,
|
|
130
|
+
body: req,
|
|
131
|
+
});
|
|
132
|
+
} catch (err) {
|
|
133
|
+
logger.error(
|
|
134
|
+
`${params.label ?? "sendMessageItemWeixin"}: failed to=${to} clientId=${clientId} err=${String(err)}`,
|
|
135
|
+
);
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
return { messageId: clientId };
|
|
139
|
+
}
|
|
140
|
+
|
|
90
141
|
/**
|
|
91
142
|
* Send one or more MessageItems (optionally preceded by a text caption) downstream.
|
|
92
143
|
* Each item is sent as its own request so that item_list always has exactly one entry.
|
|
@@ -95,10 +146,11 @@ async function sendMediaItems(params: {
|
|
|
95
146
|
to: string;
|
|
96
147
|
text: string;
|
|
97
148
|
mediaItem: MessageItem;
|
|
98
|
-
opts:
|
|
149
|
+
opts: WeixinMessageSendOptions;
|
|
99
150
|
label: string;
|
|
100
151
|
}): Promise<{ messageId: string }> {
|
|
101
152
|
const { to, text, mediaItem, opts, label } = params;
|
|
153
|
+
const runId = opts.runId;
|
|
102
154
|
|
|
103
155
|
const items: MessageItem[] = [];
|
|
104
156
|
if (text) {
|
|
@@ -118,6 +170,7 @@ async function sendMediaItems(params: {
|
|
|
118
170
|
message_state: MessageState.FINISH,
|
|
119
171
|
item_list: [item],
|
|
120
172
|
context_token: opts.contextToken ?? undefined,
|
|
173
|
+
run_id: runId,
|
|
121
174
|
},
|
|
122
175
|
};
|
|
123
176
|
try {
|
|
@@ -152,7 +205,7 @@ export async function sendImageMessageWeixin(params: {
|
|
|
152
205
|
to: string;
|
|
153
206
|
text: string;
|
|
154
207
|
uploaded: UploadedFileInfo;
|
|
155
|
-
opts:
|
|
208
|
+
opts: WeixinMessageSendOptions;
|
|
156
209
|
}): Promise<{ messageId: string }> {
|
|
157
210
|
const { to, text, uploaded, opts } = params;
|
|
158
211
|
if (!opts.contextToken) {
|
|
@@ -186,7 +239,7 @@ export async function sendVideoMessageWeixin(params: {
|
|
|
186
239
|
to: string;
|
|
187
240
|
text: string;
|
|
188
241
|
uploaded: UploadedFileInfo;
|
|
189
|
-
opts:
|
|
242
|
+
opts: WeixinMessageSendOptions;
|
|
190
243
|
}): Promise<{ messageId: string }> {
|
|
191
244
|
const { to, text, uploaded, opts } = params;
|
|
192
245
|
if (!opts.contextToken) {
|
|
@@ -218,7 +271,7 @@ export async function sendFileMessageWeixin(params: {
|
|
|
218
271
|
text: string;
|
|
219
272
|
fileName: string;
|
|
220
273
|
uploaded: UploadedFileInfo;
|
|
221
|
-
opts:
|
|
274
|
+
opts: WeixinMessageSendOptions;
|
|
222
275
|
}): Promise<{ messageId: string }> {
|
|
223
276
|
const { to, text, fileName, uploaded, opts } = params;
|
|
224
277
|
if (!opts.contextToken) {
|
package/src/monitor/monitor.ts
CHANGED
|
@@ -96,6 +96,9 @@ export async function monitorWeixinProvider(opts: MonitorWeixinOpts): Promise<vo
|
|
|
96
96
|
token,
|
|
97
97
|
get_updates_buf: getUpdatesBuf,
|
|
98
98
|
timeoutMs: nextTimeoutMs,
|
|
99
|
+
// Stop/hot-reload should cancel the in-flight long-poll immediately
|
|
100
|
+
// instead of waiting for the server-side long-poll timeout.
|
|
101
|
+
abortSignal,
|
|
99
102
|
});
|
|
100
103
|
aLog.debug(
|
|
101
104
|
`getUpdates response: ret=${resp.ret}, msgs=${resp.msgs?.length ?? 0}, get_updates_buf_length=${resp.get_updates_buf?.length ?? 0}`,
|