koishi-plugin-echo-cave 1.11.0 → 1.12.0
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/forward-helper.d.ts +2 -1
- package/lib/image-helper.d.ts +4 -2
- package/lib/index.cjs +95 -16
- package/lib/index.d.ts +4 -0
- package/lib/msg-helper.d.ts +2 -1
- package/package.json +1 -1
package/lib/forward-helper.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { Config } from './index';
|
|
1
2
|
import { CQCode } from '@pynickle/koishi-plugin-adapter-onebot';
|
|
2
3
|
import { Message } from '@pynickle/koishi-plugin-adapter-onebot/lib/types';
|
|
3
4
|
import { Context } from 'koishi';
|
|
4
|
-
export declare function reconstructForwardMsg(ctx: Context, message: Message[]): Promise<CQCode[]>;
|
|
5
|
+
export declare function reconstructForwardMsg(ctx: Context, message: Message[], cfg: Config): Promise<CQCode[]>;
|
package/lib/image-helper.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Config } from './index';
|
|
1
2
|
import { Context } from 'koishi';
|
|
2
|
-
export declare function saveMedia(ctx: Context, mediaElement: Record<string, any>, type: 'image' | 'video' | 'file'): Promise<string>;
|
|
3
|
-
export declare function processMediaElement(ctx: Context, element: any): Promise<any>;
|
|
3
|
+
export declare function saveMedia(ctx: Context, mediaElement: Record<string, any>, type: 'image' | 'video' | 'file', cfg: Config): Promise<string>;
|
|
4
|
+
export declare function processMediaElement(ctx: Context, element: any, cfg: Config): Promise<any>;
|
|
5
|
+
export declare function checkAndCleanMediaFiles(ctx: Context, cfg: Config, type: 'image' | 'video' | 'file'): Promise<void>;
|
package/lib/index.cjs
CHANGED
|
@@ -36,7 +36,11 @@ var require_zh_CN = __commonJS({
|
|
|
36
36
|
_config: {
|
|
37
37
|
adminMessageProtection: "\u5F00\u542F\u7BA1\u7406\u5458\u6D88\u606F\u4FDD\u62A4\uFF0C\u5F00\u542F\u540E\u7BA1\u7406\u5458\u53D1\u5E03\u7684\u6D88\u606F\u53EA\u80FD\u7531\u7BA1\u7406\u5458\u5220\u9664",
|
|
38
38
|
allowContributorDelete: "\u5141\u8BB8\u6295\u7A3F\u8005\u5220\u9664\u81EA\u5DF1\u6295\u7A3F\u7684\u56DE\u58F0\u6D1E",
|
|
39
|
-
allowSenderDelete: "\u5141\u8BB8\u539F\u59CB\u53D1\u9001\u8005\u5220\u9664\u88AB\u6295\u7A3F\u7684\u56DE\u58F0\u6D1E"
|
|
39
|
+
allowSenderDelete: "\u5141\u8BB8\u539F\u59CB\u53D1\u9001\u8005\u5220\u9664\u88AB\u6295\u7A3F\u7684\u56DE\u58F0\u6D1E",
|
|
40
|
+
enableSizeLimit: "\u662F\u5426\u542F\u7528\u5A92\u4F53\u6587\u4EF6\u5927\u5C0F\u9650\u5236",
|
|
41
|
+
maxImageSize: "\u6700\u5927\u56FE\u7247\u5927\u5C0F (MB)",
|
|
42
|
+
maxVideoSize: "\u6700\u5927\u89C6\u9891\u5927\u5C0F (MB)",
|
|
43
|
+
maxFileSize: "\u6700\u5927\u6587\u4EF6\u5927\u5C0F (MB)"
|
|
40
44
|
},
|
|
41
45
|
"echo-cave": {
|
|
42
46
|
general: {
|
|
@@ -219,7 +223,7 @@ function formatDate(date) {
|
|
|
219
223
|
var import_axios = __toESM(require("axios"), 1);
|
|
220
224
|
var import_node_fs = require("node:fs");
|
|
221
225
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
222
|
-
async function saveMedia(ctx, mediaElement, type) {
|
|
226
|
+
async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
223
227
|
const mediaUrl = mediaElement.url;
|
|
224
228
|
const originalMediaName = mediaElement.file;
|
|
225
229
|
const ext = (() => {
|
|
@@ -262,30 +266,100 @@ async function saveMedia(ctx, mediaElement, type) {
|
|
|
262
266
|
ctx.logger.info(
|
|
263
267
|
`${type.charAt(0).toUpperCase() + type.slice(1)} saved successfully: ${fullMediaPath}`
|
|
264
268
|
);
|
|
269
|
+
await checkAndCleanMediaFiles(ctx, cfg, type);
|
|
265
270
|
return fullMediaPath;
|
|
266
271
|
} catch (err) {
|
|
267
272
|
ctx.logger.error(`Failed to save ${type}: ${err}`);
|
|
268
273
|
return mediaUrl;
|
|
269
274
|
}
|
|
270
275
|
}
|
|
271
|
-
async function processMediaElement(ctx, element) {
|
|
276
|
+
async function processMediaElement(ctx, element, cfg) {
|
|
272
277
|
if (element.type === "image" || element.type === "video" || element.type === "file") {
|
|
273
278
|
return {
|
|
274
279
|
...element,
|
|
275
280
|
data: {
|
|
276
281
|
...element.data,
|
|
277
|
-
url: await saveMedia(
|
|
282
|
+
url: await saveMedia(
|
|
283
|
+
ctx,
|
|
284
|
+
element.data,
|
|
285
|
+
element.type,
|
|
286
|
+
cfg
|
|
287
|
+
)
|
|
278
288
|
}
|
|
279
289
|
};
|
|
280
290
|
}
|
|
281
291
|
return element;
|
|
282
292
|
}
|
|
293
|
+
async function checkAndCleanMediaFiles(ctx, cfg, type) {
|
|
294
|
+
if (!cfg.enableSizeLimit) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const mediaDir = import_node_path.default.join(ctx.baseDir, "data", "cave", type + "s");
|
|
298
|
+
const maxSize = (() => {
|
|
299
|
+
switch (type) {
|
|
300
|
+
case "image":
|
|
301
|
+
return (cfg.maxImageSize || 100) * 1024 * 1024;
|
|
302
|
+
// 转换为字节
|
|
303
|
+
case "video":
|
|
304
|
+
return (cfg.maxVideoSize || 500) * 1024 * 1024;
|
|
305
|
+
case "file":
|
|
306
|
+
return (cfg.maxFileSize || 1e3) * 1024 * 1024;
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
try {
|
|
310
|
+
const files = await import_node_fs.promises.readdir(mediaDir);
|
|
311
|
+
if (files.length === 0) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const fileInfos = await Promise.all(
|
|
315
|
+
files.map(async (file) => {
|
|
316
|
+
const filePath = import_node_path.default.join(mediaDir, file);
|
|
317
|
+
const stats = await import_node_fs.promises.stat(filePath);
|
|
318
|
+
return {
|
|
319
|
+
path: filePath,
|
|
320
|
+
size: stats.size,
|
|
321
|
+
mtime: stats.mtimeMs
|
|
322
|
+
};
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
const totalSize = fileInfos.reduce((sum, file) => sum + file.size, 0);
|
|
326
|
+
ctx.logger.info(
|
|
327
|
+
`${type} directory total size: ${(totalSize / (1024 * 1024)).toFixed(2)} MB, max allowed: ${(maxSize / (1024 * 1024)).toFixed(2)} MB`
|
|
328
|
+
);
|
|
329
|
+
if (totalSize > maxSize) {
|
|
330
|
+
ctx.logger.warn(
|
|
331
|
+
`${type} directory size exceeds limit! Total: ${(totalSize / (1024 * 1024)).toFixed(2)} MB, Max: ${(maxSize / (1024 * 1024)).toFixed(2)} MB`
|
|
332
|
+
);
|
|
333
|
+
fileInfos.sort((a, b) => a.mtime - b.mtime);
|
|
334
|
+
let currentSize = totalSize;
|
|
335
|
+
let filesToDelete = [];
|
|
336
|
+
for (const file of fileInfos) {
|
|
337
|
+
if (currentSize <= maxSize) {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
filesToDelete.push(file);
|
|
341
|
+
currentSize -= file.size;
|
|
342
|
+
}
|
|
343
|
+
for (const file of filesToDelete) {
|
|
344
|
+
await import_node_fs.promises.unlink(file.path);
|
|
345
|
+
ctx.logger.info(
|
|
346
|
+
`Deleted oldest ${type} file: ${import_node_path.default.basename(file.path)} (${(file.size / (1024 * 1024)).toFixed(2)} MB)`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
ctx.logger.info(
|
|
350
|
+
`Cleanup completed. ${type} directory new size: ${(currentSize / (1024 * 1024)).toFixed(2)} MB`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
} catch (err) {
|
|
354
|
+
ctx.logger.error(`Failed to check and clean ${type} files: ${err}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
283
357
|
|
|
284
358
|
// src/forward-helper.ts
|
|
285
|
-
async function reconstructForwardMsg(ctx, message) {
|
|
359
|
+
async function reconstructForwardMsg(ctx, message, cfg) {
|
|
286
360
|
return Promise.all(
|
|
287
361
|
message.map(async (msg) => {
|
|
288
|
-
const content = await processForwardMessageContent(ctx, msg);
|
|
362
|
+
const content = await processForwardMessageContent(ctx, msg, cfg);
|
|
289
363
|
return {
|
|
290
364
|
type: "node",
|
|
291
365
|
data: {
|
|
@@ -297,29 +371,29 @@ async function reconstructForwardMsg(ctx, message) {
|
|
|
297
371
|
})
|
|
298
372
|
);
|
|
299
373
|
}
|
|
300
|
-
async function processForwardMessageContent(ctx, msg) {
|
|
374
|
+
async function processForwardMessageContent(ctx, msg, cfg) {
|
|
301
375
|
if (typeof msg.message === "string") {
|
|
302
376
|
return msg.message;
|
|
303
377
|
}
|
|
304
378
|
const firstElement = msg.message[0];
|
|
305
379
|
if (firstElement?.type === "forward") {
|
|
306
|
-
return reconstructForwardMsg(ctx, firstElement.data.content);
|
|
380
|
+
return reconstructForwardMsg(ctx, firstElement.data.content, cfg);
|
|
307
381
|
}
|
|
308
382
|
return Promise.all(
|
|
309
383
|
msg.message.map(async (element) => {
|
|
310
|
-
return processMediaElement(ctx, element);
|
|
384
|
+
return processMediaElement(ctx, element, cfg);
|
|
311
385
|
})
|
|
312
386
|
);
|
|
313
387
|
}
|
|
314
388
|
|
|
315
389
|
// src/msg-helper.ts
|
|
316
|
-
async function processMessageContent(ctx, msg) {
|
|
390
|
+
async function processMessageContent(ctx, msg, cfg) {
|
|
317
391
|
return Promise.all(
|
|
318
392
|
msg.map(async (element) => {
|
|
319
393
|
if (element.type === "reply") {
|
|
320
394
|
return element;
|
|
321
395
|
}
|
|
322
|
-
return processMediaElement(ctx, element);
|
|
396
|
+
return processMediaElement(ctx, element, cfg);
|
|
323
397
|
})
|
|
324
398
|
);
|
|
325
399
|
}
|
|
@@ -334,7 +408,11 @@ var inject = ["database"];
|
|
|
334
408
|
var Config = import_koishi.Schema.object({
|
|
335
409
|
adminMessageProtection: import_koishi.Schema.boolean().default(false),
|
|
336
410
|
allowContributorDelete: import_koishi.Schema.boolean().default(true),
|
|
337
|
-
allowSenderDelete: import_koishi.Schema.boolean().default(true)
|
|
411
|
+
allowSenderDelete: import_koishi.Schema.boolean().default(true),
|
|
412
|
+
enableSizeLimit: import_koishi.Schema.boolean().default(false),
|
|
413
|
+
maxImageSize: import_koishi.Schema.number().default(2048),
|
|
414
|
+
maxVideoSize: import_koishi.Schema.number().default(512),
|
|
415
|
+
maxFileSize: import_koishi.Schema.number().default(512)
|
|
338
416
|
}).i18n({
|
|
339
417
|
"zh-CN": require_zh_CN()._config
|
|
340
418
|
});
|
|
@@ -363,7 +441,7 @@ function apply(ctx, cfg) {
|
|
|
363
441
|
ctx.command("cave [id:number]").action(
|
|
364
442
|
async ({ session }, id) => await getCave(ctx, session, id)
|
|
365
443
|
);
|
|
366
|
-
ctx.command("cave.echo").action(async ({ session }) => await addCave(ctx, session));
|
|
444
|
+
ctx.command("cave.echo").action(async ({ session }) => await addCave(ctx, session, cfg));
|
|
367
445
|
ctx.command("cave.wipe <id:number>").action(
|
|
368
446
|
async ({ session }, id) => await deleteCave(ctx, session, cfg, id)
|
|
369
447
|
);
|
|
@@ -473,7 +551,7 @@ async function deleteCave(ctx, session, cfg, id) {
|
|
|
473
551
|
await ctx.database.remove("echo_cave", id);
|
|
474
552
|
return session.text(".msgDeleted", [id]);
|
|
475
553
|
}
|
|
476
|
-
async function addCave(ctx, session) {
|
|
554
|
+
async function addCave(ctx, session, cfg) {
|
|
477
555
|
if (!session.guildId) {
|
|
478
556
|
return session.text("echo-cave.general.privateChatReminder");
|
|
479
557
|
}
|
|
@@ -488,7 +566,8 @@ async function addCave(ctx, session) {
|
|
|
488
566
|
type = "forward";
|
|
489
567
|
const message = await reconstructForwardMsg(
|
|
490
568
|
ctx,
|
|
491
|
-
await session.onebot.getForwardMsg(messageId)
|
|
569
|
+
await session.onebot.getForwardMsg(messageId),
|
|
570
|
+
cfg
|
|
492
571
|
);
|
|
493
572
|
content = JSON.stringify(message);
|
|
494
573
|
} else {
|
|
@@ -503,7 +582,7 @@ async function addCave(ctx, session) {
|
|
|
503
582
|
}
|
|
504
583
|
msgJson = message;
|
|
505
584
|
}
|
|
506
|
-
content = JSON.stringify(await processMessageContent(ctx, msgJson));
|
|
585
|
+
content = JSON.stringify(await processMessageContent(ctx, msgJson, cfg));
|
|
507
586
|
}
|
|
508
587
|
await ctx.database.get("echo_cave", { content }).then((existing) => {
|
|
509
588
|
if (existing) {
|
package/lib/index.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export interface Config {
|
|
|
6
6
|
adminMessageProtection?: boolean;
|
|
7
7
|
allowContributorDelete?: boolean;
|
|
8
8
|
allowSenderDelete?: boolean;
|
|
9
|
+
enableSizeLimit?: boolean;
|
|
10
|
+
maxImageSize?: number;
|
|
11
|
+
maxVideoSize?: number;
|
|
12
|
+
maxFileSize?: number;
|
|
9
13
|
}
|
|
10
14
|
export declare const Config: Schema<Config>;
|
|
11
15
|
export interface EchoCave {
|
package/lib/msg-helper.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Config } from './index';
|
|
1
2
|
import { CQCode } from '@pynickle/koishi-plugin-adapter-onebot';
|
|
2
3
|
import { Context } from 'koishi';
|
|
3
|
-
export declare function processMessageContent(ctx: Context, msg: CQCode[]): Promise<CQCode[]>;
|
|
4
|
+
export declare function processMessageContent(ctx: Context, msg: CQCode[], cfg: Config): Promise<CQCode[]>;
|