claw-subagent-service 0.0.114 → 0.0.116
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
CHANGED
|
@@ -29,6 +29,10 @@ class MessageHandler {
|
|
|
29
29
|
this._groupConfigCache = new Map();
|
|
30
30
|
this._defaultMaxRounds = 10;
|
|
31
31
|
this._groupConfigCacheTTL = config.groupConfigCacheTTL || 60000; // 默认缓存 60 秒
|
|
32
|
+
|
|
33
|
+
// 消息合并相关
|
|
34
|
+
this._pendingMessages = new Map(); // 待合并的消息
|
|
35
|
+
this._messageMergeTimeout = 500; // 合并等待时间(毫秒)
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
/**
|
|
@@ -215,42 +219,102 @@ class MessageHandler {
|
|
|
215
219
|
});
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
//
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
222
|
+
// 消息合并逻辑:如果是图片消息,等待一段时间看是否有文字消息跟随
|
|
223
|
+
if (msg.messageType === 'RC:ImgMsg') {
|
|
224
|
+
await this._handleImageMessageWithMerge(msg, maxRounds);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 普通消息直接处理
|
|
229
|
+
await this._processMessage(msg, maxRounds);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
this.log?.error(`[MessageHandler] 处理消息异常: ${err.message}`);
|
|
232
|
+
const targetId = msg.conversationType === 3 ? msg.targetId : msg.senderUserId;
|
|
233
|
+
await this.sendFn(targetId, `处理失败: ${err.message}`, msg.conversationType);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 处理图片消息,支持合并后续文字消息
|
|
239
|
+
*/
|
|
240
|
+
async _handleImageMessageWithMerge(msg, maxRounds) {
|
|
241
|
+
const userId = msg.senderUserId;
|
|
242
|
+
const conversationKey = `${msg.conversationType}-${msg.targetId}-${userId}`;
|
|
243
|
+
|
|
244
|
+
// 设置待处理图片消息
|
|
245
|
+
this._pendingMessages.set(conversationKey, {
|
|
246
|
+
imageMsg: msg,
|
|
247
|
+
timestamp: Date.now(),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// 等待一段时间,看是否有文字消息跟随
|
|
251
|
+
await new Promise(resolve => setTimeout(resolve, this._messageMergeTimeout));
|
|
252
|
+
|
|
253
|
+
// 获取待处理消息
|
|
254
|
+
const pending = this._pendingMessages.get(conversationKey);
|
|
255
|
+
this._pendingMessages.delete(conversationKey);
|
|
256
|
+
|
|
257
|
+
if (!pending) {
|
|
258
|
+
return; // 消息已被处理
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 构建合并后的消息内容
|
|
262
|
+
const imageContent = this._extractMessageContent(pending.imageMsg);
|
|
263
|
+
let mergedContent = imageContent;
|
|
264
|
+
|
|
265
|
+
if (pending.textMsg) {
|
|
266
|
+
const textContent = typeof pending.textMsg.content === 'string'
|
|
267
|
+
? pending.textMsg.content
|
|
268
|
+
: (pending.textMsg.content?.content || '');
|
|
269
|
+
mergedContent = `${textContent}\n${imageContent}`;
|
|
270
|
+
this.log?.info(`[MessageHandler] 合并消息: 图片+文字`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 创建合并后的消息对象
|
|
274
|
+
const mergedMsg = {
|
|
275
|
+
...pending.imageMsg,
|
|
276
|
+
content: mergedContent,
|
|
277
|
+
messageType: 'RC:TxtMsg', // 转为文本消息处理
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
await this._processMessage(mergedMsg, maxRounds);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* 处理普通消息(包括合并后的消息)
|
|
285
|
+
*/
|
|
286
|
+
async _processMessage(msg, maxRounds) {
|
|
287
|
+
// 如果配置了代理地址,使用流式处理
|
|
288
|
+
if (this.isStreamingEnabled) {
|
|
289
|
+
try {
|
|
290
|
+
await this.handleNormalMessageStream(msg);
|
|
291
|
+
// 流式处理成功,群聊轮数 +1
|
|
292
|
+
if (msg.conversationType === 3) {
|
|
293
|
+
this._incrementGroupRoundCount(msg.targetId, maxRounds);
|
|
237
294
|
}
|
|
238
|
-
}
|
|
239
|
-
|
|
295
|
+
} catch (err) {
|
|
296
|
+
this.log?.error(`[MessageHandler] 流式处理失败,回退到非流式: ${err.message}`);
|
|
240
297
|
const reply = await this.handleNormalMessage(msg);
|
|
241
298
|
if (reply) {
|
|
242
299
|
const targetId = this.getReplyTarget(msg);
|
|
243
300
|
await this.sendFn(targetId, reply, msg.conversationType);
|
|
244
|
-
//
|
|
301
|
+
// 非流式回退成功,群聊轮数 +1
|
|
245
302
|
if (msg.conversationType === 3) {
|
|
246
303
|
this._incrementGroupRoundCount(msg.targetId, maxRounds);
|
|
247
304
|
}
|
|
248
305
|
}
|
|
249
306
|
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
307
|
+
} else {
|
|
308
|
+
// 降级到非流式处理
|
|
309
|
+
const reply = await this.handleNormalMessage(msg);
|
|
310
|
+
if (reply) {
|
|
311
|
+
const targetId = this.getReplyTarget(msg);
|
|
312
|
+
await this.sendFn(targetId, reply, msg.conversationType);
|
|
313
|
+
// 非流式处理成功,群聊轮数 +1
|
|
314
|
+
if (msg.conversationType === 3) {
|
|
315
|
+
this._incrementGroupRoundCount(msg.targetId, maxRounds);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
254
318
|
}
|
|
255
319
|
}
|
|
256
320
|
|
|
@@ -545,44 +609,6 @@ class MessageHandler {
|
|
|
545
609
|
return '[图片](无法获取图片地址)';
|
|
546
610
|
}
|
|
547
611
|
|
|
548
|
-
// 检查是否有文字内容(从 content 或 extra 中提取)
|
|
549
|
-
let textContent = '';
|
|
550
|
-
|
|
551
|
-
// 1. 从 content 对象中提取文字
|
|
552
|
-
if (typeof content === 'object' && content !== null && content.content) {
|
|
553
|
-
textContent = content.content;
|
|
554
|
-
} else if (typeof content === 'string') {
|
|
555
|
-
// 尝试解析 content 是否为 JSON
|
|
556
|
-
try {
|
|
557
|
-
const contentObj = JSON.parse(content);
|
|
558
|
-
if (contentObj.content) {
|
|
559
|
-
textContent = contentObj.content;
|
|
560
|
-
}
|
|
561
|
-
} catch (e) {
|
|
562
|
-
// content 不是 JSON,可能是纯文本
|
|
563
|
-
if (content && !content.startsWith('data:image')) {
|
|
564
|
-
textContent = content;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// 2. 从 extra 字段中提取文字
|
|
570
|
-
if (!textContent && msg.extra) {
|
|
571
|
-
try {
|
|
572
|
-
const extraData = JSON.parse(msg.extra);
|
|
573
|
-
if (extraData.textContent) {
|
|
574
|
-
textContent = extraData.textContent;
|
|
575
|
-
}
|
|
576
|
-
} catch (e) {
|
|
577
|
-
// extra 不是 JSON,忽略
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// 构建返回内容:如果有文字,返回文字+图片;否则只返回图片
|
|
582
|
-
if (textContent) {
|
|
583
|
-
return `${textContent}\n[图片] ${imageUri}`;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
612
|
return `[图片] ${imageUri}`;
|
|
587
613
|
}
|
|
588
614
|
|