@dcrays/dcgchat-test 0.2.21 → 0.2.22
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/package.json +1 -1
- package/src/bot.ts +331 -268
- package/src/channel.ts +38 -38
- package/src/monitor.ts +73 -22
- package/src/tool.ts +11 -10
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ClawdbotConfig,
|
|
5
|
+
ReplyPayload,
|
|
6
|
+
RuntimeEnv,
|
|
7
|
+
} from "openclaw/plugin-sdk";
|
|
4
8
|
import { createReplyPrefixContext } from "openclaw/plugin-sdk";
|
|
5
9
|
import type { InboundMessage, OutboundReply } from "./types.js";
|
|
6
10
|
import { getDcgchatRuntime, getWorkspaceDir } from "./runtime.js";
|
|
@@ -16,33 +20,68 @@ type MediaInfo = {
|
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
const mediaMaxBytes = 300 * 1024 * 1024;
|
|
19
|
-
|
|
23
|
+
|
|
24
|
+
/** Active LLM generation abort controllers, keyed by conversationId */
|
|
25
|
+
const activeGenerations = new Map<string, AbortController>();
|
|
26
|
+
|
|
27
|
+
/** Abort an in-progress LLM generation for a given conversationId */
|
|
28
|
+
export function abortMobookappGeneration(conversationId: string): void {
|
|
29
|
+
const ctrl = activeGenerations.get(conversationId);
|
|
30
|
+
console.log("🚀 ~ abortMobookappGeneration ~ ctrl:", ctrl)
|
|
31
|
+
if (ctrl) {
|
|
32
|
+
ctrl.abort();
|
|
33
|
+
activeGenerations.delete(conversationId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extract agentId from conversation_id formatted as "agentId::suffix".
|
|
39
|
+
* Returns null if the conversation_id does not contain the "::" separator.
|
|
40
|
+
*/
|
|
41
|
+
export function extractAgentIdFromConversationId(
|
|
42
|
+
conversationId: string,
|
|
43
|
+
): string | null {
|
|
44
|
+
const idx = conversationId.indexOf("::");
|
|
45
|
+
if (idx <= 0) return null;
|
|
46
|
+
return conversationId.slice(0, idx);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function resolveMediaFromUrls(
|
|
50
|
+
files: { name: string; url: string }[],
|
|
51
|
+
botToken: string,
|
|
52
|
+
log: (message: string) => void,
|
|
53
|
+
): Promise<MediaInfo[]> {
|
|
20
54
|
const core = getDcgchatRuntime();
|
|
21
55
|
const out: MediaInfo[] = [];
|
|
22
|
-
log(
|
|
56
|
+
log(
|
|
57
|
+
`dcgchat media: starting resolve for ${files.length} file(s): ${JSON.stringify(files)}`,
|
|
58
|
+
);
|
|
23
59
|
|
|
24
60
|
for (let i = 0; i < files.length; i++) {
|
|
25
61
|
const file = files[i];
|
|
26
62
|
try {
|
|
27
|
-
let data =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
63
|
+
let data = "";
|
|
64
|
+
if (/^https?:\/\//i.test(file.url)) {
|
|
65
|
+
data = file.url;
|
|
66
|
+
} else {
|
|
67
|
+
data = await generateSignUrl(file.url, botToken);
|
|
68
|
+
}
|
|
33
69
|
log(`dcgchat media: [${i + 1}/${files.length}] generateSignUrl: ${data}`);
|
|
34
70
|
const response = await fetch(data);
|
|
35
71
|
if (!response.ok) {
|
|
36
|
-
log?.(
|
|
72
|
+
log?.(
|
|
73
|
+
`dcgchat media: [${i + 1}/${files.length}] fetch failed with HTTP ${response.status}, skipping`,
|
|
74
|
+
);
|
|
37
75
|
continue;
|
|
38
76
|
}
|
|
39
77
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
40
78
|
|
|
41
79
|
let contentType = response.headers.get("content-type") || "";
|
|
42
80
|
if (!contentType) {
|
|
43
|
-
contentType = await core.media.detectMime({ buffer }) || "";
|
|
81
|
+
contentType = (await core.media.detectMime({ buffer })) || "";
|
|
44
82
|
}
|
|
45
|
-
const fileName =
|
|
83
|
+
const fileName =
|
|
84
|
+
file.name || path.basename(new URL(file.url).pathname) || "file";
|
|
46
85
|
const saved = await core.channel.media.saveMediaBuffer(
|
|
47
86
|
buffer,
|
|
48
87
|
contentType,
|
|
@@ -57,12 +96,15 @@ async function resolveMediaFromUrls(files: { name: string, url: string }[], botT
|
|
|
57
96
|
contentType: saved.contentType || "",
|
|
58
97
|
placeholder: isImage ? "<media:image>" : "<media:file>",
|
|
59
98
|
});
|
|
60
|
-
|
|
61
99
|
} catch (err) {
|
|
62
|
-
log(
|
|
100
|
+
log(
|
|
101
|
+
`dcgchat media: [${i + 1}/${files.length}] FAILED to process ${file.url}: ${String(err)}`,
|
|
102
|
+
);
|
|
63
103
|
}
|
|
64
104
|
}
|
|
65
|
-
log(
|
|
105
|
+
log(
|
|
106
|
+
`dcgchat media: resolve complete, ${out.length}/${files.length} file(s) succeeded`,
|
|
107
|
+
);
|
|
66
108
|
|
|
67
109
|
return out;
|
|
68
110
|
}
|
|
@@ -99,58 +141,6 @@ function resolveReplyMediaList(payload: ReplyPayload): string[] {
|
|
|
99
141
|
return payload.mediaUrl ? [payload.mediaUrl] : [];
|
|
100
142
|
}
|
|
101
143
|
|
|
102
|
-
function createFileExtractor() {
|
|
103
|
-
const globalSet = new Set()
|
|
104
|
-
|
|
105
|
-
function getNewFiles(text: string) {
|
|
106
|
-
if (!text) return []
|
|
107
|
-
|
|
108
|
-
const currentSet = new Set()
|
|
109
|
-
const lines = text.split(/\n+/)
|
|
110
|
-
|
|
111
|
-
for (const line of lines) {
|
|
112
|
-
const cleanLine = line.trim()
|
|
113
|
-
if (!cleanLine) continue
|
|
114
|
-
|
|
115
|
-
const matches = cleanLine.matchAll(/`([^`]+)`/g)
|
|
116
|
-
for (const m of matches) {
|
|
117
|
-
handlePath(m[1])
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const rawMatches = cleanLine.match(/\/[^\s))]+/g) || []
|
|
121
|
-
for (const p of rawMatches) {
|
|
122
|
-
handlePath(p)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function handlePath(p: string) {
|
|
127
|
-
const filePath = p.trim()
|
|
128
|
-
if (filePath.includes('\n')) return
|
|
129
|
-
if (isValidFile(filePath)) {
|
|
130
|
-
currentSet.add(filePath)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const newFiles = []
|
|
135
|
-
for (const file of currentSet) {
|
|
136
|
-
if (!globalSet.has(file)) {
|
|
137
|
-
globalSet.add(file)
|
|
138
|
-
newFiles.push(file)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return newFiles
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function isValidFile(p: string) {
|
|
146
|
-
return (
|
|
147
|
-
/\/(upload|mobook)\//i.test(p) &&
|
|
148
|
-
/\.[a-zA-Z0-9]+$/.test(p)
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return { getNewFiles }
|
|
153
|
-
}
|
|
154
144
|
|
|
155
145
|
/**
|
|
156
146
|
* 从文本中提取 /mobook 目录下的文件
|
|
@@ -159,91 +149,131 @@ function createFileExtractor() {
|
|
|
159
149
|
*/
|
|
160
150
|
const EXT_LIST = [
|
|
161
151
|
// 文档类
|
|
162
|
-
|
|
152
|
+
"doc",
|
|
153
|
+
"docx",
|
|
154
|
+
"xls",
|
|
155
|
+
"xlsx",
|
|
156
|
+
"ppt",
|
|
157
|
+
"pptx",
|
|
158
|
+
"pdf",
|
|
159
|
+
"txt",
|
|
160
|
+
"rtf",
|
|
161
|
+
"odt",
|
|
163
162
|
|
|
164
163
|
// 数据/开发
|
|
165
|
-
|
|
164
|
+
"json",
|
|
165
|
+
"xml",
|
|
166
|
+
"csv",
|
|
167
|
+
"yaml",
|
|
168
|
+
"yml",
|
|
166
169
|
|
|
167
170
|
// 前端/文本
|
|
168
|
-
|
|
171
|
+
"html",
|
|
172
|
+
"htm",
|
|
173
|
+
"md",
|
|
174
|
+
"markdown",
|
|
175
|
+
"css",
|
|
176
|
+
"js",
|
|
177
|
+
"ts",
|
|
169
178
|
|
|
170
179
|
// 图片
|
|
171
|
-
|
|
180
|
+
"png",
|
|
181
|
+
"jpg",
|
|
182
|
+
"jpeg",
|
|
183
|
+
"gif",
|
|
184
|
+
"bmp",
|
|
185
|
+
"webp",
|
|
186
|
+
"svg",
|
|
187
|
+
"ico",
|
|
188
|
+
"tiff",
|
|
172
189
|
|
|
173
190
|
// 音频
|
|
174
|
-
|
|
191
|
+
"mp3",
|
|
192
|
+
"wav",
|
|
193
|
+
"ogg",
|
|
194
|
+
"aac",
|
|
195
|
+
"flac",
|
|
196
|
+
"m4a",
|
|
175
197
|
|
|
176
198
|
// 视频
|
|
177
|
-
|
|
199
|
+
"mp4",
|
|
200
|
+
"avi",
|
|
201
|
+
"mov",
|
|
202
|
+
"wmv",
|
|
203
|
+
"flv",
|
|
204
|
+
"mkv",
|
|
205
|
+
"webm",
|
|
178
206
|
|
|
179
207
|
// 压缩包
|
|
180
|
-
|
|
208
|
+
"zip",
|
|
209
|
+
"rar",
|
|
210
|
+
"7z",
|
|
211
|
+
"tar",
|
|
212
|
+
"gz",
|
|
213
|
+
"bz2",
|
|
214
|
+
"xz",
|
|
181
215
|
|
|
182
216
|
// 可执行/程序
|
|
183
|
-
|
|
217
|
+
"exe",
|
|
218
|
+
"dmg",
|
|
219
|
+
"pkg",
|
|
220
|
+
"apk",
|
|
221
|
+
"ipa",
|
|
184
222
|
|
|
185
223
|
// 其他常见
|
|
186
|
-
|
|
224
|
+
"log",
|
|
225
|
+
"dat",
|
|
226
|
+
"bin",
|
|
187
227
|
];
|
|
188
228
|
|
|
189
|
-
function extractMobookFiles(text =
|
|
190
|
-
if (typeof text !==
|
|
191
|
-
|
|
229
|
+
function extractMobookFiles(text = "") {
|
|
230
|
+
if (typeof text !== "string" || !text.trim()) return [];
|
|
192
231
|
const result = new Set();
|
|
193
|
-
|
|
194
232
|
// ✅ 扩展名
|
|
195
|
-
const EXT = `(${EXT_LIST.join(
|
|
196
|
-
|
|
233
|
+
const EXT = `(${EXT_LIST.join("|")})`;
|
|
197
234
|
// ✅ 文件名字符(增强:支持中文、符号)
|
|
198
235
|
const FILE_NAME = `[\\w\\u4e00-\\u9fa5::《》()()\\-\\s]+?`;
|
|
199
|
-
|
|
200
236
|
try {
|
|
201
237
|
// 1️⃣ `xxx.xxx`
|
|
202
|
-
const backtickReg = new RegExp(`\`([^\\\`]+?\\.${EXT})\``,
|
|
203
|
-
(text.match(backtickReg) || []).forEach(item => {
|
|
204
|
-
const name = item.replace(/`/g,
|
|
238
|
+
const backtickReg = new RegExp(`\`([^\\\`]+?\\.${EXT})\``, "gi");
|
|
239
|
+
(text.match(backtickReg) || []).forEach((item) => {
|
|
240
|
+
const name = item.replace(/`/g, "").trim();
|
|
205
241
|
if (isValidFileName(name)) {
|
|
206
242
|
result.add(`/mobook/${name}`);
|
|
207
243
|
}
|
|
208
244
|
});
|
|
209
|
-
|
|
210
245
|
// 2️⃣ /mobook/xxx.xxx
|
|
211
|
-
const fullPathReg = new RegExp(`/mobook/${FILE_NAME}\\.${EXT}`,
|
|
212
|
-
(text.match(fullPathReg) || []).forEach(p => {
|
|
246
|
+
const fullPathReg = new RegExp(`/mobook/${FILE_NAME}\\.${EXT}`, "gi");
|
|
247
|
+
(text.match(fullPathReg) || []).forEach((p) => {
|
|
213
248
|
result.add(normalizePath(p));
|
|
214
249
|
});
|
|
215
|
-
|
|
216
250
|
// 3️⃣ mobook下的 xxx.xxx
|
|
217
|
-
const inlineReg = new RegExp(`mobook下的\\s*(${FILE_NAME}\\.${EXT})`,
|
|
218
|
-
(text.match(inlineReg) || []).forEach(item => {
|
|
219
|
-
const match = item.match(new RegExp(`${FILE_NAME}\\.${EXT}`,
|
|
251
|
+
const inlineReg = new RegExp(`mobook下的\\s*(${FILE_NAME}\\.${EXT})`, "gi");
|
|
252
|
+
(text.match(inlineReg) || []).forEach((item) => {
|
|
253
|
+
const match = item.match(new RegExp(`${FILE_NAME}\\.${EXT}`, "i"));
|
|
220
254
|
if (match && isValidFileName(match[0])) {
|
|
221
255
|
result.add(`/mobook/${match[0].trim()}`);
|
|
222
256
|
}
|
|
223
257
|
});
|
|
224
|
-
|
|
225
258
|
// 🆕 4️⃣ **xxx.xxx**
|
|
226
|
-
const boldReg = new RegExp(`\\*\\*(${FILE_NAME}\\.${EXT})\\*\\*`,
|
|
227
|
-
(text.match(boldReg) || []).forEach(item => {
|
|
228
|
-
const name = item.replace(/\*\*/g,
|
|
259
|
+
const boldReg = new RegExp(`\\*\\*(${FILE_NAME}\\.${EXT})\\*\\*`, "gi");
|
|
260
|
+
(text.match(boldReg) || []).forEach((item) => {
|
|
261
|
+
const name = item.replace(/\*\*/g, "").trim();
|
|
229
262
|
if (isValidFileName(name)) {
|
|
230
263
|
result.add(`/mobook/${name}`);
|
|
231
264
|
}
|
|
232
265
|
});
|
|
233
|
-
|
|
234
266
|
// 🆕 5️⃣ xxx.xxx (123字节)
|
|
235
|
-
const looseReg = new RegExp(`(${FILE_NAME}\\.${EXT})\\s*\\(`,
|
|
236
|
-
(text.match(looseReg) || []).forEach(item => {
|
|
237
|
-
const name = item.replace(/\s*\(.+$/,
|
|
267
|
+
const looseReg = new RegExp(`(${FILE_NAME}\\.${EXT})\\s*\\(`, "gi");
|
|
268
|
+
(text.match(looseReg) || []).forEach((item) => {
|
|
269
|
+
const name = item.replace(/\s*\(.+$/, "").trim();
|
|
238
270
|
if (isValidFileName(name)) {
|
|
239
271
|
result.add(`/mobook/${name}`);
|
|
240
272
|
}
|
|
241
273
|
});
|
|
242
|
-
|
|
243
274
|
} catch (e) {
|
|
244
|
-
console.warn(
|
|
275
|
+
console.warn("extractMobookFiles error:", e);
|
|
245
276
|
}
|
|
246
|
-
|
|
247
277
|
return [...result];
|
|
248
278
|
}
|
|
249
279
|
|
|
@@ -252,13 +282,10 @@ function extractMobookFiles(text = '') {
|
|
|
252
282
|
*/
|
|
253
283
|
function isValidFileName(name: string) {
|
|
254
284
|
if (!name) return false;
|
|
255
|
-
|
|
256
285
|
// 过滤异常字符
|
|
257
286
|
if (/[\/\\<>:"|?*]/.test(name)) return false;
|
|
258
|
-
|
|
259
287
|
// 长度限制(防止异常长字符串)
|
|
260
288
|
if (name.length > 200) return false;
|
|
261
|
-
|
|
262
289
|
return true;
|
|
263
290
|
}
|
|
264
291
|
|
|
@@ -267,8 +294,8 @@ function isValidFileName(name: string) {
|
|
|
267
294
|
*/
|
|
268
295
|
function normalizePath(path: string) {
|
|
269
296
|
return path
|
|
270
|
-
.replace(/\/+/g,
|
|
271
|
-
.replace(/\/$/,
|
|
297
|
+
.replace(/\/+/g, "/") // 多斜杠 → 单斜杠
|
|
298
|
+
.replace(/\/$/, ""); // 去掉结尾 /
|
|
272
299
|
}
|
|
273
300
|
|
|
274
301
|
/**
|
|
@@ -285,7 +312,7 @@ export async function handleDcgchatMessage(params: {
|
|
|
285
312
|
const log = runtime?.log ?? console.log;
|
|
286
313
|
const error = runtime?.error ?? console.error;
|
|
287
314
|
// 完整的文本
|
|
288
|
-
let completeText =
|
|
315
|
+
let completeText = "";
|
|
289
316
|
|
|
290
317
|
const account = resolveAccount(cfg, accountId);
|
|
291
318
|
const userId = msg._userId.toString();
|
|
@@ -314,23 +341,58 @@ export async function handleDcgchatMessage(params: {
|
|
|
314
341
|
try {
|
|
315
342
|
const core = getDcgchatRuntime();
|
|
316
343
|
|
|
344
|
+
const conversationId = msg.content.session_id?.trim();
|
|
345
|
+
|
|
317
346
|
const route = core.channel.routing.resolveAgentRoute({
|
|
318
347
|
cfg,
|
|
319
|
-
channel: "dcgchat",
|
|
348
|
+
channel: "dcgchat-test",
|
|
320
349
|
accountId: account.accountId,
|
|
321
|
-
peer: { kind: "direct", id:
|
|
350
|
+
peer: { kind: "direct", id: conversationId },
|
|
322
351
|
});
|
|
323
352
|
|
|
353
|
+
// If conversation_id encodes an agentId prefix ("agentId::suffix"), override the route.
|
|
354
|
+
const embeddedAgentId = extractAgentIdFromConversationId(conversationId);
|
|
355
|
+
const effectiveAgentId = embeddedAgentId ?? route.agentId;
|
|
356
|
+
const effectiveSessionKey = embeddedAgentId
|
|
357
|
+
? `agent:${embeddedAgentId}:mobook:direct:${conversationId}`.toLowerCase()
|
|
358
|
+
: route.sessionKey;
|
|
359
|
+
|
|
360
|
+
const agentEntry =
|
|
361
|
+
effectiveAgentId && effectiveAgentId !== "main"
|
|
362
|
+
? cfg.agents?.list?.find((a) => a.id === effectiveAgentId)
|
|
363
|
+
: undefined;
|
|
364
|
+
const agentDisplayName =
|
|
365
|
+
agentEntry?.name ??
|
|
366
|
+
(effectiveAgentId && effectiveAgentId !== "main"
|
|
367
|
+
? effectiveAgentId
|
|
368
|
+
: undefined);
|
|
369
|
+
|
|
370
|
+
// Abort any existing generation for this conversation, then start a new one
|
|
371
|
+
const existingCtrl = activeGenerations.get(conversationId);
|
|
372
|
+
console.log("🚀 ~ handleDcgchatMessage ~ conversationId:", existingCtrl)
|
|
373
|
+
if (existingCtrl) existingCtrl.abort();
|
|
374
|
+
const genCtrl = new AbortController();
|
|
375
|
+
const genSignal = genCtrl.signal;
|
|
376
|
+
console.log("🚀 ~ handleDcgchatMessage ~ conversationId:", conversationId)
|
|
377
|
+
activeGenerations.set(conversationId, genCtrl);
|
|
378
|
+
|
|
324
379
|
// 处理用户上传的文件
|
|
325
380
|
const files = msg.content.files ?? [];
|
|
326
381
|
let mediaPayload: Record<string, unknown> = {};
|
|
327
382
|
if (files.length > 0) {
|
|
328
|
-
const mediaList = await resolveMediaFromUrls(
|
|
383
|
+
const mediaList = await resolveMediaFromUrls(
|
|
384
|
+
files,
|
|
385
|
+
msg.content.bot_token,
|
|
386
|
+
log,
|
|
387
|
+
);
|
|
329
388
|
mediaPayload = buildMediaPayload(mediaList);
|
|
330
|
-
log(
|
|
389
|
+
log(
|
|
390
|
+
`dcgchat[${accountId}]: media resolved ${mediaList.length}/${files.length} file(s), payload=${JSON.stringify(mediaList)}`,
|
|
391
|
+
);
|
|
331
392
|
}
|
|
332
393
|
|
|
333
|
-
const envelopeOptions =
|
|
394
|
+
const envelopeOptions =
|
|
395
|
+
core.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
334
396
|
// const messageBody = `${userId}: ${text}`;
|
|
335
397
|
// 补充消息
|
|
336
398
|
const messageBody = text;
|
|
@@ -347,192 +409,173 @@ export async function handleDcgchatMessage(params: {
|
|
|
347
409
|
RawBody: text,
|
|
348
410
|
CommandBody: text,
|
|
349
411
|
From: userId,
|
|
350
|
-
To:
|
|
351
|
-
SessionKey:
|
|
352
|
-
AccountId:
|
|
412
|
+
To: conversationId,
|
|
413
|
+
SessionKey: effectiveSessionKey,
|
|
414
|
+
AccountId: route.accountId,
|
|
353
415
|
ChatType: "direct",
|
|
354
|
-
SenderName:
|
|
416
|
+
SenderName: agentDisplayName,
|
|
355
417
|
SenderId: userId,
|
|
356
|
-
Provider: "dcgchat" as const,
|
|
357
|
-
Surface: "dcgchat" as const,
|
|
418
|
+
Provider: "dcgchat-test" as const,
|
|
419
|
+
Surface: "dcgchat-test" as const,
|
|
358
420
|
MessageSid: msg.content.message_id,
|
|
359
421
|
Timestamp: Date.now(),
|
|
360
422
|
WasMentioned: true,
|
|
361
423
|
CommandAuthorized: true,
|
|
362
|
-
OriginatingChannel: "dcgchat" as const,
|
|
424
|
+
OriginatingChannel: "dcgchat-test" as const,
|
|
363
425
|
OriginatingTo: `user:${userId}`,
|
|
364
426
|
...mediaPayload,
|
|
365
427
|
});
|
|
366
428
|
|
|
367
429
|
log(`dcgchat[${accountId}]: ctxPayload=${JSON.stringify(ctxPayload)}`);
|
|
368
430
|
|
|
369
|
-
const sentMediaKeys = new Set<string>()
|
|
370
|
-
const getMediaKey = (url: string) => url.split(/[\\/]/).pop() ?? url
|
|
371
|
-
let textChunk =
|
|
431
|
+
const sentMediaKeys = new Set<string>();
|
|
432
|
+
const getMediaKey = (url: string) => url.split(/[\\/]/).pop() ?? url;
|
|
433
|
+
let textChunk = "";
|
|
372
434
|
|
|
373
|
-
const prefixContext = createReplyPrefixContext({
|
|
435
|
+
const prefixContext = createReplyPrefixContext({
|
|
436
|
+
cfg,
|
|
437
|
+
agentId: effectiveAgentId ?? "",
|
|
438
|
+
channel: "dcgchat-test",
|
|
439
|
+
accountId: account.accountId,
|
|
440
|
+
});
|
|
374
441
|
|
|
375
442
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
|
376
443
|
core.channel.reply.createReplyDispatcherWithTyping({
|
|
377
444
|
responsePrefix: prefixContext.responsePrefix,
|
|
378
|
-
responsePrefixContextProvider:
|
|
379
|
-
|
|
445
|
+
responsePrefixContextProvider:
|
|
446
|
+
prefixContext.responsePrefixContextProvider,
|
|
447
|
+
humanDelay: core.channel.reply.resolveHumanDelayConfig(
|
|
448
|
+
cfg,
|
|
449
|
+
route.agentId,
|
|
450
|
+
),
|
|
380
451
|
onReplyStart: async () => {},
|
|
381
452
|
deliver: async (payload: ReplyPayload) => {
|
|
382
|
-
log(
|
|
453
|
+
log(
|
|
454
|
+
`dcgchat[${accountId}][deliver]: received chunk, text length=${payload.text?.length || 0}`,
|
|
455
|
+
);
|
|
383
456
|
},
|
|
384
|
-
onError: (err: any, info: { kind: any
|
|
385
|
-
error(
|
|
457
|
+
onError: (err: any, info: { kind: any }) => {
|
|
458
|
+
error(
|
|
459
|
+
`dcgchat[${accountId}] ${info.kind} reply failed: ${String(err)}`,
|
|
460
|
+
);
|
|
386
461
|
},
|
|
387
462
|
onIdle: () => {},
|
|
388
463
|
});
|
|
389
464
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
message_id: msg.content.message_id,
|
|
429
|
-
response: '',
|
|
430
|
-
state: 'final',
|
|
431
|
-
},
|
|
432
|
-
});
|
|
433
|
-
} else {
|
|
434
|
-
log(`dcgchat[${accountId}]: dispatching to agent (session=${route.sessionKey})`);
|
|
435
|
-
await core.channel.reply.dispatchReplyFromConfig({
|
|
436
|
-
ctx: ctxPayload,
|
|
437
|
-
cfg,
|
|
438
|
-
dispatcher,
|
|
439
|
-
replyOptions: {
|
|
440
|
-
...replyOptions,
|
|
441
|
-
onModelSelected: prefixContext.onModelSelected,
|
|
442
|
-
onPartialReply: async (payload: ReplyPayload) => {
|
|
443
|
-
log(`dcgchat[${accountId}][deliver]: received chunk, text length=${payload.text?.length || 0}`);
|
|
444
|
-
if (payload.text) {
|
|
445
|
-
completeText = payload.text
|
|
446
|
-
}
|
|
447
|
-
const mediaList = resolveReplyMediaList(payload);
|
|
448
|
-
if (mediaList.length > 0) {
|
|
449
|
-
for (let i = 0; i < mediaList.length; i++) {
|
|
450
|
-
const mediaUrl = mediaList[i];
|
|
451
|
-
const key = getMediaKey(mediaUrl);
|
|
452
|
-
if (sentMediaKeys.has(key)) continue;
|
|
453
|
-
sentMediaKeys.add(key);
|
|
454
|
-
await sendDcgchatMedia({
|
|
455
|
-
cfg,
|
|
456
|
-
accountId,
|
|
457
|
-
log,
|
|
458
|
-
mediaUrl,
|
|
459
|
-
text: "",
|
|
460
|
-
});
|
|
465
|
+
let wasAborted = false;
|
|
466
|
+
try {
|
|
467
|
+
log(
|
|
468
|
+
`dcgchat[${accountId}]: dispatching to agent (session=${route.sessionKey})`,
|
|
469
|
+
);
|
|
470
|
+
await core.channel.reply.dispatchReplyFromConfig({
|
|
471
|
+
ctx: ctxPayload,
|
|
472
|
+
cfg,
|
|
473
|
+
dispatcher,
|
|
474
|
+
replyOptions: {
|
|
475
|
+
...replyOptions,
|
|
476
|
+
abortSignal: genSignal,
|
|
477
|
+
onModelSelected: prefixContext.onModelSelected,
|
|
478
|
+
onPartialReply: async (payload: ReplyPayload) => {
|
|
479
|
+
log(
|
|
480
|
+
`dcgchat[${accountId}][deliver]: received chunk, text length=${payload.text?.length || 0}`,
|
|
481
|
+
);
|
|
482
|
+
if (payload.text) {
|
|
483
|
+
completeText = payload.text;
|
|
484
|
+
}
|
|
485
|
+
const mediaList = resolveReplyMediaList(payload);
|
|
486
|
+
if (mediaList.length > 0) {
|
|
487
|
+
for (let i = 0; i < mediaList.length; i++) {
|
|
488
|
+
const mediaUrl = mediaList[i];
|
|
489
|
+
const key = getMediaKey(mediaUrl);
|
|
490
|
+
if (sentMediaKeys.has(key)) continue;
|
|
491
|
+
sentMediaKeys.add(key);
|
|
492
|
+
await sendDcgchatMedia({
|
|
493
|
+
cfg,
|
|
494
|
+
accountId,
|
|
495
|
+
log,
|
|
496
|
+
mediaUrl,
|
|
497
|
+
text: "",
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
log(
|
|
501
|
+
`dcgchat[${accountId}][deliver]: sent ${mediaList.length} media file(s) through channel adapter`,
|
|
502
|
+
);
|
|
461
503
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
504
|
+
if (payload.text) {
|
|
505
|
+
const nextTextChunk = payload.text.replace(textChunk, "");
|
|
506
|
+
if (nextTextChunk.trim()) {
|
|
507
|
+
log(
|
|
508
|
+
`dcgchat[${accountId}][deliver]: sending chunk to user ${msg._userId}, text="${nextTextChunk.slice(0, 50)}..."`,
|
|
509
|
+
);
|
|
510
|
+
params.onChunk({
|
|
511
|
+
messageType: "openclaw_bot_chat",
|
|
512
|
+
_userId: msg._userId,
|
|
513
|
+
source: "client",
|
|
514
|
+
content: {
|
|
515
|
+
bot_token: msg.content.bot_token,
|
|
516
|
+
domain_id: msg.content.domain_id,
|
|
517
|
+
app_id: msg.content.app_id,
|
|
518
|
+
bot_id: msg.content.bot_id,
|
|
519
|
+
agent_id: msg.content.agent_id,
|
|
520
|
+
session_id: msg.content.session_id,
|
|
521
|
+
message_id: msg.content.message_id,
|
|
522
|
+
response: nextTextChunk,
|
|
523
|
+
state: "chunk",
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
log(
|
|
527
|
+
`dcgchat[${accountId}][deliver]: chunk sent successfully`,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
textChunk = payload.text;
|
|
531
|
+
} else {
|
|
532
|
+
log(`dcgchat[${accountId}][deliver]: skipping empty chunk`);
|
|
485
533
|
}
|
|
486
|
-
|
|
487
|
-
} else {
|
|
488
|
-
log(`dcgchat[${accountId}][deliver]: skipping empty chunk`);
|
|
489
|
-
}
|
|
534
|
+
},
|
|
490
535
|
},
|
|
491
|
-
}
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
536
|
+
});
|
|
537
|
+
} catch (err: unknown) {
|
|
538
|
+
if (genSignal.aborted) {
|
|
539
|
+
wasAborted = true;
|
|
540
|
+
log(
|
|
541
|
+
`dcgchat[${accountId}]: generation aborted for conversationId=${conversationId}`,
|
|
542
|
+
);
|
|
543
|
+
} else if (err instanceof Error && err.name === "AbortError") {
|
|
544
|
+
wasAborted = true;
|
|
545
|
+
log(
|
|
546
|
+
`dcgchat[${accountId}]: generation aborted by session for conversationId=${conversationId}`,
|
|
547
|
+
);
|
|
548
|
+
} else {
|
|
549
|
+
error(
|
|
550
|
+
`dcgchat[${accountId}]: dispatchReplyFromConfig error: ${String(err)}`,
|
|
551
|
+
);
|
|
502
552
|
}
|
|
503
|
-
|
|
504
|
-
if (
|
|
505
|
-
|
|
506
|
-
continue;
|
|
553
|
+
} finally {
|
|
554
|
+
if (activeGenerations.get(conversationId) === genCtrl) {
|
|
555
|
+
activeGenerations.delete(conversationId);
|
|
507
556
|
}
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
-
continue;
|
|
557
|
+
if (wasAborted) {
|
|
558
|
+
//TODO:是否需要发消息给移动端,通知停止生成
|
|
511
559
|
}
|
|
512
|
-
sentMediaKeys.add(key);
|
|
513
|
-
await sendDcgchatMedia({
|
|
514
|
-
cfg,
|
|
515
|
-
accountId,
|
|
516
|
-
log,
|
|
517
|
-
mediaUrl: url,
|
|
518
|
-
text: "",
|
|
519
|
-
});
|
|
520
560
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const mobookFiles = extractMobookFiles(completeText)
|
|
561
|
+
|
|
562
|
+
const mobookFiles = extractMobookFiles(completeText);
|
|
524
563
|
if (mobookFiles.length > 0) {
|
|
525
564
|
for (let i = 0; i < mobookFiles.length; i++) {
|
|
526
|
-
let url = mobookFiles[i] as string
|
|
565
|
+
let url = mobookFiles[i] as string;
|
|
527
566
|
const key = getMediaKey(url);
|
|
528
567
|
if (sentMediaKeys.has(key)) {
|
|
529
|
-
log(
|
|
568
|
+
log(
|
|
569
|
+
`dcgchat[${accountId}]: mobookFiles already sent, skipping: ${url}`,
|
|
570
|
+
);
|
|
530
571
|
continue;
|
|
531
572
|
}
|
|
532
573
|
if (!fs.existsSync(url)) {
|
|
533
|
-
url = path.join(getWorkspaceDir(), url)
|
|
574
|
+
url = path.join(getWorkspaceDir(), url);
|
|
534
575
|
if (!fs.existsSync(url)) {
|
|
535
|
-
log(
|
|
576
|
+
log(
|
|
577
|
+
`dcgchat[${accountId}]: mobookFiles file not found, skipping: ${url}`,
|
|
578
|
+
);
|
|
536
579
|
continue;
|
|
537
580
|
}
|
|
538
581
|
}
|
|
@@ -545,7 +588,9 @@ export async function handleDcgchatMessage(params: {
|
|
|
545
588
|
text: "",
|
|
546
589
|
});
|
|
547
590
|
}
|
|
548
|
-
log(
|
|
591
|
+
log(
|
|
592
|
+
`dcgchat[${accountId}][deliver]: sent ${mobookFiles.length} media file(s) through channel adapter`,
|
|
593
|
+
);
|
|
549
594
|
}
|
|
550
595
|
log(`dcgchat[${accountId}]: dispatch complete, sending final state`);
|
|
551
596
|
params.onChunk({
|
|
@@ -560,18 +605,36 @@ export async function handleDcgchatMessage(params: {
|
|
|
560
605
|
agent_id: msg.content.agent_id,
|
|
561
606
|
session_id: msg.content.session_id,
|
|
562
607
|
message_id: msg.content.message_id,
|
|
563
|
-
response:
|
|
564
|
-
state:
|
|
608
|
+
response: "",
|
|
609
|
+
state: "final",
|
|
565
610
|
},
|
|
566
611
|
});
|
|
567
612
|
|
|
568
|
-
setMsgStatus(
|
|
569
|
-
textChunk =
|
|
613
|
+
setMsgStatus("finished");
|
|
614
|
+
textChunk = "";
|
|
570
615
|
log(`dcgchat[${accountId}]: final state sent`);
|
|
571
616
|
|
|
572
617
|
markDispatchIdle();
|
|
573
618
|
log(`dcgchat[${accountId}]: message handling complete`);
|
|
574
619
|
|
|
620
|
+
// Record session metadata
|
|
621
|
+
const storePath = core.channel.session.resolveStorePath(cfg.session?.store);
|
|
622
|
+
core.channel.session
|
|
623
|
+
.recordInboundSession({
|
|
624
|
+
storePath,
|
|
625
|
+
sessionKey: effectiveSessionKey,
|
|
626
|
+
ctx: ctxPayload,
|
|
627
|
+
onRecordError: (err) => {
|
|
628
|
+
log(
|
|
629
|
+
`easyclawapp[${account.accountId}]: session record error: ${String(err)}`,
|
|
630
|
+
);
|
|
631
|
+
},
|
|
632
|
+
})
|
|
633
|
+
.catch((err: unknown) => {
|
|
634
|
+
log(
|
|
635
|
+
`easyclawapp[${account.accountId}]: recordInboundSession failed: ${String(err)}`,
|
|
636
|
+
);
|
|
637
|
+
});
|
|
575
638
|
} catch (err) {
|
|
576
639
|
error(`dcgchat[${accountId}]: handle message failed: ${String(err)}`);
|
|
577
640
|
params.onChunk({
|
|
@@ -587,7 +650,7 @@ export async function handleDcgchatMessage(params: {
|
|
|
587
650
|
session_id: msg.content.session_id,
|
|
588
651
|
message_id: msg.content.message_id,
|
|
589
652
|
response: `[错误] ${err instanceof Error ? err.message : String(err)}`,
|
|
590
|
-
state:
|
|
653
|
+
state: "final",
|
|
591
654
|
},
|
|
592
655
|
});
|
|
593
656
|
}
|
package/src/channel.ts
CHANGED
|
@@ -185,47 +185,47 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
185
185
|
// textChunkLimit: 25,
|
|
186
186
|
textChunkLimit: 4000,
|
|
187
187
|
sendText: async (ctx) => {
|
|
188
|
-
|
|
188
|
+
const ws = getWsConnection()
|
|
189
189
|
const params = getMsgParams();
|
|
190
190
|
const log = console.log;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
191
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
192
|
+
const {botToken} = resolveAccount(ctx.cfg, ctx.accountId);
|
|
193
|
+
const content = {
|
|
194
|
+
messageType: "openclaw_bot_chat",
|
|
195
|
+
_userId: params.userId,
|
|
196
|
+
source: "client",
|
|
197
|
+
content: {
|
|
198
|
+
bot_token: botToken,
|
|
199
|
+
domain_id: params.domainId,
|
|
200
|
+
app_id: params.appId,
|
|
201
|
+
bot_id: params.botId,
|
|
202
|
+
agent_id: params.agentId,
|
|
203
|
+
response: ctx.text,
|
|
204
|
+
session_id: params.sessionId,
|
|
205
|
+
message_id: params.messageId || Date.now().toString(),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
ws.send(JSON.stringify(content));
|
|
209
|
+
ws.send(JSON.stringify({
|
|
210
|
+
messageType: "openclaw_bot_chat",
|
|
211
|
+
_userId: params.userId,
|
|
212
|
+
source: "client",
|
|
213
|
+
content: {
|
|
214
|
+
bot_token: botToken,
|
|
215
|
+
domain_id: params.domainId,
|
|
216
|
+
app_id: params.appId,
|
|
217
|
+
bot_id: params.botId,
|
|
218
|
+
agent_id: params.agentId,
|
|
219
|
+
ssession_id: params.sessionId,
|
|
220
|
+
message_id: params.messageId || Date.now().toString(),
|
|
221
|
+
response: '',
|
|
222
|
+
state: 'final',
|
|
223
|
+
},
|
|
224
|
+
}));
|
|
225
|
+
log(`dcgchat[${ctx.accountId}]: channel sendText to ${params.userId}, ${JSON.stringify(content)}`);
|
|
226
|
+
} else {
|
|
227
227
|
log(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> : ${ctx.text}`);
|
|
228
|
-
|
|
228
|
+
}
|
|
229
229
|
return {
|
|
230
230
|
channel: "dcgchat-test",
|
|
231
231
|
messageId: `dcg-${Date.now()}`,
|
package/src/monitor.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
2
|
import WebSocket from "ws";
|
|
3
|
-
import { handleDcgchatMessage } from "./bot.js";
|
|
3
|
+
import { abortMobookappGeneration, handleDcgchatMessage } from "./bot.js";
|
|
4
4
|
import { resolveAccount } from "./channel.js";
|
|
5
5
|
import { setWsConnection } from "./connection.js";
|
|
6
6
|
import type { InboundMessage } from "./types.js";
|
|
7
|
-
import { setMsgParams,setMsgStatus } from "./tool.js";
|
|
7
|
+
import { setMsgParams, setMsgStatus } from "./tool.js";
|
|
8
8
|
import { installSkill, uninstallSkill } from "./skill.js";
|
|
9
9
|
|
|
10
10
|
export type MonitorDcgchatOpts = {
|
|
@@ -16,7 +16,13 @@ export type MonitorDcgchatOpts = {
|
|
|
16
16
|
|
|
17
17
|
const RECONNECT_DELAY_MS = 3000;
|
|
18
18
|
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
19
|
-
const emptyToolText = [
|
|
19
|
+
const emptyToolText = [
|
|
20
|
+
"/new",
|
|
21
|
+
"/search",
|
|
22
|
+
"/stop",
|
|
23
|
+
"/abort",
|
|
24
|
+
"/queue interrupt",
|
|
25
|
+
];
|
|
20
26
|
|
|
21
27
|
function buildConnectUrl(account: Record<string, string>): string {
|
|
22
28
|
const { wsUrl, botToken, userId, domainId, appId } = account;
|
|
@@ -28,7 +34,9 @@ function buildConnectUrl(account: Record<string, string>): string {
|
|
|
28
34
|
return url.toString();
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
export async function monitorDcgchatProvider(
|
|
37
|
+
export async function monitorDcgchatProvider(
|
|
38
|
+
opts: MonitorDcgchatOpts,
|
|
39
|
+
): Promise<void> {
|
|
32
40
|
const { config, runtime, abortSignal, accountId } = opts;
|
|
33
41
|
// @ts-ignore
|
|
34
42
|
const cfg = config ?? (runtime?.config?.() as ClawdbotConfig | undefined);
|
|
@@ -100,7 +108,9 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
if (parsed.messageType === "openclaw_bot_heartbeat") {
|
|
103
|
-
log(
|
|
111
|
+
log(
|
|
112
|
+
`dcgchat[${account.accountId}]: heartbeat ack received, ${data.toString()}`,
|
|
113
|
+
);
|
|
104
114
|
return;
|
|
105
115
|
}
|
|
106
116
|
try {
|
|
@@ -109,25 +119,36 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
109
119
|
err(`dcgchat[${account.accountId}]: invalid JSON received`);
|
|
110
120
|
return;
|
|
111
121
|
}
|
|
112
|
-
|
|
122
|
+
|
|
113
123
|
if (parsed.messageType == "openclaw_bot_chat") {
|
|
114
124
|
const msg = parsed as unknown as InboundMessage;
|
|
115
125
|
if (!emptyToolText.includes(msg.content.text?.trim())) {
|
|
116
|
-
setMsgStatus(
|
|
126
|
+
setMsgStatus("running");
|
|
127
|
+
}
|
|
128
|
+
log(
|
|
129
|
+
`dcgchat[${account.accountId}]: openclaw_bot_chat received, ${JSON.stringify(msg)}`,
|
|
130
|
+
);
|
|
131
|
+
if (msg.content.text === "/stop") {
|
|
132
|
+
const rawConvId = msg.content.session_id as string | undefined;
|
|
133
|
+
const conversationId =
|
|
134
|
+
rawConvId || `${accountId}:${account.botToken}`;
|
|
135
|
+
console.log("🚀 ~ connect ~ conversationId:", conversationId)
|
|
136
|
+
abortMobookappGeneration(conversationId);
|
|
137
|
+
log(`[dcgchat][in] abort conversationId=${conversationId}`);
|
|
138
|
+
return;
|
|
117
139
|
}
|
|
118
|
-
log(`dcgchat[${account.accountId}]: openclaw_bot_chat received, ${JSON.stringify(msg)}`);
|
|
119
140
|
setMsgParams({
|
|
120
141
|
userId: msg._userId,
|
|
121
142
|
token: msg.content.bot_token,
|
|
122
143
|
sessionId: msg.content.session_id,
|
|
123
144
|
messageId: msg.content.message_id,
|
|
124
|
-
domainId: account.domainId ||
|
|
125
|
-
appId: account.appId ||
|
|
145
|
+
domainId: account.domainId || 1000,
|
|
146
|
+
appId: account.appId || "100",
|
|
126
147
|
botId: msg.content.bot_id,
|
|
127
148
|
agentId: msg.content.agent_id,
|
|
128
149
|
});
|
|
129
|
-
msg.content.app_id = account.appId ||
|
|
130
|
-
msg.content.domain_id = account.domainId ||
|
|
150
|
+
msg.content.app_id = account.appId || "100";
|
|
151
|
+
msg.content.domain_id = account.domainId || "1000";
|
|
131
152
|
await handleDcgchatMessage({
|
|
132
153
|
cfg,
|
|
133
154
|
msg,
|
|
@@ -140,24 +161,52 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
140
161
|
}
|
|
141
162
|
},
|
|
142
163
|
});
|
|
143
|
-
}
|
|
144
|
-
const {
|
|
145
|
-
|
|
164
|
+
} else if (parsed.messageType == "openclaw_bot_event") {
|
|
165
|
+
const {
|
|
166
|
+
event_type,
|
|
167
|
+
operation_type,
|
|
168
|
+
skill_url,
|
|
169
|
+
skill_code,
|
|
170
|
+
skill_id,
|
|
171
|
+
bot_token,
|
|
172
|
+
websocket_trace_id,
|
|
173
|
+
} = parsed.content ? parsed.content : ({} as Record<string, any>);
|
|
174
|
+
const content = {
|
|
175
|
+
event_type,
|
|
176
|
+
operation_type,
|
|
177
|
+
skill_url,
|
|
178
|
+
skill_code,
|
|
179
|
+
skill_id,
|
|
180
|
+
bot_token,
|
|
181
|
+
websocket_trace_id,
|
|
182
|
+
};
|
|
146
183
|
if (event_type === "skill") {
|
|
147
|
-
if (
|
|
184
|
+
if (
|
|
185
|
+
operation_type === "install" ||
|
|
186
|
+
operation_type === "enable" ||
|
|
187
|
+
operation_type === "update"
|
|
188
|
+
) {
|
|
148
189
|
installSkill({ path: skill_url, code: skill_code }, content);
|
|
149
|
-
} else if (
|
|
190
|
+
} else if (
|
|
191
|
+
operation_type === "remove" ||
|
|
192
|
+
operation_type === "disable"
|
|
193
|
+
) {
|
|
150
194
|
uninstallSkill({ code: skill_code }, content);
|
|
151
195
|
} else {
|
|
152
|
-
log(
|
|
196
|
+
log(
|
|
197
|
+
`dcgchat[${account.accountId}]: openclaw_bot_event unknown event_type: ${event_type}, ${data.toString()}`,
|
|
198
|
+
);
|
|
153
199
|
}
|
|
154
200
|
} else {
|
|
155
|
-
log(
|
|
201
|
+
log(
|
|
202
|
+
`dcgchat[${account.accountId}]: openclaw_bot_event unknown operation_type: ${operation_type}, ${data.toString()}`,
|
|
203
|
+
);
|
|
156
204
|
}
|
|
157
205
|
} else {
|
|
158
|
-
log(
|
|
206
|
+
log(
|
|
207
|
+
`dcgchat[${account.accountId}]: ignoring unknown messageType: ${parsed.messageType}`,
|
|
208
|
+
);
|
|
159
209
|
}
|
|
160
|
-
|
|
161
210
|
});
|
|
162
211
|
|
|
163
212
|
ws.on("close", (code, reason) => {
|
|
@@ -167,7 +216,9 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
167
216
|
`dcgchat[${account.accountId}]: disconnected (code=${code}, reason=${reason?.toString() || ""})`,
|
|
168
217
|
);
|
|
169
218
|
if (shouldReconnect) {
|
|
170
|
-
log(
|
|
219
|
+
log(
|
|
220
|
+
`dcgchat[${account.accountId}]: reconnecting in ${RECONNECT_DELAY_MS}ms...`,
|
|
221
|
+
);
|
|
171
222
|
setTimeout(connect, RECONNECT_DELAY_MS);
|
|
172
223
|
}
|
|
173
224
|
});
|
package/src/tool.ts
CHANGED
|
@@ -32,19 +32,20 @@ let toolCallId = '';
|
|
|
32
32
|
let toolName = '';
|
|
33
33
|
type PluginHookName = "before_model_resolve" | "before_prompt_build" | "before_agent_start" | "llm_input" | "llm_output" | "agent_end" | "before_compaction" | "after_compaction" | "before_reset" | "message_received" | "message_sending" | "message_sent" | "before_tool_call" | "after_tool_call" | "tool_result_persist" | "before_message_write" | "session_start" | "session_end" | "subagent_spawning" | "subagent_delivery_target" | "subagent_spawned" | "subagent_ended" | "gateway_start" | "gateway_stop";
|
|
34
34
|
const eventList = [
|
|
35
|
-
{event: 'message_received', message: '
|
|
35
|
+
{event: 'message_received', message: ''},
|
|
36
|
+
{event: 'before_model_resolve', message: ''},
|
|
36
37
|
// {event: 'before_prompt_build', message: '正在查阅背景资料,构建思考逻辑'},
|
|
37
38
|
// {event: 'before_agent_start', message: '书灵墨宝已就位,准备开始执行任务'},
|
|
38
|
-
{event: 'subagent_spawning', message: '
|
|
39
|
-
{event: 'subagent_spawned', message: '
|
|
40
|
-
{event: 'subagent_delivery_target', message: '
|
|
41
|
-
{event: 'llm_input', message: '
|
|
42
|
-
{event: 'llm_output', message: '
|
|
43
|
-
{event: 'agent_end', message: '核心任务已处理完毕...'},
|
|
44
|
-
{event: 'subagent_ended', message: '
|
|
39
|
+
{event: 'subagent_spawning', message: ''},
|
|
40
|
+
{event: 'subagent_spawned', message: ''},
|
|
41
|
+
{event: 'subagent_delivery_target', message: ''},
|
|
42
|
+
{event: 'llm_input', message: ''},
|
|
43
|
+
{event: 'llm_output', message: ''},
|
|
44
|
+
// {event: 'agent_end', message: '核心任务已处理完毕...'},
|
|
45
|
+
{event: 'subagent_ended', message: ''},
|
|
45
46
|
// {event: 'before_message_write', message: '正在将本次对话存入记忆库...'},
|
|
46
|
-
{event: 'message_sending', message: ''},
|
|
47
|
-
{event: 'message_send', message: ''},
|
|
47
|
+
// {event: 'message_sending', message: ''},
|
|
48
|
+
// {event: 'message_send', message: ''},
|
|
48
49
|
{event: 'before_tool_call', message: ''},
|
|
49
50
|
{event: 'after_tool_call', message: ''},
|
|
50
51
|
];
|