koishi-plugin-chat-patch 1.2.0 → 1.3.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.
@@ -1,283 +1,305 @@
1
- import { BotInfo, ChannelInfo, MessageInfo, QuoteInfo } from './types'
2
- import { Context, Session, h, Logger } from 'koishi'
3
- import { FileManager } from './file-manager'
4
- import { Config } from './config'
5
- import { Utils } from './utils'
6
- import { } from '@koishijs/plugin-console'
7
-
8
- export class MessageHandler {
9
- private logger: Logger
10
- private utils: Utils
11
- // 存储正确的 channelId 映射,key 是 selfId,value 是正确的 channelId
12
- private correctChannelIds: Map<string, string> = new Map()
13
-
14
- constructor(
15
- private ctx: Context,
16
- private config: Config,
17
- private fileManager: FileManager
18
- ) {
19
- this.logger = ctx.logger('chat-patch')
20
- this.utils = new Utils(config)
21
- }
22
-
23
- // 设置正确的 channelId
24
- setCorrectChannelId(selfId: string, channelId: string) {
25
- this.correctChannelIds.set(selfId, channelId)
26
- this.logInfo('设置正确的 channelId:', { selfId, channelId })
27
- }
28
-
29
- // 获取正确的 channelId
30
- getCorrectChannelId(selfId: string): string | undefined {
31
- return this.correctChannelIds.get(selfId)
32
- }
33
-
34
- // 更新机器人信息到JSON文件
35
- async updateBotInfoToFile(session: Session) {
36
- const data = this.fileManager.readChatDataFromFile()
37
-
38
- const botInfo: BotInfo = {
39
- selfId: session.selfId,
40
- platform: session.platform || 'unknown',
41
- username: session.bot.user?.name || `Bot-${session.selfId}`,
42
- avatar: session.bot.user?.avatar,
43
- status: 'online'
44
- }
45
-
46
- data.bots[session.selfId] = botInfo
47
- this.fileManager.writeChatDataToFile(data)
48
- this.logInfo('更新机器人信息到文件:', botInfo.username)
49
- }
50
-
51
- // 更新频道信息到JSON文件
52
- async updateChannelInfoToFile(session: Session): Promise<string> {
53
- let guildName = session.channelId
54
-
55
- const data = this.fileManager.readChatDataFromFile()
56
-
57
- try {
58
- if (session.guildId && session.bot.getGuild && typeof session.bot.getGuild === 'function') {
59
- const guild = await session.bot.getGuild(session.guildId)
60
- guildName = guild?.name || session.channelId
61
- }
62
- } catch (guildError) {
63
- this.logInfo('获取群组信息失败,使用频道ID作为备用:', guildError)
64
- guildName = session.channelId
65
- }
66
-
67
- if (!data.channels[session.selfId]) {
68
- data.channels[session.selfId] = {}
69
- }
70
-
71
- const channelInfo: ChannelInfo = {
72
- id: session.channelId,
73
- name: session.isDirect
74
- ? `私信 ${session.channelId}`
75
- : `${guildName} (${session.channelId})`,
76
- type: session.type || 0,
77
- channelId: session.channelId,
78
- guildName: guildName,
79
- isDirect: session.isDirect
80
- }
81
-
82
- data.channels[session.selfId][session.channelId] = channelInfo
83
- this.fileManager.writeChatDataToFile(data)
84
- this.logInfo('更新频道信息到文件:', channelInfo.name)
85
-
86
- return guildName
87
- }
88
-
89
- async broadcastMessageEvent(session: Session) {
90
- try {
91
- await this.updateBotInfoToFile(session)
92
- const guildName = await this.updateChannelInfoToFile(session)
93
-
94
- const timestamp = Date.now()
95
-
96
- // 处理 quote 信息
97
- let quoteInfo: QuoteInfo | undefined = undefined
98
- if (session.quote) {
99
- quoteInfo = {
100
- messageId: session.quote.messageId || session.quote.id,
101
- id: session.quote.id,
102
- content: session.quote.content || '',
103
- elements: session.quote.elements,
104
- user: {
105
- id: session.quote.user?.id || session.quote.user?.userId || 'unknown',
106
- name: session.quote.user?.name || session.quote.user?.username || 'unknown',
107
- userId: session.quote.user?.userId || session.quote.user?.id || 'unknown',
108
- avatar: session.quote.user?.avatar,
109
- username: session.quote.user?.username || session.quote.user?.name || 'unknown'
110
- },
111
- timestamp: session.quote.timestamp || Date.now()
112
- }
113
- }
114
-
115
- // 从 session 中提取用户消息内容
116
- let content = ''
117
- let elements: h[] = []
118
-
119
- if (session.content) {
120
- content = session.content
121
- } else if (session.stripped?.content) {
122
- content = session.stripped.content
123
- }
124
-
125
- if (session.elements) {
126
- elements = session.elements
127
- if (!content) {
128
- content = session.elements
129
- .filter((element: any) => element.type === 'text')
130
- .map((element: any) => element.attrs?.content || '')
131
- .join('')
132
- }
133
- }
134
-
135
- // 创建消息信息对象
136
- const messageInfo: MessageInfo = {
137
- id: session.event?.message?.id || `msg-${timestamp}`,
138
- content: content || session.content || '',
139
- userId: session.userId || session.event?.user?.id || 'unknown',
140
- username: session.username || session.event?.user?.name || session.userId || 'unknown',
141
- avatar: session.event?.user?.avatar,
142
- timestamp: timestamp,
143
- channelId: session.channelId,
144
- selfId: session.selfId,
145
- elements: this.utils.cleanBase64Content(elements),
146
- type: 'user',
147
- guildName: guildName,
148
- platform: session.platform || 'unknown',
149
- quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo) : undefined,
150
- isDirect: session.isDirect
151
- }
152
-
153
- await this.fileManager.addMessageToFile(messageInfo)
154
-
155
- const messageEvent = {
156
- type: 'message',
157
- selfId: session.selfId,
158
- platform: session.platform || 'unknown',
159
- channelId: session.channelId,
160
- messageId: session.event?.message?.id || `msg-${timestamp}`,
161
- content: content || session.content || '',
162
- userId: session.userId || session.event?.user?.id || 'unknown',
163
- username: session.username || session.event?.user?.name || session.userId || 'unknown',
164
- avatar: session.event?.user?.avatar,
165
- timestamp: timestamp,
166
- guildName: guildName,
167
- channelType: session.type || 0,
168
- elements: this.utils.cleanBase64Content(elements),
169
- quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo) : undefined,
170
- isDirect: session.isDirect,
171
- bot: {
172
- avatar: session.bot.user?.avatar,
173
- name: session.bot.user?.name,
174
- }
175
- }
176
-
177
- this.ctx.console.broadcast('chat-message-event', messageEvent)
178
- } catch (error) {
179
- this.logger.error('广播消息事件失败:', error)
180
- }
181
- }
182
-
183
- // 处理机器人发送的消息
184
- async broadcastBotMessageEvent(session: Session) {
185
- try {
186
- // 获取正确的 channelId
187
- const correctChannelId = this.getCorrectChannelId(session.selfId)
188
-
189
- // 调试日志:检查 channelId 的来源
190
- this.logInfo('机器人发送消息调试信息:', {
191
- 'session.channelId': session.channelId,
192
- 'session.event?.channel?.id': session.event?.channel?.id,
193
- 'correctChannelId': correctChannelId,
194
- 'session.guildId': session.guildId,
195
- 'session.isDirect': session.isDirect,
196
- 'session.platform': session.platform
197
- })
198
-
199
- // 使用正确的 channelId,如果没有则使用 session.channelId
200
- const finalChannelId = correctChannelId || session.channelId
201
-
202
- await this.updateBotInfoToFile(session)
203
- const guildName = await this.updateChannelInfoToFile(session)
204
-
205
- const timestamp = Date.now()
206
-
207
- let content = ''
208
-
209
- if (session.event?.message?.elements) {
210
- content = this.utils.extractTextContent(session.event.message.elements).trim()
211
- }
212
-
213
- // 创建机器人消息信息对象
214
- const messageInfo: MessageInfo = {
215
- id: `bot-msg-${timestamp}`,
216
- content: content,
217
- userId: session.selfId,
218
- username: session.bot.user?.name || `Bot-${session.selfId}`,
219
- avatar: session.bot.user?.avatar,
220
- timestamp: timestamp,
221
- channelId: finalChannelId,
222
- selfId: session.selfId,
223
- elements: this.utils.cleanBase64Content(session.event?.message?.elements),
224
- type: 'bot',
225
- guildName: guildName,
226
- platform: session.platform || 'unknown',
227
- isDirect: session.isDirect
228
- }
229
-
230
- await this.fileManager.addMessageToFile(messageInfo)
231
-
232
- const messageEvent = {
233
- type: 'bot-message',
234
- selfId: session.selfId,
235
- platform: session.platform || 'unknown',
236
- channelId: finalChannelId,
237
- messageId: `bot-msg-${timestamp}`,
238
- content: content,
239
- userId: session.selfId,
240
- username: session.bot.user?.name || `Bot-${session.selfId}`,
241
- avatar: session.bot.user?.avatar,
242
- timestamp: timestamp,
243
- guildName: guildName,
244
- channelType: session.event?.channel?.type || session.type || 0,
245
- elements: this.utils.cleanBase64Content(session.event?.message?.elements),
246
- isDirect: session.isDirect,
247
- bot: {
248
- avatar: session.bot.user?.avatar,
249
- name: session.bot.user?.name,
250
- }
251
- }
252
-
253
- // 检查是否包含图片元素
254
- const imageElements = session.event?.message?.elements?.filter((element: any) =>
255
- element.type === 'img' || element.type === 'image' || element.type === 'mface'
256
- ) || []
257
-
258
- this.logInfo('机器人发送消息 (before-send):', {
259
- selfId: session.selfId,
260
- channelId: messageEvent.channelId,
261
- originalChannelId: session.channelId,
262
- correctedChannelId: finalChannelId,
263
- content: content,
264
- platform: session.platform,
265
- imageCount: imageElements.length,
266
- imageUrls: imageElements.map((el: any) => el.attrs?.src || el.attrs?.url || el.attrs?.file)
267
- })
268
-
269
- // 清理已使用的 channelId 映射
270
- this.correctChannelIds.delete(session.selfId)
271
-
272
- this.ctx.console.broadcast('chat-bot-message-event', messageEvent)
273
- } catch (error) {
274
- this.logger.error('广播机器人消息事件失败:', error)
275
- }
276
- }
277
-
278
- private logInfo(...args: any[]) {
279
- if (this.config.loggerinfo) {
280
- (this.logger.info as (...args: any[]) => void)(...args)
281
- }
282
- }
1
+ import { BotInfo, ChannelInfo, MessageInfo, QuoteInfo } from './types'
2
+ import { Context, Session, h, Logger } from 'koishi'
3
+ import { FileManager } from './file-manager'
4
+ import { Config } from './config'
5
+ import { Utils } from './utils'
6
+ import { } from '@koishijs/plugin-console'
7
+
8
+ export class MessageHandler {
9
+ private logger: Logger
10
+ private utils: Utils
11
+ // 存储正确的 channelId 映射,key 是 selfId,value 是正确的 channelId
12
+ private correctChannelIds: Map<string, string> = new Map()
13
+
14
+ constructor(
15
+ private ctx: Context,
16
+ private config: Config,
17
+ private fileManager: FileManager
18
+ ) {
19
+ this.logger = ctx.logger('chat-patch')
20
+ this.utils = new Utils(config)
21
+ }
22
+
23
+ // 设置正确的 channelId
24
+ setCorrectChannelId(selfId: string, channelId: string) {
25
+ this.correctChannelIds.set(selfId, channelId)
26
+ this.logInfo('设置正确的 channelId:', { selfId, channelId })
27
+ }
28
+
29
+ // 获取正确的 channelId
30
+ getCorrectChannelId(selfId: string): string | undefined {
31
+ return this.correctChannelIds.get(selfId)
32
+ }
33
+
34
+ // 更新机器人信息到JSON文件
35
+ async updateBotInfoToFile(session: Session) {
36
+ const data = this.fileManager.readChatDataFromFile()
37
+
38
+ const botInfo: BotInfo = {
39
+ selfId: session.selfId,
40
+ platform: session.platform || 'unknown',
41
+ username: session.bot.user?.name || `Bot-${session.selfId}`,
42
+ avatar: session.bot.user?.avatar,
43
+ status: 'online'
44
+ }
45
+
46
+ data.bots[session.selfId] = botInfo
47
+ this.fileManager.writeChatDataToFile(data)
48
+ this.logInfo('更新机器人信息到文件:', botInfo.username)
49
+ }
50
+
51
+ // 更新频道信息到JSON文件
52
+ async updateChannelInfoToFile(session: Session): Promise<string> {
53
+ let guildName = session.channelId
54
+ let directUserName = session.username || session.userId || '未知用户'
55
+
56
+ const data = this.fileManager.readChatDataFromFile()
57
+
58
+ try {
59
+ // 获取群组名称
60
+ if (session.guildId && session.bot.getGuild && typeof session.bot.getGuild === 'function') {
61
+ const guild = await session.bot.getGuild(session.guildId)
62
+ guildName = guild?.name || session.channelId
63
+ }
64
+
65
+ // 获取私聊用户昵称
66
+ if (session.isDirect && session.bot.getUser && typeof session.bot.getUser === 'function') {
67
+ try {
68
+ const user = await session.bot.getUser(session.userId)
69
+ directUserName = user?.name || session.username || session.userId || '未知用户'
70
+ } catch (userError) {
71
+ this.logInfo('获取用户信息失败,使用备用名称:', userError)
72
+ }
73
+ }
74
+
75
+ // 如果没有getGuild方法,尝试使用getChannel方法
76
+ if (session.guildId && !session.bot.getGuild && session.bot.getChannel && typeof session.bot.getChannel === 'function') {
77
+ try {
78
+ const channel = await session.bot.getChannel(session.guildId)
79
+ guildName = channel?.name || session.channelId
80
+ } catch (channelError) {
81
+ this.logInfo('获取频道信息失败,使用频道ID作为备用:', channelError)
82
+ }
83
+ }
84
+ } catch (error) {
85
+ this.logInfo('获取频道信息失败,使用频道ID作为备用:', error)
86
+ guildName = session.channelId
87
+ }
88
+
89
+ if (!data.channels[session.selfId]) {
90
+ data.channels[session.selfId] = {}
91
+ }
92
+
93
+ const channelInfo: ChannelInfo = {
94
+ id: session.channelId,
95
+ name: session.isDirect
96
+ ? `私聊(${directUserName})`
97
+ : `${guildName} (${session.channelId})`,
98
+ type: session.type || 0,
99
+ channelId: session.channelId,
100
+ guildName: guildName,
101
+ isDirect: session.isDirect
102
+ }
103
+
104
+ data.channels[session.selfId][session.channelId] = channelInfo
105
+ this.fileManager.writeChatDataToFile(data)
106
+ this.logInfo('更新频道信息到文件:', channelInfo.name)
107
+
108
+ return guildName
109
+ }
110
+
111
+ async broadcastMessageEvent(session: Session) {
112
+ try {
113
+ await this.updateBotInfoToFile(session)
114
+ const guildName = await this.updateChannelInfoToFile(session)
115
+
116
+ const timestamp = Date.now()
117
+
118
+ // 处理 quote 信息
119
+ let quoteInfo: QuoteInfo | undefined = undefined
120
+ if (session.quote) {
121
+ quoteInfo = {
122
+ messageId: session.quote.messageId || session.quote.id,
123
+ id: session.quote.id,
124
+ content: session.quote.content || '',
125
+ elements: session.quote.elements,
126
+ user: {
127
+ id: session.quote.user?.id || session.quote.user?.userId || 'unknown',
128
+ name: session.quote.user?.name || session.quote.user?.username || 'unknown',
129
+ userId: session.quote.user?.userId || session.quote.user?.id || 'unknown',
130
+ avatar: session.quote.user?.avatar,
131
+ username: session.quote.user?.username || session.quote.user?.name || 'unknown'
132
+ },
133
+ timestamp: session.quote.timestamp || Date.now()
134
+ }
135
+ }
136
+
137
+ // session 中提取用户消息内容
138
+ let content = ''
139
+ let elements: h[] = []
140
+
141
+ if (session.content) {
142
+ content = session.content
143
+ } else if (session.stripped?.content) {
144
+ content = session.stripped.content
145
+ }
146
+
147
+ if (session.elements) {
148
+ elements = session.elements
149
+ if (!content) {
150
+ content = session.elements
151
+ .filter((element: any) => element.type === 'text')
152
+ .map((element: any) => element.attrs?.content || '')
153
+ .join('')
154
+ }
155
+ }
156
+
157
+ // 创建消息信息对象
158
+ const messageInfo: MessageInfo = {
159
+ id: session.event?.message?.id || `msg-${timestamp}`,
160
+ content: content || session.content || '',
161
+ userId: session.userId || session.event?.user?.id || 'unknown',
162
+ username: session.username || session.event?.user?.name || session.userId || 'unknown',
163
+ avatar: session.event?.user?.avatar,
164
+ timestamp: timestamp,
165
+ channelId: session.channelId,
166
+ selfId: session.selfId,
167
+ elements: this.utils.cleanBase64Content(elements),
168
+ type: 'user',
169
+ guildName: guildName,
170
+ platform: session.platform || 'unknown',
171
+ quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo) : undefined,
172
+ isDirect: session.isDirect
173
+ }
174
+
175
+ await this.fileManager.addMessageToFile(messageInfo)
176
+
177
+ const messageEvent = {
178
+ type: 'message',
179
+ selfId: session.selfId,
180
+ platform: session.platform || 'unknown',
181
+ channelId: session.channelId,
182
+ messageId: session.event?.message?.id || `msg-${timestamp}`,
183
+ content: content || session.content || '',
184
+ userId: session.userId || session.event?.user?.id || 'unknown',
185
+ username: session.username || session.event?.user?.name || session.userId || 'unknown',
186
+ avatar: session.event?.user?.avatar,
187
+ timestamp: timestamp,
188
+ guildName: guildName,
189
+ channelType: session.type || 0,
190
+ elements: this.utils.cleanBase64Content(elements),
191
+ quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo) : undefined,
192
+ isDirect: session.isDirect,
193
+ bot: {
194
+ avatar: session.bot.user?.avatar,
195
+ name: session.bot.user?.name,
196
+ }
197
+ }
198
+
199
+ this.ctx.console.broadcast('chat-message-event', messageEvent)
200
+ } catch (error) {
201
+ this.logger.error('广播消息事件失败:', error)
202
+ }
203
+ }
204
+
205
+ // 处理机器人发送的消息
206
+ async broadcastBotMessageEvent(session: Session) {
207
+ try {
208
+ // 获取正确的 channelId
209
+ const correctChannelId = this.getCorrectChannelId(session.selfId)
210
+
211
+ // 调试日志:检查 channelId 的来源
212
+ this.logInfo('机器人发送消息调试信息:', {
213
+ 'session.channelId': session.channelId,
214
+ 'session.event?.channel?.id': session.event?.channel?.id,
215
+ 'correctChannelId': correctChannelId,
216
+ 'session.guildId': session.guildId,
217
+ 'session.isDirect': session.isDirect,
218
+ 'session.platform': session.platform
219
+ })
220
+
221
+ // 使用正确的 channelId,如果没有则使用 session.channelId
222
+ const finalChannelId = correctChannelId || session.channelId
223
+
224
+ await this.updateBotInfoToFile(session)
225
+ const guildName = await this.updateChannelInfoToFile(session)
226
+
227
+ const timestamp = Date.now()
228
+
229
+ let content = ''
230
+
231
+ if (session.event?.message?.elements) {
232
+ content = this.utils.extractTextContent(session.event.message.elements).trim()
233
+ }
234
+
235
+ // 创建机器人消息信息对象
236
+ const messageInfo: MessageInfo = {
237
+ id: `bot-msg-${timestamp}`,
238
+ content: content,
239
+ userId: session.selfId,
240
+ username: session.bot.user?.name || `Bot-${session.selfId}`,
241
+ avatar: session.bot.user?.avatar,
242
+ timestamp: timestamp,
243
+ channelId: finalChannelId,
244
+ selfId: session.selfId,
245
+ elements: this.utils.cleanBase64Content(session.event?.message?.elements),
246
+ type: 'bot',
247
+ guildName: guildName,
248
+ platform: session.platform || 'unknown',
249
+ isDirect: session.isDirect
250
+ }
251
+
252
+ await this.fileManager.addMessageToFile(messageInfo)
253
+
254
+ const messageEvent = {
255
+ type: 'bot-message',
256
+ selfId: session.selfId,
257
+ platform: session.platform || 'unknown',
258
+ channelId: finalChannelId,
259
+ messageId: `bot-msg-${timestamp}`,
260
+ content: content,
261
+ userId: session.selfId,
262
+ username: session.bot.user?.name || `Bot-${session.selfId}`,
263
+ avatar: session.bot.user?.avatar,
264
+ timestamp: timestamp,
265
+ guildName: guildName,
266
+ channelType: session.event?.channel?.type || session.type || 0,
267
+ elements: this.utils.cleanBase64Content(session.event?.message?.elements),
268
+ isDirect: session.isDirect,
269
+ bot: {
270
+ avatar: session.bot.user?.avatar,
271
+ name: session.bot.user?.name,
272
+ }
273
+ }
274
+
275
+ // 检查是否包含图片元素
276
+ const imageElements = session.event?.message?.elements?.filter((element: any) =>
277
+ element.type === 'img' || element.type === 'image' || element.type === 'mface'
278
+ ) || []
279
+
280
+ this.logInfo('机器人发送消息 (before-send):', {
281
+ selfId: session.selfId,
282
+ channelId: messageEvent.channelId,
283
+ originalChannelId: session.channelId,
284
+ correctedChannelId: finalChannelId,
285
+ content: content,
286
+ platform: session.platform,
287
+ imageCount: imageElements.length,
288
+ imageUrls: imageElements.map((el: any) => el.attrs?.src || el.attrs?.url || el.attrs?.file)
289
+ })
290
+
291
+ // 清理已使用的 channelId 映射
292
+ this.correctChannelIds.delete(session.selfId)
293
+
294
+ this.ctx.console.broadcast('chat-bot-message-event', messageEvent)
295
+ } catch (error) {
296
+ this.logger.error('广播机器人消息事件失败:', error)
297
+ }
298
+ }
299
+
300
+ private logInfo(...args: any[]) {
301
+ if (this.config.loggerinfo) {
302
+ (this.logger.info as (...args: any[]) => void)(...args)
303
+ }
304
+ }
283
305
  }