koishi-plugin-chat-patch 2.1.1 → 2.2.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/vue/chat-logic.ts +683 -678
- package/client/vue/composables/useVideoCache.ts +130 -0
- package/client/vue/index.vue +658 -612
- package/dist/index.js +2 -2
- package/lib/api-handlers.d.ts +1 -0
- package/lib/index.js +91 -20
- package/lib/utils.d.ts +13 -0
- package/package.json +52 -49
- package/src/api-handlers.ts +750 -704
- package/src/index.ts +148 -125
- package/src/message-handler.ts +413 -407
- package/src/utils.ts +163 -144
package/lib/api-handlers.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare class ApiHandlers {
|
|
|
8
8
|
private fileManager;
|
|
9
9
|
private messageHandler;
|
|
10
10
|
private logger;
|
|
11
|
+
private currentTempVideo;
|
|
11
12
|
constructor(ctx: Context, config: Config, fileManager: FileManager, messageHandler: MessageHandler);
|
|
12
13
|
registerApiHandlers(): void;
|
|
13
14
|
private isFileUrl;
|
package/lib/index.js
CHANGED
|
@@ -128,26 +128,40 @@ var Utils = class {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
// 清理对象中的base64内容,改为持久化存储
|
|
131
|
-
|
|
131
|
+
// isBotMessage: 是否为机器人发送的消息
|
|
132
|
+
cleanBase64Content(obj, isBotMessage = false) {
|
|
132
133
|
if (obj === null || obj === void 0) {
|
|
133
134
|
return obj;
|
|
134
135
|
}
|
|
135
136
|
if (typeof obj === "string") {
|
|
136
137
|
if (this.isBase64(obj)) {
|
|
138
|
+
if (isBotMessage && !obj.startsWith("data:image/")) {
|
|
139
|
+
return "[富媒体内容已省略]";
|
|
140
|
+
}
|
|
137
141
|
return this.persistBase64Image(obj);
|
|
138
142
|
}
|
|
139
143
|
return obj;
|
|
140
144
|
}
|
|
141
145
|
if (Array.isArray(obj)) {
|
|
142
|
-
return obj.map((item) => this.cleanBase64Content(item));
|
|
146
|
+
return obj.map((item) => this.cleanBase64Content(item, isBotMessage));
|
|
143
147
|
}
|
|
144
148
|
if (typeof obj === "object") {
|
|
145
149
|
const cleaned = {};
|
|
146
150
|
for (const [key, value] of Object.entries(obj)) {
|
|
151
|
+
if (isBotMessage && obj.type && !["text", "image", "img"].includes(obj.type)) {
|
|
152
|
+
if (typeof value === "string" && (key === "src" || key === "url" || key === "file") && this.isBase64(value)) {
|
|
153
|
+
cleaned[key] = "[富媒体内容已省略]";
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
147
157
|
if (typeof value === "string" && (key === "src" || key === "url" || key === "file" || key === "data" || key === "content") && this.isBase64(value)) {
|
|
148
|
-
|
|
158
|
+
if (isBotMessage && !value.startsWith("data:image/")) {
|
|
159
|
+
cleaned[key] = "[富媒体内容已省略]";
|
|
160
|
+
} else {
|
|
161
|
+
cleaned[key] = this.persistBase64Image(value);
|
|
162
|
+
}
|
|
149
163
|
} else {
|
|
150
|
-
cleaned[key] = this.cleanBase64Content(value);
|
|
164
|
+
cleaned[key] = this.cleanBase64Content(value, isBotMessage);
|
|
151
165
|
}
|
|
152
166
|
}
|
|
153
167
|
return cleaned;
|
|
@@ -285,17 +299,22 @@ var MessageHandler = class {
|
|
|
285
299
|
}
|
|
286
300
|
}
|
|
287
301
|
// 处理消息中的媒体元素并缓存
|
|
288
|
-
|
|
302
|
+
// 只缓存用户的图片和语音,不缓存视频和文件
|
|
303
|
+
async processMediaElements(elements, isUserMessage = true) {
|
|
289
304
|
if (!elements) return elements;
|
|
290
305
|
for (const el of elements) {
|
|
291
306
|
if (["image", "img", "mface"].includes(el.type)) {
|
|
292
307
|
const src = el.attrs.src || el.attrs.url || el.attrs.file;
|
|
293
|
-
if (src
|
|
294
|
-
|
|
308
|
+
if (src && isUserMessage) {
|
|
309
|
+
el.attrs.src = await this.downloadAndCacheMedia(src, "image");
|
|
310
|
+
}
|
|
311
|
+
} else if (el.type === "audio") {
|
|
295
312
|
const src = el.attrs.src || el.attrs.url || el.attrs.file;
|
|
296
|
-
if (src
|
|
313
|
+
if (src && isUserMessage) {
|
|
314
|
+
el.attrs.src = await this.downloadAndCacheMedia(src, "media");
|
|
315
|
+
}
|
|
297
316
|
}
|
|
298
|
-
if (el.children) await this.processMediaElements(el.children);
|
|
317
|
+
if (el.children) await this.processMediaElements(el.children, isUserMessage);
|
|
299
318
|
}
|
|
300
319
|
return elements;
|
|
301
320
|
}
|
|
@@ -306,10 +325,10 @@ var MessageHandler = class {
|
|
|
306
325
|
const isDirect = session.isDirect || session.channelId?.includes("private");
|
|
307
326
|
const timestamp = Date.now();
|
|
308
327
|
if (session.elements) {
|
|
309
|
-
await this.processMediaElements(session.elements);
|
|
328
|
+
await this.processMediaElements(session.elements, true);
|
|
310
329
|
}
|
|
311
330
|
if (session.quote?.elements) {
|
|
312
|
-
await this.processMediaElements(session.quote.elements);
|
|
331
|
+
await this.processMediaElements(session.quote.elements, true);
|
|
313
332
|
}
|
|
314
333
|
let quoteInfo = void 0;
|
|
315
334
|
if (session.quote) {
|
|
@@ -350,11 +369,11 @@ var MessageHandler = class {
|
|
|
350
369
|
timestamp,
|
|
351
370
|
channelId: session.channelId,
|
|
352
371
|
selfId: session.selfId,
|
|
353
|
-
elements: this.utils.cleanBase64Content(elements),
|
|
372
|
+
elements: this.utils.cleanBase64Content(elements, false),
|
|
354
373
|
type: "user",
|
|
355
374
|
guildName,
|
|
356
375
|
platform: session.platform || "unknown",
|
|
357
|
-
quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo) : void 0,
|
|
376
|
+
quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo, false) : void 0,
|
|
358
377
|
isDirect: !!isDirect
|
|
359
378
|
};
|
|
360
379
|
await this.fileManager.addMessageToFile(messageInfo);
|
|
@@ -371,8 +390,8 @@ var MessageHandler = class {
|
|
|
371
390
|
timestamp,
|
|
372
391
|
guildName,
|
|
373
392
|
channelType: session.type || 0,
|
|
374
|
-
elements: this.utils.cleanBase64Content(elements),
|
|
375
|
-
quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo) : void 0,
|
|
393
|
+
elements: this.utils.cleanBase64Content(elements, false),
|
|
394
|
+
quote: quoteInfo ? this.utils.cleanBase64Content(quoteInfo, false) : void 0,
|
|
376
395
|
isDirect: session.isDirect,
|
|
377
396
|
bot: {
|
|
378
397
|
avatar: session.bot.user?.avatar,
|
|
@@ -393,9 +412,6 @@ var MessageHandler = class {
|
|
|
393
412
|
const guildName = await this.updateChannelInfoToFile(session);
|
|
394
413
|
const isDirect = session.isDirect || finalChannelId?.includes("private");
|
|
395
414
|
const timestamp = Date.now();
|
|
396
|
-
if (session.event?.message?.elements) {
|
|
397
|
-
await this.processMediaElements(session.event.message.elements);
|
|
398
|
-
}
|
|
399
415
|
let content = session.content || "";
|
|
400
416
|
if (!content && session.event?.message?.elements) {
|
|
401
417
|
content = this.utils.extractTextContent(session.event.message.elements).trim();
|
|
@@ -438,7 +454,7 @@ var MessageHandler = class {
|
|
|
438
454
|
timestamp,
|
|
439
455
|
channelId: finalChannelId,
|
|
440
456
|
selfId: session.selfId,
|
|
441
|
-
elements: this.utils.cleanBase64Content(session.event?.message?.elements),
|
|
457
|
+
elements: this.utils.cleanBase64Content(session.event?.message?.elements, true),
|
|
442
458
|
type: "bot",
|
|
443
459
|
guildName,
|
|
444
460
|
platform: session.platform || "unknown",
|
|
@@ -461,7 +477,7 @@ var MessageHandler = class {
|
|
|
461
477
|
timestamp,
|
|
462
478
|
guildName,
|
|
463
479
|
channelType: session.event?.channel?.type || session.type || 0,
|
|
464
|
-
elements: this.utils.cleanBase64Content(session.event?.message?.elements),
|
|
480
|
+
elements: this.utils.cleanBase64Content(session.event?.message?.elements, true),
|
|
465
481
|
quote: quoteInfo,
|
|
466
482
|
isDirect: !!isDirect,
|
|
467
483
|
sending: true,
|
|
@@ -642,6 +658,8 @@ var ApiHandlers = class {
|
|
|
642
658
|
__name(this, "ApiHandlers");
|
|
643
659
|
}
|
|
644
660
|
logger;
|
|
661
|
+
// 当前临时缓存的视频文件路径
|
|
662
|
+
currentTempVideo = null;
|
|
645
663
|
registerApiHandlers() {
|
|
646
664
|
this.ctx.console.addListener("clear-all-indexeddb-data", async () => {
|
|
647
665
|
try {
|
|
@@ -1067,6 +1085,38 @@ var ApiHandlers = class {
|
|
|
1067
1085
|
return { success: false, error: error?.message || String(error) };
|
|
1068
1086
|
}
|
|
1069
1087
|
});
|
|
1088
|
+
this.ctx.console.addListener("fetch-video-temp", async (data) => {
|
|
1089
|
+
try {
|
|
1090
|
+
this.logInfo("收到视频临时加载请求:", data.url);
|
|
1091
|
+
if (this.isFileUrl(data.url)) {
|
|
1092
|
+
const result = await this.handleLocalFileRequest(data.url);
|
|
1093
|
+
return result;
|
|
1094
|
+
}
|
|
1095
|
+
const response = await fetch(data.url, {
|
|
1096
|
+
headers: {
|
|
1097
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
1098
|
+
"Referer": ""
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
if (!response.ok) {
|
|
1102
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1103
|
+
}
|
|
1104
|
+
const buffer = await response.arrayBuffer();
|
|
1105
|
+
const base64 = Buffer.from(buffer).toString("base64");
|
|
1106
|
+
const contentType = response.headers.get("content-type") || "video/mp4";
|
|
1107
|
+
this.logInfo("视频下载成功:", { size: buffer.byteLength, contentType });
|
|
1108
|
+
return {
|
|
1109
|
+
success: true,
|
|
1110
|
+
base64,
|
|
1111
|
+
contentType,
|
|
1112
|
+
dataUrl: `data:${contentType};base64,${base64}`,
|
|
1113
|
+
size: buffer.byteLength
|
|
1114
|
+
};
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
this.logger.error("视频临时加载失败:", error);
|
|
1117
|
+
return { success: false, error: error?.message || String(error) };
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1070
1120
|
}
|
|
1071
1121
|
// 检查是否为文件 URL
|
|
1072
1122
|
isFileUrl(url) {
|
|
@@ -1197,6 +1247,27 @@ var usage = `
|
|
|
1197
1247
|
`;
|
|
1198
1248
|
async function apply(ctx, config) {
|
|
1199
1249
|
const logger = ctx.logger("chat-patch");
|
|
1250
|
+
const mediaDir = import_node_path3.default.join(ctx.baseDir, "data", "chat-patch", "persist-media", "media");
|
|
1251
|
+
if (require("node:fs").existsSync(mediaDir)) {
|
|
1252
|
+
try {
|
|
1253
|
+
const files = require("node:fs").readdirSync(mediaDir);
|
|
1254
|
+
let deletedCount = 0;
|
|
1255
|
+
for (const file of files) {
|
|
1256
|
+
const filePath = import_node_path3.default.join(mediaDir, file);
|
|
1257
|
+
try {
|
|
1258
|
+
require("node:fs").unlinkSync(filePath);
|
|
1259
|
+
deletedCount++;
|
|
1260
|
+
} catch (e) {
|
|
1261
|
+
logger.warn("删除媒体文件失败:", filePath, e);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (deletedCount > 0) {
|
|
1265
|
+
logger.info(`启动时清理了 ${deletedCount} 个旧版本的媒体缓存文件`);
|
|
1266
|
+
}
|
|
1267
|
+
} catch (e) {
|
|
1268
|
+
logger.warn("清理媒体文件夹失败:", e);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1200
1271
|
const fileManager = new FileManager(ctx, config);
|
|
1201
1272
|
const messageHandler = new MessageHandler(ctx, config, fileManager);
|
|
1202
1273
|
const apiHandlers = new ApiHandlers(ctx, config, fileManager, messageHandler);
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Config } from './config';
|
|
2
|
+
import { Context } from 'koishi';
|
|
3
|
+
export declare class Utils {
|
|
4
|
+
private config;
|
|
5
|
+
private ctx?;
|
|
6
|
+
constructor(config: Config, ctx?: Context);
|
|
7
|
+
isPlatformBlocked(platform: string): boolean;
|
|
8
|
+
extractTextContent(elements: any[]): string;
|
|
9
|
+
private isBase64;
|
|
10
|
+
persistBase64Image(base64Data: string): string;
|
|
11
|
+
private cleanupPersistImages;
|
|
12
|
+
cleanBase64Content(obj: any, isBotMessage?: boolean): any;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,49 +1,52 @@
|
|
|
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": "2.
|
|
5
|
-
"main": "lib/index.js",
|
|
6
|
-
"typings": "lib/index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"client",
|
|
9
|
-
"dist",
|
|
10
|
-
"lib",
|
|
11
|
-
"src"
|
|
12
|
-
],
|
|
13
|
-
"license": "MIT",
|
|
14
|
-
"homepage": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/",
|
|
15
|
-
"bugs": {
|
|
16
|
-
"url": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/issues"
|
|
17
|
-
},
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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": "2.2.0",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"client",
|
|
9
|
+
"dist",
|
|
10
|
+
"lib",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"homepage": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/issues"
|
|
17
|
+
},
|
|
18
|
+
"contributors": [
|
|
19
|
+
"shangxueink <1919892171@qq.com>"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"koishi",
|
|
23
|
+
"plugin",
|
|
24
|
+
"chat",
|
|
25
|
+
"聊天室",
|
|
26
|
+
"console"
|
|
27
|
+
],
|
|
28
|
+
"koishi": {
|
|
29
|
+
"service": {
|
|
30
|
+
"required": [
|
|
31
|
+
"console"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@koishijs/plugin-console": "^5.11.0",
|
|
37
|
+
"koishi": "^4.16.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@koishijs/client": "^5.11.0",
|
|
41
|
+
"@types/mime-types": "^3.0.1",
|
|
42
|
+
"autoprefixer": "^10.4.23",
|
|
43
|
+
"koishi": "^4.16.0",
|
|
44
|
+
"postcss": "^8.5.6",
|
|
45
|
+
"tailwindcss": "^3.4.17"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@element-plus/icons-vue": "^2.3.2",
|
|
49
|
+
"element-plus": "^2.13.0",
|
|
50
|
+
"mime-types": "^3.0.2"
|
|
51
|
+
}
|
|
52
|
+
}
|