@pynickle/koishi-plugin-chat-summarizer 1.0.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 +46 -0
- package/lib/ai-service.d.ts +83 -0
- package/lib/card-renderer.d.ts +81 -0
- package/lib/commands.d.ts +34 -0
- package/lib/config.d.ts +30 -0
- package/lib/database.d.ts +32 -0
- package/lib/export.d.ts +72 -0
- package/lib/file-writer.d.ts +12 -0
- package/lib/index.cjs +6513 -0
- package/lib/index.d.ts +6 -0
- package/lib/md-to-image.d.ts +36 -0
- package/lib/message-processor.d.ts +68 -0
- package/lib/s3-uploader.d.ts +125 -0
- package/lib/services.d.ts +26 -0
- package/lib/statistics.d.ts +74 -0
- package/lib/types.d.ts +216 -0
- package/lib/utils.d.ts +47 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# koishi-plugin-chat-summarizer
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/koishi-plugin-chat-summarizer)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> Koishi 聊天记录收集和上传插件
|
|
7
|
+
|
|
8
|
+
## 主要功能
|
|
9
|
+
|
|
10
|
+
- 自动收集群聊消息并保存到本地
|
|
11
|
+
- 自动上传图片、文件到 S3 存储
|
|
12
|
+
- 支持多种格式和时间范围的聊天记录导出
|
|
13
|
+
- 支持 AI 总结功能(文本/图片格式)
|
|
14
|
+
- 定时任务和数据库缓存管理
|
|
15
|
+
- 支持 AWS S3、MinIO 等 S3 兼容存储
|
|
16
|
+
|
|
17
|
+
## 📚 API
|
|
18
|
+
|
|
19
|
+
### 命令
|
|
20
|
+
|
|
21
|
+
- `cs.status`: 查看插件状态
|
|
22
|
+
- `cs.geturl`: 获取回复消息中图片/文件的 S3 链接(仅管理员可用)
|
|
23
|
+
- 使用方法:回复包含图片或文件的消息,然后发送 `cs.geturl` 命令
|
|
24
|
+
- 权限要求:需要在配置中的 `admin.adminIds` 列表中
|
|
25
|
+
- ⚠️ 限制:只能查询最近 24 小时内的消息(数据库缓存期限)
|
|
26
|
+
- `cs.export [群组] [时间范围] [格式] [-t 消息类型] [-s]`: 导出指定时间范围的聊天记录(仅管理员可用)
|
|
27
|
+
- 时间范围:预设(today/yesterday/last7days 等)或具体日期(2024-01-01)
|
|
28
|
+
- 格式:json(默认)、txt、csv
|
|
29
|
+
- 可选参数:-t 过滤消息类型,-s 生成 AI 总结
|
|
30
|
+
- `cs.summary.check [天数]`: 检查缺失的 AI 总结(仅管理员可用)
|
|
31
|
+
- 默认检查最近 7 天,可指定 1-365 天
|
|
32
|
+
- 显示哪些日期的群组缺失 AI 总结
|
|
33
|
+
- `cs.summary.retry <日期> [群组ID]`: 重新生成指定日期的 AI 总结(仅管理员可用)
|
|
34
|
+
- 支持重新生成单个群组或该日期所有群组的总结
|
|
35
|
+
- 自动清除旧的总结记录并重新生成
|
|
36
|
+
- `cs.summary.get <日期> [群组ID]`: 获取指定日期的 AI 总结图片(仅管理员可用)
|
|
37
|
+
- 支持预设日期(today、yesterday 等)和具体日期格式
|
|
38
|
+
- 在群聊中可省略群组参数,自动使用当前群组
|
|
39
|
+
- 群组参数:
|
|
40
|
+
- `current` - 当前群(仅在群聊中有效)
|
|
41
|
+
- `123456789` - 具体群号
|
|
42
|
+
- `cs.analysis <自然语言查询>`: AI 分析聊天记录(仅管理员可用)
|
|
43
|
+
- 自动识别时间范围并转换为具体日期(如:昨天 → 2025-01-07,最近 3 天 → 2025-01-05, 2025-01-06, 2025-01-07)
|
|
44
|
+
- 根据问题生成针对性分析结果(限制 100 字以内)
|
|
45
|
+
- 支持各种类型的查询(事件总结、话题分析、金句提取等)
|
|
46
|
+
- 输出纯文本格式,简洁直接
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config, AISummaryOutput } from './types';
|
|
3
|
+
export declare class AIService {
|
|
4
|
+
private logger;
|
|
5
|
+
private globalConfig;
|
|
6
|
+
constructor(ctx: Context, config: Config);
|
|
7
|
+
/**
|
|
8
|
+
* 获取全局 AI 配置
|
|
9
|
+
*/
|
|
10
|
+
private get config();
|
|
11
|
+
/**
|
|
12
|
+
* 获取群组专用的 AI 配置
|
|
13
|
+
*/
|
|
14
|
+
private getGroupAIConfig;
|
|
15
|
+
/**
|
|
16
|
+
* 检查 AI 服务是否已启用并配置正确
|
|
17
|
+
*/
|
|
18
|
+
isEnabled(guildId?: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* 替换模板变量
|
|
21
|
+
*/
|
|
22
|
+
private replaceTemplate;
|
|
23
|
+
/**
|
|
24
|
+
* 获取群组信息描述
|
|
25
|
+
*/
|
|
26
|
+
private getGroupInfo;
|
|
27
|
+
private getApiMode;
|
|
28
|
+
private getApiBaseUrl;
|
|
29
|
+
private buildModel;
|
|
30
|
+
private generateTextWithMessages;
|
|
31
|
+
/**
|
|
32
|
+
* 生成聊天记录总结
|
|
33
|
+
*/
|
|
34
|
+
generateSummary(content: string, timeRange: string, messageCount: number, guildId: string): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* 构建文件模式的用户提示词
|
|
37
|
+
*/
|
|
38
|
+
private buildFilePrompt;
|
|
39
|
+
/**
|
|
40
|
+
* 获取默认系统提示词(作为备用)
|
|
41
|
+
*/
|
|
42
|
+
private getDefaultSystemPrompt;
|
|
43
|
+
/**
|
|
44
|
+
* 获取默认用户提示词模板(作为备用)
|
|
45
|
+
*/
|
|
46
|
+
private getDefaultUserPromptTemplate;
|
|
47
|
+
/**
|
|
48
|
+
* 测试 AI 接口连接
|
|
49
|
+
*/
|
|
50
|
+
testConnection(): Promise<{
|
|
51
|
+
success: boolean;
|
|
52
|
+
error?: string;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* 解析用户的自然语言分析查询
|
|
56
|
+
* 返回时间范围和分析提示词
|
|
57
|
+
*/
|
|
58
|
+
parseAnalysisQuery(userQuery: string, guildId: string): Promise<{
|
|
59
|
+
timeRange: string;
|
|
60
|
+
analysisPrompt: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* 执行聊天记录分析
|
|
64
|
+
*/
|
|
65
|
+
analyzeChat(content: string, analysisPrompt: string, timeRange: string, messageCount: number, guildId: string): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* 生成结构化的 AI 总结
|
|
68
|
+
* 返回固定格式的 JSON 数据,由前端代码负责渲染
|
|
69
|
+
*/
|
|
70
|
+
generateStructuredSummary(content: string, timeRange: string, messageCount: number, guildId: string, uniqueUsers: number): Promise<AISummaryOutput>;
|
|
71
|
+
/**
|
|
72
|
+
* 解析结构化响应
|
|
73
|
+
*/
|
|
74
|
+
private parseStructuredResponse;
|
|
75
|
+
/**
|
|
76
|
+
* 验证并规范化输出结构
|
|
77
|
+
*/
|
|
78
|
+
private validateAndNormalizeOutput;
|
|
79
|
+
/**
|
|
80
|
+
* 获取默认的 AI 总结输出
|
|
81
|
+
*/
|
|
82
|
+
private getDefaultAISummaryOutput;
|
|
83
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { DailyReport } from './types';
|
|
3
|
+
export declare class CardRenderer {
|
|
4
|
+
private ctx;
|
|
5
|
+
private logger;
|
|
6
|
+
private isRendering;
|
|
7
|
+
private renderQueue;
|
|
8
|
+
constructor(ctx: Context);
|
|
9
|
+
/**
|
|
10
|
+
* 等待渲染槽位
|
|
11
|
+
*/
|
|
12
|
+
private waitForRenderSlot;
|
|
13
|
+
/**
|
|
14
|
+
* 释放渲染槽位
|
|
15
|
+
*/
|
|
16
|
+
private releaseRenderSlot;
|
|
17
|
+
/**
|
|
18
|
+
* 渲染完整的群日报
|
|
19
|
+
*/
|
|
20
|
+
renderDailyReport(report: DailyReport): Promise<Buffer>;
|
|
21
|
+
/**
|
|
22
|
+
* 生成完整的 HTML
|
|
23
|
+
*/
|
|
24
|
+
private generateHTML;
|
|
25
|
+
/**
|
|
26
|
+
* 获取 CSS 样式
|
|
27
|
+
*/
|
|
28
|
+
private getStyles;
|
|
29
|
+
/**
|
|
30
|
+
* 渲染头部卡片
|
|
31
|
+
*/
|
|
32
|
+
private renderHeaderCard;
|
|
33
|
+
/**
|
|
34
|
+
* 渲染总结卡片
|
|
35
|
+
*/
|
|
36
|
+
private renderSummaryCard;
|
|
37
|
+
/**
|
|
38
|
+
* 渲染热点话题卡片
|
|
39
|
+
*/
|
|
40
|
+
private renderHotTopicsCard;
|
|
41
|
+
/**
|
|
42
|
+
* 渲染重要信息卡片
|
|
43
|
+
*/
|
|
44
|
+
private renderImportantInfoCard;
|
|
45
|
+
/**
|
|
46
|
+
* 渲染金句卡片
|
|
47
|
+
*/
|
|
48
|
+
private renderQuotesCard;
|
|
49
|
+
/**
|
|
50
|
+
* 渲染活跃排行卡片
|
|
51
|
+
*/
|
|
52
|
+
private renderActivityRankingCard;
|
|
53
|
+
/**
|
|
54
|
+
* 渲染时段分布卡片
|
|
55
|
+
*/
|
|
56
|
+
private renderHourlyDistributionCard;
|
|
57
|
+
/**
|
|
58
|
+
* 渲染互动关系卡片
|
|
59
|
+
*/
|
|
60
|
+
private renderInteractionsCard;
|
|
61
|
+
/**
|
|
62
|
+
* 获取时段描述
|
|
63
|
+
*/
|
|
64
|
+
private getPeriodDescription;
|
|
65
|
+
/**
|
|
66
|
+
* HTML 转义
|
|
67
|
+
*/
|
|
68
|
+
private escapeHtml;
|
|
69
|
+
/**
|
|
70
|
+
* 将文本中的 emoji 转换为图片标签
|
|
71
|
+
*/
|
|
72
|
+
private convertEmojiToImages;
|
|
73
|
+
/**
|
|
74
|
+
* 获取 emoji 的 Unicode 码点
|
|
75
|
+
*/
|
|
76
|
+
private getEmojiCodePoint;
|
|
77
|
+
/**
|
|
78
|
+
* 清理文本中的 URL 和图片链接
|
|
79
|
+
*/
|
|
80
|
+
private cleanUrlsAndImages;
|
|
81
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config } from './types';
|
|
3
|
+
import { DatabaseOperations } from './database';
|
|
4
|
+
import { S3Uploader } from './s3-uploader';
|
|
5
|
+
export declare class CommandHandler {
|
|
6
|
+
private ctx;
|
|
7
|
+
private config;
|
|
8
|
+
private dbOps;
|
|
9
|
+
private s3Uploader;
|
|
10
|
+
private getStorageDir;
|
|
11
|
+
private getNextExecutionTime;
|
|
12
|
+
private generateSummaryForRecord;
|
|
13
|
+
private exportManager;
|
|
14
|
+
private aiService;
|
|
15
|
+
private mdToImageService;
|
|
16
|
+
constructor(ctx: Context, config: Config, dbOps: DatabaseOperations, s3Uploader: S3Uploader | null, getStorageDir: (subDir: string) => string, getNextExecutionTime: (targetTime: string) => Date, generateSummaryForRecord: (record: any, skipPush?: boolean) => Promise<string | undefined>);
|
|
17
|
+
private normalizeQQId;
|
|
18
|
+
private isAdmin;
|
|
19
|
+
private sendMessage;
|
|
20
|
+
registerCommands(): void;
|
|
21
|
+
private handleGetUrlCommand;
|
|
22
|
+
private handleStatusCommand;
|
|
23
|
+
private handleExportCommand;
|
|
24
|
+
private downloadExportContent;
|
|
25
|
+
private extractMessageCount;
|
|
26
|
+
private sendSummaryAsForward;
|
|
27
|
+
private handleSummaryCheckCommand;
|
|
28
|
+
private handleSummaryRetryCommand;
|
|
29
|
+
private handleSummaryGetCommand;
|
|
30
|
+
private parseDate;
|
|
31
|
+
private handleMdTestCommand;
|
|
32
|
+
private generateTestMarkdown;
|
|
33
|
+
private handleAnalysisCommand;
|
|
34
|
+
}
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Schema } from 'koishi';
|
|
2
|
+
import { Config } from './types';
|
|
3
|
+
export declare const name = "chat-summarizer";
|
|
4
|
+
export declare const inject: {
|
|
5
|
+
required: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare const ConfigSchema: Schema<Config>;
|
|
8
|
+
export declare const STRUCTURED_SYSTEM_PROMPT = "\u4F60\u662F\u4E13\u4E1A\u7684\u7FA4\u804A\u8BB0\u5F55\u5206\u6790\u52A9\u624B\u3002\u4F60\u9700\u8981\u5206\u6790\u804A\u5929\u8BB0\u5F55\u5E76\u8F93\u51FA\u7ED3\u6784\u5316\u7684 JSON \u6570\u636E\u3002\n\n\u4F60\u5FC5\u987B\u4E14\u53EA\u80FD\u8F93\u51FA\u4EE5\u4E0B JSON \u683C\u5F0F\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u89E3\u91CA\u6027\u6587\u5B57\u3001\u4EE3\u7801\u5757\u6807\u8BB0\u6216\u5176\u4ED6\u5185\u5BB9\uFF1A\n{\n \"summary\": {\n \"overview\": \"30-50 \u5B57\u7684\u6574\u4F53\u6982\u8FF0\uFF0C\u63CF\u8FF0\u4ECA\u5929\u7FA4\u5185\u7684\u4E3B\u8981\u6D3B\u52A8\u548C\u6C1B\u56F4\",\n \"highlights\": [\"\u8981\u70B9 1\", \"\u8981\u70B9 2\", \"\u8981\u70B9 3\"],\n \"atmosphere\": \"\u7528 2-4 \u4E2A\u8BCD\u63CF\u8FF0\u7FA4\u5185\u6C1B\u56F4\uFF0C\u5982\uFF1A\u8F7B\u677E\u6109\u5FEB\u3001\u70ED\u70C8\u8BA8\u8BBA\u3001\u6E29\u99A8\u4E92\u52A9\u7B49\"\n },\n \"hotTopics\": [\n {\n \"topic\": \"\u8BDD\u9898\u540D\u79F0\",\n \"description\": \"\u7B80\u77ED\u63CF\u8FF0\u8BE5\u8BDD\u9898\u7684\u5185\u5BB9\",\n \"participants\": [\"\u53C2\u4E0E\u8005 1\", \"\u53C2\u4E0E\u8005 2\"],\n \"heatLevel\": \"high/medium/low\"\n }\n ],\n \"importantInfo\": [\n {\n \"type\": \"announcement/link/resource/decision/other\",\n \"content\": \"\u91CD\u8981\u4FE1\u606F\u5185\u5BB9\",\n \"source\": \"\u4FE1\u606F\u6765\u6E90\uFF08\u53EF\u9009\uFF09\"\n }\n ],\n \"quotes\": [\n {\n \"content\": \"\u6709\u8DA3\u6216\u7CBE\u5F69\u7684\u53D1\u8A00\u5185\u5BB9\",\n \"author\": \"\u53D1\u8A00\u4EBA\"\n }\n ]\n}\n\n\u5206\u6790\u8981\u6C42\uFF1A\n1. summary.overview - 30-50 \u5B57\u6574\u4F53\u6982\u8FF0\uFF0C\u7528\u8BCD\u8981\u751F\u52A8\u4F46\u6E05\u6670\n2. summary.highlights - 3-5 \u4E2A\u8981\u70B9\uFF0C\u6BCF\u4E2A\u8981\u70B9\u4E00\u53E5\u8BDD\n3. summary.atmosphere - \u63CF\u8FF0\u7FA4\u5185\u6C1B\u56F4\u7684\u7B80\u77ED\u8BCD\u7EC4\n4. hotTopics - \u6309\u70ED\u5EA6\u6392\u5E8F\uFF0C\u6700\u591A 5 \u4E2A\uFF0CheatLevel \u6839\u636E\u8BA8\u8BBA\u70ED\u5EA6\u5224\u65AD\n5. importantInfo - \u63D0\u53D6\u516C\u544A\u3001\u91CD\u8981\u51B3\u5B9A\u7B49\uFF0C\u6CA1\u6709\u5219\u8FD4\u56DE\u7A7A\u6570\u7EC4\n6. quotes - \u6700\u6709\u8DA3/\u7CBE\u5F69/\u6709\u54F2\u7406\u7684\u53D1\u8A00\uFF0C\u6700\u591A 5 \u53E5\uFF0C\u6CA1\u6709\u5219\u8FD4\u56DE\u7A7A\u6570\u7EC4\n\n\u91CD\u8981\u6CE8\u610F\u4E8B\u9879\uFF1A\n- \u4E25\u683C\u6309 JSON \u683C\u5F0F\u8F93\u51FA\uFF0C\u786E\u4FDD JSON \u8BED\u6CD5\u6B63\u786E\n- \u4E0D\u8981\u5728 JSON \u524D\u540E\u6DFB\u52A0\u4EFB\u4F55\u6587\u5B57\u8BF4\u660E\n- \u4E0D\u8981\u4F7F\u7528 markdown \u4EE3\u7801\u5757\u5305\u88F9 JSON\n- \u5982\u679C\u804A\u5929\u5185\u5BB9\u8F83\u5C11\uFF0C\u5404\u5B57\u6BB5\u53EF\u4EE5\u7CBE\u7B80\u4F46\u7ED3\u6784\u5FC5\u987B\u5B8C\u6574\n- \u4FDD\u62A4\u9690\u79C1\uFF0C\u4E0D\u900F\u9732\u654F\u611F\u4E2A\u4EBA\u4FE1\u606F\n- **importantInfo \u7684 content \u5B57\u6BB5\u5FC5\u987B\u662F\u5BF9\u4FE1\u606F\u7684\u63CF\u8FF0\uFF0C\u7981\u6B62\u76F4\u63A5\u653E\u5165\u539F\u59CB URL \u94FE\u63A5\u6216\u56FE\u7247\u6807\u8BB0**\n- **\u4E0D\u8981\u628A\u7EAF\u94FE\u63A5\u3001\u7EAF\u56FE\u7247\u5F53\u4F5C\u91CD\u8981\u4FE1\u606F\uFF0C\u53EA\u63D0\u53D6\u6709\u5B9E\u9645\u5185\u5BB9\u63CF\u8FF0\u7684\u4FE1\u606F**";
|
|
9
|
+
export declare const CONSTANTS: {
|
|
10
|
+
readonly STORAGE_DIRS: {
|
|
11
|
+
readonly DATA: "data";
|
|
12
|
+
};
|
|
13
|
+
readonly URL_REPLACEMENTS: {
|
|
14
|
+
readonly OLD_DOMAIN: "cn-sy1.rains3.com/qqmsg";
|
|
15
|
+
readonly NEW_DOMAIN: "qqmsg.pan.wittf.ink";
|
|
16
|
+
};
|
|
17
|
+
readonly FILE_SETTINGS: {
|
|
18
|
+
readonly ENCODING: "utf8";
|
|
19
|
+
readonly LINE_SEPARATOR: "\n";
|
|
20
|
+
readonly JSON_EXTENSION: ".jsonl";
|
|
21
|
+
};
|
|
22
|
+
readonly DEFAULTS: {
|
|
23
|
+
readonly UNKNOWN_USER: "未知用户";
|
|
24
|
+
readonly PRIVATE_GROUP: "private";
|
|
25
|
+
readonly QUOTE_AUTHOR_FALLBACK: "某用户";
|
|
26
|
+
};
|
|
27
|
+
readonly S3_REGION: "auto";
|
|
28
|
+
readonly MAX_CONTENT_PREVIEW: 50;
|
|
29
|
+
readonly IMAGE_UPLOAD_TIMEOUT: 60000;
|
|
30
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { ChatRecord, ImageRecord, FileRecord, VideoRecord, ChatLogFileRecord, PluginStats } from './types';
|
|
3
|
+
export declare function extendDatabase(ctx: Context): void;
|
|
4
|
+
export declare class DatabaseOperations {
|
|
5
|
+
private ctx;
|
|
6
|
+
constructor(ctx: Context);
|
|
7
|
+
createChatRecord(record: Omit<ChatRecord, 'id'>): Promise<ChatRecord>;
|
|
8
|
+
createImageRecord(record: Omit<ImageRecord, 'id'>): Promise<ImageRecord>;
|
|
9
|
+
createFileRecord(record: Omit<FileRecord, 'id'>): Promise<FileRecord>;
|
|
10
|
+
createVideoRecord(record: Omit<VideoRecord, 'id'>): Promise<VideoRecord>;
|
|
11
|
+
createChatLogFileRecord(record: Omit<ChatLogFileRecord, 'id'>): Promise<ChatLogFileRecord>;
|
|
12
|
+
updateChatLogFileRecord(id: number, updates: Partial<ChatLogFileRecord>): Promise<void>;
|
|
13
|
+
checkChatLogFileUploaded(date: string, guildId?: string): Promise<boolean>;
|
|
14
|
+
getChatLogFileRecord(date: string, guildId?: string): Promise<ChatLogFileRecord | null>;
|
|
15
|
+
updateChatLogFileSummaryImage(id: number, summaryImageUrl: string): Promise<void>;
|
|
16
|
+
getChatLogFilesForSummary(date: string): Promise<ChatLogFileRecord[]>;
|
|
17
|
+
getMissingSummaryRecords(startDate: string, endDate: string): Promise<ChatLogFileRecord[]>;
|
|
18
|
+
getChatLogFileForRetry(date: string, guildId?: string): Promise<ChatLogFileRecord | null>;
|
|
19
|
+
clearSummaryImage(id: number): Promise<void>;
|
|
20
|
+
getSummaryImageUrl(date: string, guildId?: string): Promise<string | null>;
|
|
21
|
+
getRecordsWithSummary(startDate: string, endDate: string): Promise<ChatLogFileRecord[]>;
|
|
22
|
+
updateChatRecord(messageId: string, updates: Partial<ChatRecord>): Promise<void>;
|
|
23
|
+
getUnuploadedRecords(startTime: number, endTime: number): Promise<ChatRecord[]>;
|
|
24
|
+
markAsUploaded(recordIds: number[]): Promise<void>;
|
|
25
|
+
getPluginStats(): Promise<PluginStats>;
|
|
26
|
+
cleanupExpiredRecords(retentionHours: number): Promise<{
|
|
27
|
+
deletedChatRecords: number;
|
|
28
|
+
deletedImageRecords: number;
|
|
29
|
+
deletedFileRecords: number;
|
|
30
|
+
deletedVideoRecords: number;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
package/lib/export.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { S3Uploader } from './s3-uploader';
|
|
3
|
+
export interface ExportRequest {
|
|
4
|
+
guildId?: string;
|
|
5
|
+
timeRange: string;
|
|
6
|
+
format: 'json' | 'txt' | 'csv';
|
|
7
|
+
messageTypes?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ExportResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
s3Url?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ParsedTimeRange {
|
|
16
|
+
startDate: Date;
|
|
17
|
+
endDate: Date;
|
|
18
|
+
dateStrings: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface ChatMessage {
|
|
21
|
+
time: string;
|
|
22
|
+
username: string;
|
|
23
|
+
content: string;
|
|
24
|
+
guildId?: string;
|
|
25
|
+
messageType: string;
|
|
26
|
+
}
|
|
27
|
+
export declare class ExportManager {
|
|
28
|
+
private ctx;
|
|
29
|
+
private s3Uploader;
|
|
30
|
+
private getStorageDir;
|
|
31
|
+
constructor(ctx: Context, s3Uploader: S3Uploader | null, getStorageDir: (subDir: string) => string);
|
|
32
|
+
/**
|
|
33
|
+
* 解析时间范围
|
|
34
|
+
*/
|
|
35
|
+
parseTimeRange(timeRange: string): ParsedTimeRange;
|
|
36
|
+
/**
|
|
37
|
+
* 解析日期字符串
|
|
38
|
+
*/
|
|
39
|
+
private parseDate;
|
|
40
|
+
/**
|
|
41
|
+
* 检查本地文件是否存在
|
|
42
|
+
*/
|
|
43
|
+
private checkLocalFiles;
|
|
44
|
+
/**
|
|
45
|
+
* 检查 S3 文件是否存在
|
|
46
|
+
*/
|
|
47
|
+
private checkS3Files;
|
|
48
|
+
/**
|
|
49
|
+
* 从 S3 下载文件到本地临时目录
|
|
50
|
+
*/
|
|
51
|
+
private downloadFromS3;
|
|
52
|
+
/**
|
|
53
|
+
* 读取和解析聊天记录文件
|
|
54
|
+
*/
|
|
55
|
+
private parseMessageFiles;
|
|
56
|
+
/**
|
|
57
|
+
* 格式化导出内容
|
|
58
|
+
*/
|
|
59
|
+
private formatExportContent;
|
|
60
|
+
/**
|
|
61
|
+
* 清理临时文件
|
|
62
|
+
*/
|
|
63
|
+
private cleanupTempFiles;
|
|
64
|
+
/**
|
|
65
|
+
* 执行导出
|
|
66
|
+
*/
|
|
67
|
+
exportChatData(request: ExportRequest): Promise<ExportResult>;
|
|
68
|
+
/**
|
|
69
|
+
* 获取内容类型
|
|
70
|
+
*/
|
|
71
|
+
private getContentType;
|
|
72
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Logger } from 'koishi';
|
|
2
|
+
export declare class SafeFileWriter {
|
|
3
|
+
private writeChains;
|
|
4
|
+
private logger;
|
|
5
|
+
constructor(logger: Logger);
|
|
6
|
+
safeAppend(filePath: string, content: string): Promise<void>;
|
|
7
|
+
safeUpdate(filePath: string, messageId: string, newContent: string): Promise<void>;
|
|
8
|
+
safeRead(filePath: string): Promise<string>;
|
|
9
|
+
private enqueueWrite;
|
|
10
|
+
flush(): Promise<void>;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|