koishi-plugin-best-cave 1.5.4 → 1.5.6

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/index.d.ts CHANGED
@@ -1,6 +1,30 @@
1
1
  import { Context, Schema } from 'koishi';
2
2
  export declare const name = "best-cave";
3
3
  export declare const inject: string[];
4
+ export interface BaseElement {
5
+ type: 'text' | 'img' | 'video';
6
+ index: number;
7
+ }
8
+ export interface TextElement extends BaseElement {
9
+ type: 'text';
10
+ content: string;
11
+ }
12
+ export interface MediaElement extends BaseElement {
13
+ type: 'img' | 'video';
14
+ file?: string;
15
+ fileName?: string;
16
+ fileSize?: string;
17
+ filePath?: string;
18
+ }
19
+ export type Element = TextElement | MediaElement;
20
+ export interface CaveObject {
21
+ cave_id: number;
22
+ elements: Element[];
23
+ contributor_number: string;
24
+ contributor_name: string;
25
+ }
26
+ export interface PendingCave extends CaveObject {
27
+ }
4
28
  /**
5
29
  * 插件配置项
6
30
  * @type {Schema}
package/lib/index.js CHANGED
@@ -54,10 +54,10 @@ __export(src_exports, {
54
54
  });
55
55
  module.exports = __toCommonJS(src_exports);
56
56
  var import_koishi6 = require("koishi");
57
- var fs6 = __toESM(require("fs"));
58
- var path6 = __toESM(require("path"));
57
+ var fs7 = __toESM(require("fs"));
58
+ var path7 = __toESM(require("path"));
59
59
 
60
- // src/utils/FileHandle.ts
60
+ // src/utils/FileHandler.ts
61
61
  var fs = __toESM(require("fs"));
62
62
  var path = __toESM(require("path"));
63
63
  var import_koishi = require("koishi");
@@ -216,7 +216,7 @@ var FileHandler = class {
216
216
  }
217
217
  };
218
218
 
219
- // src/utils/IdManage.ts
219
+ // src/utils/IdManager.ts
220
220
  var fs2 = __toESM(require("fs"));
221
221
  var path2 = __toESM(require("path"));
222
222
  var import_koishi2 = require("koishi");
@@ -440,12 +440,12 @@ var IdManager = class {
440
440
  }
441
441
  };
442
442
 
443
- // src/utils/HashManage.ts
443
+ // src/utils/HashManager.ts
444
444
  var import_koishi3 = require("koishi");
445
445
  var fs3 = __toESM(require("fs"));
446
446
  var path3 = __toESM(require("path"));
447
447
 
448
- // src/utils/ContentHash.ts
448
+ // src/utils/ContentHasher.ts
449
449
  var import_sharp = __toESM(require("sharp"));
450
450
  var ContentHasher = class {
451
451
  static {
@@ -605,22 +605,13 @@ var ContentHasher = class {
605
605
  }
606
606
  return hash.toString(36);
607
607
  }
608
- /**
609
- * 批量比较一个新哈希值与多个已存在哈希值的相似度
610
- * @param newHash - 新的哈希值
611
- * @param existingHashes - 已存在的哈希值数组
612
- * @returns 相似度数组,每个元素对应一个已存在哈希值的相似度
613
- */
614
- static batchCompareSimilarity(newHash, existingHashes) {
615
- return existingHashes.map((hash) => this.calculateSimilarity(newHash, hash));
616
- }
617
608
  };
618
609
 
619
- // src/utils/HashManage.ts
610
+ // src/utils/HashManager.ts
620
611
  var import_util = require("util");
621
612
  var logger3 = new import_koishi3.Logger("HashManager");
622
613
  var readFileAsync = (0, import_util.promisify)(fs3.readFile);
623
- var ContentHashManager = class _ContentHashManager {
614
+ var HashManager = class _HashManager {
624
615
  /**
625
616
  * 初始化HashManager实例
626
617
  * @param caveDir 回声洞数据目录路径
@@ -629,7 +620,7 @@ var ContentHashManager = class _ContentHashManager {
629
620
  this.caveDir = caveDir;
630
621
  }
631
622
  static {
632
- __name(this, "ContentHashManager");
623
+ __name(this, "HashManager");
633
624
  }
634
625
  // 哈希数据文件名
635
626
  static HASH_FILE = "hash.json";
@@ -643,13 +634,13 @@ var ContentHashManager = class _ContentHashManager {
643
634
  // 初始化状态标志
644
635
  initialized = false;
645
636
  get filePath() {
646
- return path3.join(this.caveDir, _ContentHashManager.HASH_FILE);
637
+ return path3.join(this.caveDir, _HashManager.HASH_FILE);
647
638
  }
648
639
  get resourceDir() {
649
640
  return path3.join(this.caveDir, "resources");
650
641
  }
651
642
  get caveFilePath() {
652
- return path3.join(this.caveDir, _ContentHashManager.CAVE_FILE);
643
+ return path3.join(this.caveDir, _HashManager.CAVE_FILE);
653
644
  }
654
645
  /**
655
646
  * 初始化哈希存储
@@ -973,7 +964,7 @@ var ContentHashManager = class _ContentHashManager {
973
964
  * @param batchSize 批处理大小
974
965
  * @private
975
966
  */
976
- async processBatch(items, processor, batchSize = _ContentHashManager.BATCH_SIZE) {
967
+ async processBatch(items, processor, batchSize = _HashManager.BATCH_SIZE) {
977
968
  for (let i = 0; i < items.length; i += batchSize) {
978
969
  const batch = items.slice(i, i + batchSize);
979
970
  await Promise.all(
@@ -989,15 +980,14 @@ var ContentHashManager = class _ContentHashManager {
989
980
  }
990
981
  };
991
982
 
992
- // src/utils/AuditManage.ts
983
+ // src/utils/AuditHandler.ts
993
984
  var import_koishi4 = require("koishi");
994
985
  var fs4 = __toESM(require("fs"));
995
986
  var path4 = __toESM(require("path"));
996
987
  var AuditManager = class {
997
- constructor(ctx, config, caveDir, idManager) {
988
+ constructor(ctx, config, idManager) {
998
989
  this.ctx = ctx;
999
990
  this.config = config;
1000
- this.caveDir = caveDir;
1001
991
  this.idManager = idManager;
1002
992
  }
1003
993
  static {
@@ -1188,11 +1178,70 @@ ${session.text("commands.cave.audit.from")}${cave.contributor_number}`;
1188
1178
  }
1189
1179
  };
1190
1180
 
1191
- // src/utils/MediaHandle.ts
1181
+ // src/utils/MediaHandler.ts
1192
1182
  var import_koishi5 = require("koishi");
1193
1183
  var fs5 = __toESM(require("fs"));
1194
1184
  var path5 = __toESM(require("path"));
1195
1185
  var logger4 = new import_koishi5.Logger("MediaHandle");
1186
+ async function buildMessage(cave, resourceDir, session) {
1187
+ if (!cave?.elements?.length) {
1188
+ return session.text("commands.cave.error.noContent");
1189
+ }
1190
+ const videoElement = cave.elements.find((el) => el.type === "video");
1191
+ const nonVideoElements = cave.elements.filter((el) => el.type !== "video").sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1192
+ if (videoElement?.file) {
1193
+ const basicInfo = [
1194
+ session.text("commands.cave.message.caveTitle", [cave.cave_id]),
1195
+ session.text("commands.cave.message.contributorSuffix", [cave.contributor_name])
1196
+ ].join("\n");
1197
+ await session?.send(basicInfo);
1198
+ const filePath = path5.join(resourceDir, videoElement.file);
1199
+ const base64Data = await processMediaFile(filePath, "video");
1200
+ if (base64Data && session) {
1201
+ await session.send((0, import_koishi5.h)("video", { src: base64Data }));
1202
+ }
1203
+ return "";
1204
+ }
1205
+ const lines = [session.text("commands.cave.message.caveTitle", [cave.cave_id])];
1206
+ for (const element of nonVideoElements) {
1207
+ if (element.type === "text") {
1208
+ lines.push(element.content);
1209
+ } else if (element.type === "img" && element.file) {
1210
+ const filePath = path5.join(resourceDir, element.file);
1211
+ const base64Data = await processMediaFile(filePath, "image");
1212
+ if (base64Data) {
1213
+ lines.push((0, import_koishi5.h)("image", { src: base64Data }));
1214
+ }
1215
+ }
1216
+ }
1217
+ lines.push(session.text("commands.cave.message.contributorSuffix", [cave.contributor_name]));
1218
+ return lines.join("\n");
1219
+ }
1220
+ __name(buildMessage, "buildMessage");
1221
+ async function sendMessage(session, key, params = [], isTemp = true, timeout = 1e4) {
1222
+ try {
1223
+ const msg = await session.send(session.text(key, params));
1224
+ if (isTemp && msg) {
1225
+ setTimeout(async () => {
1226
+ try {
1227
+ await session.bot.deleteMessage(session.channelId, msg);
1228
+ } catch (error) {
1229
+ logger4.debug(`Failed to delete temporary message: ${error.message}`);
1230
+ }
1231
+ }, timeout);
1232
+ }
1233
+ } catch (error) {
1234
+ logger4.error(`Failed to send message: ${error.message}`);
1235
+ }
1236
+ return "";
1237
+ }
1238
+ __name(sendMessage, "sendMessage");
1239
+ async function processMediaFile(filePath, type) {
1240
+ const data = await fs5.promises.readFile(filePath).catch(() => null);
1241
+ if (!data) return null;
1242
+ return `data:${type}/${type === "image" ? "png" : "mp4"};base64,${data.toString("base64")}`;
1243
+ }
1244
+ __name(processMediaFile, "processMediaFile");
1196
1245
  async function extractMediaContent(originalContent, config, session) {
1197
1246
  const textParts = originalContent.split(/<(img|video)[^>]+>/).map((text, idx) => text.trim() && {
1198
1247
  type: "text",
@@ -1236,7 +1285,7 @@ async function extractMediaContent(originalContent, config, session) {
1236
1285
  __name(extractMediaContent, "extractMediaContent");
1237
1286
  async function saveMedia(urls, fileNames, resourceDir, caveId, mediaType, config, ctx, session, buffers) {
1238
1287
  const accept = mediaType === "img" ? "image/*" : "video/*";
1239
- const hashStorage = new ContentHashManager(path5.join(ctx.baseDir, "data", "cave"));
1288
+ const hashStorage = new HashManager(path5.join(ctx.baseDir, "data", "cave"));
1240
1289
  await hashStorage.initialize();
1241
1290
  const downloadTasks = urls.map(async (url, i) => {
1242
1291
  const fileName = fileNames[i];
@@ -1317,20 +1366,99 @@ async function saveMedia(urls, fileNames, resourceDir, caveId, mediaType, config
1317
1366
  return Promise.all(downloadTasks);
1318
1367
  }
1319
1368
  __name(saveMedia, "saveMedia");
1320
- async function buildMessage(cave, resourceDir, session) {
1321
- if (!cave?.elements?.length) {
1322
- return session.text("commands.cave.error.noContent");
1369
+
1370
+ // src/utils/ProcessHandle.ts
1371
+ var fs6 = __toESM(require("fs"));
1372
+ var path6 = __toESM(require("path"));
1373
+ async function processList(session, config, idManager, userId, pageNum = 1) {
1374
+ const stats = idManager.getStats();
1375
+ if (userId && userId in stats) {
1376
+ const ids = stats[userId];
1377
+ return session.text("commands.cave.list.totalItems", [userId, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1378
+ }
1379
+ const lines = Object.entries(stats).map(([cid, ids]) => {
1380
+ return session.text("commands.cave.list.totalItems", [cid, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1381
+ });
1382
+ const totalSubmissions = Object.values(stats).reduce((sum, arr) => sum + arr.length, 0);
1383
+ if (config.enablePagination) {
1384
+ const itemsPerPage = config.itemsPerPage;
1385
+ const totalPages = Math.max(1, Math.ceil(lines.length / itemsPerPage));
1386
+ pageNum = Math.min(Math.max(1, pageNum), totalPages);
1387
+ const start = (pageNum - 1) * itemsPerPage;
1388
+ const paginatedLines = lines.slice(start, start + itemsPerPage);
1389
+ return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + paginatedLines.join("\n") + "\n" + session.text("commands.cave.list.pageInfo", [pageNum, totalPages]);
1390
+ } else {
1391
+ return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + lines.join("\n");
1323
1392
  }
1324
- const lines = [session.text("commands.cave.message.caveTitle", [cave.cave_id])];
1325
- for (const element of cave.elements) {
1326
- if (element.type === "text") {
1327
- lines.push(element.content);
1393
+ }
1394
+ __name(processList, "processList");
1395
+ async function processView(caveFilePath, resourceDir, session, options, content) {
1396
+ const caveId = parseInt(content[0] || (typeof options.g === "string" ? options.g : ""));
1397
+ if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1398
+ const data = await FileHandler.readJsonData(caveFilePath);
1399
+ const cave = data.find((item) => item.cave_id === caveId);
1400
+ if (!cave) return sendMessage(session, "commands.cave.error.notFound", [], true);
1401
+ return buildMessage(cave, resourceDir, session);
1402
+ }
1403
+ __name(processView, "processView");
1404
+ async function processRandom(caveFilePath, resourceDir, session) {
1405
+ const data = await FileHandler.readJsonData(caveFilePath);
1406
+ if (data.length === 0) {
1407
+ return sendMessage(session, "commands.cave.error.noCave", [], true);
1408
+ }
1409
+ const cave = (() => {
1410
+ const validCaves = data.filter((cave2) => cave2.elements && cave2.elements.length > 0);
1411
+ if (!validCaves.length) return void 0;
1412
+ const randomIndex = Math.floor(Math.random() * validCaves.length);
1413
+ return validCaves[randomIndex];
1414
+ })();
1415
+ return cave ? buildMessage(cave, resourceDir, session) : sendMessage(session, "commands.cave.error.getCave", [], true);
1416
+ }
1417
+ __name(processRandom, "processRandom");
1418
+ async function processDelete(caveFilePath, resourceDir, pendingFilePath, session, config, options, content, idManager, HashManager2) {
1419
+ const caveId = parseInt(content[0] || (typeof options.r === "string" ? options.r : ""));
1420
+ if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1421
+ const data = await FileHandler.readJsonData(caveFilePath);
1422
+ const pendingData = await FileHandler.readJsonData(pendingFilePath);
1423
+ const targetInData = data.find((item) => item.cave_id === caveId);
1424
+ const targetInPending = pendingData.find((item) => item.cave_id === caveId);
1425
+ if (!targetInData && !targetInPending) {
1426
+ return sendMessage(session, "commands.cave.error.notFound", [], true);
1427
+ }
1428
+ const targetCave = targetInData || targetInPending;
1429
+ const isPending = !targetInData;
1430
+ if (targetCave.contributor_number !== session.userId && !config.manager.includes(session.userId)) {
1431
+ return sendMessage(session, "commands.cave.remove.noPermission", [], true);
1432
+ }
1433
+ const caveContent = await buildMessage(targetCave, resourceDir, session);
1434
+ if (targetCave.elements) {
1435
+ await HashManager2.updateCaveContent(caveId, {
1436
+ images: void 0,
1437
+ texts: void 0
1438
+ });
1439
+ for (const element of targetCave.elements) {
1440
+ if ((element.type === "img" || element.type === "video") && element.file) {
1441
+ const fullPath = path6.join(resourceDir, element.file);
1442
+ if (fs6.existsSync(fullPath)) {
1443
+ await fs6.promises.unlink(fullPath);
1444
+ }
1445
+ }
1328
1446
  }
1329
1447
  }
1330
- lines.push(session.text("commands.cave.message.contributorSuffix", [cave.contributor_name]));
1331
- return lines.join("\n");
1448
+ if (isPending) {
1449
+ const newPendingData = pendingData.filter((item) => item.cave_id !== caveId);
1450
+ await FileHandler.writeJsonData(pendingFilePath, newPendingData);
1451
+ } else {
1452
+ const newData = data.filter((item) => item.cave_id !== caveId);
1453
+ await FileHandler.writeJsonData(caveFilePath, newData);
1454
+ await idManager.removeStat(targetCave.contributor_number, caveId);
1455
+ }
1456
+ await idManager.markDeleted(caveId);
1457
+ const deleteStatus = isPending ? session.text("commands.cave.remove.deletePending") : "";
1458
+ const deleteMessage = session.text("commands.cave.remove.deleted");
1459
+ return `${deleteMessage}${deleteStatus}${caveContent}`;
1332
1460
  }
1333
- __name(buildMessage, "buildMessage");
1461
+ __name(processDelete, "processDelete");
1334
1462
 
1335
1463
  // src/index.ts
1336
1464
  var name = "best-cave";
@@ -1371,111 +1499,21 @@ var Config = import_koishi6.Schema.object({
1371
1499
  async function apply(ctx, config) {
1372
1500
  ctx.i18n.define("zh-CN", require_zh_CN());
1373
1501
  ctx.i18n.define("en-US", require_en_US());
1374
- const dataDir = path6.join(ctx.baseDir, "data");
1375
- const caveDir = path6.join(dataDir, "cave");
1376
- await FileHandler.ensureDirectory(dataDir);
1502
+ const dataDir = path7.join(ctx.baseDir, "data");
1503
+ const caveDir = path7.join(dataDir, "cave");
1377
1504
  await FileHandler.ensureDirectory(caveDir);
1378
- await FileHandler.ensureDirectory(path6.join(caveDir, "resources"));
1379
- await FileHandler.ensureJsonFile(path6.join(caveDir, "cave.json"));
1380
- await FileHandler.ensureJsonFile(path6.join(caveDir, "pending.json"));
1381
- await FileHandler.ensureJsonFile(path6.join(caveDir, "hash.json"));
1505
+ await FileHandler.ensureDirectory(path7.join(caveDir, "resources"));
1506
+ await FileHandler.ensureJsonFile(path7.join(caveDir, "cave.json"));
1507
+ await FileHandler.ensureJsonFile(path7.join(caveDir, "pending.json"));
1508
+ await FileHandler.ensureJsonFile(path7.join(caveDir, "hash.json"));
1382
1509
  const idManager = new IdManager(ctx.baseDir);
1383
- const contentHashManager = new ContentHashManager(caveDir);
1384
- const auditManager = new AuditManager(ctx, config, caveDir, idManager);
1510
+ const contentHashManager = new HashManager(caveDir);
1511
+ const auditManager = new AuditManager(ctx, config, idManager);
1385
1512
  await Promise.all([
1386
- idManager.initialize(path6.join(caveDir, "cave.json"), path6.join(caveDir, "pending.json")),
1513
+ idManager.initialize(path7.join(caveDir, "cave.json"), path7.join(caveDir, "pending.json")),
1387
1514
  contentHashManager.initialize()
1388
1515
  ]);
1389
1516
  const lastUsed = /* @__PURE__ */ new Map();
1390
- async function processList(session, config2, userId, pageNum = 1) {
1391
- const stats = idManager.getStats();
1392
- if (userId && userId in stats) {
1393
- const ids = stats[userId];
1394
- return session.text("commands.cave.list.totalItems", [userId, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1395
- }
1396
- const lines = Object.entries(stats).map(([cid, ids]) => {
1397
- return session.text("commands.cave.list.totalItems", [cid, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1398
- });
1399
- const totalSubmissions = Object.values(stats).reduce((sum, arr) => sum + arr.length, 0);
1400
- if (config2.enablePagination) {
1401
- const itemsPerPage = config2.itemsPerPage;
1402
- const totalPages = Math.max(1, Math.ceil(lines.length / itemsPerPage));
1403
- pageNum = Math.min(Math.max(1, pageNum), totalPages);
1404
- const start = (pageNum - 1) * itemsPerPage;
1405
- const paginatedLines = lines.slice(start, start + itemsPerPage);
1406
- return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + paginatedLines.join("\n") + "\n" + session.text("commands.cave.list.pageInfo", [pageNum, totalPages]);
1407
- } else {
1408
- return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + lines.join("\n");
1409
- }
1410
- }
1411
- __name(processList, "processList");
1412
- async function processView(caveFilePath, resourceDir, session, options, content) {
1413
- const caveId = parseInt(content[0] || (typeof options.g === "string" ? options.g : ""));
1414
- if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1415
- const data = await FileHandler.readJsonData(caveFilePath);
1416
- const cave = data.find((item) => item.cave_id === caveId);
1417
- if (!cave) return sendMessage(session, "commands.cave.error.notFound", [], true);
1418
- return buildMessage2(cave, resourceDir, session);
1419
- }
1420
- __name(processView, "processView");
1421
- async function processRandom(caveFilePath, resourceDir, session) {
1422
- const data = await FileHandler.readJsonData(caveFilePath);
1423
- if (data.length === 0) {
1424
- return sendMessage(session, "commands.cave.error.noCave", [], true);
1425
- }
1426
- const cave = (() => {
1427
- const validCaves = data.filter((cave2) => cave2.elements && cave2.elements.length > 0);
1428
- if (!validCaves.length) return void 0;
1429
- const randomIndex = Math.floor(Math.random() * validCaves.length);
1430
- return validCaves[randomIndex];
1431
- })();
1432
- return cave ? buildMessage2(cave, resourceDir, session) : sendMessage(session, "commands.cave.error.getCave", [], true);
1433
- }
1434
- __name(processRandom, "processRandom");
1435
- async function processDelete(caveFilePath, resourceDir, pendingFilePath, session, config2, options, content) {
1436
- const caveId = parseInt(content[0] || (typeof options.r === "string" ? options.r : ""));
1437
- if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1438
- const data = await FileHandler.readJsonData(caveFilePath);
1439
- const pendingData = await FileHandler.readJsonData(pendingFilePath);
1440
- const targetInData = data.find((item) => item.cave_id === caveId);
1441
- const targetInPending = pendingData.find((item) => item.cave_id === caveId);
1442
- if (!targetInData && !targetInPending) {
1443
- return sendMessage(session, "commands.cave.error.notFound", [], true);
1444
- }
1445
- const targetCave = targetInData || targetInPending;
1446
- const isPending = !targetInData;
1447
- if (targetCave.contributor_number !== session.userId && !config2.manager.includes(session.userId)) {
1448
- return sendMessage(session, "commands.cave.remove.noPermission", [], true);
1449
- }
1450
- const caveContent = await buildMessage2(targetCave, resourceDir, session);
1451
- if (targetCave.elements) {
1452
- await contentHashManager.updateCaveContent(caveId, {
1453
- images: void 0,
1454
- texts: void 0
1455
- });
1456
- for (const element of targetCave.elements) {
1457
- if ((element.type === "img" || element.type === "video") && element.file) {
1458
- const fullPath = path6.join(resourceDir, element.file);
1459
- if (fs6.existsSync(fullPath)) {
1460
- await fs6.promises.unlink(fullPath);
1461
- }
1462
- }
1463
- }
1464
- }
1465
- if (isPending) {
1466
- const newPendingData = pendingData.filter((item) => item.cave_id !== caveId);
1467
- await FileHandler.writeJsonData(pendingFilePath, newPendingData);
1468
- } else {
1469
- const newData = data.filter((item) => item.cave_id !== caveId);
1470
- await FileHandler.writeJsonData(caveFilePath, newData);
1471
- await idManager.removeStat(targetCave.contributor_number, caveId);
1472
- }
1473
- await idManager.markDeleted(caveId);
1474
- const deleteStatus = isPending ? session.text("commands.cave.remove.deletePending") : "";
1475
- const deleteMessage = session.text("commands.cave.remove.deleted");
1476
- return `${deleteMessage}${deleteStatus}${caveContent}`;
1477
- }
1478
- __name(processDelete, "processDelete");
1479
1517
  async function processAdd(ctx2, config2, caveFilePath, resourceDir, pendingFilePath, session, content) {
1480
1518
  let caveId;
1481
1519
  try {
@@ -1552,7 +1590,7 @@ async function apply(ctx, config) {
1552
1590
  index: Number.MAX_SAFE_INTEGER
1553
1591
  });
1554
1592
  }
1555
- const hashStorage = new ContentHashManager(path6.join(ctx2.baseDir, "data", "cave"));
1593
+ const hashStorage = new HashManager(path7.join(ctx2.baseDir, "data", "cave"));
1556
1594
  await hashStorage.initialize();
1557
1595
  const hashStatus = await hashStorage.getStatus();
1558
1596
  if (!hashStatus.lastUpdated || hashStatus.entries.length === 0) {
@@ -1569,7 +1607,7 @@ async function apply(ctx, config) {
1569
1607
  pendingData.push(newCave);
1570
1608
  await Promise.all([
1571
1609
  FileHandler.writeJsonData(pendingFilePath, pendingData),
1572
- auditManager.sendAuditMessage(newCave, await buildMessage2(newCave, resourceDir, session), session)
1610
+ auditManager.sendAuditMessage(newCave, await buildMessage(newCave, resourceDir, session), session)
1573
1611
  ]);
1574
1612
  return sendMessage(session, "commands.cave.add.submitPending", [caveId], false);
1575
1613
  }
@@ -1595,14 +1633,14 @@ async function apply(ctx, config) {
1595
1633
  "commands.cave.error.similarDuplicateFound",
1596
1634
  [(result.similarity * 100).toFixed(1)]
1597
1635
  );
1598
- await session.send(duplicateMessage + await buildMessage2(originalCave, resourceDir, session));
1636
+ await session.send(duplicateMessage + await buildMessage(originalCave, resourceDir, session));
1599
1637
  throw new Error("duplicate_found");
1600
1638
  }
1601
1639
  }
1602
1640
  await Promise.all([
1603
1641
  FileHandler.writeJsonData(caveFilePath, data),
1604
1642
  contentHashManager.updateCaveContent(caveId, {
1605
- images: savedImages.length > 0 ? await Promise.all(savedImages.map((file) => fs6.promises.readFile(path6.join(resourceDir, file)))) : void 0,
1643
+ images: savedImages.length > 0 ? await Promise.all(savedImages.map((file) => fs7.promises.readFile(path7.join(resourceDir, file)))) : void 0,
1606
1644
  texts: textParts.filter((p) => p.type === "text").map((p) => p.content)
1607
1645
  })
1608
1646
  ]);
@@ -1625,11 +1663,11 @@ async function apply(ctx, config) {
1625
1663
  return sendMessage(session, "commands.cave.message.blacklisted", [], true);
1626
1664
  }
1627
1665
  }).action(async ({ session, options }, ...content) => {
1628
- const dataDir2 = path6.join(ctx.baseDir, "data");
1629
- const caveDir2 = path6.join(dataDir2, "cave");
1630
- const caveFilePath = path6.join(caveDir2, "cave.json");
1631
- const resourceDir = path6.join(caveDir2, "resources");
1632
- const pendingFilePath = path6.join(caveDir2, "pending.json");
1666
+ const dataDir2 = path7.join(ctx.baseDir, "data");
1667
+ const caveDir2 = path7.join(dataDir2, "cave");
1668
+ const caveFilePath = path7.join(caveDir2, "cave.json");
1669
+ const resourceDir = path7.join(caveDir2, "resources");
1670
+ const pendingFilePath = path7.join(caveDir2, "pending.json");
1633
1671
  const needsCooldown = !options.l && !options.a;
1634
1672
  if (needsCooldown) {
1635
1673
  const guildId = session.guildId;
@@ -1648,23 +1686,23 @@ async function apply(ctx, config) {
1648
1686
  if (config.manager.includes(session.userId)) {
1649
1687
  if (!isNaN(num)) {
1650
1688
  if (num < 1e4) {
1651
- return await processList(session, config, void 0, num);
1689
+ return await processList(session, config, idManager, void 0, num);
1652
1690
  } else {
1653
- return await processList(session, config, num.toString());
1691
+ return await processList(session, config, idManager, num.toString());
1654
1692
  }
1655
1693
  } else if (input) {
1656
- return await processList(session, config, input);
1694
+ return await processList(session, config, idManager, input);
1657
1695
  }
1658
- return await processList(session, config);
1696
+ return await processList(session, config, idManager);
1659
1697
  } else {
1660
- return await processList(session, config, session.userId);
1698
+ return await processList(session, config, idManager, session.userId);
1661
1699
  }
1662
1700
  }
1663
1701
  if (options.g) {
1664
1702
  return await processView(caveFilePath, resourceDir, session, options, content);
1665
1703
  }
1666
1704
  if (options.r) {
1667
- return await processDelete(caveFilePath, resourceDir, pendingFilePath, session, config, options, content);
1705
+ return await processDelete(caveFilePath, resourceDir, pendingFilePath, session, config, options, content, idManager, contentHashManager);
1668
1706
  }
1669
1707
  if (options.a) {
1670
1708
  return await processAdd(ctx, config, caveFilePath, resourceDir, pendingFilePath, session, content);
@@ -1676,11 +1714,11 @@ async function apply(ctx, config) {
1676
1714
  return sendMessage(session, "commands.cave.message.managerOnly", [], true);
1677
1715
  }
1678
1716
  }).action(async ({ session }, id) => {
1679
- const dataDir2 = path6.join(ctx.baseDir, "data");
1680
- const caveDir2 = path6.join(dataDir2, "cave");
1681
- const caveFilePath = path6.join(caveDir2, "cave.json");
1682
- const resourceDir = path6.join(caveDir2, "resources");
1683
- const pendingFilePath = path6.join(caveDir2, "pending.json");
1717
+ const dataDir2 = path7.join(ctx.baseDir, "data");
1718
+ const caveDir2 = path7.join(dataDir2, "cave");
1719
+ const caveFilePath = path7.join(caveDir2, "cave.json");
1720
+ const resourceDir = path7.join(caveDir2, "resources");
1721
+ const pendingFilePath = path7.join(caveDir2, "pending.json");
1684
1722
  const pendingData = await FileHandler.readJsonData(pendingFilePath);
1685
1723
  return await auditManager.processAudit(pendingData, true, caveFilePath, resourceDir, pendingFilePath, session, id === "all" ? void 0 : parseInt(id));
1686
1724
  });
@@ -1689,35 +1727,17 @@ async function apply(ctx, config) {
1689
1727
  return sendMessage(session, "commands.cave.message.managerOnly", [], true);
1690
1728
  }
1691
1729
  }).action(async ({ session }, id) => {
1692
- const dataDir2 = path6.join(ctx.baseDir, "data");
1693
- const caveDir2 = path6.join(dataDir2, "cave");
1694
- const caveFilePath = path6.join(caveDir2, "cave.json");
1695
- const resourceDir = path6.join(caveDir2, "resources");
1696
- const pendingFilePath = path6.join(caveDir2, "pending.json");
1730
+ const dataDir2 = path7.join(ctx.baseDir, "data");
1731
+ const caveDir2 = path7.join(dataDir2, "cave");
1732
+ const caveFilePath = path7.join(caveDir2, "cave.json");
1733
+ const resourceDir = path7.join(caveDir2, "resources");
1734
+ const pendingFilePath = path7.join(caveDir2, "pending.json");
1697
1735
  const pendingData = await FileHandler.readJsonData(pendingFilePath);
1698
1736
  return await auditManager.processAudit(pendingData, false, caveFilePath, resourceDir, pendingFilePath, session, id === "all" ? void 0 : parseInt(id));
1699
1737
  });
1700
1738
  }
1701
1739
  __name(apply, "apply");
1702
1740
  var logger5 = new import_koishi6.Logger("cave");
1703
- async function sendMessage(session, key, params = [], isTemp = true, timeout = 1e4) {
1704
- try {
1705
- const msg = await session.send(session.text(key, params));
1706
- if (isTemp && msg) {
1707
- setTimeout(async () => {
1708
- try {
1709
- await session.bot.deleteMessage(session.channelId, msg);
1710
- } catch (error) {
1711
- logger5.debug(`Failed to delete temporary message: ${error.message}`);
1712
- }
1713
- }, timeout);
1714
- }
1715
- } catch (error) {
1716
- logger5.error(`Failed to send message: ${error.message}`);
1717
- }
1718
- return "";
1719
- }
1720
- __name(sendMessage, "sendMessage");
1721
1741
  function cleanElementsForSave(elements, keepIndex = false) {
1722
1742
  if (!elements?.length) return [];
1723
1743
  const cleanedElements = elements.map((element) => {
@@ -1742,47 +1762,6 @@ function cleanElementsForSave(elements, keepIndex = false) {
1742
1762
  return keepIndex ? cleanedElements.sort((a, b) => (a.index || 0) - (b.index || 0)) : cleanedElements;
1743
1763
  }
1744
1764
  __name(cleanElementsForSave, "cleanElementsForSave");
1745
- async function processMediaFile(filePath, type) {
1746
- const data = await fs6.promises.readFile(filePath).catch(() => null);
1747
- if (!data) return null;
1748
- return `data:${type}/${type === "image" ? "png" : "mp4"};base64,${data.toString("base64")}`;
1749
- }
1750
- __name(processMediaFile, "processMediaFile");
1751
- async function buildMessage2(cave, resourceDir, session) {
1752
- if (!cave?.elements?.length) {
1753
- return session.text("commands.cave.error.noContent");
1754
- }
1755
- const videoElement = cave.elements.find((el) => el.type === "video");
1756
- const nonVideoElements = cave.elements.filter((el) => el.type !== "video").sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1757
- if (videoElement?.file) {
1758
- const basicInfo = [
1759
- session.text("commands.cave.message.caveTitle", [cave.cave_id]),
1760
- session.text("commands.cave.message.contributorSuffix", [cave.contributor_name])
1761
- ].join("\n");
1762
- await session?.send(basicInfo);
1763
- const filePath = path6.join(resourceDir, videoElement.file);
1764
- const base64Data = await processMediaFile(filePath, "video");
1765
- if (base64Data && session) {
1766
- await session.send((0, import_koishi6.h)("video", { src: base64Data }));
1767
- }
1768
- return "";
1769
- }
1770
- const lines = [session.text("commands.cave.message.caveTitle", [cave.cave_id])];
1771
- for (const element of nonVideoElements) {
1772
- if (element.type === "text") {
1773
- lines.push(element.content);
1774
- } else if (element.type === "img" && element.file) {
1775
- const filePath = path6.join(resourceDir, element.file);
1776
- const base64Data = await processMediaFile(filePath, "image");
1777
- if (base64Data) {
1778
- lines.push((0, import_koishi6.h)("image", { src: base64Data }));
1779
- }
1780
- }
1781
- }
1782
- lines.push(session.text("commands.cave.message.contributorSuffix", [cave.contributor_name]));
1783
- return lines.join("\n");
1784
- }
1785
- __name(buildMessage2, "buildMessage");
1786
1765
  // Annotate the CommonJS export names for ESM import in node:
1787
1766
  0 && (module.exports = {
1788
1767
  Config,
package/lib/types.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ export interface BaseElement {
2
+ type: 'text' | 'img' | 'video';
3
+ index: number;
4
+ }
5
+ export interface TextElement extends BaseElement {
6
+ type: 'text';
7
+ content: string;
8
+ }
9
+ export interface MediaElement extends BaseElement {
10
+ type: 'img' | 'video';
11
+ file?: string;
12
+ fileName?: string;
13
+ fileSize?: string;
14
+ filePath?: string;
15
+ }
16
+ export type Element = TextElement | MediaElement;
17
+ export interface CaveObject {
18
+ cave_id: number;
19
+ elements: Element[];
20
+ contributor_number: string;
21
+ contributor_name: string;
22
+ }
23
+ export interface PendingCave extends CaveObject {
24
+ }
@@ -0,0 +1,17 @@
1
+ import { Context } from 'koishi';
2
+ import { Config, PendingCave } from '..';
3
+ import { IdManager } from './IdManager';
4
+ export declare class AuditManager {
5
+ private ctx;
6
+ private config;
7
+ private idManager;
8
+ private logger;
9
+ constructor(ctx: Context, config: Config, idManager: IdManager);
10
+ processAudit(pendingData: PendingCave[], isApprove: boolean, caveFilePath: string, resourceDir: string, pendingFilePath: string, session: any, targetId?: number): Promise<string>;
11
+ private handleSingleAudit;
12
+ private handleBatchAudit;
13
+ sendAuditMessage(cave: PendingCave, content: string, session: any): Promise<void>;
14
+ private deleteMediaFiles;
15
+ private cleanElementsForSave;
16
+ private sendMessage;
17
+ }
@@ -0,0 +1,80 @@
1
+ import { Buffer } from 'buffer';
2
+ /**
3
+ * 图片哈希计算工具类
4
+ * 使用 DCT(离散余弦变换)方法计算图片的感知哈希值,可用于图片相似度比较
5
+ */
6
+ export declare class ContentHasher {
7
+ /**
8
+ * 计算图片的感知哈希值
9
+ * @param imageBuffer - 图片的二进制数据
10
+ * @returns 返回64位的十六进制哈希字符串
11
+ * @throws 当图片处理失败时可能抛出错误
12
+ */
13
+ static calculateHash(imageBuffer: Buffer): Promise<string>;
14
+ /**
15
+ * 将二进制字符串转换为十六进制
16
+ * @param binary - 二进制字符串
17
+ * @returns 十六进制字符串
18
+ * @private
19
+ */
20
+ private static binaryToHex;
21
+ /**
22
+ * 将十六进制字符串转换为二进制
23
+ * @param hex - 十六进制字符串
24
+ * @returns 二进制字符串
25
+ * @private
26
+ */
27
+ private static hexToBinary;
28
+ /**
29
+ * 计算图像的DCT(离散余弦变换)
30
+ * @param data - 图像数据
31
+ * @param size - 图像尺寸
32
+ * @returns DCT变换后的矩阵
33
+ * @private
34
+ */
35
+ private static computeDCT;
36
+ /**
37
+ * 获取DCT系数
38
+ * @param index - 索引值
39
+ * @param size - 矩阵大小
40
+ * @returns DCT系数
41
+ * @private
42
+ */
43
+ private static getDCTCoefficient;
44
+ /**
45
+ * 计算数组的中位数
46
+ * @param arr - 输入数组
47
+ * @returns 中位数
48
+ * @private
49
+ */
50
+ private static calculateMedian;
51
+ /**
52
+ * 从DCT矩阵中提取特征值
53
+ * @param matrix - DCT矩阵
54
+ * @param size - 矩阵大小
55
+ * @returns 特征值数组
56
+ * @private
57
+ */
58
+ private static extractFeatures;
59
+ /**
60
+ * 计算两个哈希值之间的汉明距离
61
+ * @param hash1 - 第一个哈希值
62
+ * @param hash2 - 第二个哈希值
63
+ * @returns 汉明距离
64
+ * @throws 当两个哈希值长度不等时抛出错误
65
+ */
66
+ static calculateDistance(hash1: string, hash2: string): number;
67
+ /**
68
+ * 计算两个图片哈希值的相似度
69
+ * @param hash1 - 第一个哈希值
70
+ * @param hash2 - 第二个哈希值
71
+ * @returns 返回0-1之间的相似度值,1表示完全相同,0表示完全不同
72
+ */
73
+ static calculateSimilarity(hash1: string, hash2: string): number;
74
+ /**
75
+ * 计算文本的哈希值
76
+ * @param text - 输入文本
77
+ * @returns 文本的哈希值(36进制字符串)
78
+ */
79
+ static calculateTextHash(text: string): string;
80
+ }
@@ -0,0 +1,63 @@
1
+ export declare class FileHandler {
2
+ private static locks;
3
+ private static readonly RETRY_COUNT;
4
+ private static readonly RETRY_DELAY;
5
+ private static readonly CONCURRENCY_LIMIT;
6
+ /**
7
+ * 并发控制
8
+ * @param operation 要执行的操作
9
+ * @param limit 并发限制
10
+ * @returns 操作结果
11
+ */
12
+ private static withConcurrencyLimit;
13
+ /**
14
+ * 文件操作包装器
15
+ * @param filePath 文件路径
16
+ * @param operation 要执行的操作
17
+ * @returns 操作结果
18
+ */
19
+ private static withFileOp;
20
+ /**
21
+ * 事务处理
22
+ * @param operations 要执行的操作数组
23
+ * @returns 操作结果数组
24
+ */
25
+ static withTransaction<T>(operations: Array<{
26
+ filePath: string;
27
+ operation: () => Promise<T>;
28
+ rollback?: () => Promise<void>;
29
+ }>): Promise<T[]>;
30
+ /**
31
+ * 读取 JSON 数据
32
+ * @param filePath 文件路径
33
+ * @returns JSON 数据
34
+ */
35
+ static readJsonData<T>(filePath: string): Promise<T[]>;
36
+ /**
37
+ * 写入 JSON 数据
38
+ * @param filePath 文件路径
39
+ * @param data 要写入的数据
40
+ */
41
+ static writeJsonData<T>(filePath: string, data: T[]): Promise<void>;
42
+ /**
43
+ * 确保目录存在
44
+ * @param dir 目录路径
45
+ */
46
+ static ensureDirectory(dir: string): Promise<void>;
47
+ /**
48
+ * 确保 JSON 文件存在
49
+ * @param filePath 文件路径
50
+ */
51
+ static ensureJsonFile(filePath: string): Promise<void>;
52
+ /**
53
+ * 保存媒体文件
54
+ * @param filePath 文件路径
55
+ * @param data 文件数据
56
+ */
57
+ static saveMediaFile(filePath: string, data: Buffer | string): Promise<void>;
58
+ /**
59
+ * 删除媒体文件
60
+ * @param filePath 文件路径
61
+ */
62
+ static deleteMediaFile(filePath: string): Promise<void>;
63
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * 哈希存储状态类型
3
+ */
4
+ interface HashStorageStatus {
5
+ lastUpdated: string;
6
+ entries: Array<{
7
+ caveId: number;
8
+ imageHashes: string[];
9
+ textHashes: string[];
10
+ }>;
11
+ }
12
+ /**
13
+ * 图片哈希值存储管理类
14
+ * 负责管理和维护回声洞图片的哈希值
15
+ */
16
+ export declare class HashManager {
17
+ private readonly caveDir;
18
+ private static readonly HASH_FILE;
19
+ private static readonly CAVE_FILE;
20
+ private static readonly BATCH_SIZE;
21
+ private imageHashes;
22
+ private textHashes;
23
+ private initialized;
24
+ /**
25
+ * 初始化HashManager实例
26
+ * @param caveDir 回声洞数据目录路径
27
+ */
28
+ constructor(caveDir: string);
29
+ private get filePath();
30
+ private get resourceDir();
31
+ private get caveFilePath();
32
+ /**
33
+ * 初始化哈希存储
34
+ * 读取现有哈希数据或重新构建哈希值
35
+ * @throws 初始化失败时抛出错误
36
+ */
37
+ initialize(): Promise<void>;
38
+ /**
39
+ * 获取当前哈希存储状态
40
+ * @returns 包含最后更新时间和所有条目的状态对象
41
+ */
42
+ getStatus(): Promise<HashStorageStatus>;
43
+ /**
44
+ * 更新指定回声洞的图片哈希值
45
+ * @param caveId 回声洞ID
46
+ * @param content 图片buffer数组
47
+ */
48
+ updateCaveContent(caveId: number, content: {
49
+ images?: Buffer[];
50
+ texts?: string[];
51
+ }): Promise<void>;
52
+ /**
53
+ * 更新所有回声洞的哈希值
54
+ * @param isInitialBuild 是否为初始构建
55
+ */
56
+ updateAllCaves(isInitialBuild?: boolean): Promise<void>;
57
+ /**
58
+ * 查找重复的图片
59
+ * @param content 待查找的图片buffer数组
60
+ * @param thresholds 相似度阈值
61
+ * @returns 匹配结果数组,包含索引、回声洞ID和相似度
62
+ */
63
+ findDuplicates(content: {
64
+ images?: Buffer[];
65
+ texts?: string[];
66
+ }, thresholds: {
67
+ image: number;
68
+ text: number;
69
+ }): Promise<Array<{
70
+ type: 'image' | 'text';
71
+ index: number;
72
+ caveId: number;
73
+ similarity: number;
74
+ } | null>>;
75
+ private findTextDuplicates;
76
+ private calculateTextSimilarity;
77
+ private findImageDuplicates;
78
+ /**
79
+ * 加载回声洞数据
80
+ * @returns 回声洞数据数组
81
+ * @private
82
+ */
83
+ private loadCaveData;
84
+ /**
85
+ * 保存哈希数据到文件
86
+ * @private
87
+ */
88
+ private saveContentHashes;
89
+ /**
90
+ * 构建初始哈希数据
91
+ * @private
92
+ */
93
+ private buildInitialHashes;
94
+ /**
95
+ * 更新缺失的哈希值
96
+ * @private
97
+ */
98
+ private updateMissingHashes;
99
+ /**
100
+ * 批量处理数组项
101
+ * @param items 待处理项数组
102
+ * @param processor 处理函数
103
+ * @param batchSize 批处理大小
104
+ * @private
105
+ */
106
+ private processBatch;
107
+ }
108
+ export {};
@@ -0,0 +1,69 @@
1
+ /**
2
+ * ID管理器类
3
+ * 负责管理回声洞ID的分配、删除和统计信息
4
+ */
5
+ export declare class IdManager {
6
+ private deletedIds;
7
+ private maxId;
8
+ private initialized;
9
+ private readonly statusFilePath;
10
+ private stats;
11
+ private usedIds;
12
+ /**
13
+ * 初始化ID管理器
14
+ * @param baseDir - 基础目录路径
15
+ */
16
+ constructor(baseDir: string);
17
+ /**
18
+ * 初始化ID管理系统
19
+ * @param caveFilePath - 正式回声洞数据文件路径
20
+ * @param pendingFilePath - 待处理回声洞数据文件路径
21
+ * @throws 当初始化失败时抛出错误
22
+ */
23
+ initialize(caveFilePath: string, pendingFilePath: string): Promise<void>;
24
+ /**
25
+ * 处理ID冲突
26
+ * @param conflicts - ID冲突映射表
27
+ * @param caveFilePath - 正式回声洞数据文件路径
28
+ * @param pendingFilePath - 待处理回声洞数据文件路径
29
+ * @param caveData - 正式回声洞数据
30
+ * @param pendingData - 待处理回声洞数据
31
+ * @private
32
+ */
33
+ private handleConflicts;
34
+ /**
35
+ * 获取下一个可用的ID
36
+ * @returns 下一个可用的ID
37
+ * @throws 当ID管理器未初始化时抛出错误
38
+ */
39
+ getNextId(): number;
40
+ /**
41
+ * 标记ID为已删除状态
42
+ * @param id - 要标记为删除的ID
43
+ * @throws 当ID管理器未初始化时抛出错误
44
+ */
45
+ markDeleted(id: number): Promise<void>;
46
+ /**
47
+ * 添加贡献统计
48
+ * @param contributorNumber - 贡献者编号
49
+ * @param caveId - 回声洞ID
50
+ */
51
+ addStat(contributorNumber: string, caveId: number): Promise<void>;
52
+ /**
53
+ * 移除贡献统计
54
+ * @param contributorNumber - 贡献者编号
55
+ * @param caveId - 回声洞ID
56
+ */
57
+ removeStat(contributorNumber: string, caveId: number): Promise<void>;
58
+ /**
59
+ * 获取所有贡献统计信息
60
+ * @returns 贡献者编号到回声洞ID列表的映射
61
+ */
62
+ getStats(): Record<string, number[]>;
63
+ /**
64
+ * 保存当前状态到文件
65
+ * @private
66
+ * @throws 当保存失败时抛出错误
67
+ */
68
+ private saveStatus;
69
+ }
@@ -0,0 +1,30 @@
1
+ import { Context } from 'koishi';
2
+ import { Element, CaveObject } from '..';
3
+ export declare function buildMessage(cave: CaveObject, resourceDir: string, session?: any): Promise<string>;
4
+ export declare function sendMessage(session: any, key: string, params?: any[], isTemp?: boolean, timeout?: number): Promise<string>;
5
+ export declare function processMediaFile(filePath: string, type: 'image' | 'video'): Promise<string | null>;
6
+ export declare function extractMediaContent(originalContent: string, config: {
7
+ imageMaxSize: number;
8
+ videoMaxSize: number;
9
+ }, session: any): Promise<{
10
+ imageUrls: string[];
11
+ imageElements: Array<{
12
+ type: 'img';
13
+ index: number;
14
+ fileName?: string;
15
+ fileSize?: string;
16
+ }>;
17
+ videoUrls: string[];
18
+ videoElements: Array<{
19
+ type: 'video';
20
+ index: number;
21
+ fileName?: string;
22
+ fileSize?: string;
23
+ }>;
24
+ textParts: Element[];
25
+ }>;
26
+ export declare function saveMedia(urls: string[], fileNames: (string | undefined)[], resourceDir: string, caveId: number, mediaType: 'img' | 'video', config: {
27
+ enableImageDuplicate: boolean;
28
+ imageDuplicateThreshold: number;
29
+ textDuplicateThreshold: number;
30
+ }, ctx: Context, session: any, buffers?: Buffer[]): Promise<string[]>;
@@ -0,0 +1,7 @@
1
+ import { Config } from '..';
2
+ import { IdManager } from './IdManager';
3
+ import { HashManager } from './HashManager';
4
+ export declare function processList(session: any, config: Config, idManager: IdManager, userId?: string, pageNum?: number): Promise<string>;
5
+ export declare function processView(caveFilePath: string, resourceDir: string, session: any, options: any, content: string[]): Promise<string>;
6
+ export declare function processRandom(caveFilePath: string, resourceDir: string, session: any): Promise<string | void>;
7
+ export declare function processDelete(caveFilePath: string, resourceDir: string, pendingFilePath: string, session: any, config: Config, options: any, content: string[], idManager: IdManager, HashManager: HashManager): Promise<string>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-best-cave",
3
3
  "description": "最好的 cave 插件,可开关的审核系统,可引用添加,支持图文混合内容,可查阅投稿列表,完美复刻你的 .cave 体验!",
4
- "version": "1.5.4",
4
+ "version": "1.5.6",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],