koishi-plugin-echo-cave 1.24.3 → 1.24.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/README.md +16 -13
- package/lib/{index.cjs → index.js} +44 -78
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- 🗑️ **消息管理**:支持删除特定 ID 的消息(消息存储者、原始发送者或管理员)
|
|
16
16
|
- 👤 **个人追踪**:支持查看自己投稿的消息列表
|
|
17
17
|
- 🔍 **发言回溯**:支持查看自己被他人投稿的消息列表
|
|
18
|
+
- 📊 **排行榜功能**:支持查看不同时间段内的回声洞排行榜
|
|
18
19
|
- 🎨 **模板化展示**:消息展示时自带多种风格的外部包裹信息
|
|
19
20
|
- 🔒 **管理员保护**:可配置管理员消息保护
|
|
20
21
|
- 📏 **媒体限制**:支持配置媒体文件大小限制
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
| `cave.purge <...ids>` | 批量删除多个 ID 的回声洞消息 | 消息存储者、原始发送者或管理员 |
|
|
33
34
|
| `cave.search <id>` | 搜索特定 ID 的回声洞消息 | 所有人 |
|
|
34
35
|
| `cave.bind <id> <...userIds>` | 将用户绑定到特定 ID 的回声洞 | 所有人 |
|
|
36
|
+
| `cave.rank [period]` | 查看回声洞排行榜,支持多种时间段 | 所有人 |
|
|
35
37
|
|
|
36
38
|
## 🚀 使用指南
|
|
37
39
|
|
|
@@ -45,19 +47,20 @@ npm install koishi-plugin-echo-cave
|
|
|
45
47
|
|
|
46
48
|
## 🛠️ 配置选项
|
|
47
49
|
|
|
48
|
-
| 配置项
|
|
49
|
-
|
|
50
|
-
| `adminMessageProtection`
|
|
51
|
-
| `allowContributorDelete`
|
|
52
|
-
| `allowSenderDelete`
|
|
53
|
-
| `deleteMediaWhenDeletingMsg` | boolean | `true`
|
|
54
|
-
| `enableSizeLimit`
|
|
55
|
-
| `maxImageSize`
|
|
56
|
-
| `maxVideoSize`
|
|
57
|
-
| `maxFileSize`
|
|
58
|
-
| `maxRecordSize`
|
|
59
|
-
| `useBase64ForMedia`
|
|
60
|
-
| `sendAllAsForwardMsg`
|
|
50
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
51
|
+
|------------------------------|---------|---------|-------------------------------|
|
|
52
|
+
| `adminMessageProtection` | boolean | `false` | 开启管理员消息保护,使管理员发布的消息只能由其他管理员删除 |
|
|
53
|
+
| `allowContributorDelete` | boolean | `true` | 允许消息投稿者删除自己投稿的消息 |
|
|
54
|
+
| `allowSenderDelete` | boolean | `true` | 允许原始消息发送者删除自己被投稿的消息 |
|
|
55
|
+
| `deleteMediaWhenDeletingMsg` | boolean | `true` | 删除消息时是否同时删除媒体文件 |
|
|
56
|
+
| `enableSizeLimit` | boolean | `false` | 启用媒体文件大小限制 |
|
|
57
|
+
| `maxImageSize` | number | `2048` | 最大图片大小(KB) |
|
|
58
|
+
| `maxVideoSize` | number | `512` | 最大视频大小(KB) |
|
|
59
|
+
| `maxFileSize` | number | `512` | 最大文件大小(KB) |
|
|
60
|
+
| `maxRecordSize` | number | `512` | 最大录音大小(KB) |
|
|
61
|
+
| `useBase64ForMedia` | boolean | `false` | 是否使用 Base64 编码发送媒体文件 |
|
|
62
|
+
| `sendAllAsForwardMsg` | boolean | `false` | 是否将所有消息以转发消息形式发送 |
|
|
63
|
+
| `rankingTopCount` | number | `10` | 排行榜显示的用户数量 |
|
|
61
64
|
|
|
62
65
|
## 📝 注意事项
|
|
63
66
|
|
|
@@ -1,38 +1,12 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
1
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
2
|
var __commonJS = (cb, mod) => function __require() {
|
|
8
3
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
4
|
};
|
|
10
|
-
var __export = (target, all) => {
|
|
11
|
-
for (var name2 in all)
|
|
12
|
-
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
13
|
-
};
|
|
14
|
-
var __copyProps = (to, from, except, desc) => {
|
|
15
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
-
for (let key of __getOwnPropNames(from))
|
|
17
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
-
mod
|
|
29
|
-
));
|
|
30
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
5
|
|
|
32
6
|
// src/config/locales/zh-CN.json
|
|
33
7
|
var require_zh_CN = __commonJS({
|
|
34
|
-
"src/config/locales/zh-CN.json"(
|
|
35
|
-
|
|
8
|
+
"src/config/locales/zh-CN.json"(exports, module) {
|
|
9
|
+
module.exports = {
|
|
36
10
|
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",
|
|
37
11
|
allowContributorDelete: "\u5141\u8BB8\u6295\u7A3F\u8005\u5220\u9664\u81EA\u5DF1\u6295\u7A3F\u7684\u56DE\u58F0\u6D1E",
|
|
38
12
|
allowSenderDelete: "\u5141\u8BB8\u539F\u59CB\u53D1\u9001\u8005\u5220\u9664\u88AB\u6295\u7A3F\u7684\u56DE\u58F0\u6D1E",
|
|
@@ -51,8 +25,8 @@ var require_zh_CN = __commonJS({
|
|
|
51
25
|
|
|
52
26
|
// src/locales/zh-CN.json
|
|
53
27
|
var require_zh_CN2 = __commonJS({
|
|
54
|
-
"src/locales/zh-CN.json"(
|
|
55
|
-
|
|
28
|
+
"src/locales/zh-CN.json"(exports, module) {
|
|
29
|
+
module.exports = {
|
|
56
30
|
_config: {
|
|
57
31
|
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",
|
|
58
32
|
allowContributorDelete: "\u5141\u8BB8\u6295\u7A3F\u8005\u5220\u9664\u81EA\u5DF1\u6295\u7A3F\u7684\u56DE\u58F0\u6D1E",
|
|
@@ -199,15 +173,7 @@ var require_zh_CN2 = __commonJS({
|
|
|
199
173
|
});
|
|
200
174
|
|
|
201
175
|
// src/index.ts
|
|
202
|
-
|
|
203
|
-
__export(index_exports, {
|
|
204
|
-
Config: () => Config,
|
|
205
|
-
apply: () => apply,
|
|
206
|
-
inject: () => inject,
|
|
207
|
-
name: () => name
|
|
208
|
-
});
|
|
209
|
-
module.exports = __toCommonJS(index_exports);
|
|
210
|
-
var import_koishi_plugin_adapter_onebot2 = require("@pynickle/koishi-plugin-adapter-onebot");
|
|
176
|
+
import "@pynickle/koishi-plugin-adapter-onebot";
|
|
211
177
|
|
|
212
178
|
// src/adapters/onebot/user.ts
|
|
213
179
|
async function getUserIdFromNickname(session, nickname, userId) {
|
|
@@ -239,7 +205,7 @@ async function checkUsersInGroup(ctx, session, userIds) {
|
|
|
239
205
|
}
|
|
240
206
|
|
|
241
207
|
// src/utils/msg/element-helper.ts
|
|
242
|
-
|
|
208
|
+
import { h } from "koishi";
|
|
243
209
|
function parseUserIds(userIds) {
|
|
244
210
|
const parsedUserIds = [];
|
|
245
211
|
for (const userId of typeof userIds === "string" ? [userIds] : userIds) {
|
|
@@ -248,7 +214,7 @@ function parseUserIds(userIds) {
|
|
|
248
214
|
parsedUserIds.push(userId);
|
|
249
215
|
continue;
|
|
250
216
|
}
|
|
251
|
-
const element =
|
|
217
|
+
const element = h.parse(userId);
|
|
252
218
|
if (element.length === 1 && element[0].type === "at") {
|
|
253
219
|
const userId2 = element[0].attrs.id;
|
|
254
220
|
if (userId2 === "all") {
|
|
@@ -266,9 +232,10 @@ function parseUserIds(userIds) {
|
|
|
266
232
|
}
|
|
267
233
|
|
|
268
234
|
// src/utils/media/media-helper.ts
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
235
|
+
import axios from "axios";
|
|
236
|
+
import { promises as fs } from "node:fs";
|
|
237
|
+
import path from "node:path";
|
|
238
|
+
import { v4 as uuidv4 } from "uuid";
|
|
272
239
|
async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
273
240
|
const mediaUrl = mediaElement.url;
|
|
274
241
|
const originalMediaName = mediaElement.file;
|
|
@@ -276,13 +243,13 @@ async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
|
276
243
|
const i = originalMediaName.lastIndexOf(".");
|
|
277
244
|
return i === -1 ? type === "image" ? "png" : type === "video" ? "mp4" : type === "record" ? "mp3" : "bin" : originalMediaName.slice(i + 1).toLowerCase();
|
|
278
245
|
})();
|
|
279
|
-
const mediaDir =
|
|
280
|
-
const mediaName =
|
|
281
|
-
const fullMediaPath =
|
|
246
|
+
const mediaDir = path.join(ctx.baseDir, "data", "cave", type + "s");
|
|
247
|
+
const mediaName = uuidv4().replace(/-/g, "");
|
|
248
|
+
const fullMediaPath = path.join(mediaDir, `${mediaName}.${ext}`);
|
|
282
249
|
ctx.logger.info(`Saving ${type} from ${mediaUrl} -> ${fullMediaPath}`);
|
|
283
250
|
try {
|
|
284
|
-
await
|
|
285
|
-
const res = await
|
|
251
|
+
await fs.mkdir(mediaDir, { recursive: true });
|
|
252
|
+
const res = await axios.get(mediaUrl, {
|
|
286
253
|
responseType: "arraybuffer",
|
|
287
254
|
validateStatus: () => true
|
|
288
255
|
});
|
|
@@ -312,7 +279,7 @@ async function saveMedia(ctx, mediaElement, type, cfg) {
|
|
|
312
279
|
ctx.logger.warn(`Downloaded ${type} buffer is empty`);
|
|
313
280
|
return mediaUrl;
|
|
314
281
|
}
|
|
315
|
-
await
|
|
282
|
+
await fs.writeFile(fullMediaPath, buffer);
|
|
316
283
|
ctx.logger.info(
|
|
317
284
|
`${type.charAt(0).toUpperCase() + type.slice(1)} saved successfully: ${fullMediaPath}`
|
|
318
285
|
);
|
|
@@ -349,7 +316,7 @@ async function convertFileUriToBase64(ctx, element) {
|
|
|
349
316
|
const fileUri = element.data.file;
|
|
350
317
|
const filePath = decodeURIComponent(fileUri.replace("file:///", ""));
|
|
351
318
|
try {
|
|
352
|
-
const buffer = await
|
|
319
|
+
const buffer = await fs.readFile(filePath);
|
|
353
320
|
const base64 = buffer.toString("base64");
|
|
354
321
|
const mimeTypes = {
|
|
355
322
|
image: "image/jpeg",
|
|
@@ -390,7 +357,7 @@ async function checkAndCleanMediaFiles(ctx, cfg, type) {
|
|
|
390
357
|
if (!cfg.enableSizeLimit) {
|
|
391
358
|
return;
|
|
392
359
|
}
|
|
393
|
-
const mediaDir =
|
|
360
|
+
const mediaDir = path.join(ctx.baseDir, "data", "cave", type + "s");
|
|
394
361
|
const maxSize = (() => {
|
|
395
362
|
switch (type) {
|
|
396
363
|
case "image":
|
|
@@ -405,14 +372,14 @@ async function checkAndCleanMediaFiles(ctx, cfg, type) {
|
|
|
405
372
|
}
|
|
406
373
|
})();
|
|
407
374
|
try {
|
|
408
|
-
const files = await
|
|
375
|
+
const files = await fs.readdir(mediaDir);
|
|
409
376
|
if (files.length === 0) {
|
|
410
377
|
return;
|
|
411
378
|
}
|
|
412
379
|
const fileInfos = await Promise.all(
|
|
413
380
|
files.map(async (file) => {
|
|
414
|
-
const filePath =
|
|
415
|
-
const stats = await
|
|
381
|
+
const filePath = path.join(mediaDir, file);
|
|
382
|
+
const stats = await fs.stat(filePath);
|
|
416
383
|
return {
|
|
417
384
|
path: filePath,
|
|
418
385
|
size: stats.size,
|
|
@@ -439,9 +406,9 @@ async function checkAndCleanMediaFiles(ctx, cfg, type) {
|
|
|
439
406
|
currentSize -= file.size;
|
|
440
407
|
}
|
|
441
408
|
for (const file of filesToDelete) {
|
|
442
|
-
await
|
|
409
|
+
await fs.unlink(file.path);
|
|
443
410
|
ctx.logger.info(
|
|
444
|
-
`Deleted oldest ${type} file: ${
|
|
411
|
+
`Deleted oldest ${type} file: ${path.basename(file.path)} (${(file.size / (1024 * 1024)).toFixed(2)} MB)`
|
|
445
412
|
);
|
|
446
413
|
}
|
|
447
414
|
ctx.logger.info(
|
|
@@ -459,8 +426,8 @@ async function deleteMediaFilesFromMessage(ctx, content) {
|
|
|
459
426
|
if (fileUri && fileUri.startsWith("file:///")) {
|
|
460
427
|
const filePath = decodeURIComponent(fileUri.replace("file:///", ""));
|
|
461
428
|
try {
|
|
462
|
-
await
|
|
463
|
-
await
|
|
429
|
+
await fs.access(filePath);
|
|
430
|
+
await fs.unlink(filePath);
|
|
464
431
|
ctx.logger.info(`Deleted media file: ${filePath}`);
|
|
465
432
|
} catch (err) {
|
|
466
433
|
ctx.logger.warn(`Failed to delete media file: ${filePath}, error: ${err}`);
|
|
@@ -530,7 +497,7 @@ async function processMessageContent(ctx, msg, cfg) {
|
|
|
530
497
|
}
|
|
531
498
|
|
|
532
499
|
// src/core/command/add-cave.ts
|
|
533
|
-
|
|
500
|
+
import { CQCode } from "@pynickle/koishi-plugin-adapter-onebot";
|
|
534
501
|
async function addCave(ctx, session, cfg, userIds) {
|
|
535
502
|
if (!session.guildId) {
|
|
536
503
|
return session.text("echo-cave.general.privateChatReminder");
|
|
@@ -570,7 +537,7 @@ async function addCave(ctx, session, cfg, userIds) {
|
|
|
570
537
|
const message = (await session.onebot.getMsg(messageId)).message;
|
|
571
538
|
let msgJson;
|
|
572
539
|
if (typeof message === "string") {
|
|
573
|
-
msgJson =
|
|
540
|
+
msgJson = CQCode.parse(message);
|
|
574
541
|
} else {
|
|
575
542
|
if (message[0].type === "video" || message[0].type === "file") {
|
|
576
543
|
type = "forward";
|
|
@@ -1099,20 +1066,20 @@ async function searchCave(ctx, session, userIds) {
|
|
|
1099
1066
|
}
|
|
1100
1067
|
|
|
1101
1068
|
// src/config/config.ts
|
|
1102
|
-
|
|
1103
|
-
var Config =
|
|
1104
|
-
adminMessageProtection:
|
|
1105
|
-
allowContributorDelete:
|
|
1106
|
-
allowSenderDelete:
|
|
1107
|
-
deleteMediaWhenDeletingMsg:
|
|
1108
|
-
enableSizeLimit:
|
|
1109
|
-
maxImageSize:
|
|
1110
|
-
maxVideoSize:
|
|
1111
|
-
maxFileSize:
|
|
1112
|
-
maxRecordSize:
|
|
1113
|
-
useBase64ForMedia:
|
|
1114
|
-
sendAllAsForwardMsg:
|
|
1115
|
-
rankingTopCount:
|
|
1069
|
+
import { Schema } from "koishi";
|
|
1070
|
+
var Config = Schema.object({
|
|
1071
|
+
adminMessageProtection: Schema.boolean().default(false),
|
|
1072
|
+
allowContributorDelete: Schema.boolean().default(true),
|
|
1073
|
+
allowSenderDelete: Schema.boolean().default(true),
|
|
1074
|
+
deleteMediaWhenDeletingMsg: Schema.boolean().default(true),
|
|
1075
|
+
enableSizeLimit: Schema.boolean().default(false),
|
|
1076
|
+
maxImageSize: Schema.number().default(2048),
|
|
1077
|
+
maxVideoSize: Schema.number().default(512),
|
|
1078
|
+
maxFileSize: Schema.number().default(512),
|
|
1079
|
+
maxRecordSize: Schema.number().default(512),
|
|
1080
|
+
useBase64ForMedia: Schema.boolean().default(false),
|
|
1081
|
+
sendAllAsForwardMsg: Schema.boolean().default(false),
|
|
1082
|
+
rankingTopCount: Schema.number().default(10)
|
|
1116
1083
|
}).i18n({
|
|
1117
1084
|
"zh-CN": require_zh_CN()
|
|
1118
1085
|
});
|
|
@@ -1192,10 +1159,9 @@ function apply(ctx, cfg) {
|
|
|
1192
1159
|
async ({ session }, period) => await getRanking(ctx, session, cfg, period)
|
|
1193
1160
|
);
|
|
1194
1161
|
}
|
|
1195
|
-
|
|
1196
|
-
0 && (module.exports = {
|
|
1162
|
+
export {
|
|
1197
1163
|
Config,
|
|
1198
1164
|
apply,
|
|
1199
1165
|
inject,
|
|
1200
1166
|
name
|
|
1201
|
-
}
|
|
1167
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-echo-cave",
|
|
3
3
|
"description": "Group echo cave",
|
|
4
|
-
"version": "1.24.
|
|
5
|
-
"main": "lib/index.
|
|
4
|
+
"version": "1.24.5",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"files": [
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@pynickle/koishi-plugin-adapter-onebot": "^1.0.0",
|
|
36
|
-
"axios": "^1.13.2"
|
|
36
|
+
"axios": "^1.13.2",
|
|
37
|
+
"uuid": "^13.0.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@semantic-release/changelog": "^6.0.3",
|