@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/lib/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
export declare class MarkdownToImageService {
|
|
3
|
+
private ctx;
|
|
4
|
+
private logger;
|
|
5
|
+
private isRendering;
|
|
6
|
+
private renderQueue;
|
|
7
|
+
constructor(ctx: Context);
|
|
8
|
+
/**
|
|
9
|
+
* 检查是否正在渲染,避免并发渲染
|
|
10
|
+
*/
|
|
11
|
+
private waitForRenderSlot;
|
|
12
|
+
/**
|
|
13
|
+
* 释放渲染槽位
|
|
14
|
+
*/
|
|
15
|
+
private releaseRenderSlot;
|
|
16
|
+
/**
|
|
17
|
+
* 生成字体 CSS - 使用 Google Fonts CDN
|
|
18
|
+
*/
|
|
19
|
+
private generateFontCSS;
|
|
20
|
+
/**
|
|
21
|
+
* 将文本中的 emoji 转换为图片标签
|
|
22
|
+
*/
|
|
23
|
+
private convertEmojiToImages;
|
|
24
|
+
/**
|
|
25
|
+
* 获取 emoji 的 Unicode 码点
|
|
26
|
+
*/
|
|
27
|
+
private getEmojiCodePoint;
|
|
28
|
+
/**
|
|
29
|
+
* 将 markdown 内容转换为图片
|
|
30
|
+
*/
|
|
31
|
+
convertToImage(markdownContent: string): Promise<Buffer>;
|
|
32
|
+
/**
|
|
33
|
+
* 简单的 markdown 到 HTML 转换
|
|
34
|
+
*/
|
|
35
|
+
private markdownToHtml;
|
|
36
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Element } from 'koishi';
|
|
2
|
+
export interface ProcessedMessage {
|
|
3
|
+
content: string;
|
|
4
|
+
messageType: 'text' | 'image' | 'mixed' | 'other';
|
|
5
|
+
imageUrls: string[];
|
|
6
|
+
fileUrls: Array<{
|
|
7
|
+
url: string;
|
|
8
|
+
fileName: string;
|
|
9
|
+
}>;
|
|
10
|
+
videoUrls: Array<{
|
|
11
|
+
url: string;
|
|
12
|
+
fileName: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export declare class MessageProcessor {
|
|
16
|
+
private includeImages;
|
|
17
|
+
constructor(includeImages?: boolean);
|
|
18
|
+
/**
|
|
19
|
+
* 处理消息元素数组
|
|
20
|
+
*/
|
|
21
|
+
processElements(elements: Element[]): ProcessedMessage;
|
|
22
|
+
/**
|
|
23
|
+
* 检查消息是否包含图片
|
|
24
|
+
*/
|
|
25
|
+
hasImages(elements: Element[]): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* 检查消息是否包含文件
|
|
28
|
+
*/
|
|
29
|
+
hasFiles(elements: Element[]): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 检查消息是否包含视频
|
|
32
|
+
*/
|
|
33
|
+
hasVideos(elements: Element[]): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 提取所有图片 URL
|
|
36
|
+
*/
|
|
37
|
+
extractImageUrls(elements: Element[]): string[];
|
|
38
|
+
/**
|
|
39
|
+
* 提取所有文件 URL
|
|
40
|
+
*/
|
|
41
|
+
extractFileUrls(elements: Element[]): Array<{
|
|
42
|
+
url: string;
|
|
43
|
+
fileName: string;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* 提取所有视频 URL
|
|
47
|
+
*/
|
|
48
|
+
extractVideoUrls(elements: Element[]): Array<{
|
|
49
|
+
url: string;
|
|
50
|
+
fileName: string;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* 获取纯文本内容(去除所有非文本元素)
|
|
54
|
+
*/
|
|
55
|
+
getPlainText(elements: Element[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* 检查消息是否只包含文本
|
|
58
|
+
*/
|
|
59
|
+
isTextOnly(elements: Element[]): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* 检查消息是否为空
|
|
62
|
+
*/
|
|
63
|
+
isEmpty(elements: Element[]): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* 解析 JSON 消息内容,特别处理 QQ 小程序分享卡片
|
|
66
|
+
*/
|
|
67
|
+
private parseJsonMessage;
|
|
68
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export interface S3Config {
|
|
2
|
+
region: string;
|
|
3
|
+
bucket: string;
|
|
4
|
+
accessKeyId: string;
|
|
5
|
+
secretAccessKey: string;
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
pathPrefix: string;
|
|
8
|
+
}
|
|
9
|
+
export interface UploadResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
url?: string;
|
|
12
|
+
key?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
fileSize?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class S3Uploader {
|
|
17
|
+
private client;
|
|
18
|
+
private config;
|
|
19
|
+
constructor(config: S3Config);
|
|
20
|
+
/**
|
|
21
|
+
* 上传文件缓冲区到 S3
|
|
22
|
+
*/
|
|
23
|
+
uploadBuffer(buffer: Buffer, key: string, contentType?: string): Promise<UploadResult>;
|
|
24
|
+
/**
|
|
25
|
+
* 上传本地文件到 S3
|
|
26
|
+
*/
|
|
27
|
+
uploadFile(filePath: string, key: string, contentType?: string): Promise<UploadResult>;
|
|
28
|
+
/**
|
|
29
|
+
* 上传文本内容到 S3
|
|
30
|
+
*/
|
|
31
|
+
uploadText(content: string, key: string, contentType?: string): Promise<UploadResult>;
|
|
32
|
+
/**
|
|
33
|
+
* 从 URL 下载图片并上传到 S3(使用 axios 确保兼容性)
|
|
34
|
+
*/
|
|
35
|
+
uploadImageFromUrl(imageUrl: string, key: string, httpService?: any, maxSize?: number): Promise<UploadResult>;
|
|
36
|
+
/**
|
|
37
|
+
* 从 URL 下载文件并上传到 S3
|
|
38
|
+
*/
|
|
39
|
+
uploadFileFromUrl(fileUrl: string, key: string, fileName?: string, maxSize?: number): Promise<UploadResult>;
|
|
40
|
+
/**
|
|
41
|
+
* 从 URL 下载视频并上传到 S3
|
|
42
|
+
*/
|
|
43
|
+
uploadVideoFromUrl(videoUrl: string, key: string, fileName?: string, maxSize?: number): Promise<UploadResult>;
|
|
44
|
+
/**
|
|
45
|
+
* 批量上传聊天记录文件
|
|
46
|
+
*/
|
|
47
|
+
uploadChatLogFiles(files: Array<{
|
|
48
|
+
filePath: string;
|
|
49
|
+
key: string;
|
|
50
|
+
}>): Promise<UploadResult[]>;
|
|
51
|
+
/**
|
|
52
|
+
* 生成公共 URL
|
|
53
|
+
*/
|
|
54
|
+
private generatePublicUrl;
|
|
55
|
+
/**
|
|
56
|
+
* 根据文件扩展名获取内容类型
|
|
57
|
+
*/
|
|
58
|
+
private getContentTypeFromExtension;
|
|
59
|
+
/**
|
|
60
|
+
* 获取图片内容类型
|
|
61
|
+
*/
|
|
62
|
+
private getImageContentType;
|
|
63
|
+
/**
|
|
64
|
+
* 获取文件内容类型
|
|
65
|
+
*/
|
|
66
|
+
private getFileContentType;
|
|
67
|
+
/**
|
|
68
|
+
* 获取视频内容类型
|
|
69
|
+
*/
|
|
70
|
+
private getVideoContentType;
|
|
71
|
+
/**
|
|
72
|
+
* 生成用于存储的 S3 键名
|
|
73
|
+
*/
|
|
74
|
+
static generateImageKey(messageId: string, originalUrl: string, guildId?: string, index?: number): string;
|
|
75
|
+
/**
|
|
76
|
+
* 生成用于文件存储的 S3 键名
|
|
77
|
+
*/
|
|
78
|
+
static generateFileKey(messageId: string, originalUrl: string, fileName?: string, guildId?: string, index?: number): string;
|
|
79
|
+
/**
|
|
80
|
+
* 生成用于视频存储的 S3 键名
|
|
81
|
+
*/
|
|
82
|
+
static generateVideoKey(messageId: string, originalUrl: string, fileName?: string, guildId?: string, index?: number): string;
|
|
83
|
+
/**
|
|
84
|
+
* 生成聊天记录文件的 S3 键名(JSON 格式)
|
|
85
|
+
*/
|
|
86
|
+
static generateChatLogKey(date: Date, guildId?: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* 提取图片文件扩展名
|
|
89
|
+
*/
|
|
90
|
+
private static getImageExtension;
|
|
91
|
+
/**
|
|
92
|
+
* 提取文件扩展名
|
|
93
|
+
*/
|
|
94
|
+
private static getFileExtension;
|
|
95
|
+
/**
|
|
96
|
+
* 提取视频文件扩展名
|
|
97
|
+
*/
|
|
98
|
+
private static getVideoExtension;
|
|
99
|
+
/**
|
|
100
|
+
* 检查是否支持的图片格式
|
|
101
|
+
*/
|
|
102
|
+
static isSupportedImageFormat(url: string, allowedTypes: string[]): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* 测试 S3 连接
|
|
105
|
+
*/
|
|
106
|
+
testConnection(): Promise<{
|
|
107
|
+
success: boolean;
|
|
108
|
+
error?: string;
|
|
109
|
+
}>;
|
|
110
|
+
/**
|
|
111
|
+
* 获取 S3 存储桶中的文件列表
|
|
112
|
+
*/
|
|
113
|
+
listFiles(prefix?: string): Promise<{
|
|
114
|
+
success: boolean;
|
|
115
|
+
files?: string[];
|
|
116
|
+
error?: string;
|
|
117
|
+
}>;
|
|
118
|
+
/**
|
|
119
|
+
* 下载 S3 文件到本地
|
|
120
|
+
*/
|
|
121
|
+
downloadFile(s3Key: string, localPath: string): Promise<{
|
|
122
|
+
success: boolean;
|
|
123
|
+
error?: string;
|
|
124
|
+
}>;
|
|
125
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config } from './types';
|
|
3
|
+
import { S3Uploader } from './s3-uploader';
|
|
4
|
+
export declare class LoggerService {
|
|
5
|
+
private config;
|
|
6
|
+
private logger;
|
|
7
|
+
constructor(ctx: Context, config: Config);
|
|
8
|
+
debug(message: string, data?: any): void;
|
|
9
|
+
info(message: string): void;
|
|
10
|
+
warn(message: string): void;
|
|
11
|
+
error(message: string, error?: any): void;
|
|
12
|
+
}
|
|
13
|
+
export declare class S3Service {
|
|
14
|
+
private config;
|
|
15
|
+
private logger;
|
|
16
|
+
private s3Uploader;
|
|
17
|
+
constructor(config: Config, logger: LoggerService);
|
|
18
|
+
init(): void;
|
|
19
|
+
getUploader(): S3Uploader | null;
|
|
20
|
+
}
|
|
21
|
+
export declare class MessageProcessorService {
|
|
22
|
+
private messageProcessor;
|
|
23
|
+
constructor(includeImages: boolean);
|
|
24
|
+
processElements(elements: any[]): any;
|
|
25
|
+
normalizeUserId(userId: string): string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Logger } from 'koishi';
|
|
2
|
+
import { InteractionStatistics, ParsedMessage } from './types';
|
|
3
|
+
export declare class StatisticsService {
|
|
4
|
+
private logger;
|
|
5
|
+
constructor(logger: Logger);
|
|
6
|
+
/**
|
|
7
|
+
* 解析 JSONL 内容为消息数组
|
|
8
|
+
*/
|
|
9
|
+
parseMessages(jsonlContent: string): ParsedMessage[];
|
|
10
|
+
/**
|
|
11
|
+
* 生成完整的统计数据
|
|
12
|
+
*/
|
|
13
|
+
generateStatistics(messages: ParsedMessage[], topN?: number): InteractionStatistics;
|
|
14
|
+
/**
|
|
15
|
+
* 统计每个用户发言数量,排序取 TOP N
|
|
16
|
+
*/
|
|
17
|
+
calculateActivityRanking(messages: ParsedMessage[], topN?: number): Array<{
|
|
18
|
+
username: string;
|
|
19
|
+
messageCount: number;
|
|
20
|
+
rank: number;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* 按小时统计消息数量分布
|
|
24
|
+
*/
|
|
25
|
+
calculateHourlyDistribution(messages: ParsedMessage[]): Array<{
|
|
26
|
+
hour: number;
|
|
27
|
+
count: number;
|
|
28
|
+
percentage: number;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* 解析 @/回复 关系,统计互动频次
|
|
32
|
+
*/
|
|
33
|
+
calculateInteractions(messages: ParsedMessage[]): {
|
|
34
|
+
mentions: Array<{
|
|
35
|
+
from: string;
|
|
36
|
+
to: string;
|
|
37
|
+
count: number;
|
|
38
|
+
}>;
|
|
39
|
+
replies: Array<{
|
|
40
|
+
from: string;
|
|
41
|
+
to: string;
|
|
42
|
+
count: number;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* 计算基础统计指标
|
|
47
|
+
*/
|
|
48
|
+
calculateBasicStats(messages: ParsedMessage[], hourlyDistribution: Array<{
|
|
49
|
+
hour: number;
|
|
50
|
+
count: number;
|
|
51
|
+
percentage: number;
|
|
52
|
+
}>): {
|
|
53
|
+
totalMessages: number;
|
|
54
|
+
uniqueUsers: number;
|
|
55
|
+
avgMessagesPerUser: number;
|
|
56
|
+
peakHour: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* 格式化小时为显示字符串
|
|
60
|
+
*/
|
|
61
|
+
formatHour(hour: number): string;
|
|
62
|
+
/**
|
|
63
|
+
* 获取时段描述
|
|
64
|
+
*/
|
|
65
|
+
getPeriodDescription(hour: number): string;
|
|
66
|
+
/**
|
|
67
|
+
* 生成活跃时段摘要
|
|
68
|
+
*/
|
|
69
|
+
generatePeakHourSummary(hourlyDistribution: Array<{
|
|
70
|
+
hour: number;
|
|
71
|
+
count: number;
|
|
72
|
+
percentage: number;
|
|
73
|
+
}>): string;
|
|
74
|
+
}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export interface ForwardTarget {
|
|
2
|
+
groupId: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface GroupConfig {
|
|
6
|
+
groupId: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
monitorEnabled?: boolean;
|
|
9
|
+
summaryEnabled?: boolean;
|
|
10
|
+
summaryTime?: string;
|
|
11
|
+
pushEnabled?: boolean;
|
|
12
|
+
pushTime?: string;
|
|
13
|
+
pushToSelf?: boolean;
|
|
14
|
+
forwardGroups?: ForwardTarget[];
|
|
15
|
+
systemPrompt?: string;
|
|
16
|
+
userPromptTemplate?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PushGroupConfig {
|
|
19
|
+
groupId: string;
|
|
20
|
+
channelId?: string;
|
|
21
|
+
platform?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface Config {
|
|
24
|
+
s3: {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
bucket: string;
|
|
27
|
+
accessKeyId: string;
|
|
28
|
+
secretAccessKey: string;
|
|
29
|
+
endpoint?: string;
|
|
30
|
+
pathPrefix: string;
|
|
31
|
+
};
|
|
32
|
+
chatLog: {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
includeImages: boolean;
|
|
35
|
+
maxFileSize: number;
|
|
36
|
+
autoUploadTime: string;
|
|
37
|
+
retentionDays: number;
|
|
38
|
+
dbRetentionHours: number;
|
|
39
|
+
};
|
|
40
|
+
monitor: {
|
|
41
|
+
groups: GroupConfig[];
|
|
42
|
+
excludedUsers: string[];
|
|
43
|
+
excludeBots: boolean;
|
|
44
|
+
};
|
|
45
|
+
admin: {
|
|
46
|
+
adminIds: string[];
|
|
47
|
+
};
|
|
48
|
+
ai: {
|
|
49
|
+
enabled: boolean;
|
|
50
|
+
apiUrl: string;
|
|
51
|
+
apiKey: string;
|
|
52
|
+
apiMode?: 'chat.completions' | 'responses';
|
|
53
|
+
model?: string;
|
|
54
|
+
maxTokens?: number;
|
|
55
|
+
timeout?: number;
|
|
56
|
+
systemPrompt?: string;
|
|
57
|
+
userPromptTemplate?: string;
|
|
58
|
+
useFileMode?: boolean;
|
|
59
|
+
fileName?: string;
|
|
60
|
+
defaultSummaryTime?: string;
|
|
61
|
+
defaultPushTime?: string;
|
|
62
|
+
};
|
|
63
|
+
debug: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface ChatRecord {
|
|
66
|
+
id?: number;
|
|
67
|
+
messageId: string;
|
|
68
|
+
guildId?: string;
|
|
69
|
+
channelId: string;
|
|
70
|
+
userId: string;
|
|
71
|
+
username: string;
|
|
72
|
+
content: string;
|
|
73
|
+
originalElements: string;
|
|
74
|
+
timestamp: number;
|
|
75
|
+
messageType: 'text' | 'image' | 'mixed' | 'other';
|
|
76
|
+
imageUrls?: string;
|
|
77
|
+
fileUrls?: string;
|
|
78
|
+
videoUrls?: string;
|
|
79
|
+
uploadedAt?: number;
|
|
80
|
+
isUploaded: boolean;
|
|
81
|
+
}
|
|
82
|
+
export interface ImageRecord {
|
|
83
|
+
id?: number;
|
|
84
|
+
originalUrl: string;
|
|
85
|
+
s3Url: string;
|
|
86
|
+
s3Key: string;
|
|
87
|
+
fileSize: number;
|
|
88
|
+
uploadedAt: number;
|
|
89
|
+
messageId: string;
|
|
90
|
+
}
|
|
91
|
+
export interface FileRecord {
|
|
92
|
+
id?: number;
|
|
93
|
+
originalUrl: string;
|
|
94
|
+
s3Url: string;
|
|
95
|
+
s3Key: string;
|
|
96
|
+
fileName: string;
|
|
97
|
+
fileSize: number;
|
|
98
|
+
uploadedAt: number;
|
|
99
|
+
messageId: string;
|
|
100
|
+
}
|
|
101
|
+
export interface VideoRecord {
|
|
102
|
+
id?: number;
|
|
103
|
+
originalUrl: string;
|
|
104
|
+
s3Url: string;
|
|
105
|
+
s3Key: string;
|
|
106
|
+
fileName: string;
|
|
107
|
+
fileSize: number;
|
|
108
|
+
uploadedAt: number;
|
|
109
|
+
messageId: string;
|
|
110
|
+
}
|
|
111
|
+
export interface ChatLogFileRecord {
|
|
112
|
+
id?: number;
|
|
113
|
+
guildId?: string;
|
|
114
|
+
date: string;
|
|
115
|
+
filePath: string;
|
|
116
|
+
s3Key: string;
|
|
117
|
+
s3Url?: string;
|
|
118
|
+
fileSize: number;
|
|
119
|
+
recordCount: number;
|
|
120
|
+
uploadedAt: number;
|
|
121
|
+
status: 'pending' | 'uploading' | 'uploaded' | 'failed';
|
|
122
|
+
error?: string;
|
|
123
|
+
summaryImageUrl?: string;
|
|
124
|
+
summaryGeneratedAt?: number;
|
|
125
|
+
}
|
|
126
|
+
export interface PluginStats {
|
|
127
|
+
totalMessages: number;
|
|
128
|
+
todayMessages: number;
|
|
129
|
+
imageRecords: number;
|
|
130
|
+
uploadedMessages: number;
|
|
131
|
+
}
|
|
132
|
+
export interface AISummaryOutput {
|
|
133
|
+
summary: {
|
|
134
|
+
overview: string;
|
|
135
|
+
highlights: string[];
|
|
136
|
+
atmosphere: string;
|
|
137
|
+
};
|
|
138
|
+
hotTopics: Array<{
|
|
139
|
+
topic: string;
|
|
140
|
+
description: string;
|
|
141
|
+
participants: string[];
|
|
142
|
+
heatLevel: 'high' | 'medium' | 'low';
|
|
143
|
+
}>;
|
|
144
|
+
importantInfo: Array<{
|
|
145
|
+
type: 'announcement' | 'link' | 'resource' | 'decision' | 'other';
|
|
146
|
+
content: string;
|
|
147
|
+
source?: string;
|
|
148
|
+
}>;
|
|
149
|
+
quotes: Array<{
|
|
150
|
+
content: string;
|
|
151
|
+
author: string;
|
|
152
|
+
}>;
|
|
153
|
+
}
|
|
154
|
+
export interface InteractionStatistics {
|
|
155
|
+
activityRanking: Array<{
|
|
156
|
+
username: string;
|
|
157
|
+
messageCount: number;
|
|
158
|
+
rank: number;
|
|
159
|
+
}>;
|
|
160
|
+
hourlyDistribution: Array<{
|
|
161
|
+
hour: number;
|
|
162
|
+
count: number;
|
|
163
|
+
percentage: number;
|
|
164
|
+
}>;
|
|
165
|
+
interactions: {
|
|
166
|
+
mentions: Array<{
|
|
167
|
+
from: string;
|
|
168
|
+
to: string;
|
|
169
|
+
count: number;
|
|
170
|
+
}>;
|
|
171
|
+
replies: Array<{
|
|
172
|
+
from: string;
|
|
173
|
+
to: string;
|
|
174
|
+
count: number;
|
|
175
|
+
}>;
|
|
176
|
+
};
|
|
177
|
+
basicStats: {
|
|
178
|
+
totalMessages: number;
|
|
179
|
+
uniqueUsers: number;
|
|
180
|
+
avgMessagesPerUser: number;
|
|
181
|
+
peakHour: number;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export interface DailyReport {
|
|
185
|
+
date: string;
|
|
186
|
+
guildId: string;
|
|
187
|
+
aiContent: AISummaryOutput;
|
|
188
|
+
statistics: InteractionStatistics;
|
|
189
|
+
metadata: {
|
|
190
|
+
generatedAt: number;
|
|
191
|
+
aiModel: string;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export interface ParsedMessage {
|
|
195
|
+
timestamp: number;
|
|
196
|
+
time: string;
|
|
197
|
+
messageId: string;
|
|
198
|
+
guildId?: string;
|
|
199
|
+
channelId: string;
|
|
200
|
+
userId: string;
|
|
201
|
+
username: string;
|
|
202
|
+
content: string;
|
|
203
|
+
messageType: string;
|
|
204
|
+
imageUrls: string[];
|
|
205
|
+
fileUrls: string[];
|
|
206
|
+
videoUrls: string[];
|
|
207
|
+
}
|
|
208
|
+
declare module 'koishi' {
|
|
209
|
+
interface Tables {
|
|
210
|
+
chat_records: ChatRecord;
|
|
211
|
+
image_records: ImageRecord;
|
|
212
|
+
file_records: FileRecord;
|
|
213
|
+
video_records: VideoRecord;
|
|
214
|
+
chat_log_files: ChatLogFileRecord;
|
|
215
|
+
}
|
|
216
|
+
}
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公共工具函数模块
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 将时间戳格式化为 UTC+8 可读时间
|
|
6
|
+
*/
|
|
7
|
+
export declare const formatDateInUTC8: (timestamp: number) => string;
|
|
8
|
+
/**
|
|
9
|
+
* 将时间戳格式化为简化格式(不包含毫秒)
|
|
10
|
+
*/
|
|
11
|
+
export declare const formatDateSimple: (timestamp: number) => string;
|
|
12
|
+
/**
|
|
13
|
+
* 获取 UTC+8 时区的日期字符串 (YYYY-MM-DD)
|
|
14
|
+
*/
|
|
15
|
+
export declare const getDateStringInUTC8: (timestamp: number) => string;
|
|
16
|
+
/**
|
|
17
|
+
* 获取当前 UTC+8 时间的 Date 对象
|
|
18
|
+
*/
|
|
19
|
+
export declare const getCurrentTimeInUTC8: () => Date;
|
|
20
|
+
/**
|
|
21
|
+
* 统一的错误处理器
|
|
22
|
+
*/
|
|
23
|
+
export declare const handleError: (error: any, defaultMessage?: string) => string;
|
|
24
|
+
/**
|
|
25
|
+
* 安全的 JSON 解析
|
|
26
|
+
*/
|
|
27
|
+
export declare const safeJsonParse: <T>(jsonString: string | null | undefined, defaultValue: T) => T;
|
|
28
|
+
/**
|
|
29
|
+
* 安全的 JSON 序列化
|
|
30
|
+
*/
|
|
31
|
+
export declare const safeJsonStringify: (obj: any) => string;
|
|
32
|
+
/**
|
|
33
|
+
* 延迟执行函数
|
|
34
|
+
*/
|
|
35
|
+
export declare const delay: (ms: number) => Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* 文件大小格式化
|
|
38
|
+
*/
|
|
39
|
+
export declare const formatFileSize: (bytes: number) => string;
|
|
40
|
+
/**
|
|
41
|
+
* 批量处理函数,支持并发限制
|
|
42
|
+
*/
|
|
43
|
+
export declare const processBatch: <T, R>(items: T[], processor: (item: T) => Promise<R>, concurrency?: number) => Promise<R[]>;
|
|
44
|
+
/**
|
|
45
|
+
* 替换URL中的域名
|
|
46
|
+
*/
|
|
47
|
+
export declare const replaceImageUrl: (originalUrl: string) => string;
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pynickle/koishi-plugin-chat-summarizer",
|
|
3
|
+
"description": "Koishi 聊天记录收集和上传插件",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "lib/index.cjs",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"lib"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"koishi",
|
|
14
|
+
"plugin",
|
|
15
|
+
"chat"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"lint": "npx oxlint ./src",
|
|
19
|
+
"lint:fix": "npx oxlint ./src --fix",
|
|
20
|
+
"fmt": "npx oxfmt ./src",
|
|
21
|
+
"build": "node esbuild.config.js && tsc --emitDeclarationOnly",
|
|
22
|
+
"semantic-release": "semantic-release"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@ai-sdk/openai": "^3.0.41",
|
|
26
|
+
"@aws-sdk/client-s3": "^3.1011.0",
|
|
27
|
+
"@aws-sdk/lib-storage": "^3.1011.0",
|
|
28
|
+
"ai": "^6.0.116",
|
|
29
|
+
"axios": "^1.13.6",
|
|
30
|
+
"github-markdown-css": "^5.9.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
34
|
+
"@semantic-release/git": "^10.0.1",
|
|
35
|
+
"@semantic-release/npm": "^13.1.5",
|
|
36
|
+
"@types/node": "^24.12.0",
|
|
37
|
+
"conventional-changelog-conventionalcommits": "^9.3.0",
|
|
38
|
+
"esbuild": "^0.27.4",
|
|
39
|
+
"oxfmt": "^0.41.0",
|
|
40
|
+
"oxlint": "^1.56.0",
|
|
41
|
+
"rimraf": "^6.1.3",
|
|
42
|
+
"semantic-release": "^25.0.3",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"koishi": "^4.18.0"
|
|
47
|
+
},
|
|
48
|
+
"koishi": {
|
|
49
|
+
"description": {
|
|
50
|
+
"zh": "Koishi 聊天记录收集和上传插件"
|
|
51
|
+
},
|
|
52
|
+
"service": {
|
|
53
|
+
"required": [
|
|
54
|
+
"database",
|
|
55
|
+
"http",
|
|
56
|
+
"puppeteer"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|