koishi-plugin-best-cave 2.7.1 → 2.7.3

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
@@ -88,3 +88,10 @@ export declare function handleFileUploads(ctx: Context, config: Config, fileMana
88
88
  fileName: string;
89
89
  buffer: Buffer;
90
90
  }[], reusableIds: Set<number>, session: Session, hashManager: HashManager, textHashesToStore: Omit<CaveHashObject, 'cave'>[], imageHashesToStore: Omit<CaveHashObject, 'cave'>[], aiManager: AIManager | null): Promise<void>;
91
+ /**
92
+ * @description 校验会话是否来自指定的管理群组。
93
+ * @param session 当前会话。
94
+ * @param config 插件配置。
95
+ * @returns 如果校验不通过,返回错误信息字符串;如果通过,返回 null。
96
+ */
97
+ 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"));
@@ -598,8 +504,106 @@ async function handleFileUploads(ctx, config, fileManager, logger2, reviewManage
598
504
  }
599
505
  }
600
506
  __name(handleFileUploads, "handleFileUploads");
507
+ function requireAdmin(session, config) {
508
+ if (session.cid !== config.adminChannel) return "此指令仅限在管理群组中使用";
509
+ return null;
510
+ }
511
+ __name(requireAdmin, "requireAdmin");
512
+
513
+ // src/DataManager.ts
514
+ var DataManager = class {
515
+ /**
516
+ * @constructor
517
+ * @param ctx Koishi 上下文,用于数据库操作。
518
+ * @param config 插件配置。
519
+ * @param fileManager 文件管理器实例。
520
+ * @param logger 日志记录器实例。
521
+ */
522
+ constructor(ctx, config, fileManager, logger2) {
523
+ this.ctx = ctx;
524
+ this.config = config;
525
+ this.fileManager = fileManager;
526
+ this.logger = logger2;
527
+ }
528
+ static {
529
+ __name(this, "DataManager");
530
+ }
531
+ /**
532
+ * @description 注册 `.export` 和 `.import` 子命令。
533
+ * @param cave - 主 `cave` 命令实例。
534
+ */
535
+ registerCommands(cave) {
536
+ const commandAction = /* @__PURE__ */ __name((action) => async ({ session }) => {
537
+ const adminError = requireAdmin(session, this.config);
538
+ if (adminError) return adminError;
539
+ try {
540
+ await session.send("正在处理,请稍候...");
541
+ return await action();
542
+ } catch (error) {
543
+ this.logger.error("数据操作时发生错误:", error);
544
+ return `操作失败: ${error.message}`;
545
+ }
546
+ }, "commandAction");
547
+ cave.subcommand(".export", "导出回声洞数据", { hidden: true, authority: 4 }).usage("将所有回声洞数据导出到 cave.json 中。").action(commandAction(() => this.exportData()));
548
+ cave.subcommand(".import", "导入回声洞数据", { hidden: true, authority: 4 }).usage("从 cave.json 中导入回声洞数据。").action(commandAction(() => this.importData()));
549
+ }
550
+ /**
551
+ * @description 导出所有 'active' 状态的回声洞数据到 `cave.json`。
552
+ * @returns 描述导出结果的消息字符串。
553
+ */
554
+ async exportData() {
555
+ const fileName = "cave.json";
556
+ const cavesToExport = await this.ctx.database.get("cave", { status: "active" });
557
+ await this.fileManager.saveFile(fileName, Buffer.from(JSON.stringify(cavesToExport, null, 2)));
558
+ return `成功导出 ${cavesToExport.length} 条数据`;
559
+ }
560
+ /**
561
+ * @description 从 `cave.json` 文件导入回声洞数据。
562
+ * @returns 描述导入结果的消息字符串。
563
+ */
564
+ async importData() {
565
+ const fileName = "cave.json";
566
+ let importedCaves;
567
+ try {
568
+ const fileContent = await this.fileManager.readFile(fileName);
569
+ importedCaves = JSON.parse(fileContent.toString("utf-8"));
570
+ if (!Array.isArray(importedCaves) || !importedCaves.length) throw new Error("导入文件格式无效或为空");
571
+ } catch (error) {
572
+ throw new Error(`读取导入文件失败: ${error.message}`);
573
+ }
574
+ const allDbCaves = await this.ctx.database.get("cave", {}, { fields: ["id"] });
575
+ const existingIds = new Set(allDbCaves.map((c) => c.id));
576
+ let maxId = allDbCaves.length > 0 ? Math.max(...allDbCaves.map((c) => c.id)) : 0;
577
+ const nonConflictingCaves = [];
578
+ const conflictingCaves = [];
579
+ let invalidCount = 0;
580
+ for (const importedCave of importedCaves) {
581
+ if (typeof importedCave.id !== "number" || !Array.isArray(importedCave.elements)) {
582
+ this.logger.warn(`回声洞(${importedCave.id})无效: ${JSON.stringify(importedCave)}`);
583
+ invalidCount++;
584
+ continue;
585
+ }
586
+ if (existingIds.has(importedCave.id)) {
587
+ conflictingCaves.push(importedCave);
588
+ } else {
589
+ nonConflictingCaves.push({ ...importedCave, status: "active" });
590
+ existingIds.add(importedCave.id);
591
+ maxId = Math.max(maxId, importedCave.id);
592
+ }
593
+ }
594
+ const newCavesFromConflicts = conflictingCaves.map((cave) => {
595
+ maxId++;
596
+ this.logger.info(`回声洞(${cave.id})已转移至(${maxId})`);
597
+ return { ...cave, id: maxId, status: "active" };
598
+ });
599
+ const finalCavesToUpsert = [...nonConflictingCaves, ...newCavesFromConflicts];
600
+ if (finalCavesToUpsert.length > 0) await this.ctx.database.upsert("cave", finalCavesToUpsert);
601
+ return `成功导入 ${finalCavesToUpsert.length} 条数据`;
602
+ }
603
+ };
601
604
 
602
605
  // src/PendManager.ts
606
+ var import_koishi2 = require("koishi");
603
607
  var PendManager = class {
604
608
  /**
605
609
  * @param ctx Koishi 上下文。
@@ -623,12 +627,8 @@ var PendManager = class {
623
627
  * @param cave - 主 `cave` 命令实例。
624
628
  */
625
629
  registerCommands(cave) {
626
- const requireAdmin = /* @__PURE__ */ __name((session) => {
627
- if (session.channelId !== this.config.adminChannel?.split(":")[1]) return "此指令仅限在管理群组中使用";
628
- return null;
629
- }, "requireAdmin");
630
630
  const pend = cave.subcommand(".pend [id:posint]", "审核回声洞", { hidden: true }).usage("查询待审核的回声洞列表,或指定 ID 查看对应待审核的回声洞。").action(async ({ session }, id) => {
631
- const adminError = requireAdmin(session);
631
+ const adminError = requireAdmin(session, this.config);
632
632
  if (adminError) return adminError;
633
633
  if (id) {
634
634
  const [targetCave] = await this.ctx.database.get("cave", { id });
@@ -644,7 +644,7 @@ var PendManager = class {
644
644
  ${pendingCaves.map((c) => c.id).join("|")}`;
645
645
  });
646
646
  const createPendAction = /* @__PURE__ */ __name((actionType) => async ({ session }, ...ids) => {
647
- const adminError = requireAdmin(session);
647
+ const adminError = requireAdmin(session, this.config);
648
648
  if (adminError) return adminError;
649
649
  let idsToProcess = ids;
650
650
  if (idsToProcess.length === 0) {
@@ -1015,7 +1015,8 @@ var AIManager = class {
1015
1015
  */
1016
1016
  registerCommands(cave) {
1017
1017
  cave.subcommand(".ai", "分析回声洞", { hidden: true, authority: 4 }).usage("分析尚未分析的回声洞,补全回声洞记录。").action(async ({ session }) => {
1018
- if (session.channelId !== this.config.adminChannel?.split(":")) return "此指令仅限在管理群组中使用";
1018
+ const adminError = requireAdmin(session, this.config);
1019
+ if (adminError) return adminError;
1019
1020
  try {
1020
1021
  const allCaves = await this.ctx.database.get("cave", { status: "active" });
1021
1022
  const analyzedCaveIds = new Set((await this.ctx.database.get("cave_meta", {})).map((meta) => meta.cave));
@@ -1078,7 +1079,8 @@ var AIManager = class {
1078
1079
  }
1079
1080
  if (potentialDuplicates.length === 0) return { duplicate: false };
1080
1081
  const { payload } = await this.prepareDedupePayload(newElements, potentialDuplicates);
1081
- const response = await this.http.post(`${this.config.aiEndpoint}:generateContent?key=${this.config.aiApiKey}`, payload, { headers: { "Content-Type": "application/json" }, timeout: 9e4 });
1082
+ const fullUrl = `${this.config.aiEndpoint}/models/${this.config.aiModel}:generateContent?key=${this.config.aiApiKey}`;
1083
+ const response = await this.http.post(fullUrl, payload, { headers: { "Content-Type": "application/json" }, timeout: 9e4 });
1082
1084
  return this.parseDedupeResponse(response);
1083
1085
  } catch (error) {
1084
1086
  this.logger.error("查重回声洞出错:", error);
@@ -1097,6 +1099,7 @@ var AIManager = class {
1097
1099
  if (analysisResult) await this.ctx.database.upsert("cave_meta", [{ cave: cave.id, ...analysisResult }]);
1098
1100
  } catch (error) {
1099
1101
  this.logger.error(`分析回声洞(${cave.id})失败:`, error);
1102
+ throw error;
1100
1103
  }
1101
1104
  }
1102
1105
  /**
@@ -1109,7 +1112,8 @@ var AIManager = class {
1109
1112
  async getAnalysis(elements, mediaToSave, mediaBuffers) {
1110
1113
  const { payload } = await this.preparePayload(this.config.AnalysePrompt, this.config.aiAnalyseSchema, elements, mediaToSave, mediaBuffers);
1111
1114
  if (!payload.contents) return null;
1112
- const response = await this.http.post(`${this.config.aiEndpoint}:generateContent?key=${this.config.aiApiKey}`, payload, { headers: { "Content-Type": "application/json" }, timeout: 6e4 });
1115
+ const fullUrl = `${this.config.aiEndpoint}/models/${this.config.aiModel}:generateContent?key=${this.config.aiApiKey}`;
1116
+ const response = await this.http.post(fullUrl, payload, { headers: { "Content-Type": "application/json" }, timeout: 6e4 });
1113
1117
  return this.parseAnalysisResponse(response);
1114
1118
  }
1115
1119
  /**
@@ -1197,7 +1201,7 @@ ${payloadContent}`;
1197
1201
  */
1198
1202
  parseAnalysisResponse(response) {
1199
1203
  try {
1200
- const content = response.candidates.content.parts.text;
1204
+ const content = response.candidates[0].content.parts[0].text;
1201
1205
  const parsed = JSON.parse(content);
1202
1206
  const keywords = Array.isArray(parsed.keywords) ? parsed.keywords : [];
1203
1207
  return {
@@ -1217,7 +1221,7 @@ ${payloadContent}`;
1217
1221
  */
1218
1222
  parseDedupeResponse(response) {
1219
1223
  try {
1220
- const content = response.candidates.content.parts.text;
1224
+ const content = response.candidates[0].content.parts[0].text;
1221
1225
  const parsed = JSON.parse(content);
1222
1226
  if (parsed.duplicate === true && parsed.id) return { duplicate: true, id: Number(parsed.id) };
1223
1227
  return { duplicate: false };
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.1",
4
+ "version": "2.7.3",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],