koishi-plugin-best-cave 2.7.2 → 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 +7 -0
- package/lib/index.js +109 -105
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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
|
|
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 };
|