koishi-plugin-best-cave 2.3.3 → 2.3.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/DataManager.d.ts +39 -0
- package/lib/FileManager.d.ts +48 -0
- package/lib/Utils.d.ts +0 -6
- package/lib/index.d.ts +1 -1
- package/lib/index.js +72 -44
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Context, Logger } from 'koishi';
|
|
2
|
+
import { FileManager } from './FileManager';
|
|
3
|
+
import { HashManager } from './HashManager';
|
|
4
|
+
import { Config } from './index';
|
|
5
|
+
/**
|
|
6
|
+
* @class DataManager
|
|
7
|
+
* @description 负责处理回声洞数据的导入和导出功能。
|
|
8
|
+
*/
|
|
9
|
+
export declare class DataManager {
|
|
10
|
+
private ctx;
|
|
11
|
+
private config;
|
|
12
|
+
private fileManager;
|
|
13
|
+
private logger;
|
|
14
|
+
private hashManager;
|
|
15
|
+
/**
|
|
16
|
+
* @constructor
|
|
17
|
+
* @param ctx Koishi 上下文,用于数据库操作。
|
|
18
|
+
* @param config 插件配置。
|
|
19
|
+
* @param fileManager 文件管理器实例。
|
|
20
|
+
* @param logger 日志记录器实例。
|
|
21
|
+
* @param hashManager 哈希管理器实例,用于增量更新哈希。
|
|
22
|
+
*/
|
|
23
|
+
constructor(ctx: Context, config: Config, fileManager: FileManager, logger: Logger, hashManager: HashManager | null);
|
|
24
|
+
/**
|
|
25
|
+
* @description 注册 `.export` 和 `.import` 子命令。
|
|
26
|
+
* @param cave - 主 `cave` 命令实例。
|
|
27
|
+
*/
|
|
28
|
+
registerCommands(cave: any): void;
|
|
29
|
+
/**
|
|
30
|
+
* @description 导出所有 'active' 状态的回声洞数据到 `cave_export.json`。
|
|
31
|
+
* @returns 描述导出结果的消息字符串。
|
|
32
|
+
*/
|
|
33
|
+
exportData(): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* @description 从 `cave_import.json` 文件导入回声洞数据。
|
|
36
|
+
* @returns 描述导入结果的消息字符串。
|
|
37
|
+
*/
|
|
38
|
+
importData(): Promise<string>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Logger } from 'koishi';
|
|
2
|
+
import { Config } from './index';
|
|
3
|
+
/**
|
|
4
|
+
* @class FileManager
|
|
5
|
+
* @description 封装了对文件的存储、读取和删除操作。
|
|
6
|
+
* 能根据配置自动选择使用本地文件系统或 AWS S3 作为存储后端。
|
|
7
|
+
* 内置 Promise 文件锁,防止本地文件的并发写入冲突。
|
|
8
|
+
*/
|
|
9
|
+
export declare class FileManager {
|
|
10
|
+
private logger;
|
|
11
|
+
private resourceDir;
|
|
12
|
+
private locks;
|
|
13
|
+
private s3Client?;
|
|
14
|
+
private s3Bucket?;
|
|
15
|
+
/**
|
|
16
|
+
* @constructor
|
|
17
|
+
* @param baseDir Koishi 应用的基础数据目录 (ctx.baseDir)。
|
|
18
|
+
* @param config 插件的配置对象。
|
|
19
|
+
* @param logger 日志记录器实例。
|
|
20
|
+
*/
|
|
21
|
+
constructor(baseDir: string, config: Config, logger: Logger);
|
|
22
|
+
/**
|
|
23
|
+
* @description 使用文件锁安全地执行异步文件操作,防止并发读写冲突。
|
|
24
|
+
* @template T 异步操作的返回类型。
|
|
25
|
+
* @param fullPath 需要加锁的文件的完整路径。
|
|
26
|
+
* @param operation 要执行的异步函数。
|
|
27
|
+
* @returns 异步操作的结果。
|
|
28
|
+
*/
|
|
29
|
+
private withLock;
|
|
30
|
+
/**
|
|
31
|
+
* @description 保存文件,自动选择 S3 或本地存储。
|
|
32
|
+
* @param fileName 用作 S3 Key 或本地文件名。
|
|
33
|
+
* @param data 要写入的 Buffer 数据。
|
|
34
|
+
* @returns 保存时使用的文件名。
|
|
35
|
+
*/
|
|
36
|
+
saveFile(fileName: string, data: Buffer): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* @description 读取文件,自动从 S3 或本地存储读取。
|
|
39
|
+
* @param fileName 要读取的文件名/标识符。
|
|
40
|
+
* @returns 文件的 Buffer 数据。
|
|
41
|
+
*/
|
|
42
|
+
readFile(fileName: string): Promise<Buffer>;
|
|
43
|
+
/**
|
|
44
|
+
* @description 删除文件,自动从 S3 或本地删除。
|
|
45
|
+
* @param fileIdentifier 要删除的文件名/标识符。
|
|
46
|
+
*/
|
|
47
|
+
deleteFile(fileIdentifier: string): Promise<void>;
|
|
48
|
+
}
|
package/lib/Utils.d.ts
CHANGED
|
@@ -3,12 +3,6 @@ import { CaveObject, Config, StoredElement } from './index';
|
|
|
3
3
|
import { FileManager } from './FileManager';
|
|
4
4
|
import { HashManager, CaveHashObject } from './HashManager';
|
|
5
5
|
import { PendManager } from './PendManager';
|
|
6
|
-
/**
|
|
7
|
-
* @description 将数据库存储的 StoredElement[] 转换为 Koishi 的 h() 元素数组。
|
|
8
|
-
* @param elements 从数据库读取的元素数组。
|
|
9
|
-
* @returns 转换后的 h() 元素数组。
|
|
10
|
-
*/
|
|
11
|
-
export declare function storedFormatToHElements(elements: StoredElement[]): h[];
|
|
12
6
|
/**
|
|
13
7
|
* @description 构建一条用于发送的完整回声洞消息,处理不同存储后端的资源链接。
|
|
14
8
|
* @param cave 回声洞对象。
|
package/lib/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const usage = "\n<div style=\"border-radius: 10px; border: 1px so
|
|
|
7
7
|
* @description 存储在数据库中的单个消息元素。
|
|
8
8
|
*/
|
|
9
9
|
export interface StoredElement {
|
|
10
|
-
type: 'text' | 'image' | 'video' | 'audio' | 'file';
|
|
10
|
+
type: 'text' | 'image' | 'video' | 'audio' | 'file' | 'at' | 'forward';
|
|
11
11
|
content?: string;
|
|
12
12
|
file?: string;
|
|
13
13
|
}
|
package/lib/index.js
CHANGED
|
@@ -28,15 +28,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// src/index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
33
|
Config: () => Config,
|
|
34
34
|
apply: () => apply,
|
|
35
35
|
inject: () => inject,
|
|
36
36
|
name: () => name,
|
|
37
37
|
usage: () => usage
|
|
38
38
|
});
|
|
39
|
-
module.exports = __toCommonJS(
|
|
39
|
+
module.exports = __toCommonJS(src_exports);
|
|
40
40
|
var import_koishi3 = require("koishi");
|
|
41
41
|
|
|
42
42
|
// src/FileManager.ts
|
|
@@ -294,40 +294,48 @@ var import_koishi2 = require("koishi");
|
|
|
294
294
|
var import_koishi = require("koishi");
|
|
295
295
|
var path2 = __toESM(require("path"));
|
|
296
296
|
var mimeTypeMap = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".mp4": "video/mp4", ".mp3": "audio/mpeg", ".webp": "image/webp" };
|
|
297
|
-
function storedFormatToHElements(elements) {
|
|
298
|
-
return elements.map((el) => {
|
|
299
|
-
if (el.type === "text") return import_koishi.h.text(el.content);
|
|
300
|
-
if (["image", "video", "audio", "file"].includes(el.type)) return (0, import_koishi.h)(el.type, { src: el.file });
|
|
301
|
-
return null;
|
|
302
|
-
}).filter(Boolean);
|
|
303
|
-
}
|
|
304
|
-
__name(storedFormatToHElements, "storedFormatToHElements");
|
|
305
297
|
async function buildCaveMessage(cave, config, fileManager, logger2) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
298
|
+
async function transformToH(elements) {
|
|
299
|
+
return Promise.all(elements.map(async (el) => {
|
|
300
|
+
if (el.type === "text") return import_koishi.h.text(el.content);
|
|
301
|
+
if (el.type === "at") return (0, import_koishi.h)("at", { id: el.content });
|
|
302
|
+
if (el.type === "forward") {
|
|
303
|
+
try {
|
|
304
|
+
const children = JSON.parse(el.content || "[]");
|
|
305
|
+
return (0, import_koishi.h)("forward", {}, await transformToH(children));
|
|
306
|
+
} catch (error) {
|
|
307
|
+
logger2.warn(`解析回声洞(${cave.id})合并转发内容失败:`, error);
|
|
308
|
+
return import_koishi.h.text("[合并转发]");
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (["image", "video", "audio", "file"].includes(el.type)) {
|
|
312
|
+
const fileName = el.file;
|
|
313
|
+
if (!fileName) return (0, import_koishi.h)("p", {}, `[${el.type}]`);
|
|
314
|
+
if (config.enableS3 && config.publicUrl) {
|
|
315
|
+
return (0, import_koishi.h)(el.type, { ...el, src: new URL(fileName, config.publicUrl).href });
|
|
316
|
+
}
|
|
317
|
+
if (config.localPath) {
|
|
318
|
+
return (0, import_koishi.h)(el.type, { ...el, src: `file://${path2.join(config.localPath, fileName)}` });
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
const data = await fileManager.readFile(fileName);
|
|
322
|
+
const mimeType = mimeTypeMap[path2.extname(fileName).toLowerCase()] || "application/octet-stream";
|
|
323
|
+
return (0, import_koishi.h)(el.type, { ...el, src: `data:${mimeType};base64,${data.toString("base64")}` });
|
|
324
|
+
} catch (error) {
|
|
325
|
+
logger2.warn(`转换文件 ${fileName} 为 Base64 失败:`, error);
|
|
326
|
+
return (0, import_koishi.h)("p", {}, `[${el.type}]`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
})).then((hElements) => hElements.filter(Boolean));
|
|
331
|
+
}
|
|
332
|
+
__name(transformToH, "transformToH");
|
|
333
|
+
const caveHElements = await transformToH(cave.elements);
|
|
326
334
|
const replacements = { id: cave.id.toString(), name: cave.userName };
|
|
327
335
|
const [header, footer] = config.caveFormat.split("|", 2).map((part) => part.replace(/\{id\}|\{name\}/g, (match) => replacements[match.slice(1, -1)]).trim());
|
|
328
336
|
const finalMessage = [];
|
|
329
337
|
if (header) finalMessage.push(header + "\n");
|
|
330
|
-
finalMessage.push(...
|
|
338
|
+
finalMessage.push(...caveHElements);
|
|
331
339
|
if (footer) finalMessage.push("\n" + footer);
|
|
332
340
|
return finalMessage;
|
|
333
341
|
}
|
|
@@ -399,35 +407,55 @@ function updateCooldownTimestamp(session, config, lastUsed) {
|
|
|
399
407
|
}
|
|
400
408
|
__name(updateCooldownTimestamp, "updateCooldownTimestamp");
|
|
401
409
|
async function processMessageElements(sourceElements, newId, session) {
|
|
402
|
-
const finalElementsForDb = [];
|
|
403
410
|
const mediaToSave = [];
|
|
404
411
|
let mediaIndex = 0;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
412
|
+
async function transform(elements) {
|
|
413
|
+
const result = [];
|
|
414
|
+
const typeMap = { "img": "image", "image": "image", "video": "video", "audio": "audio", "file": "file", "text": "text", "at": "at", "forward": "forward" };
|
|
415
|
+
const defaultExtMap = { "image": ".jpg", "video": ".mp4", "audio": ".mp3", "file": ".dat" };
|
|
408
416
|
for (const el of elements) {
|
|
409
417
|
const type = typeMap[el.type];
|
|
410
418
|
if (!type) {
|
|
411
|
-
if (el.children) await
|
|
419
|
+
if (el.children) result.push(...await transform(el.children));
|
|
412
420
|
continue;
|
|
413
421
|
}
|
|
414
422
|
if (type === "text" && el.attrs.content?.trim()) {
|
|
415
|
-
|
|
416
|
-
} else if (type
|
|
423
|
+
result.push({ type: "text", content: el.attrs.content.trim() });
|
|
424
|
+
} else if (type === "at" && el.attrs.id) {
|
|
425
|
+
result.push({ type: "at", content: el.attrs.id });
|
|
426
|
+
} else if (type === "forward") {
|
|
427
|
+
const childrenToProcess = [...el.children || []];
|
|
428
|
+
if (Array.isArray(el.attrs.content)) {
|
|
429
|
+
for (const rawMessage of el.attrs.content) {
|
|
430
|
+
if (rawMessage && rawMessage.message) {
|
|
431
|
+
childrenToProcess.push(...import_koishi.h.parse(rawMessage.message));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (childrenToProcess.length > 0) {
|
|
436
|
+
const transformedChildren = await transform(childrenToProcess);
|
|
437
|
+
if (transformedChildren.length > 0) {
|
|
438
|
+
result.push({ type: "forward", content: JSON.stringify(transformedChildren) });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
} else if (["image", "video", "audio", "file"].includes(type) && el.attrs.src) {
|
|
417
442
|
let fileIdentifier = el.attrs.src;
|
|
418
443
|
if (fileIdentifier.startsWith("http")) {
|
|
419
444
|
const ext = path2.extname(el.attrs.file || "") || defaultExtMap[type];
|
|
420
|
-
const
|
|
445
|
+
const currentMediaIndex = ++mediaIndex;
|
|
446
|
+
const fileName = `${newId}_${currentMediaIndex}_${session.channelId || "private"}_${session.userId}${ext}`;
|
|
421
447
|
mediaToSave.push({ sourceUrl: fileIdentifier, fileName });
|
|
422
448
|
fileIdentifier = fileName;
|
|
423
449
|
}
|
|
424
|
-
|
|
450
|
+
result.push({ type, file: fileIdentifier });
|
|
451
|
+
} else if (el.children) {
|
|
452
|
+
result.push(...await transform(el.children));
|
|
425
453
|
}
|
|
426
|
-
if (el.children) await traverse(el.children);
|
|
427
454
|
}
|
|
455
|
+
return result;
|
|
428
456
|
}
|
|
429
|
-
__name(
|
|
430
|
-
await
|
|
457
|
+
__name(transform, "transform");
|
|
458
|
+
const finalElementsForDb = await transform(sourceElements);
|
|
431
459
|
return { finalElementsForDb, mediaToSave };
|
|
432
460
|
}
|
|
433
461
|
__name(processMessageElements, "processMessageElements");
|