@dingxiang-me/openclaw-wechat 1.7.1 → 2.0.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.
- package/CHANGELOG.md +162 -0
- package/README.en.md +379 -11
- package/README.md +626 -15
- package/docs/channels/wecom.md +186 -6
- package/openclaw.plugin.json +148 -5
- package/package.json +9 -5
- package/src/core/delivery-router.js +2 -0
- package/src/core/stream-manager.js +13 -2
- package/src/core.js +96 -6
- package/src/wecom/account-config-core.js +2 -0
- package/src/wecom/account-config.js +12 -3
- package/src/wecom/agent-context.js +7 -1
- package/src/wecom/agent-dispatch-executor.js +13 -1
- package/src/wecom/agent-dispatch-fallback.js +23 -0
- package/src/wecom/agent-inbound-dispatch.js +1 -1
- package/src/wecom/agent-inbound-processor.js +33 -2
- package/src/wecom/agent-late-reply-runtime.js +31 -1
- package/src/wecom/agent-runtime-context.js +3 -0
- package/src/wecom/agent-webhook-handler.js +5 -0
- package/src/wecom/api-client-core.js +1 -1
- package/src/wecom/bot-context.js +7 -1
- package/src/wecom/bot-dispatch-fallback.js +34 -3
- package/src/wecom/bot-dispatch-handlers.js +47 -4
- package/src/wecom/bot-inbound-dispatch-runtime.js +10 -0
- package/src/wecom/bot-inbound-executor-helpers.js +51 -5
- package/src/wecom/bot-inbound-executor.js +34 -0
- package/src/wecom/bot-long-connection-manager.js +971 -0
- package/src/wecom/bot-reply-runtime.js +36 -6
- package/src/wecom/bot-runtime-context.js +3 -0
- package/src/wecom/bot-state-store.js +4 -5
- package/src/wecom/bot-webhook-dispatch.js +5 -0
- package/src/wecom/bot-webhook-handler.js +5 -0
- package/src/wecom/callback-health-diagnostics.js +86 -0
- package/src/wecom/channel-config-schema.js +242 -0
- package/src/wecom/channel-plugin.js +162 -4
- package/src/wecom/channel-status-state.js +150 -0
- package/src/wecom/command-handlers.js +6 -0
- package/src/wecom/command-status-text.js +35 -8
- package/src/wecom/doc-client.js +537 -0
- package/src/wecom/doc-schema.js +380 -0
- package/src/wecom/doc-tool.js +833 -0
- package/src/wecom/outbound-active-stream.js +17 -10
- package/src/wecom/outbound-delivery.js +49 -0
- package/src/wecom/plugin-account-policy-services.js +4 -1
- package/src/wecom/plugin-composition.js +2 -0
- package/src/wecom/plugin-constants.js +1 -1
- package/src/wecom/plugin-delivery-inbound-services.js +4 -0
- package/src/wecom/plugin-processing-deps.js +5 -0
- package/src/wecom/plugin-route-runtime-deps.js +2 -0
- package/src/wecom/plugin-services.js +37 -0
- package/src/wecom/register-runtime.js +20 -1
- package/src/wecom/request-parsers.js +1 -0
- package/src/wecom/route-registration.js +4 -1
- package/src/wecom/session-reset.js +168 -0
- package/src/wecom/text-format.js +22 -5
- package/src/wecom/text-inbound-scheduler.js +1 -1
- package/src/wecom/thinking-parser.js +74 -0
- package/src/wecom/voice-transcription-process.js +80 -8
- package/src/wecom/voice-transcription.js +11 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const QUICK_TAG_RE = /<\s*\/?\s*(?:think(?:ing)?|thought)\b/i;
|
|
2
|
+
const THINK_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought)\b[^<>]*>/gi;
|
|
3
|
+
|
|
4
|
+
function isInsideRegion(pos, regions) {
|
|
5
|
+
for (const [start, end] of regions) {
|
|
6
|
+
if (pos >= start && pos < end) return true;
|
|
7
|
+
}
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function findCodeRegions(text) {
|
|
12
|
+
const regions = [];
|
|
13
|
+
const blockRe = /```[\s\S]*?```/g;
|
|
14
|
+
for (const match of text.matchAll(blockRe)) {
|
|
15
|
+
regions.push([match.index, match.index + match[0].length]);
|
|
16
|
+
}
|
|
17
|
+
const inlineRe = /`[^`\n]+`/g;
|
|
18
|
+
for (const match of text.matchAll(inlineRe)) {
|
|
19
|
+
if (!isInsideRegion(match.index, regions)) {
|
|
20
|
+
regions.push([match.index, match.index + match[0].length]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return regions;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseThinkingContent(text) {
|
|
27
|
+
if (!text) {
|
|
28
|
+
return { visibleContent: "", thinkingContent: "", isThinking: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const input = String(text);
|
|
32
|
+
if (!QUICK_TAG_RE.test(input)) {
|
|
33
|
+
return { visibleContent: input, thinkingContent: "", isThinking: false };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const codeRegions = findCodeRegions(input);
|
|
37
|
+
const visibleParts = [];
|
|
38
|
+
const thinkingParts = [];
|
|
39
|
+
let lastIndex = 0;
|
|
40
|
+
let inThinking = false;
|
|
41
|
+
|
|
42
|
+
THINK_TAG_RE.lastIndex = 0;
|
|
43
|
+
for (const match of input.matchAll(THINK_TAG_RE)) {
|
|
44
|
+
const idx = match.index;
|
|
45
|
+
const isClose = match[1] === "/";
|
|
46
|
+
if (isInsideRegion(idx, codeRegions)) continue;
|
|
47
|
+
|
|
48
|
+
const segment = input.slice(lastIndex, idx);
|
|
49
|
+
if (!inThinking) {
|
|
50
|
+
visibleParts.push(segment);
|
|
51
|
+
if (!isClose) {
|
|
52
|
+
inThinking = true;
|
|
53
|
+
}
|
|
54
|
+
} else if (isClose) {
|
|
55
|
+
thinkingParts.push(segment);
|
|
56
|
+
inThinking = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
lastIndex = idx + match[0].length;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const remaining = input.slice(lastIndex);
|
|
63
|
+
if (inThinking) {
|
|
64
|
+
thinkingParts.push(remaining);
|
|
65
|
+
} else {
|
|
66
|
+
visibleParts.push(remaining);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
visibleContent: visibleParts.join("").replace(/\n{3,}/g, "\n\n").trim(),
|
|
71
|
+
thinkingContent: thinkingParts.join("\n").replace(/\n{3,}/g, "\n\n").trim(),
|
|
72
|
+
isThinking: inThinking,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -98,7 +98,7 @@ export function createVoiceTranscriptionProcessRuntime({
|
|
|
98
98
|
return available;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
function listLocalWhisperCommandCandidates({ voiceConfig } = {}) {
|
|
102
102
|
const provider = String(voiceConfig?.provider ?? "").trim().toLowerCase();
|
|
103
103
|
const explicitCommand = String(voiceConfig?.command ?? "").trim();
|
|
104
104
|
const fallbackCandidates =
|
|
@@ -110,33 +110,105 @@ export function createVoiceTranscriptionProcessRuntime({
|
|
|
110
110
|
const candidates = explicitCommand ? [explicitCommand, ...fallbackCandidates] : fallbackCandidates;
|
|
111
111
|
|
|
112
112
|
if (candidates.length === 0) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
return {
|
|
114
|
+
provider,
|
|
115
|
+
explicitCommand,
|
|
116
|
+
candidates: [],
|
|
117
|
+
error: `unsupported voice transcription provider: ${provider || "unknown"} (supported: local-whisper-cli/local-whisper)`,
|
|
118
|
+
};
|
|
116
119
|
}
|
|
117
120
|
|
|
118
|
-
|
|
121
|
+
return {
|
|
122
|
+
provider,
|
|
123
|
+
explicitCommand,
|
|
124
|
+
candidates,
|
|
125
|
+
error: "",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function resolveLocalWhisperCommand({ voiceConfig, logger }) {
|
|
130
|
+
const resolution = listLocalWhisperCommandCandidates({ voiceConfig });
|
|
131
|
+
if (resolution.error) {
|
|
132
|
+
throw new Error(resolution.error);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const cmd of resolution.candidates) {
|
|
119
136
|
// eslint-disable-next-line no-await-in-loop
|
|
120
137
|
if (await checkCommandAvailable(cmd)) {
|
|
121
|
-
if (explicitCommand && cmd !== explicitCommand) {
|
|
122
|
-
logger?.warn?.(`wecom: voice command ${explicitCommand} unavailable, fallback to ${cmd}`);
|
|
138
|
+
if (resolution.explicitCommand && cmd !== resolution.explicitCommand) {
|
|
139
|
+
logger?.warn?.(`wecom: voice command ${resolution.explicitCommand} unavailable, fallback to ${cmd}`);
|
|
123
140
|
}
|
|
124
141
|
return cmd;
|
|
125
142
|
}
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
|
|
145
|
+
const checkedList = resolution.candidates.join(" / ");
|
|
146
|
+
throw new Error(
|
|
147
|
+
`local transcription command not found: checked ${checkedList}. ` +
|
|
148
|
+
"Confirm the command is installed and available in PATH for the OpenClaw runtime.",
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function inspectVoiceTranscriptionRuntime({ voiceConfig, logger } = {}) {
|
|
153
|
+
const resolution = listLocalWhisperCommandCandidates({ voiceConfig });
|
|
154
|
+
const commandChecks = [];
|
|
155
|
+
for (const cmd of resolution.candidates) {
|
|
156
|
+
// eslint-disable-next-line no-await-in-loop
|
|
157
|
+
const available = await checkCommandAvailable(cmd);
|
|
158
|
+
commandChecks.push({ command: cmd, available });
|
|
159
|
+
}
|
|
160
|
+
const resolvedCommand = commandChecks.find((item) => item.available)?.command || "";
|
|
161
|
+
const ffmpegEnabled = voiceConfig?.ffmpegEnabled !== false;
|
|
162
|
+
const ffmpegAvailable = ffmpegEnabled ? await ensureFfmpegAvailable(logger) : false;
|
|
163
|
+
const provider = String(voiceConfig?.provider ?? "").trim().toLowerCase();
|
|
164
|
+
const requireModelPath = provider === "local-whisper-cli" && voiceConfig?.requireModelPath !== false;
|
|
165
|
+
const modelPath = String(voiceConfig?.modelPath ?? "").trim();
|
|
166
|
+
const issues = [];
|
|
167
|
+
if (!voiceConfig?.enabled) {
|
|
168
|
+
issues.push("voice transcription disabled");
|
|
169
|
+
}
|
|
170
|
+
if (resolution.error) {
|
|
171
|
+
issues.push(resolution.error);
|
|
172
|
+
}
|
|
173
|
+
if (resolution.candidates.length > 0 && !resolvedCommand) {
|
|
174
|
+
issues.push(`no available command in PATH: ${resolution.candidates.join(" / ")}`);
|
|
175
|
+
}
|
|
176
|
+
if (requireModelPath && !modelPath) {
|
|
177
|
+
issues.push("voiceTranscription.modelPath is required for local-whisper-cli");
|
|
178
|
+
}
|
|
179
|
+
if (ffmpegEnabled && !ffmpegAvailable) {
|
|
180
|
+
issues.push("ffmpeg not available");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
enabled: voiceConfig?.enabled === true,
|
|
185
|
+
provider,
|
|
186
|
+
explicitCommand: resolution.explicitCommand || "",
|
|
187
|
+
commandCandidates: resolution.candidates,
|
|
188
|
+
commandChecks,
|
|
189
|
+
resolvedCommand,
|
|
190
|
+
ffmpegEnabled,
|
|
191
|
+
ffmpegAvailable,
|
|
192
|
+
requireModelPath,
|
|
193
|
+
modelPathConfigured: Boolean(modelPath),
|
|
194
|
+
modelPath,
|
|
195
|
+
issues,
|
|
196
|
+
};
|
|
129
197
|
}
|
|
130
198
|
|
|
131
199
|
assertFunction("runProcessWithTimeout", runProcessWithTimeout);
|
|
132
200
|
assertFunction("checkCommandAvailable", checkCommandAvailable);
|
|
133
201
|
assertFunction("ensureFfmpegAvailable", ensureFfmpegAvailable);
|
|
202
|
+
assertFunction("listLocalWhisperCommandCandidates", listLocalWhisperCommandCandidates);
|
|
134
203
|
assertFunction("resolveLocalWhisperCommand", resolveLocalWhisperCommand);
|
|
204
|
+
assertFunction("inspectVoiceTranscriptionRuntime", inspectVoiceTranscriptionRuntime);
|
|
135
205
|
|
|
136
206
|
return {
|
|
137
207
|
runProcessWithTimeout,
|
|
138
208
|
checkCommandAvailable,
|
|
139
209
|
ensureFfmpegAvailable,
|
|
210
|
+
listLocalWhisperCommandCandidates,
|
|
140
211
|
resolveLocalWhisperCommand,
|
|
212
|
+
inspectVoiceTranscriptionRuntime,
|
|
141
213
|
};
|
|
142
214
|
}
|
|
@@ -35,6 +35,7 @@ export function createWecomVoiceTranscriber({
|
|
|
35
35
|
checkCommandAvailable,
|
|
36
36
|
ensureFfmpegAvailable,
|
|
37
37
|
resolveLocalWhisperCommand,
|
|
38
|
+
inspectVoiceTranscriptionRuntime,
|
|
38
39
|
} = processRuntime;
|
|
39
40
|
|
|
40
41
|
function resolveWecomVoiceTranscriptionConfig(api) {
|
|
@@ -232,10 +233,20 @@ export function createWecomVoiceTranscriber({
|
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
|
|
236
|
+
async function inspectWecomVoiceTranscriptionRuntime({ api, voiceConfig } = {}) {
|
|
237
|
+
const resolvedConfig = voiceConfig ?? resolveWecomVoiceTranscriptionConfig(api);
|
|
238
|
+
return inspectVoiceTranscriptionRuntime({
|
|
239
|
+
voiceConfig: resolvedConfig,
|
|
240
|
+
logger: api?.logger,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
235
244
|
return {
|
|
236
245
|
resolveWecomVoiceTranscriptionConfig,
|
|
237
246
|
transcribeInboundVoice,
|
|
247
|
+
inspectWecomVoiceTranscriptionRuntime,
|
|
238
248
|
__internal: {
|
|
249
|
+
inspectVoiceTranscriptionRuntime,
|
|
239
250
|
resolveLocalWhisperCommand,
|
|
240
251
|
checkCommandAvailable,
|
|
241
252
|
ensureFfmpegAvailable,
|