koishi-plugin-best-cave 1.5.3 → 1.5.5

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.js CHANGED
@@ -53,11 +53,11 @@ __export(src_exports, {
53
53
  name: () => name
54
54
  });
55
55
  module.exports = __toCommonJS(src_exports);
56
- var import_koishi5 = require("koishi");
57
- var fs5 = __toESM(require("fs"));
58
- var path5 = __toESM(require("path"));
56
+ var import_koishi6 = require("koishi");
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,7 +980,7 @@ 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"));
@@ -1188,37 +1179,319 @@ ${session.text("commands.cave.audit.from")}${cave.contributor_number}`;
1188
1179
  }
1189
1180
  };
1190
1181
 
1182
+ // src/utils/MediaHandler.ts
1183
+ var import_koishi5 = require("koishi");
1184
+ var fs5 = __toESM(require("fs"));
1185
+ var path5 = __toESM(require("path"));
1186
+ var logger4 = new import_koishi5.Logger("MediaHandle");
1187
+ async function buildMessage(cave, resourceDir, session) {
1188
+ if (!cave?.elements?.length) {
1189
+ return session.text("commands.cave.error.noContent");
1190
+ }
1191
+ const videoElement = cave.elements.find((el) => el.type === "video");
1192
+ const nonVideoElements = cave.elements.filter((el) => el.type !== "video").sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1193
+ if (videoElement?.file) {
1194
+ const basicInfo = [
1195
+ session.text("commands.cave.message.caveTitle", [cave.cave_id]),
1196
+ session.text("commands.cave.message.contributorSuffix", [cave.contributor_name])
1197
+ ].join("\n");
1198
+ await session?.send(basicInfo);
1199
+ const filePath = path5.join(resourceDir, videoElement.file);
1200
+ const base64Data = await processMediaFile(filePath, "video");
1201
+ if (base64Data && session) {
1202
+ await session.send((0, import_koishi5.h)("video", { src: base64Data }));
1203
+ }
1204
+ return "";
1205
+ }
1206
+ const lines = [session.text("commands.cave.message.caveTitle", [cave.cave_id])];
1207
+ for (const element of nonVideoElements) {
1208
+ if (element.type === "text") {
1209
+ lines.push(element.content);
1210
+ } else if (element.type === "img" && element.file) {
1211
+ const filePath = path5.join(resourceDir, element.file);
1212
+ const base64Data = await processMediaFile(filePath, "image");
1213
+ if (base64Data) {
1214
+ lines.push((0, import_koishi5.h)("image", { src: base64Data }));
1215
+ }
1216
+ }
1217
+ }
1218
+ lines.push(session.text("commands.cave.message.contributorSuffix", [cave.contributor_name]));
1219
+ return lines.join("\n");
1220
+ }
1221
+ __name(buildMessage, "buildMessage");
1222
+ async function sendMessage(session, key, params = [], isTemp = true, timeout = 1e4) {
1223
+ try {
1224
+ const msg = await session.send(session.text(key, params));
1225
+ if (isTemp && msg) {
1226
+ setTimeout(async () => {
1227
+ try {
1228
+ await session.bot.deleteMessage(session.channelId, msg);
1229
+ } catch (error) {
1230
+ logger4.debug(`Failed to delete temporary message: ${error.message}`);
1231
+ }
1232
+ }, timeout);
1233
+ }
1234
+ } catch (error) {
1235
+ logger4.error(`Failed to send message: ${error.message}`);
1236
+ }
1237
+ return "";
1238
+ }
1239
+ __name(sendMessage, "sendMessage");
1240
+ async function processMediaFile(filePath, type) {
1241
+ const data = await fs5.promises.readFile(filePath).catch(() => null);
1242
+ if (!data) return null;
1243
+ return `data:${type}/${type === "image" ? "png" : "mp4"};base64,${data.toString("base64")}`;
1244
+ }
1245
+ __name(processMediaFile, "processMediaFile");
1246
+ async function extractMediaContent(originalContent, config, session) {
1247
+ const textParts = originalContent.split(/<(img|video)[^>]+>/).map((text, idx) => text.trim() && {
1248
+ type: "text",
1249
+ content: text.replace(/^(img|video)$/, "").trim(),
1250
+ index: idx * 3
1251
+ }).filter((text) => text && text.content);
1252
+ const getMediaElements = /* @__PURE__ */ __name((type, maxSize) => {
1253
+ const regex = new RegExp(`<${type}[^>]+src="([^"]+)"[^>]*>`, "g");
1254
+ const elements = [];
1255
+ const urls = [];
1256
+ let match;
1257
+ let idx = 0;
1258
+ while ((match = regex.exec(originalContent)) !== null) {
1259
+ const element = match[0];
1260
+ const url = match[1];
1261
+ const fileName = element.match(/file="([^"]+)"/)?.[1];
1262
+ const fileSize = element.match(/fileSize="([^"]+)"/)?.[1];
1263
+ if (fileSize) {
1264
+ const sizeInBytes = parseInt(fileSize);
1265
+ if (sizeInBytes > maxSize * 1024 * 1024) {
1266
+ throw new Error(session.text("commands.cave.message.mediaSizeExceeded", [type]));
1267
+ }
1268
+ }
1269
+ urls.push(url);
1270
+ elements.push({
1271
+ type,
1272
+ index: type === "video" ? Number.MAX_SAFE_INTEGER : idx * 3 + 1,
1273
+ fileName,
1274
+ fileSize
1275
+ });
1276
+ idx++;
1277
+ }
1278
+ return { urls, elements };
1279
+ }, "getMediaElements");
1280
+ const { urls: imageUrls, elements: imageElementsRaw } = getMediaElements("img", config.imageMaxSize);
1281
+ const imageElements = imageElementsRaw;
1282
+ const { urls: videoUrls, elements: videoElementsRaw } = getMediaElements("video", config.videoMaxSize);
1283
+ const videoElements = videoElementsRaw;
1284
+ return { imageUrls, imageElements, videoUrls, videoElements, textParts };
1285
+ }
1286
+ __name(extractMediaContent, "extractMediaContent");
1287
+ async function saveMedia(urls, fileNames, resourceDir, caveId, mediaType, config, ctx, session, buffers) {
1288
+ const accept = mediaType === "img" ? "image/*" : "video/*";
1289
+ const hashStorage = new HashManager(path5.join(ctx.baseDir, "data", "cave"));
1290
+ await hashStorage.initialize();
1291
+ const downloadTasks = urls.map(async (url, i) => {
1292
+ const fileName = fileNames[i];
1293
+ const ext = path5.extname(fileName || url) || (mediaType === "img" ? ".png" : ".mp4");
1294
+ try {
1295
+ const response = await ctx.http(decodeURIComponent(url).replace(/&amp;/g, "&"), {
1296
+ method: "GET",
1297
+ responseType: "arraybuffer",
1298
+ timeout: 3e4,
1299
+ headers: {
1300
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
1301
+ "Accept": accept,
1302
+ "Referer": "https://qq.com"
1303
+ }
1304
+ });
1305
+ if (!response.data) throw new Error("empty_response");
1306
+ const buffer = Buffer.from(response.data);
1307
+ if (buffers && mediaType === "img") {
1308
+ buffers.push(buffer);
1309
+ }
1310
+ const md5 = path5.basename(fileName || `${mediaType}`, ext).replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
1311
+ const files = await fs5.promises.readdir(resourceDir);
1312
+ const duplicateFile = files.find((file) => {
1313
+ const match = file.match(/^\d+_([^.]+)/);
1314
+ return match && match[1] === md5;
1315
+ });
1316
+ if (duplicateFile) {
1317
+ const duplicateCaveId = parseInt(duplicateFile.split("_")[0]);
1318
+ if (!isNaN(duplicateCaveId)) {
1319
+ const caveFilePath = path5.join(ctx.baseDir, "data", "cave", "cave.json");
1320
+ const data = await FileHandler.readJsonData(caveFilePath);
1321
+ const originalCave = data.find((item) => item.cave_id === duplicateCaveId);
1322
+ if (originalCave) {
1323
+ const message = session.text("commands.cave.error.exactDuplicateFound");
1324
+ await session.send(message + await buildMessage(originalCave, resourceDir, session));
1325
+ throw new Error("duplicate_found");
1326
+ }
1327
+ }
1328
+ }
1329
+ if (mediaType === "img" && config.enableImageDuplicate) {
1330
+ const result = await hashStorage.findDuplicates(
1331
+ { images: [buffer] },
1332
+ {
1333
+ image: config.imageDuplicateThreshold,
1334
+ text: config.textDuplicateThreshold
1335
+ }
1336
+ );
1337
+ if (result.length > 0 && result[0] !== null) {
1338
+ const duplicate = result[0];
1339
+ const similarity = duplicate.similarity;
1340
+ if (similarity >= config.imageDuplicateThreshold) {
1341
+ const caveFilePath = path5.join(ctx.baseDir, "data", "cave", "cave.json");
1342
+ const data = await FileHandler.readJsonData(caveFilePath);
1343
+ const originalCave = data.find((item) => item.cave_id === duplicate.caveId);
1344
+ if (originalCave) {
1345
+ const message = session.text(
1346
+ "commands.cave.error.similarDuplicateFound",
1347
+ [(similarity * 100).toFixed(1)]
1348
+ );
1349
+ await session.send(message + await buildMessage(originalCave, resourceDir, session));
1350
+ throw new Error("duplicate_found");
1351
+ }
1352
+ }
1353
+ }
1354
+ }
1355
+ const finalFileName = `${caveId}_${md5}${ext}`;
1356
+ const filePath = path5.join(resourceDir, finalFileName);
1357
+ await FileHandler.saveMediaFile(filePath, buffer);
1358
+ return finalFileName;
1359
+ } catch (error) {
1360
+ if (error.message === "duplicate_found") {
1361
+ throw error;
1362
+ }
1363
+ logger4.error(`Failed to download media: ${error.message}`);
1364
+ throw new Error(session.text(`commands.cave.error.upload${mediaType === "img" ? "Image" : "Video"}Failed`));
1365
+ }
1366
+ });
1367
+ return Promise.all(downloadTasks);
1368
+ }
1369
+ __name(saveMedia, "saveMedia");
1370
+
1371
+ // src/utils/ProcessHandle.ts
1372
+ var fs6 = __toESM(require("fs"));
1373
+ var path6 = __toESM(require("path"));
1374
+ async function processList(session, config, idManager, userId, pageNum = 1) {
1375
+ const stats = idManager.getStats();
1376
+ if (userId && userId in stats) {
1377
+ const ids = stats[userId];
1378
+ return session.text("commands.cave.list.totalItems", [userId, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1379
+ }
1380
+ const lines = Object.entries(stats).map(([cid, ids]) => {
1381
+ return session.text("commands.cave.list.totalItems", [cid, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1382
+ });
1383
+ const totalSubmissions = Object.values(stats).reduce((sum, arr) => sum + arr.length, 0);
1384
+ if (config.enablePagination) {
1385
+ const itemsPerPage = config.itemsPerPage;
1386
+ const totalPages = Math.max(1, Math.ceil(lines.length / itemsPerPage));
1387
+ pageNum = Math.min(Math.max(1, pageNum), totalPages);
1388
+ const start = (pageNum - 1) * itemsPerPage;
1389
+ const paginatedLines = lines.slice(start, start + itemsPerPage);
1390
+ return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + paginatedLines.join("\n") + "\n" + session.text("commands.cave.list.pageInfo", [pageNum, totalPages]);
1391
+ } else {
1392
+ return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + lines.join("\n");
1393
+ }
1394
+ }
1395
+ __name(processList, "processList");
1396
+ async function processView(caveFilePath, resourceDir, session, options, content) {
1397
+ const caveId = parseInt(content[0] || (typeof options.g === "string" ? options.g : ""));
1398
+ if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1399
+ const data = await FileHandler.readJsonData(caveFilePath);
1400
+ const cave = data.find((item) => item.cave_id === caveId);
1401
+ if (!cave) return sendMessage(session, "commands.cave.error.notFound", [], true);
1402
+ return buildMessage(cave, resourceDir, session);
1403
+ }
1404
+ __name(processView, "processView");
1405
+ async function processRandom(caveFilePath, resourceDir, session) {
1406
+ const data = await FileHandler.readJsonData(caveFilePath);
1407
+ if (data.length === 0) {
1408
+ return sendMessage(session, "commands.cave.error.noCave", [], true);
1409
+ }
1410
+ const cave = (() => {
1411
+ const validCaves = data.filter((cave2) => cave2.elements && cave2.elements.length > 0);
1412
+ if (!validCaves.length) return void 0;
1413
+ const randomIndex = Math.floor(Math.random() * validCaves.length);
1414
+ return validCaves[randomIndex];
1415
+ })();
1416
+ return cave ? buildMessage(cave, resourceDir, session) : sendMessage(session, "commands.cave.error.getCave", [], true);
1417
+ }
1418
+ __name(processRandom, "processRandom");
1419
+ async function processDelete(caveFilePath, resourceDir, pendingFilePath, session, config, options, content, idManager, HashManager2) {
1420
+ const caveId = parseInt(content[0] || (typeof options.r === "string" ? options.r : ""));
1421
+ if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1422
+ const data = await FileHandler.readJsonData(caveFilePath);
1423
+ const pendingData = await FileHandler.readJsonData(pendingFilePath);
1424
+ const targetInData = data.find((item) => item.cave_id === caveId);
1425
+ const targetInPending = pendingData.find((item) => item.cave_id === caveId);
1426
+ if (!targetInData && !targetInPending) {
1427
+ return sendMessage(session, "commands.cave.error.notFound", [], true);
1428
+ }
1429
+ const targetCave = targetInData || targetInPending;
1430
+ const isPending = !targetInData;
1431
+ if (targetCave.contributor_number !== session.userId && !config.manager.includes(session.userId)) {
1432
+ return sendMessage(session, "commands.cave.remove.noPermission", [], true);
1433
+ }
1434
+ const caveContent = await buildMessage(targetCave, resourceDir, session);
1435
+ if (targetCave.elements) {
1436
+ await HashManager2.updateCaveContent(caveId, {
1437
+ images: void 0,
1438
+ texts: void 0
1439
+ });
1440
+ for (const element of targetCave.elements) {
1441
+ if ((element.type === "img" || element.type === "video") && element.file) {
1442
+ const fullPath = path6.join(resourceDir, element.file);
1443
+ if (fs6.existsSync(fullPath)) {
1444
+ await fs6.promises.unlink(fullPath);
1445
+ }
1446
+ }
1447
+ }
1448
+ }
1449
+ if (isPending) {
1450
+ const newPendingData = pendingData.filter((item) => item.cave_id !== caveId);
1451
+ await FileHandler.writeJsonData(pendingFilePath, newPendingData);
1452
+ } else {
1453
+ const newData = data.filter((item) => item.cave_id !== caveId);
1454
+ await FileHandler.writeJsonData(caveFilePath, newData);
1455
+ await idManager.removeStat(targetCave.contributor_number, caveId);
1456
+ }
1457
+ await idManager.markDeleted(caveId);
1458
+ const deleteStatus = isPending ? session.text("commands.cave.remove.deletePending") : "";
1459
+ const deleteMessage = session.text("commands.cave.remove.deleted");
1460
+ return `${deleteMessage}${deleteStatus}${caveContent}`;
1461
+ }
1462
+ __name(processDelete, "processDelete");
1463
+
1191
1464
  // src/index.ts
1192
1465
  var name = "best-cave";
1193
1466
  var inject = ["database"];
1194
- var Config = import_koishi5.Schema.object({
1195
- manager: import_koishi5.Schema.array(import_koishi5.Schema.string()).required(),
1467
+ var Config = import_koishi6.Schema.object({
1468
+ manager: import_koishi6.Schema.array(import_koishi6.Schema.string()).required(),
1196
1469
  // 管理员用户ID
1197
- number: import_koishi5.Schema.number().default(60),
1470
+ number: import_koishi6.Schema.number().default(60),
1198
1471
  // 冷却时间(秒)
1199
- enableAudit: import_koishi5.Schema.boolean().default(false),
1472
+ enableAudit: import_koishi6.Schema.boolean().default(false),
1200
1473
  // 启用审核
1201
- enableTextDuplicate: import_koishi5.Schema.boolean().default(true),
1474
+ enableTextDuplicate: import_koishi6.Schema.boolean().default(true),
1202
1475
  // 启用文本查重
1203
- textDuplicateThreshold: import_koishi5.Schema.number().default(0.9),
1476
+ textDuplicateThreshold: import_koishi6.Schema.number().default(0.9),
1204
1477
  // 文本查重阈值
1205
- enableImageDuplicate: import_koishi5.Schema.boolean().default(true),
1478
+ enableImageDuplicate: import_koishi6.Schema.boolean().default(true),
1206
1479
  // 开启图片查重
1207
- imageDuplicateThreshold: import_koishi5.Schema.number().default(0.8),
1480
+ imageDuplicateThreshold: import_koishi6.Schema.number().default(0.8),
1208
1481
  // 图片查重阈值
1209
- imageMaxSize: import_koishi5.Schema.number().default(4),
1482
+ imageMaxSize: import_koishi6.Schema.number().default(4),
1210
1483
  // 图片大小限制(MB)
1211
- allowVideo: import_koishi5.Schema.boolean().default(true),
1484
+ allowVideo: import_koishi6.Schema.boolean().default(true),
1212
1485
  // 允许视频
1213
- videoMaxSize: import_koishi5.Schema.number().default(16),
1486
+ videoMaxSize: import_koishi6.Schema.number().default(16),
1214
1487
  // 视频大小限制(MB)
1215
- enablePagination: import_koishi5.Schema.boolean().default(false),
1488
+ enablePagination: import_koishi6.Schema.boolean().default(false),
1216
1489
  // 启用分页
1217
- itemsPerPage: import_koishi5.Schema.number().default(10),
1490
+ itemsPerPage: import_koishi6.Schema.number().default(10),
1218
1491
  // 每页条数
1219
- blacklist: import_koishi5.Schema.array(import_koishi5.Schema.string()).default([]),
1492
+ blacklist: import_koishi6.Schema.array(import_koishi6.Schema.string()).default([]),
1220
1493
  // 黑名单
1221
- whitelist: import_koishi5.Schema.array(import_koishi5.Schema.string()).default([])
1494
+ whitelist: import_koishi6.Schema.array(import_koishi6.Schema.string()).default([])
1222
1495
  // 白名单
1223
1496
  }).i18n({
1224
1497
  "zh-CN": require_zh_CN()._config,
@@ -1227,124 +1500,21 @@ var Config = import_koishi5.Schema.object({
1227
1500
  async function apply(ctx, config) {
1228
1501
  ctx.i18n.define("zh-CN", require_zh_CN());
1229
1502
  ctx.i18n.define("en-US", require_en_US());
1230
- const dataDir = path5.join(ctx.baseDir, "data");
1231
- const caveDir = path5.join(dataDir, "cave");
1232
- await FileHandler.ensureDirectory(dataDir);
1503
+ const dataDir = path7.join(ctx.baseDir, "data");
1504
+ const caveDir = path7.join(dataDir, "cave");
1233
1505
  await FileHandler.ensureDirectory(caveDir);
1234
- await FileHandler.ensureDirectory(path5.join(caveDir, "resources"));
1235
- await FileHandler.ensureJsonFile(path5.join(caveDir, "cave.json"));
1236
- await FileHandler.ensureJsonFile(path5.join(caveDir, "pending.json"));
1237
- await FileHandler.ensureJsonFile(path5.join(caveDir, "hash.json"));
1506
+ await FileHandler.ensureDirectory(path7.join(caveDir, "resources"));
1507
+ await FileHandler.ensureJsonFile(path7.join(caveDir, "cave.json"));
1508
+ await FileHandler.ensureJsonFile(path7.join(caveDir, "pending.json"));
1509
+ await FileHandler.ensureJsonFile(path7.join(caveDir, "hash.json"));
1238
1510
  const idManager = new IdManager(ctx.baseDir);
1239
- const contentHashManager = new ContentHashManager(caveDir);
1511
+ const contentHashManager = new HashManager(caveDir);
1240
1512
  const auditManager = new AuditManager(ctx, config, caveDir, idManager);
1241
1513
  await Promise.all([
1242
- idManager.initialize(path5.join(caveDir, "cave.json"), path5.join(caveDir, "pending.json")),
1514
+ idManager.initialize(path7.join(caveDir, "cave.json"), path7.join(caveDir, "pending.json")),
1243
1515
  contentHashManager.initialize()
1244
1516
  ]);
1245
1517
  const lastUsed = /* @__PURE__ */ new Map();
1246
- async function processList(session, config2, userId, pageNum = 1) {
1247
- const stats = idManager.getStats();
1248
- if (userId && userId in stats) {
1249
- const ids = stats[userId];
1250
- return session.text("commands.cave.list.totalItems", [userId, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1251
- }
1252
- const lines = Object.entries(stats).map(([cid, ids]) => {
1253
- return session.text("commands.cave.list.totalItems", [cid, ids.length]) + "\n" + session.text("commands.cave.list.idsLine", [ids.join(",")]);
1254
- });
1255
- const totalSubmissions = Object.values(stats).reduce((sum, arr) => sum + arr.length, 0);
1256
- if (config2.enablePagination) {
1257
- const itemsPerPage = config2.itemsPerPage;
1258
- const totalPages = Math.max(1, Math.ceil(lines.length / itemsPerPage));
1259
- pageNum = Math.min(Math.max(1, pageNum), totalPages);
1260
- const start = (pageNum - 1) * itemsPerPage;
1261
- const paginatedLines = lines.slice(start, start + itemsPerPage);
1262
- return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + paginatedLines.join("\n") + "\n" + session.text("commands.cave.list.pageInfo", [pageNum, totalPages]);
1263
- } else {
1264
- return session.text("commands.cave.list.header", [totalSubmissions]) + "\n" + lines.join("\n");
1265
- }
1266
- }
1267
- __name(processList, "processList");
1268
- async function processAudit(pendingFilePath, caveFilePath, resourceDir, session, options, content) {
1269
- const pendingData = await FileHandler.readJsonData(pendingFilePath);
1270
- const isApprove = Boolean(options.p);
1271
- if (options.p === true && content[0] === "all" || options.d === true && content[0] === "all") {
1272
- return await auditManager.processAudit(pendingData, isApprove, caveFilePath, resourceDir, pendingFilePath, session);
1273
- }
1274
- const id = parseInt(content[0] || (typeof options.p === "string" ? options.p : "") || (typeof options.d === "string" ? options.d : ""));
1275
- if (isNaN(id)) {
1276
- return sendMessage(session, "commands.cave.error.invalidId", [], true);
1277
- }
1278
- return await auditManager.processAudit(pendingData, isApprove, caveFilePath, resourceDir, pendingFilePath, session, id);
1279
- }
1280
- __name(processAudit, "processAudit");
1281
- async function processView(caveFilePath, resourceDir, session, options, content) {
1282
- const caveId = parseInt(content[0] || (typeof options.g === "string" ? options.g : ""));
1283
- if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1284
- const data = await FileHandler.readJsonData(caveFilePath);
1285
- const cave = data.find((item) => item.cave_id === caveId);
1286
- if (!cave) return sendMessage(session, "commands.cave.error.notFound", [], true);
1287
- return buildMessage(cave, resourceDir, session);
1288
- }
1289
- __name(processView, "processView");
1290
- async function processRandom(caveFilePath, resourceDir, session) {
1291
- const data = await FileHandler.readJsonData(caveFilePath);
1292
- if (data.length === 0) {
1293
- return sendMessage(session, "commands.cave.error.noCave", [], true);
1294
- }
1295
- const cave = (() => {
1296
- const validCaves = data.filter((cave2) => cave2.elements && cave2.elements.length > 0);
1297
- if (!validCaves.length) return void 0;
1298
- const randomIndex = Math.floor(Math.random() * validCaves.length);
1299
- return validCaves[randomIndex];
1300
- })();
1301
- return cave ? buildMessage(cave, resourceDir, session) : sendMessage(session, "commands.cave.error.getCave", [], true);
1302
- }
1303
- __name(processRandom, "processRandom");
1304
- async function processDelete(caveFilePath, resourceDir, pendingFilePath, session, config2, options, content) {
1305
- const caveId = parseInt(content[0] || (typeof options.r === "string" ? options.r : ""));
1306
- if (isNaN(caveId)) return sendMessage(session, "commands.cave.error.invalidId", [], true);
1307
- const data = await FileHandler.readJsonData(caveFilePath);
1308
- const pendingData = await FileHandler.readJsonData(pendingFilePath);
1309
- const targetInData = data.find((item) => item.cave_id === caveId);
1310
- const targetInPending = pendingData.find((item) => item.cave_id === caveId);
1311
- if (!targetInData && !targetInPending) {
1312
- return sendMessage(session, "commands.cave.error.notFound", [], true);
1313
- }
1314
- const targetCave = targetInData || targetInPending;
1315
- const isPending = !targetInData;
1316
- if (targetCave.contributor_number !== session.userId && !config2.manager.includes(session.userId)) {
1317
- return sendMessage(session, "commands.cave.remove.noPermission", [], true);
1318
- }
1319
- const caveContent = await buildMessage(targetCave, resourceDir, session);
1320
- if (targetCave.elements) {
1321
- await contentHashManager.updateCaveContent(caveId, {
1322
- images: void 0,
1323
- texts: void 0
1324
- });
1325
- for (const element of targetCave.elements) {
1326
- if ((element.type === "img" || element.type === "video") && element.file) {
1327
- const fullPath = path5.join(resourceDir, element.file);
1328
- if (fs5.existsSync(fullPath)) {
1329
- await fs5.promises.unlink(fullPath);
1330
- }
1331
- }
1332
- }
1333
- }
1334
- if (isPending) {
1335
- const newPendingData = pendingData.filter((item) => item.cave_id !== caveId);
1336
- await FileHandler.writeJsonData(pendingFilePath, newPendingData);
1337
- } else {
1338
- const newData = data.filter((item) => item.cave_id !== caveId);
1339
- await FileHandler.writeJsonData(caveFilePath, newData);
1340
- await idManager.removeStat(targetCave.contributor_number, caveId);
1341
- }
1342
- await idManager.markDeleted(caveId);
1343
- const deleteStatus = isPending ? session.text("commands.cave.remove.deletePending") : "";
1344
- const deleteMessage = session.text("commands.cave.remove.deleted");
1345
- return `${deleteMessage}${deleteStatus}${caveContent}`;
1346
- }
1347
- __name(processDelete, "processDelete");
1348
1518
  async function processAdd(ctx2, config2, caveFilePath, resourceDir, pendingFilePath, session, content) {
1349
1519
  let caveId;
1350
1520
  try {
@@ -1355,9 +1525,15 @@ async function apply(ctx, config) {
1355
1525
  const inputContent = content.length > 0 ? content.join("\n") : await (async () => {
1356
1526
  await sendMessage(session, "commands.cave.add.noContent", [], true, 6e4);
1357
1527
  const reply = await session.prompt({ timeout: 6e4 });
1358
- if (!reply) sendMessage(session, "commands.cave.add.operationTimeout", [], true);
1528
+ if (!reply) {
1529
+ await sendMessage(session, "commands.cave.add.operationTimeout", [], true);
1530
+ return null;
1531
+ }
1359
1532
  return reply;
1360
1533
  })();
1534
+ if (!inputContent) {
1535
+ return "";
1536
+ }
1361
1537
  if (inputContent.includes("/app/.config/QQ/")) {
1362
1538
  return sendMessage(session, "commands.cave.add.localFileNotAllowed", [], true);
1363
1539
  }
@@ -1415,7 +1591,7 @@ async function apply(ctx, config) {
1415
1591
  index: Number.MAX_SAFE_INTEGER
1416
1592
  });
1417
1593
  }
1418
- const hashStorage = new ContentHashManager(path5.join(ctx2.baseDir, "data", "cave"));
1594
+ const hashStorage = new HashManager(path7.join(ctx2.baseDir, "data", "cave"));
1419
1595
  await hashStorage.initialize();
1420
1596
  const hashStatus = await hashStorage.getStatus();
1421
1597
  if (!hashStatus.lastUpdated || hashStatus.entries.length === 0) {
@@ -1465,7 +1641,7 @@ async function apply(ctx, config) {
1465
1641
  await Promise.all([
1466
1642
  FileHandler.writeJsonData(caveFilePath, data),
1467
1643
  contentHashManager.updateCaveContent(caveId, {
1468
- images: savedImages.length > 0 ? await Promise.all(savedImages.map((file) => fs5.promises.readFile(path5.join(resourceDir, file)))) : void 0,
1644
+ images: savedImages.length > 0 ? await Promise.all(savedImages.map((file) => fs7.promises.readFile(path7.join(resourceDir, file)))) : void 0,
1469
1645
  texts: textParts.filter((p) => p.type === "text").map((p) => p.content)
1470
1646
  })
1471
1647
  ]);
@@ -1478,7 +1654,7 @@ async function apply(ctx, config) {
1478
1654
  if (error.message === "duplicate_found") {
1479
1655
  return "";
1480
1656
  }
1481
- logger4.error(`Failed to process add command: ${error.message}`);
1657
+ logger5.error(`Failed to process add command: ${error.message}`);
1482
1658
  return sendMessage(session, "commands.cave.error.addFailed", [], true);
1483
1659
  }
1484
1660
  }
@@ -1488,11 +1664,11 @@ async function apply(ctx, config) {
1488
1664
  return sendMessage(session, "commands.cave.message.blacklisted", [], true);
1489
1665
  }
1490
1666
  }).action(async ({ session, options }, ...content) => {
1491
- const dataDir2 = path5.join(ctx.baseDir, "data");
1492
- const caveDir2 = path5.join(dataDir2, "cave");
1493
- const caveFilePath = path5.join(caveDir2, "cave.json");
1494
- const resourceDir = path5.join(caveDir2, "resources");
1495
- const pendingFilePath = path5.join(caveDir2, "pending.json");
1667
+ const dataDir2 = path7.join(ctx.baseDir, "data");
1668
+ const caveDir2 = path7.join(dataDir2, "cave");
1669
+ const caveFilePath = path7.join(caveDir2, "cave.json");
1670
+ const resourceDir = path7.join(caveDir2, "resources");
1671
+ const pendingFilePath = path7.join(caveDir2, "pending.json");
1496
1672
  const needsCooldown = !options.l && !options.a;
1497
1673
  if (needsCooldown) {
1498
1674
  const guildId = session.guildId;
@@ -1511,23 +1687,23 @@ async function apply(ctx, config) {
1511
1687
  if (config.manager.includes(session.userId)) {
1512
1688
  if (!isNaN(num)) {
1513
1689
  if (num < 1e4) {
1514
- return await processList(session, config, void 0, num);
1690
+ return await processList(session, config, idManager, void 0, num);
1515
1691
  } else {
1516
- return await processList(session, config, num.toString());
1692
+ return await processList(session, config, idManager, num.toString());
1517
1693
  }
1518
1694
  } else if (input) {
1519
- return await processList(session, config, input);
1695
+ return await processList(session, config, idManager, input);
1520
1696
  }
1521
- return await processList(session, config);
1697
+ return await processList(session, config, idManager);
1522
1698
  } else {
1523
- return await processList(session, config, session.userId);
1699
+ return await processList(session, config, idManager, session.userId);
1524
1700
  }
1525
1701
  }
1526
1702
  if (options.g) {
1527
1703
  return await processView(caveFilePath, resourceDir, session, options, content);
1528
1704
  }
1529
1705
  if (options.r) {
1530
- return await processDelete(caveFilePath, resourceDir, pendingFilePath, session, config, options, content);
1706
+ return await processDelete(caveFilePath, resourceDir, pendingFilePath, session, config, options, content, idManager, contentHashManager);
1531
1707
  }
1532
1708
  if (options.a) {
1533
1709
  return await processAdd(ctx, config, caveFilePath, resourceDir, pendingFilePath, session, content);
@@ -1539,60 +1715,30 @@ async function apply(ctx, config) {
1539
1715
  return sendMessage(session, "commands.cave.message.managerOnly", [], true);
1540
1716
  }
1541
1717
  }).action(async ({ session }, id) => {
1542
- const dataDir2 = path5.join(ctx.baseDir, "data");
1543
- const caveDir2 = path5.join(dataDir2, "cave");
1544
- const caveFilePath = path5.join(caveDir2, "cave.json");
1545
- const resourceDir = path5.join(caveDir2, "resources");
1546
- const pendingFilePath = path5.join(caveDir2, "pending.json");
1547
- return await processAudit(
1548
- pendingFilePath,
1549
- caveFilePath,
1550
- resourceDir,
1551
- session,
1552
- { p: true },
1553
- [id]
1554
- );
1718
+ const dataDir2 = path7.join(ctx.baseDir, "data");
1719
+ const caveDir2 = path7.join(dataDir2, "cave");
1720
+ const caveFilePath = path7.join(caveDir2, "cave.json");
1721
+ const resourceDir = path7.join(caveDir2, "resources");
1722
+ const pendingFilePath = path7.join(caveDir2, "pending.json");
1723
+ const pendingData = await FileHandler.readJsonData(pendingFilePath);
1724
+ return await auditManager.processAudit(pendingData, true, caveFilePath, resourceDir, pendingFilePath, session, id === "all" ? void 0 : parseInt(id));
1555
1725
  });
1556
1726
  caveCommand.subcommand(".reject <id:text>", "拒绝回声洞审核").before(async ({ session }) => {
1557
1727
  if (!config.manager.includes(session.userId)) {
1558
1728
  return sendMessage(session, "commands.cave.message.managerOnly", [], true);
1559
1729
  }
1560
1730
  }).action(async ({ session }, id) => {
1561
- const dataDir2 = path5.join(ctx.baseDir, "data");
1562
- const caveDir2 = path5.join(dataDir2, "cave");
1563
- const caveFilePath = path5.join(caveDir2, "cave.json");
1564
- const resourceDir = path5.join(caveDir2, "resources");
1565
- const pendingFilePath = path5.join(caveDir2, "pending.json");
1566
- return await processAudit(
1567
- pendingFilePath,
1568
- caveFilePath,
1569
- resourceDir,
1570
- session,
1571
- { d: true },
1572
- [id]
1573
- );
1731
+ const dataDir2 = path7.join(ctx.baseDir, "data");
1732
+ const caveDir2 = path7.join(dataDir2, "cave");
1733
+ const caveFilePath = path7.join(caveDir2, "cave.json");
1734
+ const resourceDir = path7.join(caveDir2, "resources");
1735
+ const pendingFilePath = path7.join(caveDir2, "pending.json");
1736
+ const pendingData = await FileHandler.readJsonData(pendingFilePath);
1737
+ return await auditManager.processAudit(pendingData, false, caveFilePath, resourceDir, pendingFilePath, session, id === "all" ? void 0 : parseInt(id));
1574
1738
  });
1575
1739
  }
1576
1740
  __name(apply, "apply");
1577
- var logger4 = new import_koishi5.Logger("cave");
1578
- async function sendMessage(session, key, params = [], isTemp = true, timeout = 1e4) {
1579
- try {
1580
- const msg = await session.send(session.text(key, params));
1581
- if (isTemp && msg) {
1582
- setTimeout(async () => {
1583
- try {
1584
- await session.bot.deleteMessage(session.channelId, msg);
1585
- } catch (error) {
1586
- logger4.debug(`Failed to delete temporary message: ${error.message}`);
1587
- }
1588
- }, timeout);
1589
- }
1590
- } catch (error) {
1591
- logger4.error(`Failed to send message: ${error.message}`);
1592
- }
1593
- return "";
1594
- }
1595
- __name(sendMessage, "sendMessage");
1741
+ var logger5 = new import_koishi6.Logger("cave");
1596
1742
  function cleanElementsForSave(elements, keepIndex = false) {
1597
1743
  if (!elements?.length) return [];
1598
1744
  const cleanedElements = elements.map((element) => {
@@ -1617,171 +1763,6 @@ function cleanElementsForSave(elements, keepIndex = false) {
1617
1763
  return keepIndex ? cleanedElements.sort((a, b) => (a.index || 0) - (b.index || 0)) : cleanedElements;
1618
1764
  }
1619
1765
  __name(cleanElementsForSave, "cleanElementsForSave");
1620
- async function processMediaFile(filePath, type) {
1621
- const data = await fs5.promises.readFile(filePath).catch(() => null);
1622
- if (!data) return null;
1623
- return `data:${type}/${type === "image" ? "png" : "mp4"};base64,${data.toString("base64")}`;
1624
- }
1625
- __name(processMediaFile, "processMediaFile");
1626
- async function buildMessage(cave, resourceDir, session) {
1627
- if (!cave?.elements?.length) {
1628
- return session.text("commands.cave.error.noContent");
1629
- }
1630
- const videoElement = cave.elements.find((el) => el.type === "video");
1631
- const nonVideoElements = cave.elements.filter((el) => el.type !== "video").sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1632
- if (videoElement?.file) {
1633
- const basicInfo = [
1634
- session.text("commands.cave.message.caveTitle", [cave.cave_id]),
1635
- session.text("commands.cave.message.contributorSuffix", [cave.contributor_name])
1636
- ].join("\n");
1637
- await session?.send(basicInfo);
1638
- const filePath = path5.join(resourceDir, videoElement.file);
1639
- const base64Data = await processMediaFile(filePath, "video");
1640
- if (base64Data && session) {
1641
- await session.send((0, import_koishi5.h)("video", { src: base64Data }));
1642
- }
1643
- return "";
1644
- }
1645
- const lines = [session.text("commands.cave.message.caveTitle", [cave.cave_id])];
1646
- for (const element of nonVideoElements) {
1647
- if (element.type === "text") {
1648
- lines.push(element.content);
1649
- } else if (element.type === "img" && element.file) {
1650
- const filePath = path5.join(resourceDir, element.file);
1651
- const base64Data = await processMediaFile(filePath, "image");
1652
- if (base64Data) {
1653
- lines.push((0, import_koishi5.h)("image", { src: base64Data }));
1654
- }
1655
- }
1656
- }
1657
- lines.push(session.text("commands.cave.message.contributorSuffix", [cave.contributor_name]));
1658
- return lines.join("\n");
1659
- }
1660
- __name(buildMessage, "buildMessage");
1661
- async function extractMediaContent(originalContent, config, session) {
1662
- const textParts = originalContent.split(/<(img|video)[^>]+>/).map((text, idx) => text.trim() && {
1663
- type: "text",
1664
- content: text.replace(/^(img|video)$/, "").trim(),
1665
- index: idx * 3
1666
- }).filter((text) => text && text.content);
1667
- const getMediaElements = /* @__PURE__ */ __name((type, maxSize) => {
1668
- const regex = new RegExp(`<${type}[^>]+src="([^"]+)"[^>]*>`, "g");
1669
- const elements = [];
1670
- const urls = [];
1671
- let match;
1672
- let idx = 0;
1673
- while ((match = regex.exec(originalContent)) !== null) {
1674
- const element = match[0];
1675
- const url = match[1];
1676
- const fileName = element.match(/file="([^"]+)"/)?.[1];
1677
- const fileSize = element.match(/fileSize="([^"]+)"/)?.[1];
1678
- if (fileSize) {
1679
- const sizeInBytes = parseInt(fileSize);
1680
- if (sizeInBytes > maxSize * 1024 * 1024) {
1681
- throw new Error(session.text("commands.cave.message.mediaSizeExceeded", [type]));
1682
- }
1683
- }
1684
- urls.push(url);
1685
- elements.push({
1686
- type,
1687
- index: type === "video" ? Number.MAX_SAFE_INTEGER : idx * 3 + 1,
1688
- fileName,
1689
- fileSize
1690
- });
1691
- idx++;
1692
- }
1693
- return { urls, elements };
1694
- }, "getMediaElements");
1695
- const { urls: imageUrls, elements: imageElementsRaw } = getMediaElements("img", config.imageMaxSize);
1696
- const imageElements = imageElementsRaw;
1697
- const { urls: videoUrls, elements: videoElementsRaw } = getMediaElements("video", config.videoMaxSize);
1698
- const videoElements = videoElementsRaw;
1699
- return { imageUrls, imageElements, videoUrls, videoElements, textParts };
1700
- }
1701
- __name(extractMediaContent, "extractMediaContent");
1702
- async function saveMedia(urls, fileNames, resourceDir, caveId, mediaType, config, ctx, session, buffers) {
1703
- const accept = mediaType === "img" ? "image/*" : "video/*";
1704
- const hashStorage = new ContentHashManager(path5.join(ctx.baseDir, "data", "cave"));
1705
- await hashStorage.initialize();
1706
- const downloadTasks = urls.map(async (url, i) => {
1707
- const fileName = fileNames[i];
1708
- const ext = path5.extname(fileName || url) || (mediaType === "img" ? ".png" : ".mp4");
1709
- try {
1710
- const response = await ctx.http(decodeURIComponent(url).replace(/&amp;/g, "&"), {
1711
- method: "GET",
1712
- responseType: "arraybuffer",
1713
- timeout: 3e4,
1714
- headers: {
1715
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
1716
- "Accept": accept,
1717
- "Referer": "https://qq.com"
1718
- }
1719
- });
1720
- if (!response.data) throw new Error("empty_response");
1721
- const buffer = Buffer.from(response.data);
1722
- if (buffers && mediaType === "img") {
1723
- buffers.push(buffer);
1724
- }
1725
- const md5 = path5.basename(fileName || `${mediaType}`, ext).replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
1726
- const files = await fs5.promises.readdir(resourceDir);
1727
- const duplicateFile = files.find((file) => {
1728
- const match = file.match(/^\d+_([^.]+)/);
1729
- return match && match[1] === md5;
1730
- });
1731
- if (duplicateFile) {
1732
- const duplicateCaveId = parseInt(duplicateFile.split("_")[0]);
1733
- if (!isNaN(duplicateCaveId)) {
1734
- const caveFilePath = path5.join(ctx.baseDir, "data", "cave", "cave.json");
1735
- const data = await FileHandler.readJsonData(caveFilePath);
1736
- const originalCave = data.find((item) => item.cave_id === duplicateCaveId);
1737
- if (originalCave) {
1738
- const message = session.text("commands.cave.error.exactDuplicateFound");
1739
- await session.send(message + await buildMessage(originalCave, resourceDir, session));
1740
- throw new Error("duplicate_found");
1741
- }
1742
- }
1743
- }
1744
- if (mediaType === "img" && config.enableImageDuplicate) {
1745
- const result = await hashStorage.findDuplicates(
1746
- { images: [buffer] },
1747
- {
1748
- image: config.imageDuplicateThreshold,
1749
- text: config.textDuplicateThreshold
1750
- }
1751
- );
1752
- if (result.length > 0 && result[0] !== null) {
1753
- const duplicate = result[0];
1754
- const similarity = duplicate.similarity;
1755
- if (similarity >= config.imageDuplicateThreshold) {
1756
- const caveFilePath = path5.join(ctx.baseDir, "data", "cave", "cave.json");
1757
- const data = await FileHandler.readJsonData(caveFilePath);
1758
- const originalCave = data.find((item) => item.cave_id === duplicate.caveId);
1759
- if (originalCave) {
1760
- const message = session.text(
1761
- "commands.cave.error.similarDuplicateFound",
1762
- [(similarity * 100).toFixed(1)]
1763
- );
1764
- await session.send(message + await buildMessage(originalCave, resourceDir, session));
1765
- throw new Error("duplicate_found");
1766
- }
1767
- }
1768
- }
1769
- }
1770
- const finalFileName = `${caveId}_${md5}${ext}`;
1771
- const filePath = path5.join(resourceDir, finalFileName);
1772
- await FileHandler.saveMediaFile(filePath, buffer);
1773
- return finalFileName;
1774
- } catch (error) {
1775
- if (error.message === "duplicate_found") {
1776
- throw error;
1777
- }
1778
- logger4.error(`Failed to download media: ${error.message}`);
1779
- throw new Error(session.text(`commands.cave.error.upload${mediaType === "img" ? "Image" : "Video"}Failed`));
1780
- }
1781
- });
1782
- return Promise.all(downloadTasks);
1783
- }
1784
- __name(saveMedia, "saveMedia");
1785
1766
  // Annotate the CommonJS export names for ESM import in node:
1786
1767
  0 && (module.exports = {
1787
1768
  Config,