koishi-plugin-best-cave 2.3.18 → 2.4.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/index.d.ts CHANGED
@@ -56,7 +56,6 @@ export interface Config {
56
56
  secretAccessKey?: string;
57
57
  bucket?: string;
58
58
  publicUrl?: string;
59
- debug: boolean;
60
59
  }
61
60
  export declare const Config: Schema<Config>;
62
61
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -1001,10 +1001,7 @@ var Config = import_koishi3.Schema.intersect([
1001
1001
  region: import_koishi3.Schema.string().default("auto").description("区域 (Region)"),
1002
1002
  accessKeyId: import_koishi3.Schema.string().description("Access Key ID").role("secret"),
1003
1003
  secretAccessKey: import_koishi3.Schema.string().description("Secret Access Key").role("secret")
1004
- }).description("存储配置"),
1005
- import_koishi3.Schema.object({
1006
- debug: import_koishi3.Schema.boolean().default(false).description("启用调试模式,将在控制台输出详细的操作日志。")
1007
- }).description("开发配置")
1004
+ }).description("存储配置")
1008
1005
  ]);
1009
1006
  function apply(ctx, config) {
1010
1007
  ctx.model.extend("cave", {
@@ -1057,16 +1054,8 @@ function apply(ctx, config) {
1057
1054
  if (!reply) return "等待操作超时";
1058
1055
  sourceElements = import_koishi3.h.parse(reply);
1059
1056
  }
1060
- if (config.debug) {
1061
- logger.info(`消息内容:
1062
- ${JSON.stringify(sourceElements, null, 2)}`);
1063
- logger.info(`完整会话:
1064
- ${JSON.stringify(session, null, 2)}`);
1065
- }
1066
1057
  const newId = await getNextCaveId(ctx, getScopeQuery(session, config, false), reusableIds);
1067
1058
  const { finalElementsForDb, mediaToSave } = await processMessageElements(sourceElements, newId, session, config, logger);
1068
- if (config.debug) logger.info(`数据库元素:
1069
- ${JSON.stringify(finalElementsForDb, null, 2)}`);
1070
1059
  if (finalElementsForDb.length === 0) return "无可添加内容";
1071
1060
  const textHashesToStore = [];
1072
1061
  if (hashManager) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-best-cave",
3
3
  "description": "功能强大、高度可定制的回声洞。支持丰富的媒体类型、内容查重、人工审核、用户昵称、数据迁移以及本地/S3 双重文件存储后端。",
4
- "version": "2.3.18",
4
+ "version": "2.4.0",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],
package/readme.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  - **丰富的内容形式**:不止于文本,轻松发布包含图片、视频、音频甚至文件的混合内容。插件能自动解析回复或引用的消息,并将其完整存入回声洞。
10
10
  - **灵活的存储后端**:媒体文件可存储在 **本地服务器** (`data/cave` 目录),或配置使用 **AWS S3** 兼容的云端对象存储。支持通过公共URL、本地文件路径或Base64三种方式发送媒体。
11
- - **高级内容查重**:(可选) 启用后,插件会在添加时自动计算文本(Simhash)和图片(pHash,包括整体及分块哈希)的哈希值,拒绝与现有内容相似度过高的投稿,并能对局部相似的图片进行提示,有效防止重复。
11
+ - **高级内容查重**:(可选) 启用后,插件会在添加时自动计算文本 (Simhash) 和图片 (pHash) 的哈希值。对于相似度过高的投稿将直接拒绝,有效防止重复。此外,还会对图片的四个象限进行哈希,管理员可通过维护工具找出局部相同的图片。
12
12
  - **完善的审核机制**:(可选) 启用后,所有新投稿都将进入待审核状态,并通知管理群组。只有管理员审核通过后,内容才会对用户可见,确保内容质量。
13
13
  - **精细的作用域**:通过 `perChannel` 配置,可设定回声洞是在所有群聊中共享(全局模式),还是在每个群聊中独立(分群模式)。
14
14
  - **专属用户昵称**:(可选) 用户可以为自己在回声洞中的发言设置一个专属昵称,增加趣味性。
@@ -66,8 +66,8 @@
66
66
  | :--- | :--- | :--- | :--- |
67
67
  | `enablePend` | `boolean` | `false` | 是否启用审核机制。启用后,新投稿将进入`pending`状态。 |
68
68
  | `enableSimilarity` | `boolean` | `false` | 是否启用内容相似度检查(查重)。 |
69
- | `textThreshold` | `number` | `95` | **文本**相似度阈值 (0-100)。基于Simhash汉明距离计算,超过此值将被拒绝。 |
70
- | `imageThreshold` | `number` | `95` | **图片整体**相似度阈值 (0-100)。基于pHash汉明距离计算,超过此值将被拒绝。 |
69
+ | `textThreshold` | `number` | `90` | **文本**相似度阈值 (0-100)。基于Simhash汉明距离计算,超过此值将被拒绝。 |
70
+ | `imageThreshold` | `number` | `90` | **图片整体**相似度阈值 (0-100)。基于pHash汉明距离计算,超过此值将被拒绝。 |
71
71
 
72
72
  ### 存储配置
73
73
 
@@ -91,4 +91,4 @@
91
91
  2. **文件存储权限**:若使用本地存储,请确保 Koishi 拥有对 `data/cave` 目录的读写权限。若使用 S3,请确保 Access Key 权限和存储桶策略(如ACL设为`public-read`)配置正确。
92
92
  3. **异步删除**:删除操作(`cave.del` 或审核拒绝)会将内容状态标记为 `delete`,然后由后台任务异步清理关联的文件和数据库条目,以避免阻塞当前指令。
93
93
  4. **数据迁移**:导入功能会读取插件数据目录下的 `cave_import.json`。导出功能则会生成 `cave_export.json`。请在操作前放置或备份好相应文件。导入时,ID会从现有最大ID开始自增,不会覆盖或修改老数据。
94
- 5. **查重机制**:图片查重不仅比较整图,还会比较图片的四个象限。如果象限完全一致,会发送提示但不会直接拒绝,为识别拼接图、裁剪图等提供了依据。
94
+ 5. **查重机制**:图片查重在投稿时,仅基于**整图**的相似度进行判断和拒绝。插件还会为图片的四个象限生成哈希,但这些局部哈希仅用于 `cave.check` 指令生成相似度报告,以供管理员识别拼接图、裁剪图等情况,并不会在投稿时直接导致拒绝。
@@ -1,36 +0,0 @@
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
- }
@@ -1,48 +0,0 @@
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
- }
@@ -1,102 +0,0 @@
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
- }
@@ -1,45 +0,0 @@
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
- }
@@ -1,32 +0,0 @@
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 DELETED
@@ -1,83 +0,0 @@
1
- import { Context, h, Logger, Session } from 'koishi';
2
- import { CaveObject, Config, StoredElement } from './index';
3
- import { FileManager } from './FileManager';
4
- import { HashManager, CaveHashObject } from './HashManager';
5
- import { PendManager } from './PendManager';
6
- /**
7
- * @description 构建一条用于发送的完整回声洞消息,处理不同存储后端的资源链接。
8
- * @param cave 回声洞对象。
9
- * @param config 插件配置。
10
- * @param fileManager 文件管理器实例。
11
- * @param logger 日志记录器实例。
12
- * @param platform 目标平台名称 (e.g., 'onebot')。
13
- * @param prefix 可选的消息前缀 (e.g., '已删除', '待审核')。
14
- * @returns 包含多条消息的数组,每条消息是一个 (string | h)[] 数组。
15
- */
16
- export declare function buildCaveMessage(cave: CaveObject, config: Config, fileManager: FileManager, logger: Logger, platform?: string, prefix?: string): Promise<(string | h)[][]>;
17
- /**
18
- * @description 清理数据库中标记为 'delete' 状态的回声洞及其关联文件和哈希。
19
- * @param ctx Koishi 上下文。
20
- * @param fileManager 文件管理器实例。
21
- * @param logger 日志记录器实例。
22
- * @param reusableIds 可复用 ID 的内存缓存。
23
- */
24
- export declare function cleanupPendingDeletions(ctx: Context, fileManager: FileManager, logger: Logger, reusableIds: Set<number>): Promise<void>;
25
- /**
26
- * @description 根据配置和会话,生成数据库查询的范围条件。
27
- * @param session 当前会话。
28
- * @param config 插件配置。
29
- * @param includeStatus 是否包含 status: 'active' 条件,默认为 true。
30
- * @returns 数据库查询条件对象。
31
- */
32
- export declare function getScopeQuery(session: Session, config: Config, includeStatus?: boolean): object;
33
- /**
34
- * @description 获取下一个可用的回声洞 ID,采用“回收ID > 扫描空缺 > 最大ID+1”策略。
35
- * @param ctx Koishi 上下文。
36
- * @param query 查询范围条件。
37
- * @param reusableIds 可复用 ID 的内存缓存。
38
- * @returns 可用的新 ID。
39
- */
40
- export declare function getNextCaveId(ctx: Context, query: object, reusableIds: Set<number>): Promise<number>;
41
- /**
42
- * @description 检查用户是否处于指令冷却中。
43
- * @returns 若在冷却中则提示字符串,否则 null。
44
- */
45
- export declare function checkCooldown(session: Session, config: Config, lastUsed: Map<string, number>): string | null;
46
- /**
47
- * @description 更新指定频道的指令使用时间戳。
48
- */
49
- export declare function updateCooldownTimestamp(session: Session, config: Config, lastUsed: Map<string, number>): void;
50
- /**
51
- * @description 解析消息元素,分离出文本和待下载的媒体文件。
52
- * @param sourceElements 原始的 Koishi 消息元素数组。
53
- * @param newId 这条回声洞的新 ID。
54
- * @param session 触发操作的会话。
55
- * @param config 插件配置。
56
- * @param logger 日志实例。
57
- * @returns 包含数据库元素和待保存媒体列表的对象。
58
- */
59
- export declare function processMessageElements(sourceElements: h[], newId: number, session: Session, config: Config, logger: Logger): Promise<{
60
- finalElementsForDb: StoredElement[];
61
- mediaToSave: {
62
- sourceUrl: string;
63
- fileName: string;
64
- }[];
65
- }>;
66
- /**
67
- * @description 异步处理文件上传、查重和状态更新的后台任务。
68
- * @param ctx - Koishi 上下文。
69
- * @param config - 插件配置。
70
- * @param fileManager - FileManager 实例,用于保存文件。
71
- * @param logger - 日志记录器实例。
72
- * @param reviewManager - ReviewManager 实例,用于提交审核。
73
- * @param cave - 刚刚在数据库中创建的 `preload` 状态的回声洞对象。
74
- * @param mediaToSave - 需要下载和处理的媒体文件列表。
75
- * @param reusableIds - 可复用 ID 的内存缓存。
76
- * @param session - 触发此操作的用户会话,用于发送反馈。
77
- * @param hashManager - HashManager 实例,如果启用则用于哈希计算和比较。
78
- * @param textHashesToStore - 已预先计算好的、待存入数据库的文本哈希对象数组。
79
- */
80
- export declare function handleFileUploads(ctx: Context, config: Config, fileManager: FileManager, logger: Logger, reviewManager: PendManager, cave: CaveObject, mediaToToSave: {
81
- sourceUrl: string;
82
- fileName: string;
83
- }[], reusableIds: Set<number>, session: Session, hashManager: HashManager, textHashesToStore: Omit<CaveHashObject, 'cave'>[]): Promise<void>;