koishi-plugin-chat-patch 2.2.0 → 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/client/icons/activity.vue +9 -9
- package/client/icons/index.ts +4 -4
- package/client/index.scss +90 -90
- package/client/index.ts +25 -25
- package/client/vue/composables/useChatActions.ts +1 -1
- package/client/vue/composables/useChatData.ts +1 -1
- package/client/vue/composables/useImageCache.ts +1 -1
- package/client/vue/types.ts +1 -1
- package/dist/index.js +2 -2
- package/lib/file-manager.d.ts +32 -0
- package/lib/index.js +417 -197
- package/lib/message-handler.d.ts +7 -5
- package/package.json +1 -1
- package/readme.md +25 -25
- package/src/api-handlers.ts +19 -65
- package/src/config.ts +45 -46
- package/src/file-manager.ts +367 -169
- package/src/index.ts +58 -31
- package/src/message-handler.ts +161 -134
- package/src/types.ts +62 -62
- package/src/utils.ts +4 -16
package/src/types.ts
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { h } from 'koishi'
|
|
2
|
-
|
|
3
|
-
export interface BotInfo {
|
|
4
|
-
selfId: string
|
|
5
|
-
platform: string
|
|
6
|
-
username: string
|
|
7
|
-
avatar?: string
|
|
8
|
-
status: 'online' | 'offline'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ChannelInfo {
|
|
12
|
-
id: string
|
|
13
|
-
name: string
|
|
14
|
-
type: number | string
|
|
15
|
-
channelId?: string
|
|
16
|
-
guildName?: string
|
|
17
|
-
isDirect?: boolean
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface QuoteInfo {
|
|
21
|
-
messageId: string
|
|
22
|
-
id: string
|
|
23
|
-
content: string
|
|
24
|
-
elements?: h[]
|
|
25
|
-
user: {
|
|
26
|
-
id: string
|
|
27
|
-
name: string
|
|
28
|
-
userId: string
|
|
29
|
-
avatar?: string
|
|
30
|
-
username: string
|
|
31
|
-
}
|
|
32
|
-
timestamp: number
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface MessageInfo {
|
|
36
|
-
id: string
|
|
37
|
-
content: string
|
|
38
|
-
userId: string
|
|
39
|
-
username: string
|
|
40
|
-
avatar?: string
|
|
41
|
-
timestamp: number
|
|
42
|
-
channelId: string
|
|
43
|
-
selfId: string
|
|
44
|
-
elements?: h[]
|
|
45
|
-
type: 'user' | 'bot'
|
|
46
|
-
guildId?: string
|
|
47
|
-
guildName?: string
|
|
48
|
-
platform: string
|
|
49
|
-
quote?: QuoteInfo
|
|
50
|
-
isDirect?: boolean
|
|
51
|
-
sending?: boolean
|
|
52
|
-
realId?: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface ChatData {
|
|
56
|
-
bots: Record<string, BotInfo>
|
|
57
|
-
channels: Record<string, Record<string, ChannelInfo>>
|
|
58
|
-
messages: Record<string, MessageInfo[]>
|
|
59
|
-
pinnedBots: string[]
|
|
60
|
-
pinnedChannels: string[]
|
|
61
|
-
lastSaveTime?: number
|
|
62
|
-
}
|
|
1
|
+
import { h } from 'koishi'
|
|
2
|
+
|
|
3
|
+
export interface BotInfo {
|
|
4
|
+
selfId: string
|
|
5
|
+
platform: string
|
|
6
|
+
username: string
|
|
7
|
+
avatar?: string
|
|
8
|
+
status: 'online' | 'offline'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ChannelInfo {
|
|
12
|
+
id: string
|
|
13
|
+
name: string
|
|
14
|
+
type: number | string
|
|
15
|
+
channelId?: string
|
|
16
|
+
guildName?: string
|
|
17
|
+
isDirect?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface QuoteInfo {
|
|
21
|
+
messageId: string
|
|
22
|
+
id: string
|
|
23
|
+
content: string
|
|
24
|
+
elements?: h[]
|
|
25
|
+
user: {
|
|
26
|
+
id: string
|
|
27
|
+
name: string
|
|
28
|
+
userId: string
|
|
29
|
+
avatar?: string
|
|
30
|
+
username: string
|
|
31
|
+
}
|
|
32
|
+
timestamp: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MessageInfo {
|
|
36
|
+
id: string
|
|
37
|
+
content: string
|
|
38
|
+
userId: string
|
|
39
|
+
username: string
|
|
40
|
+
avatar?: string
|
|
41
|
+
timestamp: number
|
|
42
|
+
channelId: string
|
|
43
|
+
selfId: string
|
|
44
|
+
elements?: h[]
|
|
45
|
+
type: 'user' | 'bot'
|
|
46
|
+
guildId?: string
|
|
47
|
+
guildName?: string
|
|
48
|
+
platform: string
|
|
49
|
+
quote?: QuoteInfo
|
|
50
|
+
isDirect?: boolean
|
|
51
|
+
sending?: boolean
|
|
52
|
+
realId?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ChatData {
|
|
56
|
+
bots: Record<string, BotInfo>
|
|
57
|
+
channels: Record<string, Record<string, ChannelInfo>>
|
|
58
|
+
messages: Record<string, MessageInfo[]>
|
|
59
|
+
pinnedBots: string[]
|
|
60
|
+
pinnedChannels: string[]
|
|
61
|
+
lastSaveTime?: number
|
|
62
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { pathToFileURL } from 'node:url'
|
|
|
8
8
|
export class Utils {
|
|
9
9
|
constructor(private config: Config, private ctx?: Context) { }
|
|
10
10
|
|
|
11
|
-
// 检查平台是否被屏蔽
|
|
12
11
|
isPlatformBlocked(platform: string): boolean {
|
|
13
12
|
if (!this.config.blockedPlatforms || this.config.blockedPlatforms.length === 0) {
|
|
14
13
|
return false
|
|
@@ -29,7 +28,6 @@ export class Utils {
|
|
|
29
28
|
return false
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
// 递归提取所有文本内容的函数
|
|
33
31
|
extractTextContent(elements: any[]): string {
|
|
34
32
|
let text = ''
|
|
35
33
|
for (const element of elements) {
|
|
@@ -46,16 +44,13 @@ export class Utils {
|
|
|
46
44
|
return text
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
// 检查字符串是否为base64格式
|
|
50
47
|
private isBase64(str: string): boolean {
|
|
51
48
|
if (!str || typeof str !== 'string') return false
|
|
52
49
|
|
|
53
|
-
// 检查是否以data:开头的base64格式
|
|
54
50
|
if (str.startsWith('data:')) {
|
|
55
51
|
return str.includes('base64,')
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
// 检查纯base64字符串(长度大于100且符合base64格式)
|
|
59
54
|
if (str.length > 100) {
|
|
60
55
|
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/
|
|
61
56
|
return base64Regex.test(str)
|
|
@@ -64,7 +59,6 @@ export class Utils {
|
|
|
64
59
|
return false
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
// 持久化 base64 图片并返回本地文件 URL
|
|
68
62
|
persistBase64Image(base64Data: string): string {
|
|
69
63
|
if (!this.ctx || !base64Data.startsWith('data:image/')) return base64Data
|
|
70
64
|
|
|
@@ -72,17 +66,14 @@ export class Utils {
|
|
|
72
66
|
const dir = join(this.ctx.baseDir, 'data', 'chat-patch', 'persist-images')
|
|
73
67
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
74
68
|
|
|
75
|
-
// 生成文件名:时间戳 + 内容哈希
|
|
76
69
|
const hash = createHash('md5').update(base64Data).digest('hex')
|
|
77
70
|
const ext = base64Data.split(';')[0].split('/')[1] || 'png'
|
|
78
71
|
const filename = `${Date.now()}_${hash}.${ext}`
|
|
79
72
|
const filePath = join(dir, filename)
|
|
80
73
|
|
|
81
|
-
// 写入文件
|
|
82
74
|
const base64Content = base64Data.split(',')[1]
|
|
83
75
|
writeFileSync(filePath, Buffer.from(base64Content, 'base64'))
|
|
84
76
|
|
|
85
|
-
// 清理旧图片
|
|
86
77
|
this.cleanupPersistImages(dir)
|
|
87
78
|
|
|
88
79
|
return pathToFileURL(filePath).href
|
|
@@ -103,8 +94,6 @@ export class Utils {
|
|
|
103
94
|
} catch (e) { }
|
|
104
95
|
}
|
|
105
96
|
|
|
106
|
-
// 清理对象中的base64内容,改为持久化存储
|
|
107
|
-
// isBotMessage: 是否为机器人发送的消息
|
|
108
97
|
cleanBase64Content(obj: any, isBotMessage: boolean = false): any {
|
|
109
98
|
if (obj === null || obj === undefined) {
|
|
110
99
|
return obj
|
|
@@ -112,7 +101,7 @@ export class Utils {
|
|
|
112
101
|
|
|
113
102
|
if (typeof obj === 'string') {
|
|
114
103
|
if (this.isBase64(obj)) {
|
|
115
|
-
|
|
104
|
+
|
|
116
105
|
if (isBotMessage && !obj.startsWith('data:image/')) {
|
|
117
106
|
return '[富媒体内容已省略]'
|
|
118
107
|
}
|
|
@@ -128,16 +117,15 @@ export class Utils {
|
|
|
128
117
|
if (typeof obj === 'object') {
|
|
129
118
|
const cleaned: any = {}
|
|
130
119
|
for (const [key, value] of Object.entries(obj)) {
|
|
131
|
-
|
|
120
|
+
|
|
132
121
|
if (isBotMessage && obj.type && !['text', 'image', 'img'].includes(obj.type)) {
|
|
133
|
-
|
|
122
|
+
|
|
134
123
|
if (typeof value === 'string' && (key === 'src' || key === 'url' || key === 'file') && this.isBase64(value)) {
|
|
135
124
|
cleaned[key] = '[富媒体内容已省略]'
|
|
136
125
|
continue
|
|
137
126
|
}
|
|
138
127
|
}
|
|
139
128
|
|
|
140
|
-
// 特别处理常见的base64字段
|
|
141
129
|
if (typeof value === 'string' && (
|
|
142
130
|
key === 'src' ||
|
|
143
131
|
key === 'url' ||
|
|
@@ -145,7 +133,7 @@ export class Utils {
|
|
|
145
133
|
key === 'data' ||
|
|
146
134
|
key === 'content'
|
|
147
135
|
) && this.isBase64(value)) {
|
|
148
|
-
|
|
136
|
+
|
|
149
137
|
if (isBotMessage && !value.startsWith('data:image/')) {
|
|
150
138
|
cleaned[key] = '[富媒体内容已省略]'
|
|
151
139
|
} else {
|