mioku-plugin-chat 2.6.0 → 2.7.1
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/personalization.ts +1 -1
- package/context.ts +2 -0
- package/core/base.ts +1 -1
- package/core/chat-engine.ts +18 -7
- package/core/chat-turn.ts +26 -9
- package/core/media/image-analyzer.ts +63 -397
- package/core/prompt.ts +11 -20
- package/db.ts +8 -12
- package/handlers/idle-debug.ts +96 -97
- package/handlers/message.ts +35 -34
- package/handlers/poke.ts +102 -124
- package/humanize/emoji-agent.ts +208 -332
- package/humanize/index.ts +2 -3
- package/index.ts +32 -44
- package/manage/cooldown.ts +60 -100
- package/manage/idle-check.ts +163 -70
- package/manage/queue-processor.ts +73 -30
- package/manage/session-turn-scheduler.ts +130 -0
- package/package.json +1 -1
- package/runtime/chat-runtime.ts +14 -2
- package/types.ts +3 -19
- package/utils/config.ts +57 -0
- package/utils/group-config.ts +9 -0
- package/utils/message.ts +4 -7
- package/utils/queue.ts +5 -0
package/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
import { CooldownManager } from "./manage/cooldown";
|
|
24
24
|
import { IdleCheckManager } from "./manage/idle-check";
|
|
25
25
|
import { QueueProcessor } from "./manage/queue-processor";
|
|
26
|
+
import { SessionTurnScheduler } from "./manage/session-turn-scheduler";
|
|
26
27
|
import { ChatDatabaseCleanup, DEFAULT_CLEANUP_CONFIG } from "./manage/cleanup";
|
|
27
28
|
import {
|
|
28
29
|
GroupStructuredHistoryManager,
|
|
@@ -41,31 +42,11 @@ import { buildHistoryMediaOptions } from "./core/media/segment";
|
|
|
41
42
|
import type { ChatConfigProvider } from "./humanize";
|
|
42
43
|
import type { ChatPluginContext, ChatHandlerState } from "./context";
|
|
43
44
|
import { mergeGroupOverrides } from "./utils/group-config";
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
input
|
|
50
|
-
.map((item) => Math.floor(Number(item)))
|
|
51
|
-
.filter((id) => Number.isFinite(id) && id > 0),
|
|
52
|
-
),
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function shallowMergeTopLevel<T extends Record<string, any>>(
|
|
57
|
-
defaults: T,
|
|
58
|
-
overrides: Record<string, any>,
|
|
59
|
-
): T {
|
|
60
|
-
const result: Record<string, any> = { ...defaults };
|
|
61
|
-
for (const key of Object.keys(overrides)) {
|
|
62
|
-
const value = overrides[key];
|
|
63
|
-
if (value !== undefined && value !== null) {
|
|
64
|
-
result[key] = value;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return result as T;
|
|
68
|
-
}
|
|
45
|
+
import {
|
|
46
|
+
mergeChatConfig,
|
|
47
|
+
normalizeIdList,
|
|
48
|
+
normalizeMediaAnalysisBlacklist,
|
|
49
|
+
} from "./utils/config";
|
|
69
50
|
|
|
70
51
|
export default definePlugin({
|
|
71
52
|
name: "chat",
|
|
@@ -91,9 +72,9 @@ export default definePlugin({
|
|
|
91
72
|
|
|
92
73
|
const buildGlobalConfig = async (): Promise<ChatConfig> => {
|
|
93
74
|
if (!configService) {
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
75
|
+
return mergeChatConfig(
|
|
76
|
+
mergeChatConfig(
|
|
77
|
+
mergeChatConfig({} as ChatConfig, BASE_CONFIG),
|
|
97
78
|
SETTINGS_CONFIG,
|
|
98
79
|
),
|
|
99
80
|
PERSONALIZATION_CONFIG,
|
|
@@ -103,15 +84,19 @@ export default definePlugin({
|
|
|
103
84
|
const settings = (await configService.getConfig("chat", "settings")) ?? {};
|
|
104
85
|
const personalization =
|
|
105
86
|
(await configService.getConfig("chat", "personalization")) ?? {};
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
87
|
+
const mergedDefaults = mergeChatConfig(
|
|
88
|
+
mergeChatConfig(
|
|
89
|
+
mergeChatConfig({} as ChatConfig, BASE_CONFIG),
|
|
109
90
|
SETTINGS_CONFIG,
|
|
110
91
|
),
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
92
|
+
PERSONALIZATION_CONFIG,
|
|
93
|
+
);
|
|
94
|
+
const merged = mergeChatConfig(
|
|
95
|
+
mergeChatConfig(
|
|
96
|
+
mergeChatConfig(mergedDefaults, settings),
|
|
97
|
+
personalization,
|
|
114
98
|
),
|
|
99
|
+
base,
|
|
115
100
|
) as ChatConfig;
|
|
116
101
|
|
|
117
102
|
if (typeof merged.stream !== "boolean") merged.stream = true;
|
|
@@ -127,9 +112,8 @@ export default definePlugin({
|
|
|
127
112
|
}
|
|
128
113
|
merged.whitelistGroups = normalizeIdList(merged.whitelistGroups);
|
|
129
114
|
merged.blacklistGroups = normalizeIdList(merged.blacklistGroups);
|
|
130
|
-
merged.mediaAnalysisBlacklistUsers =
|
|
131
|
-
(merged as
|
|
132
|
-
);
|
|
115
|
+
merged.mediaAnalysisBlacklistUsers =
|
|
116
|
+
normalizeMediaAnalysisBlacklist(merged as Record<string, any>);
|
|
133
117
|
delete (merged as any).imageAnalysisBlacklistUsers;
|
|
134
118
|
return merged;
|
|
135
119
|
};
|
|
@@ -257,11 +241,12 @@ export default definePlugin({
|
|
|
257
241
|
});
|
|
258
242
|
}
|
|
259
243
|
|
|
260
|
-
const humanize = new HumanizeEngine(
|
|
244
|
+
const humanize = new HumanizeEngine(workAIInstance, db, configProvider);
|
|
261
245
|
await humanize.init();
|
|
262
246
|
|
|
263
247
|
const pokeCooldowns = new Map<number, number>();
|
|
264
248
|
const processingSet = new Set<string>();
|
|
249
|
+
const sessionTurnScheduler = new SessionTurnScheduler();
|
|
265
250
|
const groupStructuredHistory = new GroupStructuredHistoryManager();
|
|
266
251
|
const rateLimitGuard = new RateLimitGuard(rateLimiter, ctx.logger);
|
|
267
252
|
const runWithRateLimitGuard = rateLimitGuard.run.bind(rateLimitGuard);
|
|
@@ -276,6 +261,7 @@ export default definePlugin({
|
|
|
276
261
|
cooldownManager: undefined as unknown as CooldownManager,
|
|
277
262
|
idleCheckManager: undefined as unknown as IdleCheckManager,
|
|
278
263
|
queueProcessor: undefined as unknown as QueueProcessor,
|
|
264
|
+
sessionTurnScheduler,
|
|
279
265
|
runWithRateLimitGuard,
|
|
280
266
|
buildHistoryMediaOptions,
|
|
281
267
|
getGroupHistoryMessages, getGroupInfoData, getHumanizeContexts,
|
|
@@ -342,16 +328,18 @@ export default definePlugin({
|
|
|
342
328
|
ctx.logger.info("聊天插件加载成功");
|
|
343
329
|
|
|
344
330
|
return () => {
|
|
345
|
-
db.close();
|
|
346
|
-
rateLimiter.dispose();
|
|
347
|
-
clearInterval(cleanupInterval);
|
|
348
|
-
dbCleanup.stop();
|
|
349
|
-
cooldownManager.dispose();
|
|
350
331
|
idleCheckManager.dispose();
|
|
332
|
+
cooldownManager.dispose();
|
|
351
333
|
queueProcessor.dispose();
|
|
334
|
+
sessionTurnScheduler.dispose();
|
|
335
|
+
queueManager.dispose();
|
|
336
|
+
clearInterval(cleanupInterval);
|
|
337
|
+
dbCleanup.stop();
|
|
338
|
+
rateLimiter.dispose();
|
|
352
339
|
processingSet.clear();
|
|
353
340
|
pokeCooldowns.clear();
|
|
341
|
+
db.close();
|
|
354
342
|
ctx.logger.info("聊天插件已卸载");
|
|
355
343
|
};
|
|
356
344
|
},
|
|
357
|
-
});
|
|
345
|
+
});
|
package/manage/cooldown.ts
CHANGED
|
@@ -6,23 +6,20 @@ import type { HumanizeEngine, ChatConfigProvider } from "../humanize";
|
|
|
6
6
|
import type { SkillSessionManager } from "./skill-session";
|
|
7
7
|
import type { GroupStructuredHistoryManager } from "./group-structured-history";
|
|
8
8
|
import { buildStructuredUserInputFromEvent } from "./group-structured-history";
|
|
9
|
-
import type { SessionManager } from "./session";
|
|
10
9
|
import type { RateLimiter } from "./rate-limiter";
|
|
11
|
-
import type { IdleCheckManager } from "./idle-check";
|
|
12
10
|
import type {
|
|
13
11
|
RunRateLimitGuard,
|
|
14
12
|
HistoryMediaOptions,
|
|
15
13
|
GetGroupHistoryMessages,
|
|
16
14
|
GetGroupInfoData,
|
|
17
15
|
GetHumanizeContexts,
|
|
18
|
-
SendAIResponse,
|
|
19
|
-
SaveBotMessages,
|
|
20
|
-
SendEmoji,
|
|
21
16
|
RunChat,
|
|
22
17
|
BuildToolContext,
|
|
23
18
|
BuildStructuredUserInput,
|
|
24
19
|
} from "./types";
|
|
25
20
|
import type { ChatPluginContext } from "../context";
|
|
21
|
+
import type { SessionTurnScheduler } from "./session-turn-scheduler";
|
|
22
|
+
import { finalizeChatTurn } from "../core/chat-turn";
|
|
26
23
|
import { getBotRole } from "../utils";
|
|
27
24
|
|
|
28
25
|
interface CooldownMessage {
|
|
@@ -39,6 +36,8 @@ export class CooldownManager {
|
|
|
39
36
|
private groupCooldownUntil = new Map<string, number>();
|
|
40
37
|
private groupCooldownMessages = new Map<string, CooldownMessage[]>();
|
|
41
38
|
private cooldownTimeoutIds = new Map<string, NodeJS.Timeout>();
|
|
39
|
+
private pluginCtx: ChatPluginContext;
|
|
40
|
+
private sessionTurnScheduler: SessionTurnScheduler;
|
|
42
41
|
|
|
43
42
|
private ctx: MiokiContext;
|
|
44
43
|
private configProvider: ChatConfigProvider;
|
|
@@ -49,9 +48,7 @@ export class CooldownManager {
|
|
|
49
48
|
private aiService: import("mioku").AIService;
|
|
50
49
|
private skillManager: SkillSessionManager;
|
|
51
50
|
private groupStructuredHistory: GroupStructuredHistoryManager;
|
|
52
|
-
private sessionManager: SessionManager;
|
|
53
51
|
private rateLimiter: RateLimiter;
|
|
54
|
-
private idleCheckManager: IdleCheckManager;
|
|
55
52
|
private runWithRateLimitGuard: RunRateLimitGuard;
|
|
56
53
|
private buildHistoryMediaOptions: (
|
|
57
54
|
ai: AIInstance,
|
|
@@ -60,14 +57,13 @@ export class CooldownManager {
|
|
|
60
57
|
private getGroupHistoryMessages: GetGroupHistoryMessages;
|
|
61
58
|
private getGroupInfoData: GetGroupInfoData;
|
|
62
59
|
private getHumanizeContexts: GetHumanizeContexts;
|
|
63
|
-
private sendAIResponse: SendAIResponse;
|
|
64
|
-
private saveBotMessages: SaveBotMessages;
|
|
65
|
-
private sendEmoji: SendEmoji;
|
|
66
60
|
private runChat: RunChat;
|
|
67
61
|
private buildToolContext: BuildToolContext;
|
|
68
62
|
private buildStructuredUserInputFromEvent: BuildStructuredUserInput;
|
|
69
63
|
|
|
70
64
|
constructor(pluginCtx: ChatPluginContext) {
|
|
65
|
+
this.pluginCtx = pluginCtx;
|
|
66
|
+
this.sessionTurnScheduler = pluginCtx.sessionTurnScheduler;
|
|
71
67
|
this.ctx = pluginCtx.ctx;
|
|
72
68
|
this.configProvider = pluginCtx.configProvider;
|
|
73
69
|
this.defaultConfig = pluginCtx.defaultConfig;
|
|
@@ -77,17 +73,12 @@ export class CooldownManager {
|
|
|
77
73
|
this.aiService = pluginCtx.aiService;
|
|
78
74
|
this.skillManager = pluginCtx.skillManager;
|
|
79
75
|
this.groupStructuredHistory = pluginCtx.groupStructuredHistory;
|
|
80
|
-
this.sessionManager = pluginCtx.sessionManager;
|
|
81
76
|
this.rateLimiter = pluginCtx.rateLimiter;
|
|
82
|
-
this.idleCheckManager = pluginCtx.idleCheckManager;
|
|
83
77
|
this.runWithRateLimitGuard = pluginCtx.runWithRateLimitGuard;
|
|
84
78
|
this.buildHistoryMediaOptions = pluginCtx.buildHistoryMediaOptions;
|
|
85
79
|
this.getGroupHistoryMessages = pluginCtx.getGroupHistoryMessages;
|
|
86
80
|
this.getGroupInfoData = pluginCtx.getGroupInfoData;
|
|
87
81
|
this.getHumanizeContexts = pluginCtx.getHumanizeContexts;
|
|
88
|
-
this.sendAIResponse = pluginCtx.sendAIResponse;
|
|
89
|
-
this.saveBotMessages = pluginCtx.saveBotMessages;
|
|
90
|
-
this.sendEmoji = pluginCtx.sendEmoji;
|
|
91
82
|
this.runChat = pluginCtx.runChat;
|
|
92
83
|
this.buildToolContext = pluginCtx.buildToolContext;
|
|
93
84
|
this.buildStructuredUserInputFromEvent = buildStructuredUserInputFromEvent;
|
|
@@ -102,47 +93,51 @@ export class CooldownManager {
|
|
|
102
93
|
if (existingTimer) clearTimeout(existingTimer);
|
|
103
94
|
|
|
104
95
|
const cfg = this.configProvider(groupId);
|
|
105
|
-
const cooldownMs =
|
|
96
|
+
const cooldownMs =
|
|
97
|
+
cfg.cooldownAfterReplyMs ??
|
|
98
|
+
this.defaultConfig.cooldownAfterReplyMs ??
|
|
99
|
+
20_000;
|
|
106
100
|
|
|
107
|
-
const timer = setTimeout(
|
|
101
|
+
const timer = setTimeout(() => {
|
|
102
|
+
if (this.cooldownTimeoutIds.get(groupSessionId) !== timer) return;
|
|
108
103
|
this.cooldownTimeoutIds.delete(groupSessionId);
|
|
104
|
+
this.groupCooldownUntil.delete(groupSessionId);
|
|
109
105
|
const collected = this.groupCooldownMessages.get(groupSessionId) || [];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.groupCooldownUntil.delete(groupSessionId);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
106
|
+
this.groupCooldownMessages.delete(groupSessionId);
|
|
107
|
+
if (collected.length === 0) return;
|
|
115
108
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
109
|
+
const hasDirectAt = collected.some((message) => message.isDirectAt);
|
|
110
|
+
void this.sessionTurnScheduler
|
|
111
|
+
.run(
|
|
112
|
+
groupSessionId,
|
|
113
|
+
hasDirectAt ? "cooldown" : "cooldown-planner",
|
|
114
|
+
() =>
|
|
115
|
+
hasDirectAt
|
|
116
|
+
? this.processReviewMessages(
|
|
117
|
+
groupSessionId,
|
|
118
|
+
groupId,
|
|
119
|
+
collected,
|
|
120
|
+
selfId,
|
|
121
|
+
)
|
|
122
|
+
: this.processCooldownWithPlanner(
|
|
123
|
+
groupSessionId,
|
|
124
|
+
groupId,
|
|
125
|
+
collected,
|
|
126
|
+
selfId,
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
.catch((err) =>
|
|
130
|
+
this.ctx.logger.error(
|
|
131
|
+
`[Cooldown] Group ${groupId} processing failed: ${err}`,
|
|
132
|
+
),
|
|
136
133
|
);
|
|
137
|
-
} finally {
|
|
138
|
-
this.groupCooldownMessages.delete(groupSessionId);
|
|
139
|
-
this.groupCooldownUntil.delete(groupSessionId);
|
|
140
|
-
}
|
|
141
134
|
}, cooldownMs);
|
|
142
135
|
|
|
143
136
|
this.cooldownTimeoutIds.set(groupSessionId, timer);
|
|
144
137
|
this.groupCooldownUntil.set(groupSessionId, Date.now() + cooldownMs);
|
|
145
|
-
this.groupCooldownMessages.
|
|
138
|
+
if (!this.groupCooldownMessages.has(groupSessionId)) {
|
|
139
|
+
this.groupCooldownMessages.set(groupSessionId, []);
|
|
140
|
+
}
|
|
146
141
|
}
|
|
147
142
|
|
|
148
143
|
collectMessage(
|
|
@@ -285,36 +280,18 @@ export class CooldownManager {
|
|
|
285
280
|
);
|
|
286
281
|
if (!result) return;
|
|
287
282
|
|
|
288
|
-
await this.
|
|
289
|
-
|
|
290
|
-
ctx: this.ctx,
|
|
291
|
-
groupId,
|
|
292
|
-
messages: result.messages,
|
|
293
|
-
config: cfg,
|
|
294
|
-
sentIndices: toolCtx.sentMessageIndices,
|
|
295
|
-
},
|
|
296
|
-
selfId,
|
|
297
|
-
);
|
|
298
|
-
await this.sendEmoji(this.ctx, groupId, result.emojiPath, selfId);
|
|
299
|
-
|
|
300
|
-
const now = Date.now();
|
|
301
|
-
this.saveBotMessages(
|
|
302
|
-
groupId,
|
|
303
|
-
groupSessionId,
|
|
304
|
-
result.messages,
|
|
305
|
-
now,
|
|
283
|
+
await finalizeChatTurn(this.pluginCtx, {
|
|
284
|
+
event: firstMsg.event,
|
|
306
285
|
cfg,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
selfId,
|
|
310
|
-
);
|
|
311
|
-
this.idleCheckManager.recordBotMessages(
|
|
286
|
+
result,
|
|
287
|
+
groupId,
|
|
312
288
|
groupSessionId,
|
|
313
|
-
|
|
289
|
+
userId: targetMessage.userId,
|
|
314
290
|
selfId,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
291
|
+
toolCtx,
|
|
292
|
+
send: true,
|
|
293
|
+
isLive: true,
|
|
294
|
+
});
|
|
318
295
|
}
|
|
319
296
|
|
|
320
297
|
private async processCooldownWithPlanner(
|
|
@@ -444,34 +421,17 @@ export class CooldownManager {
|
|
|
444
421
|
return;
|
|
445
422
|
}
|
|
446
423
|
|
|
447
|
-
await this.
|
|
448
|
-
|
|
449
|
-
ctx: this.ctx,
|
|
450
|
-
groupId,
|
|
451
|
-
messages: result.messages,
|
|
452
|
-
config: cfg,
|
|
453
|
-
sentIndices: toolCtx.sentMessageIndices,
|
|
454
|
-
},
|
|
455
|
-
selfId,
|
|
456
|
-
);
|
|
457
|
-
await this.sendEmoji(this.ctx, groupId, result.emojiPath, selfId);
|
|
458
|
-
const now = Date.now();
|
|
459
|
-
this.saveBotMessages(
|
|
460
|
-
groupId,
|
|
461
|
-
groupSessionId,
|
|
462
|
-
result.messages,
|
|
463
|
-
now,
|
|
424
|
+
await finalizeChatTurn(this.pluginCtx, {
|
|
425
|
+
event: firstMsg.event,
|
|
464
426
|
cfg,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
selfId,
|
|
468
|
-
);
|
|
469
|
-
this.idleCheckManager.recordBotMessages(
|
|
427
|
+
result,
|
|
428
|
+
groupId,
|
|
470
429
|
groupSessionId,
|
|
471
|
-
|
|
430
|
+
userId: targetMessage.userId,
|
|
472
431
|
selfId,
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
432
|
+
toolCtx,
|
|
433
|
+
send: true,
|
|
434
|
+
isLive: true,
|
|
435
|
+
});
|
|
476
436
|
}
|
|
477
437
|
}
|