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/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
- // 使用AI选择表情包发送
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: "meme" | "image"; // 图片类型
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
 
@@ -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
+ }
@@ -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 `[${exact.type}:${exact.description}]`;
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 `[${hit.type}:${hit.description}]`;
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: byHash.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 `[${byHash.type}:${byHash.description}]`;
339
+ return `[image:${byHash.description}]`;
343
340
  }
344
341
 
345
342
  return "[image]";
package/utils/queue.ts CHANGED
@@ -88,6 +88,11 @@ export class MessageQueueManager {
88
88
  const queue = this.pendingMessages.get(groupSessionId);
89
89
  return !!queue && queue.length > 0;
90
90
  }
91
+
92
+ dispose(): void {
93
+ this.pendingMessages.clear();
94
+ this.activeTargetMessages.clear();
95
+ }
91
96
  }
92
97
 
93
98
  /**