koishi-plugin-best-cave 2.3.14 → 2.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Utils.d.ts CHANGED
@@ -9,9 +9,10 @@ import { PendManager } from './PendManager';
9
9
  * @param config 插件配置。
10
10
  * @param fileManager 文件管理器实例。
11
11
  * @param logger 日志记录器实例。
12
- * @returns 包含 h() 元素和字符串的消息数组。
12
+ * @param platform 目标平台名称 (e.g., 'onebot')
13
+ * @returns 包含多条消息的数组,每条消息是一个 (string | h)[] 数组。
13
14
  */
14
- export declare function buildCaveMessage(cave: CaveObject, config: Config, fileManager: FileManager, logger: Logger): Promise<(string | h)[]>;
15
+ export declare function buildCaveMessage(cave: CaveObject, config: Config, fileManager: FileManager, logger: Logger, platform?: string): Promise<(string | h)[][]>;
15
16
  /**
16
17
  * @description 清理数据库中标记为 'delete' 状态的回声洞及其关联文件和哈希。
17
18
  * @param ctx Koishi 上下文。
package/lib/index.d.ts CHANGED
@@ -3,12 +3,20 @@ import { CaveHashObject } from './HashManager';
3
3
  export declare const name = "best-cave";
4
4
  export declare const inject: string[];
5
5
  export declare const usage = "\n<div style=\"border-radius: 10px; border: 1px solid #ddd; padding: 16px; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);\">\n <h2 style=\"margin-top: 0; color: #4a6ee0;\">\uD83D\uDCCC \u63D2\u4EF6\u8BF4\u660E</h2>\n <p>\uD83D\uDCD6 <strong>\u4F7F\u7528\u6587\u6863</strong>\uFF1A\u8BF7\u70B9\u51FB\u5DE6\u4E0A\u89D2\u7684 <strong>\u63D2\u4EF6\u4E3B\u9875</strong> \u67E5\u770B\u63D2\u4EF6\u4F7F\u7528\u6587\u6863</p>\n <p>\uD83D\uDD0D <strong>\u66F4\u591A\u63D2\u4EF6</strong>\uFF1A\u53EF\u8BBF\u95EE <a href=\"https://github.com/YisRime\" style=\"color:#4a6ee0;text-decoration:none;\">\u82E1\u6DDE\u7684 GitHub</a> \u67E5\u770B\u672C\u4EBA\u7684\u6240\u6709\u63D2\u4EF6</p>\n</div>\n<div style=\"border-radius: 10px; border: 1px solid #ddd; padding: 16px; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);\">\n <h2 style=\"margin-top: 0; color: #e0574a;\">\u2764\uFE0F \u652F\u6301\u4E0E\u53CD\u9988</h2>\n <p>\uD83C\uDF1F \u559C\u6B22\u8FD9\u4E2A\u63D2\u4EF6\uFF1F\u8BF7\u5728 <a href=\"https://github.com/YisRime\" style=\"color:#e0574a;text-decoration:none;\">GitHub</a> \u4E0A\u7ED9\u6211\u4E00\u4E2A Star\uFF01</p>\n <p>\uD83D\uDC1B \u9047\u5230\u95EE\u9898\uFF1F\u8BF7\u901A\u8FC7 <strong>Issues</strong> \u63D0\u4EA4\u53CD\u9988\uFF0C\u6216\u52A0\u5165 QQ \u7FA4 <a href=\"https://qm.qq.com/q/PdLMx9Jowq\" style=\"color:#e0574a;text-decoration:none;\"><strong>855571375</strong></a> \u8FDB\u884C\u4EA4\u6D41</p>\n</div>\n";
6
+ /**
7
+ * @description 存储在合并转发中的单个节点的数据结构。
8
+ */
9
+ export interface ForwardNode {
10
+ userId: string;
11
+ userName: string;
12
+ elements: StoredElement[];
13
+ }
6
14
  /**
7
15
  * @description 存储在数据库中的单个消息元素。
8
16
  */
9
17
  export interface StoredElement {
10
18
  type: 'text' | 'image' | 'video' | 'audio' | 'file' | 'at' | 'forward' | 'reply';
11
- content?: string;
19
+ content?: string | ForwardNode[];
12
20
  file?: string;
13
21
  }
14
22
  /**
package/lib/index.js CHANGED
@@ -292,7 +292,7 @@ var import_koishi2 = require("koishi");
292
292
  var import_koishi = require("koishi");
293
293
  var path2 = __toESM(require("path"));
294
294
  var mimeTypeMap = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".mp4": "video/mp4", ".mp3": "audio/mpeg", ".webp": "image/webp" };
295
- async function buildCaveMessage(cave, config, fileManager, logger2) {
295
+ async function buildCaveMessage(cave, config, fileManager, logger2, platform) {
296
296
  async function transformToH(elements) {
297
297
  return Promise.all(elements.map(async (el) => {
298
298
  if (el.type === "text") return import_koishi.h.text(el.content);
@@ -300,7 +300,7 @@ async function buildCaveMessage(cave, config, fileManager, logger2) {
300
300
  if (el.type === "reply") return (0, import_koishi.h)("reply", { id: el.content });
301
301
  if (el.type === "forward") {
302
302
  try {
303
- const forwardNodes = JSON.parse(el.content || "[]");
303
+ const forwardNodes = Array.isArray(el.content) ? el.content : [];
304
304
  const messageNodes = await Promise.all(forwardNodes.map(async (node) => {
305
305
  const author = (0, import_koishi.h)("author", { id: node.userId, name: node.userName });
306
306
  const content = await transformToH(node.elements);
@@ -337,11 +337,31 @@ async function buildCaveMessage(cave, config, fileManager, logger2) {
337
337
  const caveHElements = await transformToH(cave.elements);
338
338
  const replacements = { id: cave.id.toString(), name: cave.userName };
339
339
  const [header, footer] = config.caveFormat.split("|", 2).map((part) => part.replace(/\{id\}|\{name\}/g, (match) => replacements[match.slice(1, -1)]).trim());
340
- const finalMessage = [];
341
- if (header) finalMessage.push(header + "\n");
342
- finalMessage.push(...caveHElements);
343
- if (footer) finalMessage.push("\n" + footer);
344
- return finalMessage;
340
+ const problematicTypes = ["video", "audio", "file", "forward"];
341
+ const placeholderMap = { video: "[视频]", audio: "[音频]", file: "[文件]", forward: "[合并转发]" };
342
+ const containsProblematic = platform === "onebot" && caveHElements.some((el) => problematicTypes.includes(el.type));
343
+ if (!containsProblematic) {
344
+ const finalMessage = [];
345
+ if (header) finalMessage.push(header + "\n");
346
+ finalMessage.push(...caveHElements);
347
+ if (footer) finalMessage.push("\n" + footer);
348
+ return [finalMessage.length > 0 ? finalMessage : []];
349
+ }
350
+ const initialMessageContent = [];
351
+ const followUpMessages = [];
352
+ for (const el of caveHElements) {
353
+ if (problematicTypes.includes(el.type)) {
354
+ initialMessageContent.push(import_koishi.h.text(placeholderMap[el.type]));
355
+ followUpMessages.push([el]);
356
+ } else {
357
+ initialMessageContent.push(el);
358
+ }
359
+ }
360
+ const finalInitialMessage = [];
361
+ if (header) finalInitialMessage.push(header + "\n");
362
+ finalInitialMessage.push(...initialMessageContent);
363
+ if (footer) finalInitialMessage.push("\n" + footer);
364
+ return [finalInitialMessage, ...followUpMessages].filter((msg) => msg.length > 0);
345
365
  }
346
366
  __name(buildCaveMessage, "buildCaveMessage");
347
367
  async function cleanupPendingDeletions(ctx, fileManager, logger2, reusableIds) {
@@ -458,7 +478,7 @@ async function processMessageElements(sourceElements, newId, session, config, lo
458
478
  }
459
479
  }
460
480
  if (forwardNodes.length > 0) {
461
- result.push({ type: "forward", content: JSON.stringify(forwardNodes) });
481
+ result.push({ type: "forward", content: forwardNodes });
462
482
  }
463
483
  } else if (["image", "video", "audio", "file"].includes(type) && el.attrs.src) {
464
484
  let fileIdentifier = el.attrs.src;
@@ -564,7 +584,12 @@ var PendManager = class {
564
584
  const [targetCave] = await this.ctx.database.get("cave", { id });
565
585
  if (!targetCave) return `回声洞(${id})不存在`;
566
586
  if (targetCave.status !== "pending") return `回声洞(${id})无需审核`;
567
- return [`待审核`, ...await buildCaveMessage(targetCave, this.config, this.fileManager, this.logger)];
587
+ await session.send("待审核");
588
+ const caveMessages = await buildCaveMessage(targetCave, this.config, this.fileManager, this.logger, session.platform);
589
+ for (const message of caveMessages) {
590
+ if (message.length > 0) await session.send(import_koishi2.h.normalize(message));
591
+ }
592
+ return;
568
593
  }
569
594
  const pendingCaves = await this.ctx.database.get("cave", { status: "pending" }, { fields: ["id"] });
570
595
  if (!pendingCaves.length) return "当前没有需要审核的回声洞";
@@ -608,8 +633,12 @@ ${pendingCaves.map((c) => c.id).join("|")}`;
608
633
  return;
609
634
  }
610
635
  try {
611
- const pendMessage = [`待审核`, ...await buildCaveMessage(cave, this.config, this.fileManager, this.logger)];
612
- await this.ctx.broadcast([this.config.adminChannel], import_koishi2.h.normalize(pendMessage));
636
+ const [platform] = this.config.adminChannel.split(":", 1);
637
+ const caveMessages = await buildCaveMessage(cave, this.config, this.fileManager, this.logger, platform);
638
+ await this.ctx.broadcast([this.config.adminChannel], "待审核");
639
+ for (const message of caveMessages) {
640
+ if (message.length > 0) await this.ctx.broadcast([this.config.adminChannel], import_koishi2.h.normalize(message));
641
+ }
613
642
  } catch (error) {
614
643
  this.logger.error(`发送回声洞(${cave.id})审核消息失败:`, error);
615
644
  }
@@ -1067,7 +1096,10 @@ function apply(ctx, config) {
1067
1096
  const randomId = candidates[Math.floor(Math.random() * candidates.length)].id;
1068
1097
  const [randomCave] = await ctx.database.get("cave", { ...query, id: randomId });
1069
1098
  updateCooldownTimestamp(session, config, lastUsed);
1070
- return buildCaveMessage(randomCave, config, fileManager, logger);
1099
+ const messages = await buildCaveMessage(randomCave, config, fileManager, logger, session.platform);
1100
+ for (const message of messages) {
1101
+ if (message.length > 0) await session.send(import_koishi3.h.normalize(message));
1102
+ }
1071
1103
  } catch (error) {
1072
1104
  logger.error("随机获取回声洞失败:", error);
1073
1105
  return "随机获取回声洞失败";
@@ -1101,7 +1133,7 @@ ${JSON.stringify(finalElementsForDb, null, 2)}`);
1101
1133
  if (finalElementsForDb.length === 0) return "无可添加内容";
1102
1134
  const textHashesToStore = [];
1103
1135
  if (hashManager) {
1104
- const combinedText = finalElementsForDb.filter((el) => el.type === "text" && el.content).map((el) => el.content).join(" ");
1136
+ const combinedText = finalElementsForDb.filter((el) => el.type === "text" && typeof el.content === "string").map((el) => el.content).join(" ");
1105
1137
  if (combinedText) {
1106
1138
  const newSimhash = hashManager.generateTextSimhash(combinedText);
1107
1139
  if (newSimhash) {
@@ -1152,7 +1184,10 @@ ${JSON.stringify(finalElementsForDb, null, 2)}`);
1152
1184
  const [targetCave] = await ctx.database.get("cave", { ...getScopeQuery(session, config), id });
1153
1185
  if (!targetCave) return `回声洞(${id})不存在`;
1154
1186
  updateCooldownTimestamp(session, config, lastUsed);
1155
- return buildCaveMessage(targetCave, config, fileManager, logger);
1187
+ const messages = await buildCaveMessage(targetCave, config, fileManager, logger, session.platform);
1188
+ for (const message of messages) {
1189
+ if (message.length > 0) await session.send(import_koishi3.h.normalize(message));
1190
+ }
1156
1191
  } catch (error) {
1157
1192
  logger.error(`查看回声洞(${id})失败:`, error);
1158
1193
  return "查看失败,请稍后再试";
@@ -1167,9 +1202,12 @@ ${JSON.stringify(finalElementsForDb, null, 2)}`);
1167
1202
  const isAdmin = session.channelId === config.adminChannel?.split(":")[1];
1168
1203
  if (!isAuthor && !isAdmin) return "你没有权限删除这条回声洞";
1169
1204
  await ctx.database.upsert("cave", [{ id, status: "delete" }]);
1170
- const caveMessage = await buildCaveMessage(targetCave, config, fileManager, logger);
1205
+ const caveMessages = await buildCaveMessage(targetCave, config, fileManager, logger, session.platform);
1171
1206
  cleanupPendingDeletions(ctx, fileManager, logger, reusableIds);
1172
- return [`已删除`, ...caveMessage];
1207
+ await session.send("已删除");
1208
+ for (const message of caveMessages) {
1209
+ if (message.length > 0) await session.send(import_koishi3.h.normalize(message));
1210
+ }
1173
1211
  } catch (error) {
1174
1212
  logger.error(`标记回声洞(${id})失败:`, error);
1175
1213
  return "删除失败,请稍后再试";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-best-cave",
3
3
  "description": "功能强大、高度可定制的回声洞。支持丰富的媒体类型、内容查重、人工审核、用户昵称、数据迁移以及本地/S3 双重文件存储后端。",
4
- "version": "2.3.14",
4
+ "version": "2.3.16",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],