koishi-plugin-echo-cave 1.12.1 → 1.13.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/README.md +31 -5
- package/lib/index.cjs +23 -17
- package/lib/index.d.ts +1 -0
- package/lib/{image-helper.d.ts → media-helper.d.ts} +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
- 🔍 **发言回溯**:支持查看自己被他人投稿的消息列表
|
|
20
20
|
- 🎨 **精美展示**:消息展示时自带多种风格的外部包裹信息,显示消息详情
|
|
21
21
|
- 🔒 **管理员保护**:可配置管理员消息保护,确保管理员发布的消息安全
|
|
22
|
+
- 📏 **媒体限制**:支持配置媒体文件大小限制
|
|
23
|
+
- 🌐 **多语言支持**:支持中文语言包
|
|
24
|
+
- 🔄 **嵌套转发**:支持嵌套转发消息的递归处理
|
|
25
|
+
- 📝 **模板化展示**:支持多种风格的消息展示模板
|
|
26
|
+
- 🔁 **重复检测**:自动检测重复消息,避免存储重复内容
|
|
22
27
|
|
|
23
28
|
## 📋 命令列表
|
|
24
29
|
|
|
@@ -55,11 +60,14 @@ npm install koishi-plugin-echo-cave
|
|
|
55
60
|
```
|
|
56
61
|
src/
|
|
57
62
|
├── index.ts # 插件主入口,命令注册和核心功能
|
|
63
|
+
├── cave-helper.ts # 回声洞消息发送辅助函数
|
|
58
64
|
├── forward-helper.ts # 转发消息处理辅助函数
|
|
59
|
-
├──
|
|
60
|
-
├── msg-helper.ts #
|
|
65
|
+
├── media-helper.ts # 媒体文件保存辅助函数
|
|
66
|
+
├── msg-helper.ts # 消息发送辅助函数
|
|
61
67
|
├── cqcode-helper.ts # CQ 码处理辅助函数
|
|
62
|
-
|
|
68
|
+
├── onebot-helper.ts # OneBot 适配器辅助函数
|
|
69
|
+
└── locales/
|
|
70
|
+
└── zh-CN.json # 中文语言包
|
|
63
71
|
```
|
|
64
72
|
|
|
65
73
|
## 🔧 技术说明
|
|
@@ -68,16 +76,34 @@ src/
|
|
|
68
76
|
- 图片会保存在 `data/cave/images` 目录下
|
|
69
77
|
- 支持嵌套转发消息的处理
|
|
70
78
|
- 自动检测重复消息,避免存储重复内容
|
|
79
|
+
- 支持媒体文件大小限制
|
|
80
|
+
- 支持多语言配置(中文)
|
|
81
|
+
- 支持嵌套转发消息的递归处理
|
|
82
|
+
|
|
83
|
+
## 🛠️ 配置选项
|
|
84
|
+
|
|
85
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
86
|
+
|-------|------|-------|------|
|
|
87
|
+
| `adminMessageProtection` | boolean | `false` | 开启管理员消息保护,使管理员发布的消息只能由其他管理员删除 |
|
|
88
|
+
| `allowContributorDelete` | boolean | `true` | 允许消息投稿者删除自己投稿的消息 |
|
|
89
|
+
| `allowSenderDelete` | boolean | `true` | 允许原始消息发送者删除自己被投稿的消息 |
|
|
90
|
+
| `enableSizeLimit` | boolean | `false` | 启用媒体文件大小限制 |
|
|
91
|
+
| `maxImageSize` | number | `2048` | 最大图片大小(KB) |
|
|
92
|
+
| `maxVideoSize` | number | `512` | 最大视频大小(MB) |
|
|
93
|
+
| `maxFileSize` | number | `512` | 最大文件大小(MB) |
|
|
71
94
|
|
|
72
95
|
## 📝 注意事项
|
|
73
96
|
|
|
74
97
|
- 插件只能在群聊中使用,私聊模式下无法正常工作
|
|
75
98
|
- 使用 `cave.echo` 命令前必须先引用一条消息
|
|
76
|
-
-
|
|
99
|
+
- 删除消息权限可通过配置项灵活控制,包括管理员保护、投稿者删除权限和原始发送者删除权限
|
|
77
100
|
- 存储的转发消息会保留原始发送者信息
|
|
78
101
|
- 消息展示时会自动添加精美的外部包裹信息,包括消息 ID、创建时间、原始发送者和投递者信息
|
|
79
102
|
- 外部包裹信息有多种随机风格,为每次消息展示带来不同的视觉体验
|
|
80
|
-
-
|
|
103
|
+
- 支持媒体文件大小限制,可通过配置项调整不同类型文件的大小限制
|
|
104
|
+
- 自动检测重复消息,避免存储重复内容
|
|
105
|
+
- 支持嵌套转发消息的递归处理
|
|
106
|
+
- 支持多语言配置,目前已提供中文语言包
|
|
81
107
|
|
|
82
108
|
## 🤝 贡献指南
|
|
83
109
|
|
package/lib/index.cjs
CHANGED
|
@@ -40,7 +40,8 @@ var require_zh_CN = __commonJS({
|
|
|
40
40
|
enableSizeLimit: "\u662F\u5426\u542F\u7528\u5A92\u4F53\u6587\u4EF6\u5927\u5C0F\u9650\u5236",
|
|
41
41
|
maxImageSize: "\u6700\u5927\u56FE\u7247\u5927\u5C0F (MB)",
|
|
42
42
|
maxVideoSize: "\u6700\u5927\u89C6\u9891\u5927\u5C0F (MB)",
|
|
43
|
-
maxFileSize: "\u6700\u5927\u6587\u4EF6\u5927\u5C0F (MB)"
|
|
43
|
+
maxFileSize: "\u6700\u5927\u6587\u4EF6\u5927\u5C0F (MB)",
|
|
44
|
+
maxRecordSize: "\u6700\u5927\u5F55\u97F3\u5927\u5C0F (MB)"
|
|
44
45
|
},
|
|
45
46
|
"echo-cave": {
|
|
46
47
|
general: {
|
|
@@ -219,7 +220,7 @@ function formatDate(date) {
|
|
|
219
220
|
});
|
|
220
221
|
}
|
|
221
222
|
|
|
222
|
-
// src/
|
|
223
|
+
// src/media-helper.ts
|
|
223
224
|
var import_axios = __toESM(require("axios"), 1);
|
|
224
225
|
var import_node_fs = require("node:fs");
|
|
225
226
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
@@ -228,7 +229,7 @@ async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
|
228
229
|
const originalMediaName = mediaElement.file;
|
|
229
230
|
const ext = (() => {
|
|
230
231
|
const i = originalMediaName.lastIndexOf(".");
|
|
231
|
-
return i === -1 ? type === "image" ? "png" : type === "video" ? "mp4" : "bin" : originalMediaName.slice(i + 1).toLowerCase();
|
|
232
|
+
return i === -1 ? type === "image" ? "png" : type === "video" ? "mp4" : type === "record" ? "mp3" : "bin" : originalMediaName.slice(i + 1).toLowerCase();
|
|
232
233
|
})();
|
|
233
234
|
const mediaDir = import_node_path.default.join(ctx.baseDir, "data", "cave", type + "s");
|
|
234
235
|
const mediaName = Date.now().toString();
|
|
@@ -256,6 +257,10 @@ async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
|
256
257
|
ctx.logger.warn(`Invalid video content-type: ${contentType}`);
|
|
257
258
|
return mediaUrl;
|
|
258
259
|
}
|
|
260
|
+
if (type === "record" && !contentType.startsWith("audio/")) {
|
|
261
|
+
ctx.logger.warn(`Invalid record content-type: ${contentType}`);
|
|
262
|
+
return mediaUrl;
|
|
263
|
+
}
|
|
259
264
|
}
|
|
260
265
|
const buffer = Buffer.from(res.data);
|
|
261
266
|
if (!buffer || buffer.length === 0) {
|
|
@@ -274,17 +279,21 @@ async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
|
274
279
|
}
|
|
275
280
|
}
|
|
276
281
|
async function processMediaElement(ctx, element, cfg) {
|
|
277
|
-
if (element.type === "image" || element.type === "video" || element.type === "file") {
|
|
282
|
+
if (element.type === "image" || element.type === "video" || element.type === "file" || element.type === "record") {
|
|
283
|
+
const savedPath = await saveMedia(
|
|
284
|
+
ctx,
|
|
285
|
+
element.data,
|
|
286
|
+
element.type,
|
|
287
|
+
cfg
|
|
288
|
+
);
|
|
289
|
+
const fileUri = `file:///${savedPath.replace(/\\/g, "/")}`;
|
|
278
290
|
return {
|
|
279
291
|
...element,
|
|
280
292
|
data: {
|
|
281
293
|
...element.data,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
element.type,
|
|
286
|
-
cfg
|
|
287
|
-
)
|
|
294
|
+
file: fileUri,
|
|
295
|
+
// Remove the url field
|
|
296
|
+
url: void 0
|
|
288
297
|
}
|
|
289
298
|
};
|
|
290
299
|
}
|
|
@@ -304,6 +313,8 @@ async function checkAndCleanMediaFiles(ctx, cfg, type) {
|
|
|
304
313
|
return (cfg.maxVideoSize || 500) * 1024 * 1024;
|
|
305
314
|
case "file":
|
|
306
315
|
return (cfg.maxFileSize || 1e3) * 1024 * 1024;
|
|
316
|
+
case "record":
|
|
317
|
+
return (cfg.maxRecordSize || 200) * 1024 * 1024;
|
|
307
318
|
}
|
|
308
319
|
})();
|
|
309
320
|
try {
|
|
@@ -411,9 +422,7 @@ async function processMessageContent(ctx, msg, cfg) {
|
|
|
411
422
|
|
|
412
423
|
// src/index.ts
|
|
413
424
|
var import_koishi_plugin_adapter_onebot2 = require("@pynickle/koishi-plugin-adapter-onebot");
|
|
414
|
-
var import_fs = __toESM(require("fs"), 1);
|
|
415
425
|
var import_koishi = require("koishi");
|
|
416
|
-
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
417
426
|
var name = "echo-cave";
|
|
418
427
|
var inject = ["database"];
|
|
419
428
|
var Config = import_koishi.Schema.object({
|
|
@@ -423,16 +432,13 @@ var Config = import_koishi.Schema.object({
|
|
|
423
432
|
enableSizeLimit: import_koishi.Schema.boolean().default(false),
|
|
424
433
|
maxImageSize: import_koishi.Schema.number().default(2048),
|
|
425
434
|
maxVideoSize: import_koishi.Schema.number().default(512),
|
|
426
|
-
maxFileSize: import_koishi.Schema.number().default(512)
|
|
435
|
+
maxFileSize: import_koishi.Schema.number().default(512),
|
|
436
|
+
maxRecordSize: import_koishi.Schema.number().default(512)
|
|
427
437
|
}).i18n({
|
|
428
438
|
"zh-CN": require_zh_CN()._config
|
|
429
439
|
});
|
|
430
440
|
function apply(ctx, cfg) {
|
|
431
441
|
ctx.i18n.define("zh-CN", require_zh_CN());
|
|
432
|
-
const imgPath = import_node_path2.default.join(ctx.baseDir, "data", "cave", "images");
|
|
433
|
-
if (!import_fs.default.existsSync(imgPath)) {
|
|
434
|
-
import_fs.default.mkdirSync(imgPath, { recursive: true });
|
|
435
|
-
}
|
|
436
442
|
ctx.model.extend(
|
|
437
443
|
"echo_cave",
|
|
438
444
|
{
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Config } from './index';
|
|
2
2
|
import { Context } from 'koishi';
|
|
3
|
-
export declare function saveMedia(ctx: Context, mediaElement: Record<string, any>, type: 'image' | 'video' | 'file', cfg: Config): Promise<string>;
|
|
3
|
+
export declare function saveMedia(ctx: Context, mediaElement: Record<string, any>, type: 'image' | 'video' | 'file' | 'record', cfg: Config): Promise<string>;
|
|
4
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>;
|
|
5
|
+
export declare function checkAndCleanMediaFiles(ctx: Context, cfg: Config, type: 'image' | 'video' | 'file' | 'record'): Promise<void>;
|