mioku-plugin-chat 2.3.0 → 2.4.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/configs/base.ts +1 -0
- package/configs/settings.ts +1 -1
- package/context.ts +17 -0
- package/core/chat-engine.ts +6 -1
- package/core/chat-turn.ts +540 -0
- package/core/media/history-media.ts +215 -70
- package/core/media/image-analyzer.ts +28 -19
- package/core/media/image-compress.ts +75 -0
- package/core/media/segment.ts +91 -0
- package/core/multimodal.ts +24 -12
- package/core/prompt.ts +332 -445
- package/core/tools/info.ts +284 -0
- package/core/tools/load-skill.ts +149 -0
- package/core/tools/web.ts +306 -0
- package/core/tools.ts +11 -822
- package/db.ts +45 -142
- package/handlers/idle-debug.ts +135 -0
- package/handlers/message.ts +316 -0
- package/handlers/poke.ts +165 -0
- package/humanize/emotion-agent.ts +3 -4
- package/humanize/expression.ts +3 -4
- package/humanize/planner.ts +2 -2
- package/humanize/topic.ts +3 -4
- package/index.ts +73 -1705
- package/manage/cooldown.ts +52 -27
- package/manage/group-structured-history.ts +35 -0
- package/manage/idle-check.ts +40 -20
- package/manage/queue-processor.ts +51 -25
- package/manage/rate-limit-guard.ts +70 -0
- package/package.json +1 -1
- package/runtime/chat-runtime.ts +124 -0
- package/types.ts +1 -0
- package/utils/index.ts +2 -0
- package/utils/json.ts +8 -0
- package/utils/message.ts +185 -175
package/db.ts
CHANGED
|
@@ -11,6 +11,40 @@ import type {
|
|
|
11
11
|
MediaSummaryRecord,
|
|
12
12
|
} from "./types";
|
|
13
13
|
|
|
14
|
+
function toChatMessage(row: any): ChatMessage {
|
|
15
|
+
return {
|
|
16
|
+
id: row.id,
|
|
17
|
+
sessionId: row.session_id,
|
|
18
|
+
role: row.role,
|
|
19
|
+
content: row.content,
|
|
20
|
+
userId: row.user_id,
|
|
21
|
+
userName: row.user_name,
|
|
22
|
+
userRole: row.user_role,
|
|
23
|
+
userTitle: row.user_title,
|
|
24
|
+
groupId: row.group_id,
|
|
25
|
+
groupName: row.group_name,
|
|
26
|
+
timestamp: row.timestamp,
|
|
27
|
+
messageId: row.message_id,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toTopicRecord(row: any): TopicRecord {
|
|
32
|
+
return toTopicRecord(row);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function toExpressionRecord(row: any): ExpressionRecord {
|
|
36
|
+
return {
|
|
37
|
+
id: row.id,
|
|
38
|
+
sessionId: row.session_id,
|
|
39
|
+
userId: row.user_id,
|
|
40
|
+
userName: row.user_name,
|
|
41
|
+
situation: row.situation,
|
|
42
|
+
style: row.style,
|
|
43
|
+
example: row.example,
|
|
44
|
+
createdAt: row.created_at,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
14
48
|
/**
|
|
15
49
|
* 聊天数据库接口
|
|
16
50
|
*/
|
|
@@ -413,40 +447,14 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
413
447
|
: (stmts.getMessages.all({ $sessionId: sessionId, $limit: limit }) as any[]);
|
|
414
448
|
|
|
415
449
|
return rows
|
|
416
|
-
.map(
|
|
417
|
-
id: row.id,
|
|
418
|
-
sessionId: row.session_id,
|
|
419
|
-
role: row.role,
|
|
420
|
-
content: row.content,
|
|
421
|
-
userId: row.user_id,
|
|
422
|
-
userName: row.user_name,
|
|
423
|
-
userRole: row.user_role,
|
|
424
|
-
userTitle: row.user_title,
|
|
425
|
-
groupId: row.group_id,
|
|
426
|
-
groupName: row.group_name,
|
|
427
|
-
timestamp: row.timestamp,
|
|
428
|
-
messageId: row.message_id,
|
|
429
|
-
}))
|
|
450
|
+
.map(toChatMessage)
|
|
430
451
|
.reverse(); // 按时间正序
|
|
431
452
|
},
|
|
432
453
|
|
|
433
454
|
getBotMessages(groupId: number, limit: number = 50): ChatMessage[] {
|
|
434
455
|
const rows = stmts.getBotMessages.all({ $groupId: groupId, $limit: limit }) as any[];
|
|
435
456
|
return rows
|
|
436
|
-
.map(
|
|
437
|
-
id: row.id,
|
|
438
|
-
sessionId: row.session_id,
|
|
439
|
-
role: row.role,
|
|
440
|
-
content: row.content,
|
|
441
|
-
userId: row.user_id,
|
|
442
|
-
userName: row.user_name,
|
|
443
|
-
userRole: row.user_role,
|
|
444
|
-
userTitle: row.user_title,
|
|
445
|
-
groupId: row.group_id,
|
|
446
|
-
groupName: row.group_name,
|
|
447
|
-
timestamp: row.timestamp,
|
|
448
|
-
messageId: row.message_id,
|
|
449
|
-
}))
|
|
457
|
+
.map(toChatMessage)
|
|
450
458
|
.reverse(); // 按时间正序
|
|
451
459
|
},
|
|
452
460
|
|
|
@@ -458,20 +466,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
458
466
|
{ $groupId: groupId, $limit: limit },
|
|
459
467
|
) as any[];
|
|
460
468
|
return rows
|
|
461
|
-
.map(
|
|
462
|
-
id: row.id,
|
|
463
|
-
sessionId: row.session_id,
|
|
464
|
-
role: row.role,
|
|
465
|
-
content: row.content,
|
|
466
|
-
userId: row.user_id,
|
|
467
|
-
userName: row.user_name,
|
|
468
|
-
userRole: row.user_role,
|
|
469
|
-
userTitle: row.user_title,
|
|
470
|
-
groupId: row.group_id,
|
|
471
|
-
groupName: row.group_name,
|
|
472
|
-
timestamp: row.timestamp,
|
|
473
|
-
messageId: row.message_id,
|
|
474
|
-
}))
|
|
469
|
+
.map(toChatMessage)
|
|
475
470
|
.reverse();
|
|
476
471
|
},
|
|
477
472
|
|
|
@@ -489,20 +484,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
489
484
|
: (stmts.getMessagesByUser.all({ $userId: userId, $limit: limit }) as any[]);
|
|
490
485
|
|
|
491
486
|
return rows
|
|
492
|
-
.map(
|
|
493
|
-
id: row.id,
|
|
494
|
-
sessionId: row.session_id,
|
|
495
|
-
role: row.role,
|
|
496
|
-
content: row.content,
|
|
497
|
-
userId: row.user_id,
|
|
498
|
-
userName: row.user_name,
|
|
499
|
-
userRole: row.user_role,
|
|
500
|
-
userTitle: row.user_title,
|
|
501
|
-
groupId: row.group_id,
|
|
502
|
-
groupName: row.group_name,
|
|
503
|
-
timestamp: row.timestamp,
|
|
504
|
-
messageId: row.message_id,
|
|
505
|
-
}))
|
|
487
|
+
.map(toChatMessage)
|
|
506
488
|
.reverse();
|
|
507
489
|
},
|
|
508
490
|
|
|
@@ -512,20 +494,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
512
494
|
: (stmts.getAllMessagesByUser.all({ $userId: userId }) as any[]);
|
|
513
495
|
|
|
514
496
|
return rows
|
|
515
|
-
.map(
|
|
516
|
-
id: row.id,
|
|
517
|
-
sessionId: row.session_id,
|
|
518
|
-
role: row.role,
|
|
519
|
-
content: row.content,
|
|
520
|
-
userId: row.user_id,
|
|
521
|
-
userName: row.user_name,
|
|
522
|
-
userRole: row.user_role,
|
|
523
|
-
userTitle: row.user_title,
|
|
524
|
-
groupId: row.group_id,
|
|
525
|
-
groupName: row.group_name,
|
|
526
|
-
timestamp: row.timestamp,
|
|
527
|
-
messageId: row.message_id,
|
|
528
|
-
}))
|
|
497
|
+
.map(toChatMessage)
|
|
529
498
|
.reverse();
|
|
530
499
|
},
|
|
531
500
|
|
|
@@ -539,20 +508,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
539
508
|
$startTimestamp: startTimestamp,
|
|
540
509
|
$endTimestamp: endTimestamp,
|
|
541
510
|
}) as any[];
|
|
542
|
-
return rows.map(
|
|
543
|
-
id: row.id,
|
|
544
|
-
sessionId: row.session_id,
|
|
545
|
-
role: row.role,
|
|
546
|
-
content: row.content,
|
|
547
|
-
userId: row.user_id,
|
|
548
|
-
userName: row.user_name,
|
|
549
|
-
userRole: row.user_role,
|
|
550
|
-
userTitle: row.user_title,
|
|
551
|
-
groupId: row.group_id,
|
|
552
|
-
groupName: row.group_name,
|
|
553
|
-
timestamp: row.timestamp,
|
|
554
|
-
messageId: row.message_id,
|
|
555
|
-
}));
|
|
511
|
+
return rows.map(toChatMessage);
|
|
556
512
|
},
|
|
557
513
|
|
|
558
514
|
updateCompressedContext(sessionId: string, context: string): void {
|
|
@@ -579,20 +535,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
579
535
|
$limit: limit,
|
|
580
536
|
}) as any[];
|
|
581
537
|
return rows
|
|
582
|
-
.map(
|
|
583
|
-
id: row.id,
|
|
584
|
-
sessionId: row.session_id,
|
|
585
|
-
role: row.role,
|
|
586
|
-
content: row.content,
|
|
587
|
-
userId: row.user_id,
|
|
588
|
-
userName: row.user_name,
|
|
589
|
-
userRole: row.user_role,
|
|
590
|
-
userTitle: row.user_title,
|
|
591
|
-
groupId: row.group_id,
|
|
592
|
-
groupName: row.group_name,
|
|
593
|
-
timestamp: row.timestamp,
|
|
594
|
-
messageId: row.message_id,
|
|
595
|
-
}))
|
|
538
|
+
.map(toChatMessage)
|
|
596
539
|
.reverse();
|
|
597
540
|
},
|
|
598
541
|
|
|
@@ -613,18 +556,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
613
556
|
|
|
614
557
|
getTopics(sessionId: string, limit: number = 10): TopicRecord[] {
|
|
615
558
|
const rows = stmts.getTopics.all({ $sessionId: sessionId, $limit: limit }) as any[];
|
|
616
|
-
return rows.map(
|
|
617
|
-
id: row.id,
|
|
618
|
-
sessionId: row.session_id,
|
|
619
|
-
title: row.title,
|
|
620
|
-
keywords: row.keywords,
|
|
621
|
-
summary: row.summary,
|
|
622
|
-
messageCount: row.message_count,
|
|
623
|
-
windowStartAt: row.window_start_at ?? undefined,
|
|
624
|
-
windowEndAt: row.window_end_at ?? undefined,
|
|
625
|
-
createdAt: row.created_at,
|
|
626
|
-
updatedAt: row.updated_at,
|
|
627
|
-
}));
|
|
559
|
+
return rows.map(toTopicRecord);
|
|
628
560
|
},
|
|
629
561
|
|
|
630
562
|
getTopicByWindow(
|
|
@@ -638,18 +570,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
638
570
|
$windowEndAt: windowEndAt,
|
|
639
571
|
}) as any;
|
|
640
572
|
if (!row) return null;
|
|
641
|
-
return
|
|
642
|
-
id: row.id,
|
|
643
|
-
sessionId: row.session_id,
|
|
644
|
-
title: row.title,
|
|
645
|
-
keywords: row.keywords,
|
|
646
|
-
summary: row.summary,
|
|
647
|
-
messageCount: row.message_count,
|
|
648
|
-
windowStartAt: row.window_start_at ?? undefined,
|
|
649
|
-
windowEndAt: row.window_end_at ?? undefined,
|
|
650
|
-
createdAt: row.created_at,
|
|
651
|
-
updatedAt: row.updated_at,
|
|
652
|
-
};
|
|
573
|
+
return toTopicRecord(row);
|
|
653
574
|
},
|
|
654
575
|
|
|
655
576
|
updateTopic(
|
|
@@ -686,16 +607,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
686
607
|
|
|
687
608
|
getExpressions(sessionId: string, limit: number = 50): ExpressionRecord[] {
|
|
688
609
|
const rows = stmts.getExpressions.all({ $sessionId: sessionId, $limit: limit }) as any[];
|
|
689
|
-
return rows.map(
|
|
690
|
-
id: row.id,
|
|
691
|
-
sessionId: row.session_id,
|
|
692
|
-
userId: row.user_id,
|
|
693
|
-
userName: row.user_name,
|
|
694
|
-
situation: row.situation,
|
|
695
|
-
style: row.style,
|
|
696
|
-
example: row.example,
|
|
697
|
-
createdAt: row.created_at,
|
|
698
|
-
}));
|
|
610
|
+
return rows.map(toExpressionRecord);
|
|
699
611
|
},
|
|
700
612
|
|
|
701
613
|
getExpressionsByUser(
|
|
@@ -703,16 +615,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
|
|
|
703
615
|
limit: number = 50,
|
|
704
616
|
): ExpressionRecord[] {
|
|
705
617
|
const rows = stmts.getExpressionsByUser.all({ $userId: userId, $limit: limit }) as any[];
|
|
706
|
-
return rows.map(
|
|
707
|
-
id: row.id,
|
|
708
|
-
sessionId: row.session_id,
|
|
709
|
-
userId: row.user_id,
|
|
710
|
-
userName: row.user_name,
|
|
711
|
-
situation: row.situation,
|
|
712
|
-
style: row.style,
|
|
713
|
-
example: row.example,
|
|
714
|
-
createdAt: row.created_at,
|
|
715
|
-
}));
|
|
618
|
+
return rows.map(toExpressionRecord);
|
|
716
619
|
},
|
|
717
620
|
|
|
718
621
|
replaceExpressionsByUser(
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { MiokiContext } from "mioki";
|
|
2
|
+
import type { AIService } from "mioku";
|
|
3
|
+
import type { ChatPluginContext } from "../context";
|
|
4
|
+
import type { ChatConfig, TargetMessage } from "../types";
|
|
5
|
+
import { getBotRole } from "../utils";
|
|
6
|
+
import { buildStructuredUserInputFromTarget } from "../manage/group-structured-history";
|
|
7
|
+
|
|
8
|
+
export async function handleIdleCheckDebug(
|
|
9
|
+
pluginCtx: ChatPluginContext,
|
|
10
|
+
e: any,
|
|
11
|
+
cfg: ChatConfig,
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
const ctx: MiokiContext = pluginCtx.ctx;
|
|
14
|
+
const isOwner = ctx.isOwner?.(e) ?? false;
|
|
15
|
+
if (!isOwner) {
|
|
16
|
+
await e.reply("只有主人才能使用这个指令~");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const groupIdStr = e.message[0]?.text?.replace("/空闲检查", "")?.trim() || "";
|
|
20
|
+
const targetGroupId = parseInt(groupIdStr, 10);
|
|
21
|
+
if (!targetGroupId) {
|
|
22
|
+
await e.reply("请指定群号,如:/空闲检查 123456");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const groupSessionId = `group:${targetGroupId}`;
|
|
27
|
+
try {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
const botNickname =
|
|
30
|
+
cfg.nicknames[0] || ctx.pickBot(e.self_id).nickname || "Bot";
|
|
31
|
+
ctx.logger.info(`[Debug] Manual idle check: group ${targetGroupId}`);
|
|
32
|
+
|
|
33
|
+
const { history } = await pluginCtx.getGroupHistoryMessages(
|
|
34
|
+
targetGroupId,
|
|
35
|
+
groupSessionId,
|
|
36
|
+
ctx,
|
|
37
|
+
cfg.historyCount,
|
|
38
|
+
pluginCtx.db,
|
|
39
|
+
e.self_id,
|
|
40
|
+
pluginCtx.buildHistoryMediaOptions(pluginCtx.aiInstance, cfg),
|
|
41
|
+
);
|
|
42
|
+
const planResult = await pluginCtx.humanize.actionPlanner.plan(
|
|
43
|
+
groupSessionId,
|
|
44
|
+
botNickname,
|
|
45
|
+
history,
|
|
46
|
+
"[Check if you want to answer the call]",
|
|
47
|
+
true,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (planResult.action !== "reply") {
|
|
51
|
+
await e.reply(
|
|
52
|
+
`[空闲检测] 群 ${targetGroupId}\n决定: ${planResult.action}\n原因: ${planResult.reason}`,
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const targetMessage: TargetMessage = {
|
|
58
|
+
userName: "系统",
|
|
59
|
+
userId: 0,
|
|
60
|
+
userRole: "member",
|
|
61
|
+
content: "[No one in the group is talking? I'll answer!]",
|
|
62
|
+
messageId: 0,
|
|
63
|
+
timestamp: now,
|
|
64
|
+
};
|
|
65
|
+
const botRole = await getBotRole(targetGroupId, ctx, e.self_id);
|
|
66
|
+
const toolCtx = pluginCtx.buildToolContext({
|
|
67
|
+
ctx,
|
|
68
|
+
event: null,
|
|
69
|
+
groupSessionId,
|
|
70
|
+
groupId: targetGroupId,
|
|
71
|
+
userId: 0,
|
|
72
|
+
config: cfg,
|
|
73
|
+
aiService: ctx.services!.ai as AIService,
|
|
74
|
+
db: pluginCtx.db,
|
|
75
|
+
botRole,
|
|
76
|
+
humanize: pluginCtx.humanize,
|
|
77
|
+
targetMessage,
|
|
78
|
+
selfId: e.self_id,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const result = await pluginCtx.runWithRateLimitGuard(
|
|
82
|
+
() =>
|
|
83
|
+
pluginCtx.runChat(
|
|
84
|
+
pluginCtx.aiInstance,
|
|
85
|
+
toolCtx,
|
|
86
|
+
history,
|
|
87
|
+
targetMessage,
|
|
88
|
+
{
|
|
89
|
+
config: cfg,
|
|
90
|
+
botNickname,
|
|
91
|
+
botRole: toolCtx.botRole,
|
|
92
|
+
aiService: ctx.services!.ai as AIService,
|
|
93
|
+
isGroup: true,
|
|
94
|
+
plannerThoughts: `You stumbled upon some message in this group and decided to reply.\nQuote messages from group friends appropriately (using [reply:message ID] format).\nDon't mention your intentions like "I'm here to answer".`,
|
|
95
|
+
replyContext: { type: "idle" },
|
|
96
|
+
},
|
|
97
|
+
pluginCtx.humanize,
|
|
98
|
+
pluginCtx.skillManager,
|
|
99
|
+
),
|
|
100
|
+
{ groupId: targetGroupId, label: "idle-check" },
|
|
101
|
+
);
|
|
102
|
+
if (!result) {
|
|
103
|
+
await e.reply(`[空闲检测] 群 ${targetGroupId} 因限流被跳过`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await pluginCtx.sendAIResponse(
|
|
108
|
+
{
|
|
109
|
+
ctx,
|
|
110
|
+
groupId: targetGroupId,
|
|
111
|
+
messages: result.messages,
|
|
112
|
+
config: cfg,
|
|
113
|
+
sentIndices: toolCtx.sentMessageIndices,
|
|
114
|
+
},
|
|
115
|
+
e.self_id,
|
|
116
|
+
);
|
|
117
|
+
const now2 = Date.now();
|
|
118
|
+
pluginCtx.saveBotMessages(
|
|
119
|
+
targetGroupId,
|
|
120
|
+
groupSessionId,
|
|
121
|
+
result.messages,
|
|
122
|
+
now2,
|
|
123
|
+
cfg,
|
|
124
|
+
pluginCtx.db,
|
|
125
|
+
ctx,
|
|
126
|
+
e.self_id,
|
|
127
|
+
);
|
|
128
|
+
await e.reply(
|
|
129
|
+
`[空闲检测] 群 ${targetGroupId} 已发送回复: ${planResult.reason}`,
|
|
130
|
+
);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
ctx.logger.error(`[Debug] Idle check failed: ${err}`);
|
|
133
|
+
await e.reply(`[空闲检测] 失败: ${err}`);
|
|
134
|
+
}
|
|
135
|
+
}
|