@dcrays/dcgchat 0.1.8 → 0.1.9
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/README.md +83 -83
- package/index.ts +24 -19
- package/openclaw.plugin.json +9 -9
- package/package.json +58 -57
- package/src/api.ts +62 -62
- package/src/bot.ts +272 -272
- package/src/channel.ts +192 -192
- package/src/connection.ts +11 -11
- package/src/libs/ali-oss-6.23.0.tgz +0 -0
- package/src/libs/axios-1.13.6.tgz +0 -0
- package/src/libs/md5-2.3.0.tgz +0 -0
- package/src/libs/unzipper-0.12.3.tgz +0 -0
- package/src/libs/ws-8.19.0.tgz +0 -0
- package/src/log.ts +46 -46
- package/src/monitor.ts +191 -171
- package/src/oss.ts +72 -72
- package/src/request.ts +194 -194
- package/src/runtime.ts +38 -14
- package/src/skill.ts +111 -0
- package/src/tool.ts +74 -64
- package/src/types.ts +103 -103
- package/src/userInfo.ts +97 -97
package/src/bot.ts
CHANGED
|
@@ -1,272 +1,272 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
3
|
-
import { createReplyPrefixContext } from "openclaw/plugin-sdk";
|
|
4
|
-
import type { InboundMessage, OutboundReply } from "./types.js";
|
|
5
|
-
import { getDcgchatRuntime } from "./runtime.js";
|
|
6
|
-
import { resolveAccount } from "./channel.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
type MediaInfo = {
|
|
10
|
-
path: string;
|
|
11
|
-
contentType: string;
|
|
12
|
-
placeholder: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
async function resolveMediaFromUrls(
|
|
16
|
-
fileUrls: string[],
|
|
17
|
-
maxBytes: number,
|
|
18
|
-
log?: (msg: string) => void,
|
|
19
|
-
): Promise<MediaInfo[]> {
|
|
20
|
-
const core = getDcgchatRuntime();
|
|
21
|
-
const out: MediaInfo[] = [];
|
|
22
|
-
|
|
23
|
-
log?.(`dcgchat media: starting resolve for ${fileUrls.length} file(s): ${JSON.stringify(fileUrls)}`);
|
|
24
|
-
|
|
25
|
-
for (let i = 0; i < fileUrls.length; i++) {
|
|
26
|
-
const url = fileUrls[i];
|
|
27
|
-
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fetching ${url}`);
|
|
28
|
-
try {
|
|
29
|
-
const response = await fetch(url);
|
|
30
|
-
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fetch response status=${response.status}, content-type=${response.headers.get("content-type")}, content-length=${response.headers.get("content-length")}`);
|
|
31
|
-
if (!response.ok) {
|
|
32
|
-
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fetch failed with HTTP ${response.status}, skipping`);
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
36
|
-
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] downloaded buffer size=${buffer.length} bytes`);
|
|
37
|
-
|
|
38
|
-
let contentType = response.headers.get("content-type") || "";
|
|
39
|
-
if (!contentType) {
|
|
40
|
-
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] no content-type header, detecting mime...`);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}]
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
log(`dcgchat[${accountId}]: final state sent`);
|
|
253
|
-
|
|
254
|
-
markDispatchIdle();
|
|
255
|
-
log(`dcgchat[${accountId}]: message handling complete`);
|
|
256
|
-
|
|
257
|
-
} catch (err) {
|
|
258
|
-
error(`dcgchat[${accountId}]: handle message failed: ${String(err)}`);
|
|
259
|
-
params.onChunk({
|
|
260
|
-
messageType: "openclaw_bot_chat",
|
|
261
|
-
_userId: msg._userId,
|
|
262
|
-
source: "client",
|
|
263
|
-
content: {
|
|
264
|
-
bot_token: msg.content.bot_token,
|
|
265
|
-
session_id: msg.content.session_id,
|
|
266
|
-
message_id: msg.content.message_id,
|
|
267
|
-
response: `[错误] ${err instanceof Error ? err.message : String(err)}`,
|
|
268
|
-
state: 'final',
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
}
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
3
|
+
import { createReplyPrefixContext } from "openclaw/plugin-sdk";
|
|
4
|
+
import type { InboundMessage, OutboundReply } from "./types.js";
|
|
5
|
+
import { getDcgchatRuntime } from "./runtime.js";
|
|
6
|
+
import { resolveAccount } from "./channel.js";
|
|
7
|
+
import { setMsgStatus } from "./tool.js";
|
|
8
|
+
|
|
9
|
+
type MediaInfo = {
|
|
10
|
+
path: string;
|
|
11
|
+
contentType: string;
|
|
12
|
+
placeholder: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
async function resolveMediaFromUrls(
|
|
16
|
+
fileUrls: string[],
|
|
17
|
+
maxBytes: number,
|
|
18
|
+
log?: (msg: string) => void,
|
|
19
|
+
): Promise<MediaInfo[]> {
|
|
20
|
+
const core = getDcgchatRuntime();
|
|
21
|
+
const out: MediaInfo[] = [];
|
|
22
|
+
|
|
23
|
+
log?.(`dcgchat media: starting resolve for ${fileUrls.length} file(s): ${JSON.stringify(fileUrls)}`);
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < fileUrls.length; i++) {
|
|
26
|
+
const url = fileUrls[i];
|
|
27
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fetching ${url}`);
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(url);
|
|
30
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fetch response status=${response.status}, content-type=${response.headers.get("content-type")}, content-length=${response.headers.get("content-length")}`);
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fetch failed with HTTP ${response.status}, skipping`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
36
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] downloaded buffer size=${buffer.length} bytes`);
|
|
37
|
+
|
|
38
|
+
let contentType = response.headers.get("content-type") || "";
|
|
39
|
+
if (!contentType) {
|
|
40
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] no content-type header, detecting mime...`);
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
contentType = await core.media.detectMime({ buffer });
|
|
43
|
+
}
|
|
44
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] resolved contentType=${contentType}`);
|
|
45
|
+
|
|
46
|
+
const fileName = path.basename(new URL(url).pathname) || "file";
|
|
47
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] fileName=${fileName}, saving to disk (maxBytes=${maxBytes})...`);
|
|
48
|
+
|
|
49
|
+
const saved = await core.channel.media.saveMediaBuffer(
|
|
50
|
+
buffer,
|
|
51
|
+
contentType,
|
|
52
|
+
"inbound",
|
|
53
|
+
maxBytes,
|
|
54
|
+
fileName,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const isImage = contentType.startsWith("image/");
|
|
58
|
+
out.push({
|
|
59
|
+
path: saved.path,
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
contentType: saved.contentType,
|
|
62
|
+
placeholder: isImage ? "<media:image>" : "<media:file>",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] saved to ${saved.path} (contentType=${saved.contentType}, isImage=${isImage})`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
log?.(`dcgchat media: [${i + 1}/${fileUrls.length}] FAILED to process ${url}: ${String(err)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
log?.(`dcgchat media: resolve complete, ${out.length}/${fileUrls.length} file(s) succeeded`);
|
|
72
|
+
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildMediaPayload(mediaList: MediaInfo[]): {
|
|
77
|
+
MediaPath?: string;
|
|
78
|
+
MediaType?: string;
|
|
79
|
+
MediaUrl?: string;
|
|
80
|
+
MediaPaths?: string[];
|
|
81
|
+
MediaUrls?: string[];
|
|
82
|
+
MediaTypes?: string[];
|
|
83
|
+
} {
|
|
84
|
+
if (mediaList.length === 0) return {};
|
|
85
|
+
const first = mediaList[0];
|
|
86
|
+
const mediaPaths = mediaList.map((m) => m.path);
|
|
87
|
+
const mediaTypes = mediaList.map((m) => m.contentType).filter(Boolean);
|
|
88
|
+
return {
|
|
89
|
+
MediaPath: first?.path,
|
|
90
|
+
MediaType: first?.contentType,
|
|
91
|
+
MediaUrl: first?.path,
|
|
92
|
+
MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,
|
|
93
|
+
MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined,
|
|
94
|
+
MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 处理一条用户消息,调用 Agent 并返回回复
|
|
100
|
+
*/
|
|
101
|
+
export async function handleDcgchatMessage(params: {
|
|
102
|
+
cfg: ClawdbotConfig;
|
|
103
|
+
msg: InboundMessage;
|
|
104
|
+
accountId: string;
|
|
105
|
+
runtime?: RuntimeEnv;
|
|
106
|
+
onChunk: (reply: OutboundReply) => void;
|
|
107
|
+
}): Promise<void> {
|
|
108
|
+
const { cfg, msg, accountId, runtime } = params;
|
|
109
|
+
const log = runtime?.log ?? console.log;
|
|
110
|
+
const error = runtime?.error ?? console.error;
|
|
111
|
+
|
|
112
|
+
const account = resolveAccount(cfg, accountId);
|
|
113
|
+
const userId = msg._userId.toString();
|
|
114
|
+
// const text = msg.text?.trim();
|
|
115
|
+
const text = msg.content.text?.trim();
|
|
116
|
+
|
|
117
|
+
if (!userId || !text) {
|
|
118
|
+
params.onChunk({
|
|
119
|
+
messageType: "openclaw_bot_chat",
|
|
120
|
+
_userId: msg._userId,
|
|
121
|
+
source: "client",
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
content: {
|
|
124
|
+
bot_token: msg.content.bot_token,
|
|
125
|
+
session_id: msg.content.session_id,
|
|
126
|
+
message_id: msg.content.message_id,
|
|
127
|
+
response: "[错误] 消息格式不正确",
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const core = getDcgchatRuntime();
|
|
135
|
+
|
|
136
|
+
const route = core.channel.routing.resolveAgentRoute({
|
|
137
|
+
cfg,
|
|
138
|
+
channel: "dcgchat",
|
|
139
|
+
accountId: account.accountId,
|
|
140
|
+
peer: { kind: "direct", id: userId },
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// 处理用户上传的文件
|
|
144
|
+
const fileUrls = msg.content.file_urls ?? [];
|
|
145
|
+
log(`dcgchat[${accountId}]: incoming message from user=${userId}, text="${text?.slice(0, 80)}", file_urls count=${fileUrls.length}`);
|
|
146
|
+
let mediaPayload: Record<string, unknown> = {};
|
|
147
|
+
if (fileUrls.length > 0) {
|
|
148
|
+
log(`dcgchat[${accountId}]: processing ${fileUrls.length} file(s): ${JSON.stringify(fileUrls)}`);
|
|
149
|
+
const mediaMaxBytes = 30 * 1024 * 1024;
|
|
150
|
+
const mediaList = await resolveMediaFromUrls(fileUrls, mediaMaxBytes, log);
|
|
151
|
+
mediaPayload = buildMediaPayload(mediaList);
|
|
152
|
+
log(`dcgchat[${accountId}]: media resolved ${mediaList.length}/${fileUrls.length} file(s), payload=${JSON.stringify(mediaPayload)}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
156
|
+
// const messageBody = `${userId}: ${text}`;
|
|
157
|
+
const messageBody = text;
|
|
158
|
+
const bodyFormatted = core.channel.reply.formatAgentEnvelope({
|
|
159
|
+
channel: "DCG Chat",
|
|
160
|
+
from: userId,
|
|
161
|
+
timestamp: new Date(),
|
|
162
|
+
envelope: envelopeOptions,
|
|
163
|
+
body: messageBody,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
167
|
+
Body: bodyFormatted,
|
|
168
|
+
RawBody: text,
|
|
169
|
+
CommandBody: text,
|
|
170
|
+
From: userId,
|
|
171
|
+
To: userId,
|
|
172
|
+
SessionKey: route.sessionKey,
|
|
173
|
+
AccountId: msg.content.session_id,
|
|
174
|
+
ChatType: "direct",
|
|
175
|
+
SenderName: userId,
|
|
176
|
+
SenderId: userId,
|
|
177
|
+
Provider: "dcgchat" as const,
|
|
178
|
+
Surface: "dcgchat" as const,
|
|
179
|
+
MessageSid: msg.content.message_id,
|
|
180
|
+
Timestamp: Date.now(),
|
|
181
|
+
WasMentioned: true,
|
|
182
|
+
CommandAuthorized: true,
|
|
183
|
+
OriginatingChannel: "dcgchat" as const,
|
|
184
|
+
OriginatingTo: `user:${userId}`,
|
|
185
|
+
...mediaPayload,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
log(`dcgchat[${accountId}]: ctxPayload=${JSON.stringify(ctxPayload)}`);
|
|
189
|
+
|
|
190
|
+
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
|
191
|
+
|
|
192
|
+
const { dispatcher, replyOptions, markDispatchIdle } =
|
|
193
|
+
core.channel.reply.createReplyDispatcherWithTyping({
|
|
194
|
+
responsePrefix: prefixContext.responsePrefix,
|
|
195
|
+
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
196
|
+
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
|
|
197
|
+
onReplyStart: async () => {},
|
|
198
|
+
deliver: async (payload) => {
|
|
199
|
+
log(`dcgchat[${accountId}][deliver]: received chunk, text length=${payload.text?.length || 0}`);
|
|
200
|
+
const t = payload.text?.trim();
|
|
201
|
+
if (t) {
|
|
202
|
+
log(`dcgchat[${accountId}][deliver]: sending chunk to user ${msg._userId}, text="${t.slice(0, 50)}..."`);
|
|
203
|
+
params.onChunk({
|
|
204
|
+
messageType: "openclaw_bot_chat",
|
|
205
|
+
_userId: msg._userId,
|
|
206
|
+
source: "client",
|
|
207
|
+
content: {
|
|
208
|
+
bot_token: msg.content.bot_token,
|
|
209
|
+
session_id: msg.content.session_id,
|
|
210
|
+
message_id: msg.content.message_id,
|
|
211
|
+
response: t,
|
|
212
|
+
state: 'chunk',
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
log(`dcgchat[${accountId}][deliver]: chunk sent successfully`);
|
|
216
|
+
} else {
|
|
217
|
+
log(`dcgchat[${accountId}][deliver]: skipping empty chunk`);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
onError: (err, info) => {
|
|
221
|
+
error(`dcgchat[${accountId}] ${info.kind} reply failed: ${String(err)}`);
|
|
222
|
+
},
|
|
223
|
+
onIdle: () => {},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
log(`dcgchat[${accountId}]: dispatching to agent (session=${route.sessionKey})`);
|
|
227
|
+
|
|
228
|
+
await core.channel.reply.dispatchReplyFromConfig({
|
|
229
|
+
ctx: ctxPayload,
|
|
230
|
+
cfg,
|
|
231
|
+
dispatcher,
|
|
232
|
+
replyOptions: {
|
|
233
|
+
...replyOptions,
|
|
234
|
+
onModelSelected: prefixContext.onModelSelected,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
log(`dcgchat[${accountId}]: dispatch complete, sending final state`);
|
|
239
|
+
params.onChunk({
|
|
240
|
+
messageType: "openclaw_bot_chat",
|
|
241
|
+
_userId: msg._userId,
|
|
242
|
+
source: "client",
|
|
243
|
+
content: {
|
|
244
|
+
bot_token: msg.content.bot_token,
|
|
245
|
+
session_id: msg.content.session_id,
|
|
246
|
+
message_id: msg.content.message_id,
|
|
247
|
+
response: '',
|
|
248
|
+
state: 'final',
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
setMsgStatus('finished');
|
|
252
|
+
log(`dcgchat[${accountId}]: final state sent`);
|
|
253
|
+
|
|
254
|
+
markDispatchIdle();
|
|
255
|
+
log(`dcgchat[${accountId}]: message handling complete`);
|
|
256
|
+
|
|
257
|
+
} catch (err) {
|
|
258
|
+
error(`dcgchat[${accountId}]: handle message failed: ${String(err)}`);
|
|
259
|
+
params.onChunk({
|
|
260
|
+
messageType: "openclaw_bot_chat",
|
|
261
|
+
_userId: msg._userId,
|
|
262
|
+
source: "client",
|
|
263
|
+
content: {
|
|
264
|
+
bot_token: msg.content.bot_token,
|
|
265
|
+
session_id: msg.content.session_id,
|
|
266
|
+
message_id: msg.content.message_id,
|
|
267
|
+
response: `[错误] ${err instanceof Error ? err.message : String(err)}`,
|
|
268
|
+
state: 'final',
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|