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.
@@ -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
- cleanBase64Content(obj) {
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
- cleaned[key] = this.persistBase64Image(value);
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
- async processMediaElements(elements) {
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) el.attrs.src = await this.downloadAndCacheMedia(src, "image");
294
- } else if (["audio", "video"].includes(el.type)) {
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) el.attrs.src = await this.downloadAndCacheMedia(src, "media");
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.1.1",
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
- "keywords": [
19
- "koishi",
20
- "plugin",
21
- "chat",
22
- "聊天室",
23
- "console"
24
- ],
25
- "koishi": {
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
- "@types/mime-types": "^3.0.1",
39
- "autoprefixer": "^10.4.23",
40
- "koishi": "^4.16.0",
41
- "postcss": "^8.5.6",
42
- "tailwindcss": "^3.4.17"
43
- },
44
- "dependencies": {
45
- "@element-plus/icons-vue": "^2.3.2",
46
- "element-plus": "^2.13.0",
47
- "mime-types": "^3.0.2"
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
+ }