koishi-plugin-chat-patch 0.8.2

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.js ADDED
@@ -0,0 +1,594 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/index.ts
5
+ import { Schema } from "koishi";
6
+ import path from "node:path";
7
+ import fs from "node:fs";
8
+ var name = "chat-patch";
9
+ var reusable = false;
10
+ var filter = false;
11
+ var inject = {
12
+ required: ["console"]
13
+ };
14
+ var usage = `
15
+ ---
16
+
17
+ 开启后,即可在koishi控制台操作机器人收发消息啦
18
+
19
+ 暂时只支持接受图文消息 / 发送文字消息
20
+
21
+ ---
22
+ `;
23
+ var Config = Schema.intersect([
24
+ Schema.object({
25
+ maxMessagesPerChannel: Schema.number().default(500).description("每个群组最大保存消息数量").min(50).max(5e3),
26
+ keepMessagesOnClear: Schema.number().default(0).description("手动清理历史记录时保留的消息数量").min(0).max(1e3),
27
+ blockedPlatforms: Schema.array(Schema.object({
28
+ platformName: Schema.string().description("平台名称或关键词"),
29
+ exactMatch: Schema.boolean().default(false).description("完全匹配?如果关闭,包含关键词即屏蔽").default(true)
30
+ })).role("table").description("屏蔽的平台列表").default([
31
+ {
32
+ "platformName": "qq",
33
+ "exactMatch": true
34
+ },
35
+ {
36
+ "platformName": "qqguild",
37
+ "exactMatch": true
38
+ },
39
+ {
40
+ "platformName": "sandbox",
41
+ "exactMatch": false
42
+ }
43
+ ])
44
+ }).description("基础设置"),
45
+ Schema.object({
46
+ chatContainerHeight: Schema.number().default(80).description("手机端使用的视口高度(防止文本输入框被挡住)").min(50).max(100),
47
+ loggerinfo: Schema.boolean().default(false).description("日志调试模式").experimental()
48
+ }).description("进阶设置")
49
+ ]);
50
+ async function apply(ctx, config) {
51
+ const logger = ctx.logger("chat-patch");
52
+ const dataFilePath = path.resolve(ctx.baseDir, "data", "chat-patch", "chat-data.json");
53
+ const initialData = readChatDataFromFile();
54
+ const cleanedData = cleanExcessMessages(initialData);
55
+ const originalCount = Object.values(initialData.messages).reduce((total, msgs) => total + msgs.length, 0);
56
+ const cleanedCount = Object.values(cleanedData.messages).reduce((total, msgs) => total + msgs.length, 0);
57
+ if (originalCount !== cleanedCount) {
58
+ writeChatDataToFile(cleanedData);
59
+ }
60
+ logInfo("插件加载完成,数据统计:", {
61
+ 机器人数量: Object.keys(cleanedData.bots).length,
62
+ 频道数量: Object.keys(cleanedData.channels).reduce((total, botId) => total + Object.keys(cleanedData.channels[botId] || {}).length, 0),
63
+ 消息频道数: Object.keys(cleanedData.messages).length,
64
+ 总消息数: Object.values(cleanedData.messages).reduce((total, msgs) => total + msgs.length, 0)
65
+ });
66
+ ctx.on("message", async (session) => {
67
+ if (isPlatformBlocked(session.platform || "unknown")) {
68
+ logInfo(`忽略来自被屏蔽平台的消息: ${session.platform}`);
69
+ return;
70
+ }
71
+ await broadcastMessageEvent(session);
72
+ });
73
+ ctx.on("ready", async () => {
74
+ logInfo("插件启动完成,开始监听消息");
75
+ setInterval(() => {
76
+ const data = readChatDataFromFile();
77
+ const cleanedData2 = cleanExcessMessages(data);
78
+ const originalCount2 = Object.values(data.messages).reduce((total, msgs) => total + msgs.length, 0);
79
+ const cleanedCount2 = Object.values(cleanedData2.messages).reduce((total, msgs) => total + msgs.length, 0);
80
+ if (originalCount2 !== cleanedCount2) {
81
+ writeChatDataToFile(cleanedData2);
82
+ logInfo("定期清理完成,清理了", originalCount2 - cleanedCount2, "条超量消息");
83
+ }
84
+ }, 3e5);
85
+ });
86
+ ctx.console.addListener("get-chat-data", async () => {
87
+ try {
88
+ const data = readChatDataFromFile();
89
+ const cleanedData2 = cleanExcessMessages(data);
90
+ logInfo("获取聊天数据:", {
91
+ 机器人数量: Object.keys(cleanedData2.bots).length,
92
+ 频道数量: Object.keys(cleanedData2.channels).reduce((total, botId) => total + Object.keys(cleanedData2.channels[botId] || {}).length, 0),
93
+ 消息频道数: Object.keys(cleanedData2.messages).length,
94
+ 总消息数: Object.values(cleanedData2.messages).reduce((total, msgs) => total + msgs.length, 0)
95
+ });
96
+ return {
97
+ success: true,
98
+ data: {
99
+ ...cleanedData2,
100
+ pinnedBots: cleanedData2.pinnedBots,
101
+ // 包含置顶机器人
102
+ pinnedChannels: cleanedData2.pinnedChannels
103
+ // 包含置顶频道
104
+ }
105
+ };
106
+ } catch (error) {
107
+ logger.error("获取聊天数据失败:", error);
108
+ return { success: false, error: error?.message || String(error) };
109
+ }
110
+ });
111
+ ctx.console.addListener("get-history-messages", async (requestData) => {
112
+ try {
113
+ const data = readChatDataFromFile();
114
+ const channelKey = `${requestData.selfId}-${requestData.channelId}`;
115
+ const messages = data.messages[channelKey] || [];
116
+ const sortedMessages = messages.sort((a, b) => a.timestamp - b.timestamp);
117
+ logInfo("获取历史消息:", channelKey, "共", sortedMessages.length, "条消息");
118
+ return {
119
+ success: true,
120
+ messages: sortedMessages
121
+ };
122
+ } catch (error) {
123
+ logger.error("获取历史消息失败:", error);
124
+ return { success: false, error: error?.message || String(error), messages: [] };
125
+ }
126
+ });
127
+ ctx.console.addListener("get-all-channel-message-counts", async () => {
128
+ try {
129
+ const data = readChatDataFromFile();
130
+ const counts = {};
131
+ for (const [channelKey, messages] of Object.entries(data.messages)) {
132
+ counts[channelKey] = messages.length;
133
+ }
134
+ logInfo("获取所有频道消息数量:", {
135
+ 频道数: Object.keys(counts).length,
136
+ 总消息数: Object.values(counts).reduce((total, count) => total + count, 0)
137
+ });
138
+ return {
139
+ success: true,
140
+ counts
141
+ };
142
+ } catch (error) {
143
+ logger.error("获取频道消息数量失败:", error);
144
+ return { success: false, error: error?.message || String(error), counts: {} };
145
+ }
146
+ });
147
+ ctx.console.addListener("fetch-image", async (data) => {
148
+ try {
149
+ const response = await fetch(data.url, {
150
+ headers: {
151
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
152
+ "Referer": ""
153
+ }
154
+ });
155
+ if (!response.ok) {
156
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
157
+ }
158
+ const buffer = await response.arrayBuffer();
159
+ const base64 = Buffer.from(buffer).toString("base64");
160
+ const contentType = response.headers.get("content-type") || "image/jpeg";
161
+ return {
162
+ success: true,
163
+ base64,
164
+ contentType,
165
+ dataUrl: `data:${contentType};base64,${base64}`
166
+ };
167
+ } catch (error) {
168
+ return { success: false, error: error?.message || String(error) };
169
+ }
170
+ });
171
+ ctx.console.addListener("clear-channel-history", async (data) => {
172
+ try {
173
+ logInfo("收到清理历史记录请求:", data);
174
+ const chatData = readChatDataFromFile();
175
+ const channelKey = `${data.selfId}-${data.channelId}`;
176
+ if (!chatData.messages[channelKey]) {
177
+ return { success: true, message: "频道没有历史消息" };
178
+ }
179
+ const messages = chatData.messages[channelKey];
180
+ const originalCount2 = messages.length;
181
+ const keepCount = data.keepCount || config.keepMessagesOnClear;
182
+ if (keepCount > 0 && originalCount2 <= keepCount) {
183
+ return { success: true, message: `消息数量(${originalCount2})未超过保留数量(${keepCount}),无需清理` };
184
+ }
185
+ const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp);
186
+ const keptMessages = keepCount > 0 ? sortedMessages.slice(-keepCount) : [];
187
+ const clearedCount = originalCount2 - keptMessages.length;
188
+ chatData.messages[channelKey] = keptMessages;
189
+ writeChatDataToFile(chatData);
190
+ logInfo(`频道 ${channelKey} 历史记录清理完成:`, {
191
+ 原始消息数: originalCount2,
192
+ 保留消息数: keptMessages.length,
193
+ 清理消息数: clearedCount
194
+ });
195
+ return {
196
+ success: true,
197
+ message: `成功清理 ${clearedCount} 条历史消息,保留最新 ${keptMessages.length} 条`,
198
+ clearedCount,
199
+ keptCount: keptMessages.length
200
+ };
201
+ } catch (error) {
202
+ logger.error("清理频道历史记录失败:", error);
203
+ return { success: false, error: error?.message || String(error) };
204
+ }
205
+ });
206
+ ctx.console.addListener("send-message", async (data) => {
207
+ try {
208
+ logInfo("收到发送消息请求:", data);
209
+ const bot = ctx.bots.find((bot2) => bot2.selfId === data.selfId);
210
+ if (!bot) {
211
+ logger.error("未找到机器人:", data.selfId);
212
+ return { success: false, error: "未找到指定的机器人" };
213
+ }
214
+ const result = await bot.sendMessage(data.channelId, data.content);
215
+ logInfo("消息发送成功:", result);
216
+ const timestamp = Date.now();
217
+ const chatData = readChatDataFromFile();
218
+ const botInfo = {
219
+ selfId: bot.selfId,
220
+ platform: bot.platform || "unknown",
221
+ username: bot.user?.name || `Bot-${data.selfId}`,
222
+ avatar: bot.user?.avatar,
223
+ status: "online"
224
+ };
225
+ chatData.bots[bot.selfId] = botInfo;
226
+ writeChatDataToFile(chatData);
227
+ const botMessageInfo = {
228
+ id: Array.isArray(result) ? result[0] : result || `bot-msg-${timestamp}`,
229
+ content: data.content,
230
+ userId: bot.selfId,
231
+ username: bot.user?.name || `Bot-${data.selfId}`,
232
+ avatar: bot.user?.avatar,
233
+ timestamp,
234
+ channelId: data.channelId,
235
+ selfId: data.selfId,
236
+ type: "bot",
237
+ platform: bot.platform || "unknown"
238
+ };
239
+ addMessageToFile(botMessageInfo);
240
+ const sentMessageEvent = {
241
+ type: "bot-message-sent",
242
+ selfId: data.selfId,
243
+ channelId: data.channelId,
244
+ messageId: Array.isArray(result) ? result[0] : result,
245
+ content: data.content,
246
+ timestamp,
247
+ botUsername: bot.user?.name || `Bot-${data.selfId}`,
248
+ botAvatar: bot.user?.avatar
249
+ };
250
+ ctx.console.broadcast("bot-message-sent-event", sentMessageEvent);
251
+ return { success: true, messageId: Array.isArray(result) ? result[0] : result };
252
+ } catch (error) {
253
+ logger.error("发送消息失败:", error);
254
+ return { success: false, error: error?.message || String(error) };
255
+ }
256
+ });
257
+ ctx.console.addListener("delete-bot-data", async (data) => {
258
+ try {
259
+ logInfo("收到删除机器人数据请求:", data);
260
+ const chatData = readChatDataFromFile();
261
+ let deletedChannels = 0;
262
+ let deletedMessages = 0;
263
+ if (chatData.channels[data.selfId]) {
264
+ deletedChannels = Object.keys(chatData.channels[data.selfId]).length;
265
+ delete chatData.channels[data.selfId];
266
+ }
267
+ const channelsToDelete = Object.keys(chatData.messages).filter((key) => key.startsWith(`${data.selfId}-`));
268
+ for (const channelKey of channelsToDelete) {
269
+ deletedMessages += chatData.messages[channelKey].length;
270
+ delete chatData.messages[channelKey];
271
+ }
272
+ delete chatData.bots[data.selfId];
273
+ writeChatDataToFile(chatData);
274
+ logInfo(`机器人 ${data.selfId} 数据删除完成:`, {
275
+ 删除频道数: deletedChannels,
276
+ 删除消息数: deletedMessages
277
+ });
278
+ return {
279
+ success: true,
280
+ message: `成功删除机器人数据:${deletedChannels} 个频道,${deletedMessages} 条消息`,
281
+ deletedChannels,
282
+ deletedMessages
283
+ };
284
+ } catch (error) {
285
+ logger.error("删除机器人数据失败:", error);
286
+ return { success: false, error: error?.message || String(error) };
287
+ }
288
+ });
289
+ ctx.console.addListener("delete-channel-data", async (data) => {
290
+ try {
291
+ logInfo("收到删除频道数据请求:", data);
292
+ const chatData = readChatDataFromFile();
293
+ const channelKey = `${data.selfId}-${data.channelId}`;
294
+ let deletedMessages = 0;
295
+ if (chatData.messages[channelKey]) {
296
+ deletedMessages = chatData.messages[channelKey].length;
297
+ delete chatData.messages[channelKey];
298
+ }
299
+ if (chatData.channels[data.selfId] && chatData.channels[data.selfId][data.channelId]) {
300
+ delete chatData.channels[data.selfId][data.channelId];
301
+ }
302
+ writeChatDataToFile(chatData);
303
+ logInfo(`频道 ${channelKey} 数据删除完成:`, {
304
+ 删除消息数: deletedMessages
305
+ });
306
+ return {
307
+ success: true,
308
+ message: `成功删除频道数据:${deletedMessages} 条消息`,
309
+ deletedMessages
310
+ };
311
+ } catch (error) {
312
+ logger.error("删除频道数据失败:", error);
313
+ return { success: false, error: error?.message || String(error) };
314
+ }
315
+ });
316
+ ctx.console.addListener("set-pinned-bots", async (data) => {
317
+ try {
318
+ logInfo("收到设置置顶机器人请求:", data.pinnedBots);
319
+ const chatData = readChatDataFromFile();
320
+ chatData.pinnedBots = data.pinnedBots;
321
+ writeChatDataToFile(chatData);
322
+ return { success: true };
323
+ } catch (error) {
324
+ logger.error("设置置顶机器人失败:", error);
325
+ return { success: false, error: error?.message || String(error) };
326
+ }
327
+ });
328
+ ctx.console.addListener("set-pinned-channels", async (data) => {
329
+ try {
330
+ logInfo("收到设置置顶频道请求:", data.pinnedChannels);
331
+ const chatData = readChatDataFromFile();
332
+ chatData.pinnedChannels = data.pinnedChannels;
333
+ writeChatDataToFile(chatData);
334
+ return { success: true };
335
+ } catch (error) {
336
+ logger.error("设置置顶频道失败:", error);
337
+ return { success: false, error: error?.message || String(error) };
338
+ }
339
+ });
340
+ ctx.console.addListener("get-plugin-config", async () => {
341
+ try {
342
+ return {
343
+ success: true,
344
+ config: {
345
+ maxMessagesPerChannel: config.maxMessagesPerChannel,
346
+ keepMessagesOnClear: config.keepMessagesOnClear,
347
+ loggerinfo: config.loggerinfo,
348
+ blockedPlatforms: config.blockedPlatforms || [],
349
+ chatContainerHeight: config.chatContainerHeight
350
+ }
351
+ };
352
+ } catch (error) {
353
+ logger.error("获取插件配置失败:", error);
354
+ return { success: false, error: error?.message || String(error) };
355
+ }
356
+ });
357
+ ctx.console.addEntry({
358
+ dev: path.resolve(__dirname, "../client/index.ts"),
359
+ prod: path.resolve(__dirname, "../dist")
360
+ });
361
+ function logInfo(...args) {
362
+ if (config.loggerinfo) {
363
+ logger.info(...args);
364
+ }
365
+ }
366
+ __name(logInfo, "logInfo");
367
+ function isPlatformBlocked(platform) {
368
+ if (!config.blockedPlatforms || config.blockedPlatforms.length === 0) {
369
+ return false;
370
+ }
371
+ for (const blockedPlatform of config.blockedPlatforms) {
372
+ if (blockedPlatform.exactMatch) {
373
+ if (platform === blockedPlatform.platformName) {
374
+ logInfo(`平台 ${platform} 被屏蔽 (完全匹配: ${blockedPlatform.platformName})`);
375
+ return true;
376
+ }
377
+ } else {
378
+ if (platform.includes(blockedPlatform.platformName)) {
379
+ logInfo(`平台 ${platform} 被屏蔽 (包含匹配: ${blockedPlatform.platformName})`);
380
+ return true;
381
+ }
382
+ }
383
+ }
384
+ return false;
385
+ }
386
+ __name(isPlatformBlocked, "isPlatformBlocked");
387
+ function ensureDataDir() {
388
+ const dir = path.dirname(dataFilePath);
389
+ if (!fs.existsSync(dir)) {
390
+ fs.mkdirSync(dir, { recursive: true });
391
+ }
392
+ }
393
+ __name(ensureDataDir, "ensureDataDir");
394
+ function readChatDataFromFile() {
395
+ try {
396
+ if (fs.existsSync(dataFilePath)) {
397
+ const jsonData = fs.readFileSync(dataFilePath, "utf8");
398
+ const data = JSON.parse(jsonData);
399
+ return {
400
+ bots: data.bots || {},
401
+ channels: data.channels || {},
402
+ messages: data.messages || {},
403
+ pinnedBots: data.pinnedBots || [],
404
+ // 读取置顶机器人
405
+ pinnedChannels: data.pinnedChannels || [],
406
+ // 读取置顶频道
407
+ lastSaveTime: data.lastSaveTime
408
+ };
409
+ }
410
+ } catch (error) {
411
+ logger.error("读取聊天数据失败:", error);
412
+ }
413
+ return {
414
+ bots: {},
415
+ channels: {},
416
+ messages: {},
417
+ pinnedBots: [],
418
+ // 默认空数组
419
+ pinnedChannels: []
420
+ // 默认空数组
421
+ };
422
+ }
423
+ __name(readChatDataFromFile, "readChatDataFromFile");
424
+ function writeChatDataToFile(data) {
425
+ try {
426
+ ensureDataDir();
427
+ data.lastSaveTime = Date.now();
428
+ const jsonData = JSON.stringify(data, null, 2);
429
+ fs.writeFileSync(dataFilePath, jsonData, "utf8");
430
+ } catch (error) {
431
+ logger.error("写入聊天数据失败:", error);
432
+ }
433
+ }
434
+ __name(writeChatDataToFile, "writeChatDataToFile");
435
+ function cleanExcessMessages(data) {
436
+ let cleanedCount2 = 0;
437
+ const cleanedMessages = {};
438
+ for (const [channelKey, messages] of Object.entries(data.messages)) {
439
+ if (messages.length > config.maxMessagesPerChannel) {
440
+ const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp);
441
+ const keptMessages = sortedMessages.slice(-config.maxMessagesPerChannel);
442
+ cleanedCount2 += messages.length - keptMessages.length;
443
+ cleanedMessages[channelKey] = keptMessages;
444
+ logInfo(`频道 ${channelKey} 清理了 ${messages.length - keptMessages.length} 条旧消息,保留最新 ${keptMessages.length} 条`);
445
+ } else {
446
+ cleanedMessages[channelKey] = messages;
447
+ }
448
+ }
449
+ if (cleanedCount2 > 0) {
450
+ logInfo("总共清理超量消息:", cleanedCount2, "条");
451
+ }
452
+ return {
453
+ ...data,
454
+ messages: cleanedMessages
455
+ };
456
+ }
457
+ __name(cleanExcessMessages, "cleanExcessMessages");
458
+ function addMessageToFile(messageInfo) {
459
+ const data = readChatDataFromFile();
460
+ const channelKey = `${messageInfo.selfId}-${messageInfo.channelId}`;
461
+ if (!data.messages[channelKey]) {
462
+ data.messages[channelKey] = [];
463
+ }
464
+ messageInfo.timestamp = Date.now();
465
+ data.messages[channelKey].push(messageInfo);
466
+ if (data.messages[channelKey].length > config.maxMessagesPerChannel) {
467
+ data.messages[channelKey].sort((a, b) => a.timestamp - b.timestamp);
468
+ const removedCount = data.messages[channelKey].length - config.maxMessagesPerChannel;
469
+ data.messages[channelKey] = data.messages[channelKey].slice(-config.maxMessagesPerChannel);
470
+ logInfo(`频道 ${channelKey} 达到消息上限,清理了 ${removedCount} 条旧消息`);
471
+ }
472
+ writeChatDataToFile(data);
473
+ logInfo("添加消息到文件:", channelKey, "当前消息数:", data.messages[channelKey].length);
474
+ }
475
+ __name(addMessageToFile, "addMessageToFile");
476
+ function updateBotInfoToFile(session) {
477
+ const data = readChatDataFromFile();
478
+ const botInfo = {
479
+ selfId: session.selfId,
480
+ platform: session.platform || "unknown",
481
+ username: session.bot.user?.name || `Bot-${session.selfId}`,
482
+ avatar: session.bot.user?.avatar,
483
+ status: "online"
484
+ };
485
+ data.bots[session.selfId] = botInfo;
486
+ writeChatDataToFile(data);
487
+ logInfo("更新机器人信息到文件:", botInfo.username);
488
+ }
489
+ __name(updateBotInfoToFile, "updateBotInfoToFile");
490
+ async function updateChannelInfoToFile(session) {
491
+ const data = readChatDataFromFile();
492
+ let guildName = session.channelId;
493
+ try {
494
+ if (session.guildId && session.bot.getGuild && typeof session.bot.getGuild === "function") {
495
+ const guild = await session.bot.getGuild(session.guildId);
496
+ guildName = guild?.name || session.channelId;
497
+ }
498
+ } catch (guildError) {
499
+ logInfo("获取群组信息失败,使用频道ID作为备用:", guildError);
500
+ guildName = session.channelId;
501
+ }
502
+ if (!data.channels[session.selfId]) {
503
+ data.channels[session.selfId] = {};
504
+ }
505
+ const channelInfo = {
506
+ id: session.channelId,
507
+ name: session.guildId ? `${guildName} (${session.channelId})` : `私信 ${session.channelId}`,
508
+ type: session.type || 0,
509
+ guildId: session.guildId,
510
+ guildName
511
+ };
512
+ data.channels[session.selfId][session.channelId] = channelInfo;
513
+ writeChatDataToFile(data);
514
+ logInfo("更新频道信息到文件:", channelInfo.name);
515
+ return guildName;
516
+ }
517
+ __name(updateChannelInfoToFile, "updateChannelInfoToFile");
518
+ async function broadcastMessageEvent(session) {
519
+ try {
520
+ updateBotInfoToFile(session);
521
+ const guildName = await updateChannelInfoToFile(session);
522
+ const timestamp = Date.now();
523
+ let quoteInfo = void 0;
524
+ if (session.quote) {
525
+ quoteInfo = {
526
+ messageId: session.quote.messageId || session.quote.id,
527
+ id: session.quote.id,
528
+ content: session.quote.content || "",
529
+ elements: session.quote.elements,
530
+ user: {
531
+ id: session.quote.user?.id || session.quote.user?.userId || "unknown",
532
+ name: session.quote.user?.name || session.quote.user?.username || "unknown",
533
+ userId: session.quote.user?.userId || session.quote.user?.id || "unknown",
534
+ avatar: session.quote.user?.avatar,
535
+ username: session.quote.user?.username || session.quote.user?.name || "unknown"
536
+ },
537
+ timestamp: session.quote.timestamp || Date.now()
538
+ };
539
+ }
540
+ const messageInfo = {
541
+ id: session.messageId || `msg-${timestamp}`,
542
+ content: session.content || "",
543
+ userId: session.userId || "unknown",
544
+ username: session.username || session.userId || "unknown",
545
+ avatar: session.author?.avatar,
546
+ timestamp,
547
+ channelId: session.channelId,
548
+ selfId: session.selfId,
549
+ elements: session.elements,
550
+ type: "user",
551
+ guildId: session.guildId,
552
+ guildName,
553
+ platform: session.platform || "unknown",
554
+ quote: quoteInfo
555
+ };
556
+ addMessageToFile(messageInfo);
557
+ const messageEvent = {
558
+ type: "message",
559
+ selfId: session.selfId,
560
+ platform: session.platform || "unknown",
561
+ channelId: session.channelId,
562
+ messageId: session.messageId,
563
+ content: session.content,
564
+ userId: session.userId || "unknown",
565
+ username: session.username || session.userId || "unknown",
566
+ avatar: session.author?.avatar,
567
+ timestamp,
568
+ guildId: session.guildId,
569
+ guildName,
570
+ channelType: session.type || 0,
571
+ elements: session.elements,
572
+ quote: quoteInfo,
573
+ bot: {
574
+ avatar: session.bot.user?.avatar,
575
+ name: session.bot.user?.name
576
+ }
577
+ };
578
+ ctx.console.broadcast("chat-message-event", messageEvent);
579
+ } catch (error) {
580
+ logger.error("广播消息事件失败:", error);
581
+ }
582
+ }
583
+ __name(broadcastMessageEvent, "broadcastMessageEvent");
584
+ }
585
+ __name(apply, "apply");
586
+ export {
587
+ Config,
588
+ apply,
589
+ filter,
590
+ inject,
591
+ name,
592
+ reusable,
593
+ usage
594
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "koishi-plugin-chat-patch",
3
+ "description": "[<ruby>chat-patch<rp>(</rp><rt>点我预览效果</rt><rp>)</rp></ruby>](https://i0.hdslb.com/bfs/openplatform/71074dfc9e5256fc3333d8bd8478bec1af874046.png) 视奸小插件((bushi( (低性能警告)。手机端适配。灵感来自 chat 插件。",
4
+ "version": "0.8.2",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "files": [
8
+ "client",
9
+ "dist",
10
+ "lib",
11
+ "src"
12
+ ],
13
+ "license": "MIT",
14
+ "homepage": "https://github.com/shangxueink/koishi-shangxue-apps/",
15
+ "bugs": {
16
+ "url": "https://github.com/shangxueink/koishi-shangxue-apps/issues"
17
+ },
18
+ "keywords": [
19
+ "koishi",
20
+ "plugin",
21
+ "chat",
22
+ "聊天室",
23
+ "console"
24
+ ],
25
+ "cordis": {
26
+ "service": {
27
+ "required": [
28
+ "console"
29
+ ]
30
+ }
31
+ },
32
+ "peerDependencies": {
33
+ "@koishijs/plugin-console": "^5.11.0",
34
+ "koishi": "^4.16.0"
35
+ },
36
+ "devDependencies": {
37
+ "@koishijs/client": "^5.11.0",
38
+ "koishi": "^4.16.0"
39
+ }
40
+ }
package/readme.md ADDED
@@ -0,0 +1,25 @@
1
+ # koishi-plugin-chat-patch
2
+
3
+ [![npm](https://img.shields.io/npm/v/koishi-plugin-chat-patch?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-chat-patch) [![npm downloads](https://img.shields.io/npm/dm/koishi-plugin-chat-patch)](https://www.npmjs.com/package/koishi-plugin-chat-patch)
4
+
5
+ Koishi 控制台聊天插件,允许用户直接在控制台中管理机器人、频道和消息。
6
+
7
+ 支持消息的收发、图片缓存、频道置顶等。
8
+
9
+ ## ✨ 功能特性
10
+
11
+ - **聊天界面**:在 Koishi 控制台内直接查看机器人的聊天记录。
12
+ - **消息收发**:支持接收文本、图片等多种类型的消息。
13
+ - **图片缓存**:通过 IndexedDB 缓存图片,优化加载速度并减少网络请求。
14
+ - **手机端优化**:针对手机端进行适配,提供滑动返回等手势操作,并可配置聊天容器高度以防止输入框被遮挡。
15
+ - **平台屏蔽**:可配置屏蔽特定平台的消息,避免不必要的干扰。
16
+
17
+ ## 🛠️ 开发与贡献
18
+
19
+ 欢迎 PR ~
20
+
21
+ PR 方式请参考 -> https://github.com/shangxueink/koishi-shangxue-apps/tree/main?tab=readme-ov-file#%E5%A6%82%E4%BD%95%E5%9C%A8%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF%E4%B8%AD%E5%BC%80%E5%8F%91%E6%AD%A4%E4%BB%93%E5%BA%93
22
+
23
+ ## 许可证
24
+
25
+ 本项目采用 [MIT 许可证](LICENSE) 开源。