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/types.ts
CHANGED
|
@@ -65,10 +65,8 @@ export interface PlannerConfig {
|
|
|
65
65
|
*/
|
|
66
66
|
export interface EmojiConfig {
|
|
67
67
|
enabled: boolean;
|
|
68
|
-
// 允许的表情包角色
|
|
69
68
|
characters: string[];
|
|
70
|
-
|
|
71
|
-
useAISelection: boolean;
|
|
69
|
+
stickers: string[];
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
/**
|
|
@@ -236,6 +234,7 @@ export interface ChatGroupOverrides {
|
|
|
236
234
|
dynamicDelay?: { enabled?: boolean };
|
|
237
235
|
enableMarkdownScreenshot?: boolean;
|
|
238
236
|
enableMediaRecognition?: boolean;
|
|
237
|
+
allowedExternalSkills?: string[];
|
|
239
238
|
}
|
|
240
239
|
|
|
241
240
|
export type ChatGroupsFile = {
|
|
@@ -376,18 +375,6 @@ export interface ExpressionRecord {
|
|
|
376
375
|
createdAt: number;
|
|
377
376
|
}
|
|
378
377
|
|
|
379
|
-
/**
|
|
380
|
-
* 表情包注册记录
|
|
381
|
-
*/
|
|
382
|
-
export interface EmojiRecord {
|
|
383
|
-
id?: number;
|
|
384
|
-
fileName: string;
|
|
385
|
-
description: string; // AI 生成的描述
|
|
386
|
-
emotion: string; // 情感标签
|
|
387
|
-
usageCount: number;
|
|
388
|
-
createdAt: number;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
378
|
/**
|
|
392
379
|
* 图片记录
|
|
393
380
|
*/
|
|
@@ -395,11 +382,8 @@ export interface ImageRecord {
|
|
|
395
382
|
id?: number;
|
|
396
383
|
hash: string; // 图片哈希
|
|
397
384
|
url: string; // 原始 URL
|
|
398
|
-
type: "
|
|
385
|
+
type: "image";
|
|
399
386
|
description: string; // AI 生成的简要描述
|
|
400
|
-
emotion?: string; // 情感标签(仅表情包)
|
|
401
|
-
character?: string; // 角色名称(仅表情包)
|
|
402
|
-
filePath?: string; // 本地文件路径(仅表情包)
|
|
403
387
|
createdAt: number;
|
|
404
388
|
}
|
|
405
389
|
|
package/utils/config.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function isConfigObject(value: unknown): value is Record<string, any> {
|
|
2
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function mergeChatConfig<T extends Record<string, any>>(
|
|
6
|
+
defaults: T,
|
|
7
|
+
overrides: Record<string, any>,
|
|
8
|
+
): T {
|
|
9
|
+
const result: Record<string, any> = {};
|
|
10
|
+
|
|
11
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
12
|
+
if (isConfigObject(value)) {
|
|
13
|
+
result[key] = mergeChatConfig(value, {});
|
|
14
|
+
} else if (Array.isArray(value)) {
|
|
15
|
+
result[key] = [...value];
|
|
16
|
+
} else {
|
|
17
|
+
result[key] = value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
22
|
+
if (value === undefined || value === null) continue;
|
|
23
|
+
if (isConfigObject(value)) {
|
|
24
|
+
const baseValue = isConfigObject(result[key]) ? result[key] : {};
|
|
25
|
+
result[key] = mergeChatConfig(baseValue, value);
|
|
26
|
+
} else if (Array.isArray(value)) {
|
|
27
|
+
result[key] = [...value];
|
|
28
|
+
} else {
|
|
29
|
+
result[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result as T;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function normalizeIdList(input: unknown): number[] {
|
|
37
|
+
if (!Array.isArray(input)) return [];
|
|
38
|
+
return Array.from(
|
|
39
|
+
new Set(
|
|
40
|
+
input
|
|
41
|
+
.map((item) => Math.floor(Number(item)))
|
|
42
|
+
.filter((id) => Number.isFinite(id) && id > 0),
|
|
43
|
+
),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function normalizeMediaAnalysisBlacklist(
|
|
48
|
+
config: Record<string, any>,
|
|
49
|
+
): number[] {
|
|
50
|
+
const current = config.mediaAnalysisBlacklistUsers;
|
|
51
|
+
const legacy = config.imageAnalysisBlacklistUsers;
|
|
52
|
+
const source =
|
|
53
|
+
legacy !== undefined && Array.isArray(current) && current.length === 0
|
|
54
|
+
? legacy
|
|
55
|
+
: (current ?? legacy);
|
|
56
|
+
return normalizeIdList(source);
|
|
57
|
+
}
|
package/utils/group-config.ts
CHANGED
|
@@ -18,6 +18,8 @@ const OVERRIDABLE_BOOLEAN_FIELDS = [
|
|
|
18
18
|
"enableMediaRecognition",
|
|
19
19
|
] as const;
|
|
20
20
|
|
|
21
|
+
const OVERRIDABLE_ARRAY_FIELDS = ["allowedExternalSkills"] as const;
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* 把每个群的覆盖配置浅合并到全局配置上
|
|
23
25
|
* - 子对象(如 emoji)只覆盖其中存在的字段,未填字段继承全局
|
|
@@ -55,6 +57,13 @@ export function mergeGroupOverrides(
|
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
for (const key of OVERRIDABLE_ARRAY_FIELDS) {
|
|
61
|
+
const value = overridesMap[key];
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
resultMap[key] = value.filter((v) => typeof v === "string");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
return result;
|
|
59
68
|
}
|
|
60
69
|
|
package/utils/message.ts
CHANGED
|
@@ -308,12 +308,12 @@ async function getImageTagWithHashCache(
|
|
|
308
308
|
ctx: HistoryFormatContext,
|
|
309
309
|
): Promise<string> {
|
|
310
310
|
const exact = db.getImageByUrl?.(imageUrl);
|
|
311
|
-
if (exact) return `[
|
|
311
|
+
if (exact) return `[image:${exact.description}]`;
|
|
312
312
|
|
|
313
313
|
const normalized = normalizeImageUrl(imageUrl);
|
|
314
314
|
if (normalized && normalized !== imageUrl) {
|
|
315
315
|
const hit = db.getImageByUrl?.(normalized);
|
|
316
|
-
if (hit) return `[
|
|
316
|
+
if (hit) return `[image:${hit.description}]`;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
let hash = ctx.hashLookupCache.get(`hash:${imageUrl}`);
|
|
@@ -329,17 +329,14 @@ async function getImageTagWithHashCache(
|
|
|
329
329
|
db.saveImage?.({
|
|
330
330
|
hash: byHash.hash,
|
|
331
331
|
url: normalized || imageUrl,
|
|
332
|
-
type:
|
|
332
|
+
type: "image",
|
|
333
333
|
description: byHash.description,
|
|
334
|
-
emotion: byHash.emotion,
|
|
335
|
-
character: byHash.character,
|
|
336
|
-
filePath: byHash.filePath,
|
|
337
334
|
createdAt: byHash.createdAt,
|
|
338
335
|
});
|
|
339
336
|
} catch {
|
|
340
337
|
// best-effort URL caching for next call
|
|
341
338
|
}
|
|
342
|
-
return `[
|
|
339
|
+
return `[image:${byHash.description}]`;
|
|
343
340
|
}
|
|
344
341
|
|
|
345
342
|
return "[image]";
|
package/utils/queue.ts
CHANGED