koishi-plugin-best-cave 2.7.3 → 2.7.4

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/lib/Utils.d.ts CHANGED
@@ -2,8 +2,6 @@ import { Context, h, Logger, Session } from 'koishi';
2
2
  import { CaveObject, Config, StoredElement } from './index';
3
3
  import { FileManager } from './FileManager';
4
4
  import { HashManager, CaveHashObject } from './HashManager';
5
- import { PendManager } from './PendManager';
6
- import { AIManager } from './AIManager';
7
5
  /**
8
6
  * @description 构建一条用于发送的完整回声洞消息,处理不同存储后端的资源链接。
9
7
  * @param cave 回声洞对象。
@@ -69,25 +67,21 @@ export declare function performSimilarityChecks(ctx: Context, config: Config, ha
69
67
  imageHashesToStore?: Omit<CaveHashObject, 'cave'>[];
70
68
  }>;
71
69
  /**
72
- * @description 异步处理文件上传和状态更新的后台任务。
70
+ * @description 异步处理文件上传,并在成功后更新回声洞状态。
73
71
  * @param ctx - Koishi 上下文。
74
72
  * @param config - 插件配置。
75
73
  * @param fileManager - FileManager 实例,用于保存文件。
76
74
  * @param logger - 日志记录器实例。
77
- * @param reviewManager - ReviewManager 实例,用于提交审核。
78
75
  * @param cave - 刚刚在数据库中创建的 `preload` 状态的回声洞对象。
79
76
  * @param downloadedMedia - 需要保存的媒体文件及其 Buffer。
80
77
  * @param reusableIds - 可复用 ID 的内存缓存。
81
- * @param session - 触发此操作的用户会话,用于发送反馈。
82
- * @param hashManager - HashManager 实例,如果启用则用于哈希计算和比较。
83
- * @param textHashesToStore - 已预先计算好的、待存入数据库的文本哈希对象数组。
84
- * @param imageHashesToStore - 已预先计算好的、待存入数据库的图片哈希对象数组。
85
- * @param aiManager - AIManager 实例,如果启用则用于 AI 分析。
78
+ * @param session - 触发此操作的用户会话。
79
+ * @returns 成功则返回最终状态 ('pending' or 'active'),失败则返回 'delete'。
86
80
  */
87
- export declare function handleFileUploads(ctx: Context, config: Config, fileManager: FileManager, logger: Logger, reviewManager: PendManager, cave: CaveObject, downloadedMedia: {
81
+ export declare function handleFileUploads(ctx: Context, config: Config, fileManager: FileManager, logger: Logger, cave: CaveObject, downloadedMedia: {
88
82
  fileName: string;
89
83
  buffer: Buffer;
90
- }[], reusableIds: Set<number>, session: Session, hashManager: HashManager, textHashesToStore: Omit<CaveHashObject, 'cave'>[], imageHashesToStore: Omit<CaveHashObject, 'cave'>[], aiManager: AIManager | null): Promise<void>;
84
+ }[], reusableIds: Set<number>, session: Session): Promise<'pending' | 'active' | 'delete'>;
91
85
  /**
92
86
  * @description 校验会话是否来自指定的管理群组。
93
87
  * @param session 当前会话。
package/lib/index.js CHANGED
@@ -482,25 +482,18 @@ async function performSimilarityChecks(ctx, config, hashManager, finalElementsFo
482
482
  return { duplicate: false, textHashesToStore, imageHashesToStore };
483
483
  }
484
484
  __name(performSimilarityChecks, "performSimilarityChecks");
485
- async function handleFileUploads(ctx, config, fileManager, logger2, reviewManager, cave, downloadedMedia, reusableIds, session, hashManager, textHashesToStore, imageHashesToStore, aiManager) {
485
+ async function handleFileUploads(ctx, config, fileManager, logger2, cave, downloadedMedia, reusableIds, session) {
486
486
  try {
487
- if (aiManager) await aiManager.analyzeAndStore(cave, downloadedMedia);
488
487
  await Promise.all(downloadedMedia.map((item) => fileManager.saveFile(item.fileName, item.buffer)));
489
488
  const needsReview = config.enablePend && session.channelId !== config.adminChannel?.split(":")[1];
490
489
  const finalStatus = needsReview ? "pending" : "active";
491
490
  await ctx.database.upsert("cave", [{ id: cave.id, status: finalStatus }]);
492
- if (hashManager) {
493
- const allHashesToInsert = [...textHashesToStore, ...imageHashesToStore].map((h5) => ({ ...h5, cave: cave.id }));
494
- if (allHashesToInsert.length > 0) await ctx.database.upsert("cave_hash", allHashesToInsert);
495
- }
496
- if (finalStatus === "pending" && reviewManager) {
497
- const [finalCave] = await ctx.database.get("cave", { id: cave.id });
498
- if (finalCave) reviewManager.sendForPend(finalCave);
499
- }
491
+ return finalStatus;
500
492
  } catch (fileProcessingError) {
501
493
  logger2.error(`回声洞(${cave.id})文件处理失败:`, fileProcessingError);
502
494
  await ctx.database.upsert("cave", [{ id: cave.id, status: "delete" }]);
503
495
  cleanupPendingDeletions(ctx, fileManager, logger2, reusableIds);
496
+ return "delete";
504
497
  }
505
498
  }
506
499
  __name(handleFileUploads, "handleFileUploads");
@@ -1164,9 +1157,17 @@ var AIManager = class {
1164
1157
  if (parts.length <= 1) return { payload: {} };
1165
1158
  try {
1166
1159
  const schema = JSON.parse(schemaString);
1167
- return { payload: { contents: [{ parts }], generationConfig: { response_schema: schema } } };
1160
+ return {
1161
+ payload: {
1162
+ contents: [{ parts }],
1163
+ generationConfig: {
1164
+ responseMimeType: "application/json",
1165
+ responseSchema: schema
1166
+ }
1167
+ }
1168
+ };
1168
1169
  } catch (error) {
1169
- this.logger.error("解析JSON Schema失败:", error);
1170
+ this.logger.error("分析 JSON Schema 解析失败:", error);
1170
1171
  return { payload: {} };
1171
1172
  }
1172
1173
  }
@@ -1182,15 +1183,20 @@ var AIManager = class {
1182
1183
  new_content: { text: formatContent(newElements) },
1183
1184
  existing_contents: existingCaves.map((cave) => ({ id: cave.id, text: formatContent(cave.elements) }))
1184
1185
  });
1185
- const fullPrompt = `${this.config.aiCheckPrompt}
1186
-
1187
- 以下是需要处理的数据:
1188
- ${payloadContent}`;
1186
+ const parts = [{ text: this.config.aiCheckPrompt }, { text: payloadContent }];
1189
1187
  try {
1190
1188
  const schema = JSON.parse(this.config.aiCheckSchema);
1191
- return { payload: { contents: [{ parts: [{ text: fullPrompt }] }], generationConfig: { response_schema: schema } } };
1189
+ return {
1190
+ payload: {
1191
+ contents: [{ parts }],
1192
+ generationConfig: {
1193
+ responseMimeType: "application/json",
1194
+ responseSchema: schema
1195
+ }
1196
+ }
1197
+ };
1192
1198
  } catch (error) {
1193
- this.logger.error("解析查重JSON Schema失败:", error);
1199
+ this.logger.error("查重 JSON Schema 解析失败:", error);
1194
1200
  return { payload: {} };
1195
1201
  }
1196
1202
  }
@@ -1201,6 +1207,7 @@ ${payloadContent}`;
1201
1207
  */
1202
1208
  parseAnalysisResponse(response) {
1203
1209
  try {
1210
+ if (!response?.candidates?.[0]?.content?.parts?.[0]?.text) throw new Error("分析响应解析失败");
1204
1211
  const content = response.candidates[0].content.parts[0].text;
1205
1212
  const parsed = JSON.parse(content);
1206
1213
  const keywords = Array.isArray(parsed.keywords) ? parsed.keywords : [];
@@ -1211,7 +1218,7 @@ ${payloadContent}`;
1211
1218
  };
1212
1219
  } catch (error) {
1213
1220
  this.logger.error("分析响应解析失败:", error, "原始响应:", JSON.stringify(response));
1214
- return { keywords: [], description: "解析失败", rating: 0 };
1221
+ throw error;
1215
1222
  }
1216
1223
  }
1217
1224
  /**
@@ -1221,13 +1228,14 @@ ${payloadContent}`;
1221
1228
  */
1222
1229
  parseDedupeResponse(response) {
1223
1230
  try {
1231
+ if (!response?.candidates?.[0]?.content?.parts?.[0]?.text) throw new Error("查重响应解析失败");
1224
1232
  const content = response.candidates[0].content.parts[0].text;
1225
1233
  const parsed = JSON.parse(content);
1226
1234
  if (parsed.duplicate === true && parsed.id) return { duplicate: true, id: Number(parsed.id) };
1227
1235
  return { duplicate: false };
1228
1236
  } catch (error) {
1229
1237
  this.logger.error("查重响应解析失败:", error, "原始响应:", JSON.stringify(response));
1230
- return { duplicate: false };
1238
+ throw error;
1231
1239
  }
1232
1240
  }
1233
1241
  };
@@ -1409,22 +1417,25 @@ function apply(ctx, config) {
1409
1417
  }
1410
1418
  const userName = (config.enableName ? await profileManager.getNickname(session.userId) : null) || session.username;
1411
1419
  const needsReview = config.enablePend && session.channelId !== config.adminChannel?.split(":")[1];
1412
- const initialStatus = hasMedia ? "preload" : needsReview ? "pending" : "active";
1420
+ let finalStatus = hasMedia ? "preload" : needsReview ? "pending" : "active";
1413
1421
  const newCave = await ctx.database.create("cave", {
1414
1422
  id: newId,
1415
1423
  elements: finalElementsForDb,
1416
1424
  channelId: session.channelId,
1417
1425
  userId: session.userId,
1418
1426
  userName,
1419
- status: initialStatus,
1427
+ status: finalStatus,
1420
1428
  time: creationTime
1421
1429
  });
1422
- if (hasMedia) {
1423
- handleFileUploads(ctx, config, fileManager, logger, reviewManager, newCave, downloadedMedia, reusableIds, session, hashManager, textHashesToStore, imageHashesToStore, aiManager);
1424
- } else {
1425
- if (aiManager) await aiManager.analyzeAndStore(newCave);
1426
- if (hashManager && textHashesToStore.length > 0) await ctx.database.upsert("cave_hash", textHashesToStore.map((h5) => ({ ...h5, cave: newCave.id })));
1427
- if (initialStatus === "pending") reviewManager.sendForPend(newCave);
1430
+ if (hasMedia) finalStatus = await handleFileUploads(ctx, config, fileManager, logger, newCave, downloadedMedia, reusableIds, session);
1431
+ if (finalStatus !== "preload" && finalStatus !== "delete") {
1432
+ newCave.status = finalStatus;
1433
+ if (aiManager) await aiManager.analyzeAndStore(newCave, downloadedMedia);
1434
+ if (hashManager) {
1435
+ const allHashesToInsert = [...textHashesToStore, ...imageHashesToStore].map((h5) => ({ ...h5, cave: newCave.id }));
1436
+ if (allHashesToInsert.length > 0) await ctx.database.upsert("cave_hash", allHashesToInsert);
1437
+ }
1438
+ if (finalStatus === "pending" && reviewManager) reviewManager.sendForPend(newCave);
1428
1439
  }
1429
1440
  return needsReview ? `提交成功,序号为(${newCave.id})` : `添加成功,序号为(${newCave.id})`;
1430
1441
  } catch (error) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-best-cave",
3
3
  "description": "功能强大、高度可定制的回声洞。支持丰富的媒体类型、内容查重、人工审核、用户昵称、数据迁移以及本地/S3 双重文件存储后端。",
4
- "version": "2.7.3",
4
+ "version": "2.7.4",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],