koishi-plugin-best-cave 2.7.26 → 2.7.28

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.
@@ -40,15 +40,6 @@ export declare class AIManager {
40
40
  * @param {any} cave - 主命令的实例,用于挂载子命令。
41
41
  */
42
42
  registerCommands(cave: any): void;
43
- /**
44
- * @description 递归处理和分析回声洞批次,失败时按 1/5 拆分以定位问题。
45
- * @param {CaveObject[]} caves - 当前要处理的回声洞对象数组。
46
- * @param {number} totalCaves - 要分析的回声洞总数。
47
- * @param {{ count: number }} progress - 用于跟踪总体进度的计数器对象。
48
- * @returns {Promise<number>} 成功分析的回声洞数量。
49
- * @private
50
- */
51
- private processCaveBatch;
52
43
  /**
53
44
  * @description 对新提交的内容执行 AI 驱动的查重检查。
54
45
  * @param {StoredElement[]} newElements - 新提交的内容元素数组。
@@ -78,7 +69,6 @@ export declare class AIManager {
78
69
  * @param {CaveObject} caveA - 第一个回声洞对象。
79
70
  * @param {CaveObject} caveB - 第二个回声洞对象。
80
71
  * @returns {Promise<boolean>} 如果内容被 AI 判断为重复,则返回 true,否则返回 false。
81
- * @throws {Error} 当 AI 请求失败时抛出。
82
72
  * @private
83
73
  */
84
74
  private isContentDuplicateAI;
package/lib/index.js CHANGED
@@ -1154,16 +1154,29 @@ var AIManager = class {
1154
1154
  if (cavesToAnalyze.length === 0) return "无需分析回声洞";
1155
1155
  await session.send(`开始分析 ${cavesToAnalyze.length} 个回声洞...`);
1156
1156
  let successCount = 0;
1157
- const totalToAnalyze = cavesToAnalyze.length;
1158
- const progress = { count: 0 };
1159
- const batchSize = 25;
1160
- for (let i = 0; i < cavesToAnalyze.length; i += batchSize) {
1161
- const batch = cavesToAnalyze.slice(i, i + batchSize);
1157
+ let failedCount = 0;
1158
+ for (let i = 0; i < cavesToAnalyze.length; i += 25) {
1159
+ const batch = cavesToAnalyze.slice(i, i + 25);
1162
1160
  this.logger.info(`[${i + 1}/${cavesToAnalyze.length}] 正在分析 ${batch.length} 个回声洞...`);
1163
- successCount += await this.processCaveBatch(batch, totalToAnalyze, progress);
1161
+ const analysisPromises = batch.map((cave2) => this.analyze([cave2]));
1162
+ const results = await Promise.allSettled(analysisPromises);
1163
+ const successfulAnalyses = [];
1164
+ for (let j = 0; j < results.length; j++) {
1165
+ const result = results[j];
1166
+ const cave2 = batch[j];
1167
+ if (result.status === "fulfilled" && result.value.length > 0) {
1168
+ successfulAnalyses.push(result.value[0]);
1169
+ } else {
1170
+ failedCount++;
1171
+ if (result.status === "rejected") this.logger.error(`分析回声洞(${cave2.id})失败:`, result.reason);
1172
+ }
1173
+ }
1174
+ if (successfulAnalyses.length > 0) {
1175
+ await this.ctx.database.upsert("cave_meta", successfulAnalyses);
1176
+ successCount += successfulAnalyses.length;
1177
+ }
1164
1178
  }
1165
- const failedCount = totalToAnalyze - successCount;
1166
- if (failedCount > 0) return `已分析 ${successCount} 个回声洞(失败 ${failedCount} 个)`;
1179
+ return `已分析 ${successCount} 个回声洞(失败 ${failedCount} 个)`;
1167
1180
  } catch (error) {
1168
1181
  this.logger.error("分析回声洞失败:", error);
1169
1182
  return `操作失败: ${error.message}`;
@@ -1177,8 +1190,12 @@ var AIManager = class {
1177
1190
  if (allMeta.length < 2) return "无可比较数据";
1178
1191
  const candidatePairs = generateFromLSH(allMeta, (meta) => ({ id: meta.cave, keys: meta.keywords }));
1179
1192
  if (candidatePairs.size === 0) return "未发现相似内容";
1180
- const allCaves = new Map((await this.ctx.database.get("cave", { status: "active" })).map((c) => [c.id, c]));
1181
- const duplicatePairs = [];
1193
+ const idsToCompare = /* @__PURE__ */ new Set();
1194
+ candidatePairs.forEach((pairKey) => {
1195
+ pairKey.split("-").map(Number).forEach((id) => idsToCompare.add(id));
1196
+ });
1197
+ const caveData = await this.ctx.database.get("cave", { id: { $in: Array.from(idsToCompare) }, status: "active" });
1198
+ const allCaves = new Map(caveData.map((c) => [c.id, c]));
1182
1199
  const comparisonPromises = Array.from(candidatePairs).map(async (pairKey) => {
1183
1200
  const [id1, id2] = pairKey.split("-").map(Number);
1184
1201
  const cave1 = allCaves.get(id1);
@@ -1187,7 +1204,7 @@ var AIManager = class {
1187
1204
  return null;
1188
1205
  });
1189
1206
  const results = await Promise.all(comparisonPromises);
1190
- duplicatePairs.push(...results.filter(Boolean));
1207
+ const duplicatePairs = results.filter(Boolean);
1191
1208
  if (duplicatePairs.length === 0) return "未发现高重复性的内容";
1192
1209
  const dsu = new DSU();
1193
1210
  const allIds = /* @__PURE__ */ new Set();
@@ -1217,38 +1234,6 @@ var AIManager = class {
1217
1234
  }
1218
1235
  });
1219
1236
  }
1220
- /**
1221
- * @description 递归处理和分析回声洞批次,失败时按 1/5 拆分以定位问题。
1222
- * @param {CaveObject[]} caves - 当前要处理的回声洞对象数组。
1223
- * @param {number} totalCaves - 要分析的回声洞总数。
1224
- * @param {{ count: number }} progress - 用于跟踪总体进度的计数器对象。
1225
- * @returns {Promise<number>} 成功分析的回声洞数量。
1226
- * @private
1227
- */
1228
- async processCaveBatch(caves, totalCaves, progress) {
1229
- if (caves.length === 0) return 0;
1230
- this.logger.info(`[${progress.count + 1}/${totalCaves}] 正在分析回声洞(${caves.map((c) => c.id).join("|")})...`);
1231
- try {
1232
- const analyses = await this.analyze(caves);
1233
- if (analyses.length > 0) await this.ctx.database.upsert("cave_meta", analyses);
1234
- progress.count += caves.length;
1235
- return analyses.length;
1236
- } catch (error) {
1237
- if (caves.length > 1) {
1238
- const subBatches = [];
1239
- const subBatchSize = Math.ceil(caves.length / 5);
1240
- for (let i = 0; i < caves.length; i += subBatchSize) subBatches.push(caves.slice(i, i + subBatchSize));
1241
- const processingPromises = subBatches.map((subBatch) => this.processCaveBatch(subBatch, totalCaves, progress));
1242
- const results = await Promise.all(processingPromises);
1243
- return results.reduce((sum, count) => sum + count, 0);
1244
- } else {
1245
- const failedCave = caves[0];
1246
- progress.count++;
1247
- this.logger.error(`[${progress.count}/${totalCaves}] 分析回声洞(${failedCave.id})失败:`, error);
1248
- return 0;
1249
- }
1250
- }
1251
- }
1252
1237
  /**
1253
1238
  * @description 对新提交的内容执行 AI 驱动的查重检查。
1254
1239
  * @param {StoredElement[]} newElements - 新提交的内容元素数组。
@@ -1310,12 +1295,14 @@ ${combinedText}` });
1310
1295
  contentForAI.push(...images);
1311
1296
  const userMessage = { role: "user", content: contentForAI };
1312
1297
  const response = await this.requestAI([userMessage], this.ANALYSIS_SYSTEM_PROMPT);
1313
- if (response) return {
1314
- cave: cave.id,
1315
- keywords: response.keywords || [],
1316
- description: response.description || "",
1317
- rating: Math.max(0, Math.min(100, response.rating || 0))
1318
- };
1298
+ if (response) {
1299
+ return {
1300
+ cave: cave.id,
1301
+ keywords: response.keywords || [],
1302
+ description: response.description || "",
1303
+ rating: Math.max(0, Math.min(100, response.rating || 0))
1304
+ };
1305
+ }
1319
1306
  return null;
1320
1307
  });
1321
1308
  const results = await Promise.all(analysisPromises);
@@ -1326,7 +1313,6 @@ ${combinedText}` });
1326
1313
  * @param {CaveObject} caveA - 第一个回声洞对象。
1327
1314
  * @param {CaveObject} caveB - 第二个回声洞对象。
1328
1315
  * @returns {Promise<boolean>} 如果内容被 AI 判断为重复,则返回 true,否则返回 false。
1329
- * @throws {Error} 当 AI 请求失败时抛出。
1330
1316
  * @private
1331
1317
  */
1332
1318
  async isContentDuplicateAI(caveA, caveB) {
@@ -1379,28 +1365,21 @@ ${combinedText}` });
1379
1365
  "Content-Type": "application/json",
1380
1366
  "Authorization": `Bearer ${this.config.aiApiKey}`
1381
1367
  };
1382
- try {
1383
- const response = await this.http.post(fullUrl, payload, { headers, timeout: 6e5 });
1384
- const content = response?.choices?.[0]?.message?.content;
1385
- if (!content?.trim()) throw new Error();
1386
- const candidates = [];
1387
- const jsonBlockMatch = content.match(/```json\s*([\s\S]*?)\s*```/i);
1388
- if (jsonBlockMatch && jsonBlockMatch[1]) candidates.push(jsonBlockMatch[1]);
1389
- candidates.push(content);
1390
- const firstBrace = content.indexOf("{");
1391
- const lastBrace = content.lastIndexOf("}");
1392
- if (firstBrace !== -1 && lastBrace > firstBrace) candidates.push(content.substring(firstBrace, lastBrace + 1));
1393
- for (const candidate of [...new Set(candidates)]) {
1394
- try {
1395
- return JSON.parse(candidate);
1396
- } catch (parseError) {
1397
- }
1398
- }
1399
- this.logger.error("解析失败", "原始响应:", JSON.stringify(response, null, 2));
1400
- throw new Error();
1401
- } catch (e) {
1402
- throw e;
1368
+ const response = await this.http.post(fullUrl, payload, { headers, timeout: 6e5 });
1369
+ const content = response?.choices?.[0]?.message?.content;
1370
+ if (!content?.trim()) throw new Error();
1371
+ const candidates = [];
1372
+ const jsonBlockMatch = content.match(/```json\s*([\s\S]*?)\s*```/i);
1373
+ if (jsonBlockMatch && jsonBlockMatch[1]) candidates.push(jsonBlockMatch[1]);
1374
+ candidates.push(content);
1375
+ const firstBrace = content.indexOf("{");
1376
+ const lastBrace = content.lastIndexOf("}");
1377
+ if (firstBrace !== -1 && lastBrace > firstBrace) candidates.push(content.substring(firstBrace, lastBrace + 1));
1378
+ for (const candidate of [...new Set(candidates)]) try {
1379
+ return JSON.parse(candidate);
1380
+ } catch (parseError) {
1403
1381
  }
1382
+ this.logger.error("原始响应:", JSON.stringify(response, null, 2));
1404
1383
  }
1405
1384
  };
1406
1385
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-best-cave",
3
3
  "description": "功能强大、高度可定制的回声洞插件。支持丰富的媒体类型、内容查重、AI分析、人工审核、用户昵称、数据迁移以及本地/S3 双重文件存储后端。",
4
- "version": "2.7.26",
4
+ "version": "2.7.28",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],