koishi-plugin-best-cave 2.4.1 → 2.4.3
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 +36 -0
- package/lib/FileManager.d.ts +48 -0
- package/lib/HashManager.d.ts +102 -0
- package/lib/NameManager.d.ts +45 -0
- package/lib/PendManager.d.ts +32 -0
- package/lib/Utils.d.ts +0 -9
- package/lib/index.d.ts +0 -1
- package/lib/index.js +19 -41
- package/package.json +1 -1
- package/readme.md +2 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Context, Logger } from 'koishi';
|
|
2
|
+
import { FileManager } from './FileManager';
|
|
3
|
+
import { Config } from './index';
|
|
4
|
+
/**
|
|
5
|
+
* @class DataManager
|
|
6
|
+
* @description 负责处理回声洞数据的导入和导出功能。
|
|
7
|
+
*/
|
|
8
|
+
export declare class DataManager {
|
|
9
|
+
private ctx;
|
|
10
|
+
private config;
|
|
11
|
+
private fileManager;
|
|
12
|
+
private logger;
|
|
13
|
+
/**
|
|
14
|
+
* @constructor
|
|
15
|
+
* @param ctx Koishi 上下文,用于数据库操作。
|
|
16
|
+
* @param config 插件配置。
|
|
17
|
+
* @param fileManager 文件管理器实例。
|
|
18
|
+
* @param logger 日志记录器实例。
|
|
19
|
+
*/
|
|
20
|
+
constructor(ctx: Context, config: Config, fileManager: FileManager, logger: Logger);
|
|
21
|
+
/**
|
|
22
|
+
* @description 注册 `.export` 和 `.import` 子命令。
|
|
23
|
+
* @param cave - 主 `cave` 命令实例。
|
|
24
|
+
*/
|
|
25
|
+
registerCommands(cave: any): void;
|
|
26
|
+
/**
|
|
27
|
+
* @description 导出所有 'active' 状态的回声洞数据到 `cave_export.json`。
|
|
28
|
+
* @returns 描述导出结果的消息字符串。
|
|
29
|
+
*/
|
|
30
|
+
exportData(): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* @description 从 `cave_import.json` 文件导入回声洞数据。
|
|
33
|
+
* @returns 描述导入结果的消息字符串。
|
|
34
|
+
*/
|
|
35
|
+
importData(): Promise<string>;
|
|
36
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Context, Logger } from 'koishi';
|
|
2
|
+
import { Config, CaveObject } from './index';
|
|
3
|
+
import { FileManager } from './FileManager';
|
|
4
|
+
/**
|
|
5
|
+
* @description 数据库 `cave_hash` 表的完整对象模型。
|
|
6
|
+
*/
|
|
7
|
+
export interface CaveHashObject {
|
|
8
|
+
cave: number;
|
|
9
|
+
hash: string;
|
|
10
|
+
type: 'simhash' | 'phash_g' | 'phash_q1' | 'phash_q2' | 'phash_q3' | 'phash_q4';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @class HashManager
|
|
14
|
+
* @description 负责生成、存储和比较文本与图片的哈希值。
|
|
15
|
+
* 实现了基于 Simhash 的文本查重和基于 DCT 感知哈希 (pHash) 的图片查重方案。
|
|
16
|
+
*/
|
|
17
|
+
export declare class HashManager {
|
|
18
|
+
private ctx;
|
|
19
|
+
private config;
|
|
20
|
+
private logger;
|
|
21
|
+
private fileManager;
|
|
22
|
+
/**
|
|
23
|
+
* @constructor
|
|
24
|
+
* @param ctx - Koishi 上下文,用于数据库操作。
|
|
25
|
+
* @param config - 插件配置,用于获取相似度阈值等。
|
|
26
|
+
* @param logger - 日志记录器实例。
|
|
27
|
+
* @param fileManager - 文件管理器实例,用于读取图片文件。
|
|
28
|
+
*/
|
|
29
|
+
constructor(ctx: Context, config: Config, logger: Logger, fileManager: FileManager);
|
|
30
|
+
/**
|
|
31
|
+
* @description 注册与哈希功能相关的 `.hash` 和 `.check` 子命令。
|
|
32
|
+
* @param cave - 主 `cave` 命令实例。
|
|
33
|
+
*/
|
|
34
|
+
registerCommands(cave: any): void;
|
|
35
|
+
/**
|
|
36
|
+
* @description 检查数据库中所有回声洞,为没有哈希记录的历史数据生成哈希。
|
|
37
|
+
* @returns 一个包含操作结果的报告字符串。
|
|
38
|
+
*/
|
|
39
|
+
generateHashesForHistoricalCaves(): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* @description 为单个回声洞对象生成所有类型的哈希(文本+图片)。
|
|
42
|
+
* @param cave - 回声洞对象。
|
|
43
|
+
* @returns 生成的哈希对象数组。
|
|
44
|
+
*/
|
|
45
|
+
generateAllHashesForCave(cave: Pick<CaveObject, 'id' | 'elements'>): Promise<CaveHashObject[]>;
|
|
46
|
+
/**
|
|
47
|
+
* @description 对数据库中所有哈希进行两两比较,找出相似度过高的内容。
|
|
48
|
+
* @param options 包含临时阈值的可选对象。
|
|
49
|
+
* @returns 一个包含检查结果的报告字符串。
|
|
50
|
+
*/
|
|
51
|
+
checkForSimilarCaves(options?: {
|
|
52
|
+
textThreshold?: number;
|
|
53
|
+
imageThreshold?: number;
|
|
54
|
+
}): Promise<string>;
|
|
55
|
+
/**
|
|
56
|
+
* @description 为单个图片Buffer生成全局pHash和四个象限的局部pHash。
|
|
57
|
+
* @param imageBuffer - 图片的Buffer数据。
|
|
58
|
+
* @returns 包含全局哈希和四象限哈希的对象。
|
|
59
|
+
*/
|
|
60
|
+
generateAllImageHashes(imageBuffer: Buffer): Promise<{
|
|
61
|
+
globalHash: string;
|
|
62
|
+
quadrantHashes: {
|
|
63
|
+
q1: string;
|
|
64
|
+
q2: string;
|
|
65
|
+
q3: string;
|
|
66
|
+
q4: string;
|
|
67
|
+
};
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* @description 执行二维离散余弦变换 (DCT-II)。
|
|
71
|
+
* @param matrix - 输入的 N x N 像素亮度矩阵。
|
|
72
|
+
* @returns DCT变换后的 N x N 系数矩阵。
|
|
73
|
+
*/
|
|
74
|
+
private _dct2D;
|
|
75
|
+
/**
|
|
76
|
+
* @description pHash 算法核心实现。
|
|
77
|
+
* @param imageBuffer - 图片的Buffer。
|
|
78
|
+
* @param size - 期望的哈希位数 (必须是完全平方数, 如 64 或 256)。
|
|
79
|
+
* @returns 十六进制pHash字符串。
|
|
80
|
+
*/
|
|
81
|
+
private _generatePHash;
|
|
82
|
+
/**
|
|
83
|
+
* @description 计算两个十六进制哈希字符串之间的汉明距离 (不同位的数量)。
|
|
84
|
+
* @param hex1 - 第一个哈希。
|
|
85
|
+
* @param hex2 - 第二个哈希。
|
|
86
|
+
* @returns 汉明距离。
|
|
87
|
+
*/
|
|
88
|
+
calculateHammingDistance(hex1: string, hex2: string): number;
|
|
89
|
+
/**
|
|
90
|
+
* @description 根据汉明距离计算相似度百分比。
|
|
91
|
+
* @param hex1 - 第一个哈希。
|
|
92
|
+
* @param hex2 - 第二个哈希。
|
|
93
|
+
* @returns 相似度 (0-100)。
|
|
94
|
+
*/
|
|
95
|
+
calculateSimilarity(hex1: string, hex2: string): number;
|
|
96
|
+
/**
|
|
97
|
+
* @description 为文本生成 64 位 Simhash 字符串。
|
|
98
|
+
* @param text - 需要处理的文本。
|
|
99
|
+
* @returns 16位十六进制 Simhash 字符串。
|
|
100
|
+
*/
|
|
101
|
+
generateTextSimhash(text: string): string;
|
|
102
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
/** 数据库 `cave_user` 表的结构。 */
|
|
3
|
+
export interface UserName {
|
|
4
|
+
userId: string;
|
|
5
|
+
nickname: string;
|
|
6
|
+
}
|
|
7
|
+
declare module 'koishi' {
|
|
8
|
+
interface Tables {
|
|
9
|
+
cave_user: UserName;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @class NameManager
|
|
14
|
+
* @description 负责管理用户在回声洞中的自定义昵称。
|
|
15
|
+
*/
|
|
16
|
+
export declare class NameManager {
|
|
17
|
+
private ctx;
|
|
18
|
+
/**
|
|
19
|
+
* @constructor
|
|
20
|
+
* @param ctx - Koishi 上下文,用于初始化数据库模型。
|
|
21
|
+
*/
|
|
22
|
+
constructor(ctx: Context);
|
|
23
|
+
/**
|
|
24
|
+
* @description 注册 `.name` 子命令,用于管理用户昵称。
|
|
25
|
+
* @param cave - 主 `cave` 命令实例。
|
|
26
|
+
*/
|
|
27
|
+
registerCommands(cave: any): void;
|
|
28
|
+
/**
|
|
29
|
+
* @description 设置或更新指定用户的昵称。
|
|
30
|
+
* @param userId - 目标用户的 ID。
|
|
31
|
+
* @param nickname - 要设置的新昵称。
|
|
32
|
+
*/
|
|
33
|
+
setNickname(userId: string, nickname: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* @description 获取指定用户的昵称。
|
|
36
|
+
* @param userId - 目标用户的 ID。
|
|
37
|
+
* @returns 用户的昵称字符串或 null。
|
|
38
|
+
*/
|
|
39
|
+
getNickname(userId: string): Promise<string | null>;
|
|
40
|
+
/**
|
|
41
|
+
* @description 清除指定用户的昵称设置。
|
|
42
|
+
* @param userId - 目标用户的 ID。
|
|
43
|
+
*/
|
|
44
|
+
clearNickname(userId: string): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Context, Logger } from 'koishi';
|
|
2
|
+
import { CaveObject, Config } from './index';
|
|
3
|
+
import { FileManager } from './FileManager';
|
|
4
|
+
/**
|
|
5
|
+
* @class PendManager
|
|
6
|
+
* @description 负责处理回声洞的审核流程,处理新洞的提交、审核通知和审核操作。
|
|
7
|
+
*/
|
|
8
|
+
export declare class PendManager {
|
|
9
|
+
private ctx;
|
|
10
|
+
private config;
|
|
11
|
+
private fileManager;
|
|
12
|
+
private logger;
|
|
13
|
+
private reusableIds;
|
|
14
|
+
/**
|
|
15
|
+
* @param ctx Koishi 上下文。
|
|
16
|
+
* @param config 插件配置。
|
|
17
|
+
* @param fileManager 文件管理器实例。
|
|
18
|
+
* @param logger 日志记录器实例。
|
|
19
|
+
* @param reusableIds 可复用 ID 的内存缓存。
|
|
20
|
+
*/
|
|
21
|
+
constructor(ctx: Context, config: Config, fileManager: FileManager, logger: Logger, reusableIds: Set<number>);
|
|
22
|
+
/**
|
|
23
|
+
* @description 注册与审核相关的子命令。
|
|
24
|
+
* @param cave - 主 `cave` 命令实例。
|
|
25
|
+
*/
|
|
26
|
+
registerCommands(cave: any): void;
|
|
27
|
+
/**
|
|
28
|
+
* @description 将新回声洞提交到管理群组以供审核。
|
|
29
|
+
* @param cave 新创建的、状态为 'pending' 的回声洞对象。
|
|
30
|
+
*/
|
|
31
|
+
sendForPend(cave: CaveObject): Promise<void>;
|
|
32
|
+
}
|
package/lib/Utils.d.ts
CHANGED
|
@@ -37,15 +37,6 @@ export declare function getScopeQuery(session: Session, config: Config, includeS
|
|
|
37
37
|
* @returns 可用的新 ID。
|
|
38
38
|
*/
|
|
39
39
|
export declare function getNextCaveId(ctx: Context, reusableIds: Set<number>): Promise<number>;
|
|
40
|
-
/**
|
|
41
|
-
* @description 检查用户是否处于指令冷却中。
|
|
42
|
-
* @returns 若在冷却中则提示字符串,否则 null。
|
|
43
|
-
*/
|
|
44
|
-
export declare function checkCooldown(session: Session, config: Config, lastUsed: Map<string, number>): string | null;
|
|
45
|
-
/**
|
|
46
|
-
* @description 更新指定频道的指令使用时间戳。
|
|
47
|
-
*/
|
|
48
|
-
export declare function updateCooldownTimestamp(session: Session, config: Config, lastUsed: Map<string, number>): void;
|
|
49
40
|
/**
|
|
50
41
|
* @description 解析消息元素,分离出文本和待下载的媒体文件。
|
|
51
42
|
* @param sourceElements 原始的 Koishi 消息元素数组。
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -239,8 +239,8 @@ var DataManager = class {
|
|
|
239
239
|
return `操作失败: ${error.message}`;
|
|
240
240
|
}
|
|
241
241
|
}, "requireAdmin");
|
|
242
|
-
cave.subcommand(".export", "导出回声洞数据").action(requireAdmin(() => this.exportData()));
|
|
243
|
-
cave.subcommand(".import", "导入回声洞数据").action(requireAdmin(() => this.importData()));
|
|
242
|
+
cave.subcommand(".export", "导出回声洞数据").usage("将所有回声洞数据导出到 cave_export.json 中。").action(requireAdmin(() => this.exportData()));
|
|
243
|
+
cave.subcommand(".import", "导入回声洞数据").usage("从 cave_import.json 中导入回声洞数据。").action(requireAdmin(() => this.importData()));
|
|
244
244
|
}
|
|
245
245
|
/**
|
|
246
246
|
* @description 导出所有 'active' 状态的回声洞数据到 `cave_export.json`。
|
|
@@ -399,20 +399,6 @@ async function getNextCaveId(ctx, reusableIds) {
|
|
|
399
399
|
return newId;
|
|
400
400
|
}
|
|
401
401
|
__name(getNextCaveId, "getNextCaveId");
|
|
402
|
-
function checkCooldown(session, config, lastUsed) {
|
|
403
|
-
const adminChannelId = config.adminChannel?.split(":")[1];
|
|
404
|
-
if (adminChannelId && session.channelId === adminChannelId) return null;
|
|
405
|
-
if (config.coolDown <= 0 || !session.channelId) return null;
|
|
406
|
-
const lastTime = lastUsed.get(session.channelId) || 0;
|
|
407
|
-
const remainingTime = lastTime + config.coolDown * 1e3 - Date.now();
|
|
408
|
-
if (remainingTime > 0) return `指令冷却中,请在 ${Math.ceil(remainingTime / 1e3)} 秒后重试`;
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
__name(checkCooldown, "checkCooldown");
|
|
412
|
-
function updateCooldownTimestamp(session, config, lastUsed) {
|
|
413
|
-
if (config.coolDown > 0 && session.channelId) lastUsed.set(session.channelId, Date.now());
|
|
414
|
-
}
|
|
415
|
-
__name(updateCooldownTimestamp, "updateCooldownTimestamp");
|
|
416
402
|
async function processMessageElements(sourceElements, newId, session, config, logger2) {
|
|
417
403
|
const mediaToSave = [];
|
|
418
404
|
let mediaIndex = 0;
|
|
@@ -506,7 +492,8 @@ async function handleFileUploads(ctx, config, fileManager, logger2, reviewManage
|
|
|
506
492
|
}
|
|
507
493
|
}
|
|
508
494
|
await Promise.all(downloadedMedia.map((item) => fileManager.saveFile(item.fileName, item.buffer)));
|
|
509
|
-
const
|
|
495
|
+
const needsReview = config.enablePend && session.channelId !== config.adminChannel?.split(":")[1];
|
|
496
|
+
const finalStatus = needsReview ? "pending" : "active";
|
|
510
497
|
await ctx.database.upsert("cave", [{ id: cave.id, status: finalStatus }]);
|
|
511
498
|
if (hashManager) {
|
|
512
499
|
const allHashesToInsert = [...textHashesToStore, ...imageHashesToStore].map((h4) => ({ ...h4, cave: cave.id }));
|
|
@@ -552,7 +539,7 @@ var PendManager = class {
|
|
|
552
539
|
if (session.channelId !== this.config.adminChannel?.split(":")[1]) return "此指令仅限在管理群组中使用";
|
|
553
540
|
return null;
|
|
554
541
|
}, "requireAdmin");
|
|
555
|
-
const pend = cave.subcommand(".pend [id:posint]", "审核回声洞").action(async ({ session }, id) => {
|
|
542
|
+
const pend = cave.subcommand(".pend [id:posint]", "审核回声洞").usage("查询待审核的回声洞列表,或指定 ID 查看对应待审核的回声洞。").action(async ({ session }, id) => {
|
|
556
543
|
const adminError = requireAdmin(session);
|
|
557
544
|
if (adminError) return adminError;
|
|
558
545
|
if (id) {
|
|
@@ -568,31 +555,29 @@ var PendManager = class {
|
|
|
568
555
|
return `当前共有 ${pendingCaves.length} 条待审核回声洞,序号为:
|
|
569
556
|
${pendingCaves.map((c) => c.id).join("|")}`;
|
|
570
557
|
});
|
|
571
|
-
const createPendAction = /* @__PURE__ */ __name((actionType) => async ({ session },
|
|
558
|
+
const createPendAction = /* @__PURE__ */ __name((actionType) => async ({ session }, ...ids) => {
|
|
572
559
|
const adminError = requireAdmin(session);
|
|
573
560
|
if (adminError) return adminError;
|
|
561
|
+
if (ids.length === 0) return "请指定回声洞 ID";
|
|
574
562
|
try {
|
|
575
563
|
const targetStatus = actionType === "approve" ? "active" : "delete";
|
|
576
564
|
const actionText = actionType === "approve" ? "通过" : "拒绝";
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
const [cave2] = await this.ctx.database.get("cave", { id, status: "pending" });
|
|
585
|
-
if (!cave2) return `回声洞(${id})无需审核`;
|
|
586
|
-
await this.ctx.database.upsert("cave", [{ id, status: targetStatus }]);
|
|
565
|
+
const cavesToProcess = await this.ctx.database.get("cave", {
|
|
566
|
+
id: { $in: ids },
|
|
567
|
+
status: "pending"
|
|
568
|
+
});
|
|
569
|
+
if (cavesToProcess.length === 0) return `回声洞(${ids.join("|")})无需审核或不存在`;
|
|
570
|
+
const processedIds = cavesToProcess.map((cave2) => cave2.id);
|
|
571
|
+
await this.ctx.database.upsert("cave", processedIds.map((id) => ({ id, status: targetStatus })));
|
|
587
572
|
if (targetStatus === "delete") cleanupPendingDeletions(this.ctx, this.fileManager, this.logger, this.reusableIds);
|
|
588
|
-
return
|
|
573
|
+
return `已${actionText}回声洞(${processedIds.join("|")})`;
|
|
589
574
|
} catch (error) {
|
|
590
575
|
this.logger.error(`审核操作失败:`, error);
|
|
591
576
|
return `操作失败: ${error.message}`;
|
|
592
577
|
}
|
|
593
578
|
}, "createPendAction");
|
|
594
|
-
pend.subcommand(".Y [
|
|
595
|
-
pend.subcommand(".N [
|
|
579
|
+
pend.subcommand(".Y [...ids:posint]", "通过审核").usage("通过一个或多个指定 ID 的回声洞审核。").action(createPendAction("approve"));
|
|
580
|
+
pend.subcommand(".N [...ids:posint]", "拒绝审核").usage("拒绝一个或多个指定 ID 的回声洞审核。").action(createPendAction("reject"));
|
|
596
581
|
}
|
|
597
582
|
/**
|
|
598
583
|
* @description 将新回声洞提交到管理群组以供审核。
|
|
@@ -979,7 +964,6 @@ var usage = `
|
|
|
979
964
|
var logger = new import_koishi3.Logger("best-cave");
|
|
980
965
|
var Config = import_koishi3.Schema.intersect([
|
|
981
966
|
import_koishi3.Schema.object({
|
|
982
|
-
coolDown: import_koishi3.Schema.number().default(10).description("冷却时间(秒)"),
|
|
983
967
|
perChannel: import_koishi3.Schema.boolean().default(false).description("启用分群模式"),
|
|
984
968
|
enableName: import_koishi3.Schema.boolean().default(false).description("启用自定义昵称"),
|
|
985
969
|
enableIO: import_koishi3.Schema.boolean().default(false).description("启用导入导出"),
|
|
@@ -1014,7 +998,6 @@ function apply(ctx, config) {
|
|
|
1014
998
|
time: "timestamp"
|
|
1015
999
|
}, { primary: "id" });
|
|
1016
1000
|
const fileManager = new FileManager(ctx.baseDir, config, logger);
|
|
1017
|
-
const lastUsed = /* @__PURE__ */ new Map();
|
|
1018
1001
|
const reusableIds = /* @__PURE__ */ new Set();
|
|
1019
1002
|
const profileManager = config.enableName ? new NameManager(ctx) : null;
|
|
1020
1003
|
const reviewManager = config.enablePend ? new PendManager(ctx, config, fileManager, logger, reusableIds) : null;
|
|
@@ -1025,15 +1008,12 @@ function apply(ctx, config) {
|
|
|
1025
1008
|
if (options.view) return session.execute(`cave.view ${options.view}`);
|
|
1026
1009
|
if (options.delete) return session.execute(`cave.del ${options.delete}`);
|
|
1027
1010
|
if (options.list) return session.execute("cave.list");
|
|
1028
|
-
const cdMessage = checkCooldown(session, config, lastUsed);
|
|
1029
|
-
if (cdMessage) return cdMessage;
|
|
1030
1011
|
try {
|
|
1031
1012
|
const query = getScopeQuery(session, config);
|
|
1032
1013
|
const candidates = await ctx.database.get("cave", query, { fields: ["id"] });
|
|
1033
1014
|
if (!candidates.length) return `当前${config.perChannel && session.channelId ? "本群" : ""}还没有任何回声洞`;
|
|
1034
1015
|
const randomId = candidates[Math.floor(Math.random() * candidates.length)].id;
|
|
1035
1016
|
const [randomCave] = await ctx.database.get("cave", { ...query, id: randomId });
|
|
1036
|
-
updateCooldownTimestamp(session, config, lastUsed);
|
|
1037
1017
|
const messages = await buildCaveMessage(randomCave, config, fileManager, logger, session.platform);
|
|
1038
1018
|
for (const message of messages) if (message.length > 0) await session.send(import_koishi3.h.normalize(message));
|
|
1039
1019
|
} catch (error) {
|
|
@@ -1074,7 +1054,8 @@ function apply(ctx, config) {
|
|
|
1074
1054
|
}
|
|
1075
1055
|
const userName = (config.enableName ? await profileManager.getNickname(session.userId) : null) || session.username;
|
|
1076
1056
|
const hasMedia = mediaToSave.length > 0;
|
|
1077
|
-
const
|
|
1057
|
+
const needsReview = config.enablePend && session.channelId !== config.adminChannel?.split(":")[1];
|
|
1058
|
+
const initialStatus = hasMedia ? "preload" : needsReview ? "pending" : "active";
|
|
1078
1059
|
const newCave = await ctx.database.create("cave", {
|
|
1079
1060
|
id: newId,
|
|
1080
1061
|
elements: finalElementsForDb,
|
|
@@ -1098,12 +1079,9 @@ function apply(ctx, config) {
|
|
|
1098
1079
|
});
|
|
1099
1080
|
cave.subcommand(".view <id:posint>", "查看指定回声洞").action(async ({ session }, id) => {
|
|
1100
1081
|
if (!id) return "请输入要查看的回声洞序号";
|
|
1101
|
-
const cdMessage = checkCooldown(session, config, lastUsed);
|
|
1102
|
-
if (cdMessage) return cdMessage;
|
|
1103
1082
|
try {
|
|
1104
1083
|
const [targetCave] = await ctx.database.get("cave", { ...getScopeQuery(session, config), id });
|
|
1105
1084
|
if (!targetCave) return `回声洞(${id})不存在`;
|
|
1106
|
-
updateCooldownTimestamp(session, config, lastUsed);
|
|
1107
1085
|
const messages = await buildCaveMessage(targetCave, config, fileManager, logger, session.platform);
|
|
1108
1086
|
for (const message of messages) if (message.length > 0) await session.send(import_koishi3.h.normalize(message));
|
|
1109
1087
|
} catch (error) {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
| `cave.name [昵称]` | `enableName: true` | 设置你在回声洞中显示的昵称。若不提供昵称,则清除现有设置。 |
|
|
41
41
|
| `cave.pend` | `enablePend: true` | **(仅限管理群组)** 列出所有待审核的回声洞ID。 |
|
|
42
42
|
| `cave.pend <序号>` | `enablePend: true` | **(仅限管理群组)** 查看指定待审核内容的详情。 |
|
|
43
|
-
| `cave.pend.Y [
|
|
44
|
-
| `cave.pend.N [
|
|
43
|
+
| `cave.pend.Y [...序号]` | `enablePend: true` | **(仅限管理群组)** 通过审核。若不提供序号,则通过所有待审核内容。 |
|
|
44
|
+
| `cave.pend.N [...序号]` | `enablePend: true` | **(仅限管理群组)** 拒绝审核。若不提供序号,则拒绝所有待审核内容。 |
|
|
45
45
|
| `cave.export` | `enableIO: true` | **(仅限管理群组)** 将所有`active`状态的回声洞导出到 `cave_export.json`。 |
|
|
46
46
|
| `cave.import` | `enableIO: true` | **(仅限管理群组)** 从 `cave_import.json` 文件中导入数据。 |
|
|
47
47
|
| `cave.hash` | `enableSimilarity: true` | **(仅限管理群组)** 校验所有历史数据,为缺失哈希的回声洞补全记录。 |
|
|
@@ -53,7 +53,6 @@
|
|
|
53
53
|
|
|
54
54
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
55
55
|
| :--- | :--- | :--- | :--- |
|
|
56
|
-
| `coolDown` | `number` | `10` | `cave` 和 `cave.view` 指令的冷却时间(秒)。在管理群组中无冷却。 |
|
|
57
56
|
| `perChannel` | `boolean` | `false` | 是否启用分群模式。`true` 表示各群的回声洞独立。 |
|
|
58
57
|
| `enableName` | `boolean` | `false` | 是否启用自定义昵称功能 (`cave.name` 指令)。 |
|
|
59
58
|
| `enableIO` | `boolean` | `false` | 是否启用数据导入/导出功能 (`cave.export` / `.import` 指令)。 |
|