koishi-plugin-best-cave 2.7.2 → 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 +12 -11
- package/lib/index.js +148 -133
- package/package.json +1 -1
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,22 +67,25 @@ 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
|
-
* @
|
|
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,
|
|
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
|
|
84
|
+
}[], reusableIds: Set<number>, session: Session): Promise<'pending' | 'active' | 'delete'>;
|
|
85
|
+
/**
|
|
86
|
+
* @description 校验会话是否来自指定的管理群组。
|
|
87
|
+
* @param session 当前会话。
|
|
88
|
+
* @param config 插件配置。
|
|
89
|
+
* @returns 如果校验不通过,返回错误信息字符串;如果通过,返回 null。
|
|
90
|
+
*/
|
|
91
|
+
export declare function requireAdmin(session: Session, config: Config): string | null;
|
package/lib/index.js
CHANGED
|
@@ -206,100 +206,6 @@ var NameManager = class {
|
|
|
206
206
|
}
|
|
207
207
|
};
|
|
208
208
|
|
|
209
|
-
// src/DataManager.ts
|
|
210
|
-
var DataManager = class {
|
|
211
|
-
/**
|
|
212
|
-
* @constructor
|
|
213
|
-
* @param ctx Koishi 上下文,用于数据库操作。
|
|
214
|
-
* @param config 插件配置。
|
|
215
|
-
* @param fileManager 文件管理器实例。
|
|
216
|
-
* @param logger 日志记录器实例。
|
|
217
|
-
*/
|
|
218
|
-
constructor(ctx, config, fileManager, logger2) {
|
|
219
|
-
this.ctx = ctx;
|
|
220
|
-
this.config = config;
|
|
221
|
-
this.fileManager = fileManager;
|
|
222
|
-
this.logger = logger2;
|
|
223
|
-
}
|
|
224
|
-
static {
|
|
225
|
-
__name(this, "DataManager");
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* @description 注册 `.export` 和 `.import` 子命令。
|
|
229
|
-
* @param cave - 主 `cave` 命令实例。
|
|
230
|
-
*/
|
|
231
|
-
registerCommands(cave) {
|
|
232
|
-
const requireAdmin = /* @__PURE__ */ __name((action) => async ({ session }) => {
|
|
233
|
-
if (session.channelId !== this.config.adminChannel?.split(":")[1]) return "此指令仅限在管理群组中使用";
|
|
234
|
-
try {
|
|
235
|
-
await session.send("正在处理,请稍候...");
|
|
236
|
-
return await action();
|
|
237
|
-
} catch (error) {
|
|
238
|
-
this.logger.error("数据操作时发生错误:", error);
|
|
239
|
-
return `操作失败: ${error.message}`;
|
|
240
|
-
}
|
|
241
|
-
}, "requireAdmin");
|
|
242
|
-
cave.subcommand(".export", "导出回声洞数据", { hidden: true, authority: 4 }).usage("将所有回声洞数据导出到 cave.json 中。").action(requireAdmin(() => this.exportData()));
|
|
243
|
-
cave.subcommand(".import", "导入回声洞数据", { hidden: true, authority: 4 }).usage("从 cave.json 中导入回声洞数据。").action(requireAdmin(() => this.importData()));
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* @description 导出所有 'active' 状态的回声洞数据到 `cave.json`。
|
|
247
|
-
* @returns 描述导出结果的消息字符串。
|
|
248
|
-
*/
|
|
249
|
-
async exportData() {
|
|
250
|
-
const fileName = "cave.json";
|
|
251
|
-
const cavesToExport = await this.ctx.database.get("cave", { status: "active" });
|
|
252
|
-
await this.fileManager.saveFile(fileName, Buffer.from(JSON.stringify(cavesToExport, null, 2)));
|
|
253
|
-
return `成功导出 ${cavesToExport.length} 条数据`;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* @description 从 `cave.json` 文件导入回声洞数据。
|
|
257
|
-
* @returns 描述导入结果的消息字符串。
|
|
258
|
-
*/
|
|
259
|
-
async importData() {
|
|
260
|
-
const fileName = "cave.json";
|
|
261
|
-
let importedCaves;
|
|
262
|
-
try {
|
|
263
|
-
const fileContent = await this.fileManager.readFile(fileName);
|
|
264
|
-
importedCaves = JSON.parse(fileContent.toString("utf-8"));
|
|
265
|
-
if (!Array.isArray(importedCaves) || !importedCaves.length) throw new Error("导入文件格式无效或为空");
|
|
266
|
-
} catch (error) {
|
|
267
|
-
throw new Error(`读取导入文件失败: ${error.message}`);
|
|
268
|
-
}
|
|
269
|
-
const allDbCaves = await this.ctx.database.get("cave", {}, { fields: ["id"] });
|
|
270
|
-
const existingIds = new Set(allDbCaves.map((c) => c.id));
|
|
271
|
-
let maxId = allDbCaves.length > 0 ? Math.max(...allDbCaves.map((c) => c.id)) : 0;
|
|
272
|
-
const nonConflictingCaves = [];
|
|
273
|
-
const conflictingCaves = [];
|
|
274
|
-
let invalidCount = 0;
|
|
275
|
-
for (const importedCave of importedCaves) {
|
|
276
|
-
if (typeof importedCave.id !== "number" || !Array.isArray(importedCave.elements)) {
|
|
277
|
-
this.logger.warn(`回声洞(${importedCave.id})无效: ${JSON.stringify(importedCave)}`);
|
|
278
|
-
invalidCount++;
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (existingIds.has(importedCave.id)) {
|
|
282
|
-
conflictingCaves.push(importedCave);
|
|
283
|
-
} else {
|
|
284
|
-
nonConflictingCaves.push({ ...importedCave, status: "active" });
|
|
285
|
-
existingIds.add(importedCave.id);
|
|
286
|
-
maxId = Math.max(maxId, importedCave.id);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
const newCavesFromConflicts = conflictingCaves.map((cave) => {
|
|
290
|
-
maxId++;
|
|
291
|
-
this.logger.info(`回声洞(${cave.id})已转移至(${maxId})`);
|
|
292
|
-
return { ...cave, id: maxId, status: "active" };
|
|
293
|
-
});
|
|
294
|
-
const finalCavesToUpsert = [...nonConflictingCaves, ...newCavesFromConflicts];
|
|
295
|
-
if (finalCavesToUpsert.length > 0) await this.ctx.database.upsert("cave", finalCavesToUpsert);
|
|
296
|
-
return `成功导入 ${finalCavesToUpsert.length} 条数据`;
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
// src/PendManager.ts
|
|
301
|
-
var import_koishi2 = require("koishi");
|
|
302
|
-
|
|
303
209
|
// src/Utils.ts
|
|
304
210
|
var import_koishi = require("koishi");
|
|
305
211
|
var path2 = __toESM(require("path"));
|
|
@@ -576,30 +482,121 @@ async function performSimilarityChecks(ctx, config, hashManager, finalElementsFo
|
|
|
576
482
|
return { duplicate: false, textHashesToStore, imageHashesToStore };
|
|
577
483
|
}
|
|
578
484
|
__name(performSimilarityChecks, "performSimilarityChecks");
|
|
579
|
-
async function handleFileUploads(ctx, config, fileManager, logger2,
|
|
485
|
+
async function handleFileUploads(ctx, config, fileManager, logger2, cave, downloadedMedia, reusableIds, session) {
|
|
580
486
|
try {
|
|
581
|
-
if (aiManager) await aiManager.analyzeAndStore(cave, downloadedMedia);
|
|
582
487
|
await Promise.all(downloadedMedia.map((item) => fileManager.saveFile(item.fileName, item.buffer)));
|
|
583
488
|
const needsReview = config.enablePend && session.channelId !== config.adminChannel?.split(":")[1];
|
|
584
489
|
const finalStatus = needsReview ? "pending" : "active";
|
|
585
490
|
await ctx.database.upsert("cave", [{ id: cave.id, status: finalStatus }]);
|
|
586
|
-
|
|
587
|
-
const allHashesToInsert = [...textHashesToStore, ...imageHashesToStore].map((h5) => ({ ...h5, cave: cave.id }));
|
|
588
|
-
if (allHashesToInsert.length > 0) await ctx.database.upsert("cave_hash", allHashesToInsert);
|
|
589
|
-
}
|
|
590
|
-
if (finalStatus === "pending" && reviewManager) {
|
|
591
|
-
const [finalCave] = await ctx.database.get("cave", { id: cave.id });
|
|
592
|
-
if (finalCave) reviewManager.sendForPend(finalCave);
|
|
593
|
-
}
|
|
491
|
+
return finalStatus;
|
|
594
492
|
} catch (fileProcessingError) {
|
|
595
493
|
logger2.error(`回声洞(${cave.id})文件处理失败:`, fileProcessingError);
|
|
596
494
|
await ctx.database.upsert("cave", [{ id: cave.id, status: "delete" }]);
|
|
597
495
|
cleanupPendingDeletions(ctx, fileManager, logger2, reusableIds);
|
|
496
|
+
return "delete";
|
|
598
497
|
}
|
|
599
498
|
}
|
|
600
499
|
__name(handleFileUploads, "handleFileUploads");
|
|
500
|
+
function requireAdmin(session, config) {
|
|
501
|
+
if (session.cid !== config.adminChannel) return "此指令仅限在管理群组中使用";
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
__name(requireAdmin, "requireAdmin");
|
|
505
|
+
|
|
506
|
+
// src/DataManager.ts
|
|
507
|
+
var DataManager = class {
|
|
508
|
+
/**
|
|
509
|
+
* @constructor
|
|
510
|
+
* @param ctx Koishi 上下文,用于数据库操作。
|
|
511
|
+
* @param config 插件配置。
|
|
512
|
+
* @param fileManager 文件管理器实例。
|
|
513
|
+
* @param logger 日志记录器实例。
|
|
514
|
+
*/
|
|
515
|
+
constructor(ctx, config, fileManager, logger2) {
|
|
516
|
+
this.ctx = ctx;
|
|
517
|
+
this.config = config;
|
|
518
|
+
this.fileManager = fileManager;
|
|
519
|
+
this.logger = logger2;
|
|
520
|
+
}
|
|
521
|
+
static {
|
|
522
|
+
__name(this, "DataManager");
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* @description 注册 `.export` 和 `.import` 子命令。
|
|
526
|
+
* @param cave - 主 `cave` 命令实例。
|
|
527
|
+
*/
|
|
528
|
+
registerCommands(cave) {
|
|
529
|
+
const commandAction = /* @__PURE__ */ __name((action) => async ({ session }) => {
|
|
530
|
+
const adminError = requireAdmin(session, this.config);
|
|
531
|
+
if (adminError) return adminError;
|
|
532
|
+
try {
|
|
533
|
+
await session.send("正在处理,请稍候...");
|
|
534
|
+
return await action();
|
|
535
|
+
} catch (error) {
|
|
536
|
+
this.logger.error("数据操作时发生错误:", error);
|
|
537
|
+
return `操作失败: ${error.message}`;
|
|
538
|
+
}
|
|
539
|
+
}, "commandAction");
|
|
540
|
+
cave.subcommand(".export", "导出回声洞数据", { hidden: true, authority: 4 }).usage("将所有回声洞数据导出到 cave.json 中。").action(commandAction(() => this.exportData()));
|
|
541
|
+
cave.subcommand(".import", "导入回声洞数据", { hidden: true, authority: 4 }).usage("从 cave.json 中导入回声洞数据。").action(commandAction(() => this.importData()));
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* @description 导出所有 'active' 状态的回声洞数据到 `cave.json`。
|
|
545
|
+
* @returns 描述导出结果的消息字符串。
|
|
546
|
+
*/
|
|
547
|
+
async exportData() {
|
|
548
|
+
const fileName = "cave.json";
|
|
549
|
+
const cavesToExport = await this.ctx.database.get("cave", { status: "active" });
|
|
550
|
+
await this.fileManager.saveFile(fileName, Buffer.from(JSON.stringify(cavesToExport, null, 2)));
|
|
551
|
+
return `成功导出 ${cavesToExport.length} 条数据`;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* @description 从 `cave.json` 文件导入回声洞数据。
|
|
555
|
+
* @returns 描述导入结果的消息字符串。
|
|
556
|
+
*/
|
|
557
|
+
async importData() {
|
|
558
|
+
const fileName = "cave.json";
|
|
559
|
+
let importedCaves;
|
|
560
|
+
try {
|
|
561
|
+
const fileContent = await this.fileManager.readFile(fileName);
|
|
562
|
+
importedCaves = JSON.parse(fileContent.toString("utf-8"));
|
|
563
|
+
if (!Array.isArray(importedCaves) || !importedCaves.length) throw new Error("导入文件格式无效或为空");
|
|
564
|
+
} catch (error) {
|
|
565
|
+
throw new Error(`读取导入文件失败: ${error.message}`);
|
|
566
|
+
}
|
|
567
|
+
const allDbCaves = await this.ctx.database.get("cave", {}, { fields: ["id"] });
|
|
568
|
+
const existingIds = new Set(allDbCaves.map((c) => c.id));
|
|
569
|
+
let maxId = allDbCaves.length > 0 ? Math.max(...allDbCaves.map((c) => c.id)) : 0;
|
|
570
|
+
const nonConflictingCaves = [];
|
|
571
|
+
const conflictingCaves = [];
|
|
572
|
+
let invalidCount = 0;
|
|
573
|
+
for (const importedCave of importedCaves) {
|
|
574
|
+
if (typeof importedCave.id !== "number" || !Array.isArray(importedCave.elements)) {
|
|
575
|
+
this.logger.warn(`回声洞(${importedCave.id})无效: ${JSON.stringify(importedCave)}`);
|
|
576
|
+
invalidCount++;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
if (existingIds.has(importedCave.id)) {
|
|
580
|
+
conflictingCaves.push(importedCave);
|
|
581
|
+
} else {
|
|
582
|
+
nonConflictingCaves.push({ ...importedCave, status: "active" });
|
|
583
|
+
existingIds.add(importedCave.id);
|
|
584
|
+
maxId = Math.max(maxId, importedCave.id);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const newCavesFromConflicts = conflictingCaves.map((cave) => {
|
|
588
|
+
maxId++;
|
|
589
|
+
this.logger.info(`回声洞(${cave.id})已转移至(${maxId})`);
|
|
590
|
+
return { ...cave, id: maxId, status: "active" };
|
|
591
|
+
});
|
|
592
|
+
const finalCavesToUpsert = [...nonConflictingCaves, ...newCavesFromConflicts];
|
|
593
|
+
if (finalCavesToUpsert.length > 0) await this.ctx.database.upsert("cave", finalCavesToUpsert);
|
|
594
|
+
return `成功导入 ${finalCavesToUpsert.length} 条数据`;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
601
597
|
|
|
602
598
|
// src/PendManager.ts
|
|
599
|
+
var import_koishi2 = require("koishi");
|
|
603
600
|
var PendManager = class {
|
|
604
601
|
/**
|
|
605
602
|
* @param ctx Koishi 上下文。
|
|
@@ -623,12 +620,8 @@ var PendManager = class {
|
|
|
623
620
|
* @param cave - 主 `cave` 命令实例。
|
|
624
621
|
*/
|
|
625
622
|
registerCommands(cave) {
|
|
626
|
-
const requireAdmin = /* @__PURE__ */ __name((session) => {
|
|
627
|
-
if (session.channelId !== this.config.adminChannel?.split(":")[1]) return "此指令仅限在管理群组中使用";
|
|
628
|
-
return null;
|
|
629
|
-
}, "requireAdmin");
|
|
630
623
|
const pend = cave.subcommand(".pend [id:posint]", "审核回声洞", { hidden: true }).usage("查询待审核的回声洞列表,或指定 ID 查看对应待审核的回声洞。").action(async ({ session }, id) => {
|
|
631
|
-
const adminError = requireAdmin(session);
|
|
624
|
+
const adminError = requireAdmin(session, this.config);
|
|
632
625
|
if (adminError) return adminError;
|
|
633
626
|
if (id) {
|
|
634
627
|
const [targetCave] = await this.ctx.database.get("cave", { id });
|
|
@@ -644,7 +637,7 @@ var PendManager = class {
|
|
|
644
637
|
${pendingCaves.map((c) => c.id).join("|")}`;
|
|
645
638
|
});
|
|
646
639
|
const createPendAction = /* @__PURE__ */ __name((actionType) => async ({ session }, ...ids) => {
|
|
647
|
-
const adminError = requireAdmin(session);
|
|
640
|
+
const adminError = requireAdmin(session, this.config);
|
|
648
641
|
if (adminError) return adminError;
|
|
649
642
|
let idsToProcess = ids;
|
|
650
643
|
if (idsToProcess.length === 0) {
|
|
@@ -1015,7 +1008,8 @@ var AIManager = class {
|
|
|
1015
1008
|
*/
|
|
1016
1009
|
registerCommands(cave) {
|
|
1017
1010
|
cave.subcommand(".ai", "分析回声洞", { hidden: true, authority: 4 }).usage("分析尚未分析的回声洞,补全回声洞记录。").action(async ({ session }) => {
|
|
1018
|
-
|
|
1011
|
+
const adminError = requireAdmin(session, this.config);
|
|
1012
|
+
if (adminError) return adminError;
|
|
1019
1013
|
try {
|
|
1020
1014
|
const allCaves = await this.ctx.database.get("cave", { status: "active" });
|
|
1021
1015
|
const analyzedCaveIds = new Set((await this.ctx.database.get("cave_meta", {})).map((meta) => meta.cave));
|
|
@@ -1078,7 +1072,8 @@ var AIManager = class {
|
|
|
1078
1072
|
}
|
|
1079
1073
|
if (potentialDuplicates.length === 0) return { duplicate: false };
|
|
1080
1074
|
const { payload } = await this.prepareDedupePayload(newElements, potentialDuplicates);
|
|
1081
|
-
const
|
|
1075
|
+
const fullUrl = `${this.config.aiEndpoint}/models/${this.config.aiModel}:generateContent?key=${this.config.aiApiKey}`;
|
|
1076
|
+
const response = await this.http.post(fullUrl, payload, { headers: { "Content-Type": "application/json" }, timeout: 9e4 });
|
|
1082
1077
|
return this.parseDedupeResponse(response);
|
|
1083
1078
|
} catch (error) {
|
|
1084
1079
|
this.logger.error("查重回声洞出错:", error);
|
|
@@ -1097,6 +1092,7 @@ var AIManager = class {
|
|
|
1097
1092
|
if (analysisResult) await this.ctx.database.upsert("cave_meta", [{ cave: cave.id, ...analysisResult }]);
|
|
1098
1093
|
} catch (error) {
|
|
1099
1094
|
this.logger.error(`分析回声洞(${cave.id})失败:`, error);
|
|
1095
|
+
throw error;
|
|
1100
1096
|
}
|
|
1101
1097
|
}
|
|
1102
1098
|
/**
|
|
@@ -1109,7 +1105,8 @@ var AIManager = class {
|
|
|
1109
1105
|
async getAnalysis(elements, mediaToSave, mediaBuffers) {
|
|
1110
1106
|
const { payload } = await this.preparePayload(this.config.AnalysePrompt, this.config.aiAnalyseSchema, elements, mediaToSave, mediaBuffers);
|
|
1111
1107
|
if (!payload.contents) return null;
|
|
1112
|
-
const
|
|
1108
|
+
const fullUrl = `${this.config.aiEndpoint}/models/${this.config.aiModel}:generateContent?key=${this.config.aiApiKey}`;
|
|
1109
|
+
const response = await this.http.post(fullUrl, payload, { headers: { "Content-Type": "application/json" }, timeout: 6e4 });
|
|
1113
1110
|
return this.parseAnalysisResponse(response);
|
|
1114
1111
|
}
|
|
1115
1112
|
/**
|
|
@@ -1160,9 +1157,17 @@ var AIManager = class {
|
|
|
1160
1157
|
if (parts.length <= 1) return { payload: {} };
|
|
1161
1158
|
try {
|
|
1162
1159
|
const schema = JSON.parse(schemaString);
|
|
1163
|
-
return {
|
|
1160
|
+
return {
|
|
1161
|
+
payload: {
|
|
1162
|
+
contents: [{ parts }],
|
|
1163
|
+
generationConfig: {
|
|
1164
|
+
responseMimeType: "application/json",
|
|
1165
|
+
responseSchema: schema
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1164
1169
|
} catch (error) {
|
|
1165
|
-
this.logger.error("
|
|
1170
|
+
this.logger.error("分析 JSON Schema 解析失败:", error);
|
|
1166
1171
|
return { payload: {} };
|
|
1167
1172
|
}
|
|
1168
1173
|
}
|
|
@@ -1178,15 +1183,20 @@ var AIManager = class {
|
|
|
1178
1183
|
new_content: { text: formatContent(newElements) },
|
|
1179
1184
|
existing_contents: existingCaves.map((cave) => ({ id: cave.id, text: formatContent(cave.elements) }))
|
|
1180
1185
|
});
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
以下是需要处理的数据:
|
|
1184
|
-
${payloadContent}`;
|
|
1186
|
+
const parts = [{ text: this.config.aiCheckPrompt }, { text: payloadContent }];
|
|
1185
1187
|
try {
|
|
1186
1188
|
const schema = JSON.parse(this.config.aiCheckSchema);
|
|
1187
|
-
return {
|
|
1189
|
+
return {
|
|
1190
|
+
payload: {
|
|
1191
|
+
contents: [{ parts }],
|
|
1192
|
+
generationConfig: {
|
|
1193
|
+
responseMimeType: "application/json",
|
|
1194
|
+
responseSchema: schema
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1188
1198
|
} catch (error) {
|
|
1189
|
-
this.logger.error("
|
|
1199
|
+
this.logger.error("查重 JSON Schema 解析失败:", error);
|
|
1190
1200
|
return { payload: {} };
|
|
1191
1201
|
}
|
|
1192
1202
|
}
|
|
@@ -1197,7 +1207,8 @@ ${payloadContent}`;
|
|
|
1197
1207
|
*/
|
|
1198
1208
|
parseAnalysisResponse(response) {
|
|
1199
1209
|
try {
|
|
1200
|
-
|
|
1210
|
+
if (!response?.candidates?.[0]?.content?.parts?.[0]?.text) throw new Error("分析响应解析失败");
|
|
1211
|
+
const content = response.candidates[0].content.parts[0].text;
|
|
1201
1212
|
const parsed = JSON.parse(content);
|
|
1202
1213
|
const keywords = Array.isArray(parsed.keywords) ? parsed.keywords : [];
|
|
1203
1214
|
return {
|
|
@@ -1207,7 +1218,7 @@ ${payloadContent}`;
|
|
|
1207
1218
|
};
|
|
1208
1219
|
} catch (error) {
|
|
1209
1220
|
this.logger.error("分析响应解析失败:", error, "原始响应:", JSON.stringify(response));
|
|
1210
|
-
|
|
1221
|
+
throw error;
|
|
1211
1222
|
}
|
|
1212
1223
|
}
|
|
1213
1224
|
/**
|
|
@@ -1217,13 +1228,14 @@ ${payloadContent}`;
|
|
|
1217
1228
|
*/
|
|
1218
1229
|
parseDedupeResponse(response) {
|
|
1219
1230
|
try {
|
|
1220
|
-
|
|
1231
|
+
if (!response?.candidates?.[0]?.content?.parts?.[0]?.text) throw new Error("查重响应解析失败");
|
|
1232
|
+
const content = response.candidates[0].content.parts[0].text;
|
|
1221
1233
|
const parsed = JSON.parse(content);
|
|
1222
1234
|
if (parsed.duplicate === true && parsed.id) return { duplicate: true, id: Number(parsed.id) };
|
|
1223
1235
|
return { duplicate: false };
|
|
1224
1236
|
} catch (error) {
|
|
1225
1237
|
this.logger.error("查重响应解析失败:", error, "原始响应:", JSON.stringify(response));
|
|
1226
|
-
|
|
1238
|
+
throw error;
|
|
1227
1239
|
}
|
|
1228
1240
|
}
|
|
1229
1241
|
};
|
|
@@ -1405,22 +1417,25 @@ function apply(ctx, config) {
|
|
|
1405
1417
|
}
|
|
1406
1418
|
const userName = (config.enableName ? await profileManager.getNickname(session.userId) : null) || session.username;
|
|
1407
1419
|
const needsReview = config.enablePend && session.channelId !== config.adminChannel?.split(":")[1];
|
|
1408
|
-
|
|
1420
|
+
let finalStatus = hasMedia ? "preload" : needsReview ? "pending" : "active";
|
|
1409
1421
|
const newCave = await ctx.database.create("cave", {
|
|
1410
1422
|
id: newId,
|
|
1411
1423
|
elements: finalElementsForDb,
|
|
1412
1424
|
channelId: session.channelId,
|
|
1413
1425
|
userId: session.userId,
|
|
1414
1426
|
userName,
|
|
1415
|
-
status:
|
|
1427
|
+
status: finalStatus,
|
|
1416
1428
|
time: creationTime
|
|
1417
1429
|
});
|
|
1418
|
-
if (hasMedia)
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
if (aiManager) await aiManager.analyzeAndStore(newCave);
|
|
1422
|
-
if (hashManager
|
|
1423
|
-
|
|
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);
|
|
1424
1439
|
}
|
|
1425
1440
|
return needsReview ? `提交成功,序号为(${newCave.id})` : `添加成功,序号为(${newCave.id})`;
|
|
1426
1441
|
} catch (error) {
|