karin-plugin-kkk 2.20.2 → 2.21.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,6 +1,6 @@
1
1
  import { n as __esmMin, o as __toESM, r as __export } from "./rolldown-runtime-BMXAG3ag.js";
2
- import { E as zhCN, T as init_locale, _n as zod_default, a as init_lib, cn as require_protobufjs, dn as AxiosError$1, et as init_date_fns, fn as init_axios, gn as init_zod, hn as init_esm, i as require_qr_code_styling, it as differenceInSeconds, ln as Chalk, mn as Xhshow, n as require_dist, nt as formatDistanceToNow, o as Window, on as require_jsQR, pn as axios_default, r as require_lib, rt as format, sn as require_express, t as require_heic_convert, tt as fromUnixTime, un as init_source } from "./vendor-B3KhGHI_.js";
3
- import { n as init_client, r as reactServerRender } from "./template-CQ-u6h09.js";
2
+ import { $ as init_date_fns, T as zhCN, _n as init_dist, a as Window, an as require_png, cn as require_heic_decode, dn as Chalk, et as fromUnixTime, fn as init_source, gn as Xhshow, hn as axios_default, i as init_lib, ln as require_express, mn as init_axios, n as require_lib, nt as format, on as require_jsQR, pn as AxiosError$1, r as require_qr_code_styling, rt as differenceInSeconds, sn as require_jpeg_js, t as require_dist, tt as formatDistanceToNow, un as require_protobufjs, vn as init_zod, w as init_locale, yn as zod_default } from "./vendor-CYCcUtqE.js";
3
+ import { n as init_client, r as reactServerRender } from "./template-n3eb7E79.js";
4
4
  import { createRequire } from "node:module";
5
5
  import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, components, config, copyConfigSync, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, filesByExt, getBot, hooks, karin, karinPathHtml, karinPathTemp, logger, logs, mkdirSync, range, render, requireFileSync, restart, segment, updatePkg, watch } from "node-karin";
6
6
  import fs from "node:fs";
@@ -13,10 +13,9 @@ import { karinPathBase, karinPathTemp as karinPathTemp$1 } from "node-karin/root
13
13
  import sqlite3 from "node-karin/sqlite3";
14
14
  import YAML from "node-karin/yaml";
15
15
  import util from "node:util";
16
- import { Transformer } from "@napi-rs/image";
17
16
  import axios, { AxiosError } from "node-karin/axios";
18
- import { pipeline } from "node:stream/promises";
19
17
  import { Transform } from "node:stream";
18
+ import { pipeline } from "node:stream/promises";
20
19
  import express from "node-karin/express";
21
20
  import template from "node-karin/template";
22
21
  import _ from "node-karin/lodash";
@@ -905,7 +904,7 @@ var init_kuaishou$2 = __esmMin(() => {
905
904
  });
906
905
  var xiaohongshuSign;
907
906
  var init_sign$1 = __esmMin(() => {
908
- init_esm();
907
+ init_dist();
909
908
  xiaohongshuSign = class {
910
909
  static client = new Xhshow();
911
910
  static generateXSGet(path$1, a1Cookie, clientType = "xhs-pc-web", params = {}) {
@@ -2207,8 +2206,8 @@ var init_sign = __esmMin(() => {
2207
2206
  douyinSign = class {
2208
2207
  static Mstoken(length) {
2209
2208
  const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2210
- const randomBytes = crypto.randomBytes(length ?? 116);
2211
- return Array.from(randomBytes, (byte) => characters[byte % 62]).join("");
2209
+ const randomBytes$1 = crypto.randomBytes(length ?? 116);
2210
+ return Array.from(randomBytes$1, (byte) => characters[byte % 62]).join("");
2212
2211
  }
2213
2212
  static AB(url, userAgent) {
2214
2213
  return a_bogus_default(url, userAgent ?? defaultUserAgent);
@@ -5768,7 +5767,7 @@ var init_src = __esmMin(() => {
5768
5767
  init_server();
5769
5768
  init_types$1();
5770
5769
  init_api_spec();
5771
- getVersion = () => "6.0.0-beta.2";
5770
+ getVersion = () => "6.0.0-beta.3";
5772
5771
  VERSION = getVersion();
5773
5772
  Object.defineProperty(CreateAmagiApp, "version", {
5774
5773
  value: VERSION,
@@ -6343,9 +6342,12 @@ var init_build_metadata = __esmMin(() => {
6343
6342
  return `${date.getFullYear()}年${String(date.getMonth() + 1).padStart(2, "0")}月${String(date.getDate()).padStart(2, "0")}日 ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
6344
6343
  };
6345
6344
  });
6346
- var import_jsQR, QRCodeScanner;
6345
+ var import_heic_decode$1, import_jpeg_js$1, import_jsQR, import_png, QRCodeScanner;
6347
6346
  var init_QRCodeScanner = __esmMin(() => {
6347
+ import_heic_decode$1 = __toESM(require_heic_decode(), 1);
6348
+ import_jpeg_js$1 = __toESM(require_jpeg_js(), 1);
6348
6349
  import_jsQR = __toESM(require_jsQR(), 1);
6350
+ import_png = require_png();
6349
6351
  QRCodeScanner = class {
6350
6352
  static async scanFromUrl(imageUrl) {
6351
6353
  try {
@@ -6403,31 +6405,166 @@ var init_QRCodeScanner = __esmMin(() => {
6403
6405
  data: newData
6404
6406
  };
6405
6407
  }
6408
+ static binarize(imageData, threshold) {
6409
+ const { width, height, data: data$1 } = imageData;
6410
+ const newData = new Uint8ClampedArray(data$1.length);
6411
+ if (threshold === void 0) {
6412
+ const histogram = new Array(256).fill(0);
6413
+ for (let i = 0; i < data$1.length; i += 4) {
6414
+ const gray = Math.floor(.299 * data$1[i] + .587 * data$1[i + 1] + .114 * data$1[i + 2]);
6415
+ histogram[gray]++;
6416
+ }
6417
+ const total = width * height;
6418
+ let sum = 0;
6419
+ for (let i = 0; i < 256; i++) sum += i * histogram[i];
6420
+ let sumB = 0;
6421
+ let wB = 0;
6422
+ let wF = 0;
6423
+ let maxVariance = 0;
6424
+ threshold = 0;
6425
+ for (let t = 0; t < 256; t++) {
6426
+ wB += histogram[t];
6427
+ if (wB === 0) continue;
6428
+ wF = total - wB;
6429
+ if (wF === 0) break;
6430
+ sumB += t * histogram[t];
6431
+ const mB = sumB / wB;
6432
+ const mF = (sum - sumB) / wF;
6433
+ const variance = wB * wF * (mB - mF) * (mB - mF);
6434
+ if (variance > maxVariance) {
6435
+ maxVariance = variance;
6436
+ threshold = t;
6437
+ }
6438
+ }
6439
+ }
6440
+ for (let i = 0; i < data$1.length; i += 4) {
6441
+ const binary = Math.floor(.299 * data$1[i] + .587 * data$1[i + 1] + .114 * data$1[i + 2]) > threshold ? 255 : 0;
6442
+ newData[i] = binary;
6443
+ newData[i + 1] = binary;
6444
+ newData[i + 2] = binary;
6445
+ newData[i + 3] = data$1[i + 3];
6446
+ }
6447
+ return {
6448
+ width,
6449
+ height,
6450
+ data: newData
6451
+ };
6452
+ }
6453
+ static sharpen(imageData) {
6454
+ const { width, height, data: data$1 } = imageData;
6455
+ const newData = new Uint8ClampedArray(data$1.length);
6456
+ const kernel = [
6457
+ 0,
6458
+ -1,
6459
+ 0,
6460
+ -1,
6461
+ 5,
6462
+ -1,
6463
+ 0,
6464
+ -1,
6465
+ 0
6466
+ ];
6467
+ for (let y = 1; y < height - 1; y++) for (let x = 1; x < width - 1; x++) {
6468
+ let r = 0, g = 0, b = 0;
6469
+ for (let ky = -1; ky <= 1; ky++) for (let kx = -1; kx <= 1; kx++) {
6470
+ const idx$1 = ((y + ky) * width + (x + kx)) * 4;
6471
+ const k = kernel[(ky + 1) * 3 + (kx + 1)];
6472
+ r += data$1[idx$1] * k;
6473
+ g += data$1[idx$1 + 1] * k;
6474
+ b += data$1[idx$1 + 2] * k;
6475
+ }
6476
+ const idx = (y * width + x) * 4;
6477
+ newData[idx] = Math.max(0, Math.min(255, r));
6478
+ newData[idx + 1] = Math.max(0, Math.min(255, g));
6479
+ newData[idx + 2] = Math.max(0, Math.min(255, b));
6480
+ newData[idx + 3] = data$1[idx + 3];
6481
+ }
6482
+ for (let x = 0; x < width; x++) {
6483
+ const topIdx = x * 4;
6484
+ const bottomIdx = ((height - 1) * width + x) * 4;
6485
+ for (let c = 0; c < 4; c++) {
6486
+ newData[topIdx + c] = data$1[topIdx + c];
6487
+ newData[bottomIdx + c] = data$1[bottomIdx + c];
6488
+ }
6489
+ }
6490
+ for (let y = 0; y < height; y++) {
6491
+ const leftIdx = y * width * 4;
6492
+ const rightIdx = (y * width + width - 1) * 4;
6493
+ for (let c = 0; c < 4; c++) {
6494
+ newData[leftIdx + c] = data$1[leftIdx + c];
6495
+ newData[rightIdx + c] = data$1[rightIdx + c];
6496
+ }
6497
+ }
6498
+ return {
6499
+ width,
6500
+ height,
6501
+ data: newData
6502
+ };
6503
+ }
6406
6504
  static tryRecognizeInRegion(imageData, regionName) {
6407
6505
  for (const strategy of [
6408
6506
  {
6409
6507
  name: "默认",
6410
6508
  enhance: false,
6509
+ binarize: false,
6510
+ sharpen: false,
6511
+ options: void 0
6512
+ },
6513
+ {
6514
+ name: "二值化",
6515
+ enhance: false,
6516
+ binarize: true,
6517
+ sharpen: false,
6518
+ options: void 0
6519
+ },
6520
+ {
6521
+ name: "锐化",
6522
+ enhance: false,
6523
+ binarize: false,
6524
+ sharpen: true,
6411
6525
  options: void 0
6412
6526
  },
6413
6527
  {
6414
6528
  name: "增强对比度",
6415
6529
  enhance: true,
6530
+ binarize: false,
6531
+ sharpen: false,
6532
+ options: void 0
6533
+ },
6534
+ {
6535
+ name: "增强+二值化",
6536
+ enhance: true,
6537
+ binarize: true,
6538
+ sharpen: false,
6539
+ options: void 0
6540
+ },
6541
+ {
6542
+ name: "锐化+二值化",
6543
+ enhance: false,
6544
+ binarize: true,
6545
+ sharpen: true,
6416
6546
  options: void 0
6417
6547
  },
6418
6548
  {
6419
6549
  name: "attemptBoth",
6420
6550
  enhance: false,
6551
+ binarize: false,
6552
+ sharpen: false,
6421
6553
  options: { inversionAttempts: "attemptBoth" }
6422
6554
  },
6423
6555
  {
6424
- name: "增强+attemptBoth",
6425
- enhance: true,
6556
+ name: "二值化+attemptBoth",
6557
+ enhance: false,
6558
+ binarize: true,
6559
+ sharpen: false,
6426
6560
  options: { inversionAttempts: "attemptBoth" }
6427
6561
  }
6428
6562
  ]) try {
6429
6563
  logger.debug(` 尝试策略: ${strategy.name}`);
6430
- const processedData = strategy.enhance ? this.enhanceContrast(imageData) : imageData;
6564
+ let processedData = imageData;
6565
+ if (strategy.sharpen) processedData = this.sharpen(processedData);
6566
+ if (strategy.enhance) processedData = this.enhanceContrast(processedData);
6567
+ if (strategy.binarize) processedData = this.binarize(processedData);
6431
6568
  const code = (0, import_jsQR.default)(processedData.data, processedData.width, processedData.height, strategy.options);
6432
6569
  if (code && code.data) {
6433
6570
  logger.debug(`✓ 成功识别二维码 [区域: ${regionName}] [策略: ${strategy.name}]`);
@@ -6440,142 +6577,233 @@ var init_QRCodeScanner = __esmMin(() => {
6440
6577
  logger.debug(` 区域 ${regionName} 识别失败,尝试下一个区域`);
6441
6578
  return null;
6442
6579
  }
6443
- static parseImageBuffer(buffer) {
6580
+ static detectImageFormat(buffer) {
6581
+ if (buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71) return "png";
6582
+ if (buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) return "jpeg";
6583
+ if (buffer.length >= 12) {
6584
+ if (buffer.toString("ascii", 4, 8) === "ftyp") {
6585
+ const brand = buffer.toString("ascii", 8, 12);
6586
+ if (brand === "heic" || brand === "heix" || brand === "hevc" || brand === "hevx" || brand === "mif1" || brand === "msf1") return "heic";
6587
+ }
6588
+ }
6589
+ if (buffer.length >= 6 && buffer[0] === 71 && buffer[1] === 73 && buffer[2] === 70 && buffer[3] === 56) return "gif";
6590
+ if (buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77) return "bmp";
6591
+ if (buffer.length >= 12 && buffer[0] === 82 && buffer[1] === 73 && buffer[2] === 70 && buffer[3] === 70 && buffer[8] === 87 && buffer[9] === 69 && buffer[10] === 66 && buffer[11] === 80) return "webp";
6592
+ return null;
6593
+ }
6594
+ static parsePNG(buffer) {
6444
6595
  try {
6445
- const transformer = new Transformer(buffer);
6446
- const metadata = transformer.metadataSync();
6447
- const width = metadata.width;
6448
- const height = metadata.height;
6449
- const rawPixels = transformer.rawPixelsSync();
6450
- logger.debug(`图片解析成功: ${width}x${height}, 格式: ${metadata.format}`);
6451
- logger.debug(`原始像素数据长度: ${rawPixels.length}, 预期长度(RGBA): ${width * height * 4}`);
6452
- const expectedRGBA = width * height * 4;
6453
- const expectedRGB = width * height * 3;
6454
- if (rawPixels.length === expectedRGB) {
6455
- logger.debug("检测到 RGB 格式,转换为 RGBA");
6456
- const rgbaData = new Uint8ClampedArray(expectedRGBA);
6457
- for (let i = 0, j = 0; i < rawPixels.length; i += 3, j += 4) {
6458
- rgbaData[j] = rawPixels[i];
6459
- rgbaData[j + 1] = rawPixels[i + 1];
6460
- rgbaData[j + 2] = rawPixels[i + 2];
6461
- rgbaData[j + 3] = 255;
6462
- }
6463
- return {
6464
- width,
6465
- height,
6466
- data: rgbaData
6467
- };
6468
- } else if (rawPixels.length === expectedRGBA) {
6469
- logger.debug("检测到 RGBA 格式");
6470
- return {
6471
- width,
6472
- height,
6473
- data: Uint8ClampedArray.from(rawPixels)
6474
- };
6475
- } else {
6476
- logger.warn(`像素数据长度不匹配: ${rawPixels.length}, 预期 RGB: ${expectedRGB}, RGBA: ${expectedRGBA}`);
6596
+ const png = import_png.PNG.sync.read(buffer);
6597
+ logger.debug(`PNG 解析成功: ${png.width}x${png.height}`);
6598
+ return {
6599
+ width: png.width,
6600
+ height: png.height,
6601
+ data: Uint8ClampedArray.from(png.data)
6602
+ };
6603
+ } catch (err) {
6604
+ logger.warn("PNG 解析失败:", err);
6605
+ return null;
6606
+ }
6607
+ }
6608
+ static parseJPEG(buffer) {
6609
+ try {
6610
+ const decoded = import_jpeg_js$1.default.decode(buffer, { useTArray: true });
6611
+ logger.debug(`JPEG 解析成功: ${decoded.width}x${decoded.height}`);
6612
+ return {
6613
+ width: decoded.width,
6614
+ height: decoded.height,
6615
+ data: Uint8ClampedArray.from(decoded.data)
6616
+ };
6617
+ } catch (err) {
6618
+ logger.warn("JPEG 解析失败:", err);
6619
+ return null;
6620
+ }
6621
+ }
6622
+ static async parseHEIC(buffer) {
6623
+ try {
6624
+ const decoded = await (0, import_heic_decode$1.default)({ buffer });
6625
+ logger.debug(`HEIC 解析成功: ${decoded.width}x${decoded.height}`);
6626
+ return {
6627
+ width: decoded.width,
6628
+ height: decoded.height,
6629
+ data: Uint8ClampedArray.from(decoded.data)
6630
+ };
6631
+ } catch (err) {
6632
+ logger.warn("HEIC 解析失败:", err);
6633
+ return null;
6634
+ }
6635
+ }
6636
+ static async parseImageBuffer(buffer) {
6637
+ try {
6638
+ const format$1 = this.detectImageFormat(buffer);
6639
+ logger.debug(`检测到图片格式: ${format$1 || "未知"}`);
6640
+ if (!format$1) {
6641
+ logger.warn("无法识别图片格式");
6477
6642
  return null;
6478
6643
  }
6644
+ switch (format$1) {
6645
+ case "png": return this.parsePNG(buffer);
6646
+ case "jpeg": return this.parseJPEG(buffer);
6647
+ case "heic": return await this.parseHEIC(buffer);
6648
+ default:
6649
+ logger.warn(`不支持的图片格式: ${format$1}`);
6650
+ return null;
6651
+ }
6479
6652
  } catch (err) {
6480
6653
  logger.warn("图片解析失败:", err);
6481
6654
  return null;
6482
6655
  }
6483
6656
  }
6484
- static scanFromBuffer(buffer) {
6657
+ static async scanFromBuffer(buffer) {
6485
6658
  try {
6486
- const imageData = this.parseImageBuffer(buffer);
6659
+ const imageData = await this.parseImageBuffer(buffer);
6487
6660
  if (!imageData) return null;
6488
6661
  const { width, height } = imageData;
6489
6662
  const dataSizeMB = (width * height * 4 / 1024 / 1024).toFixed(2);
6490
6663
  logger.debug(`图片数据: ${width}x${height}, 内存占用: ${dataSizeMB}MB`);
6491
- if (width <= 2048 && height <= 2048) {
6492
- logger.debug("图片尺寸适中,使用全图识别策略");
6664
+ if (width <= 1024 && height <= 1024) {
6665
+ logger.debug("图片尺寸较小,使用全图识别策略");
6493
6666
  const result = this.tryRecognizeInRegion(imageData, "全图");
6494
6667
  if (result) return result;
6495
6668
  }
6496
- logger.debug(`图片尺寸较大 (${width}x${height}),使用分块扫描策略`);
6669
+ logger.debug(`使用分块扫描策略 (${width}x${height})`);
6497
6670
  const scanRegions = [];
6498
- const blockSize = 1024;
6499
- logger.debug("添加四角扫描区域");
6671
+ const smallBlock = Math.min(400, Math.floor(Math.min(width, height) * .3));
6672
+ const mediumBlock = Math.min(600, Math.floor(Math.min(width, height) * .5));
6673
+ const largeBlock = Math.min(800, Math.floor(Math.max(width, height) * .6));
6674
+ logger.debug("添加四角小块扫描区域(优先)");
6675
+ scanRegions.push({
6676
+ name: "左上角-小",
6677
+ x: 0,
6678
+ y: 0,
6679
+ w: Math.min(smallBlock, width),
6680
+ h: Math.min(smallBlock, height)
6681
+ });
6682
+ scanRegions.push({
6683
+ name: "右上角-小",
6684
+ x: Math.max(0, width - smallBlock),
6685
+ y: 0,
6686
+ w: Math.min(smallBlock, width),
6687
+ h: Math.min(smallBlock, height)
6688
+ });
6689
+ scanRegions.push({
6690
+ name: "左下角-小",
6691
+ x: 0,
6692
+ y: Math.max(0, height - smallBlock),
6693
+ w: Math.min(smallBlock, width),
6694
+ h: Math.min(smallBlock, height)
6695
+ });
6500
6696
  scanRegions.push({
6501
- name: "左上角",
6697
+ name: "右下角-小",
6698
+ x: Math.max(0, width - smallBlock),
6699
+ y: Math.max(0, height - smallBlock),
6700
+ w: Math.min(smallBlock, width),
6701
+ h: Math.min(smallBlock, height)
6702
+ });
6703
+ logger.debug("添加四角中块扫描区域");
6704
+ scanRegions.push({
6705
+ name: "左上角-中",
6502
6706
  x: 0,
6503
6707
  y: 0,
6504
- w: Math.min(blockSize, width),
6505
- h: Math.min(blockSize, height)
6708
+ w: Math.min(mediumBlock, width),
6709
+ h: Math.min(mediumBlock, height)
6506
6710
  });
6507
- if (width > blockSize) scanRegions.push({
6508
- name: "右上角",
6509
- x: width - Math.min(blockSize, width),
6711
+ scanRegions.push({
6712
+ name: "右上角-中",
6713
+ x: Math.max(0, width - mediumBlock),
6510
6714
  y: 0,
6511
- w: Math.min(blockSize, width),
6512
- h: Math.min(blockSize, height)
6715
+ w: Math.min(mediumBlock, width),
6716
+ h: Math.min(mediumBlock, height)
6513
6717
  });
6514
- if (height > blockSize) scanRegions.push({
6515
- name: "左下角",
6718
+ scanRegions.push({
6719
+ name: "左下角-中",
6516
6720
  x: 0,
6517
- y: height - Math.min(blockSize, height),
6518
- w: Math.min(blockSize, width),
6519
- h: Math.min(blockSize, height)
6721
+ y: Math.max(0, height - mediumBlock),
6722
+ w: Math.min(mediumBlock, width),
6723
+ h: Math.min(mediumBlock, height)
6520
6724
  });
6521
- if (width > blockSize && height > blockSize) scanRegions.push({
6522
- name: "右下角",
6523
- x: width - Math.min(blockSize, width),
6524
- y: height - Math.min(blockSize, height),
6525
- w: Math.min(blockSize, width),
6526
- h: Math.min(blockSize, height)
6725
+ scanRegions.push({
6726
+ name: "右下角-中",
6727
+ x: Math.max(0, width - mediumBlock),
6728
+ y: Math.max(0, height - mediumBlock),
6729
+ w: Math.min(mediumBlock, width),
6730
+ h: Math.min(mediumBlock, height)
6527
6731
  });
6528
- if (width > blockSize * 2) {
6732
+ logger.debug("添加四角大块扫描区域");
6733
+ const blockW = Math.min(largeBlock, width);
6734
+ const blockH = Math.min(largeBlock, height);
6735
+ scanRegions.push({
6736
+ name: "左上角-大",
6737
+ x: 0,
6738
+ y: 0,
6739
+ w: blockW,
6740
+ h: blockH
6741
+ });
6742
+ scanRegions.push({
6743
+ name: "右上角-大",
6744
+ x: Math.max(0, width - blockW),
6745
+ y: 0,
6746
+ w: blockW,
6747
+ h: blockH
6748
+ });
6749
+ scanRegions.push({
6750
+ name: "左下角-大",
6751
+ x: 0,
6752
+ y: Math.max(0, height - blockH),
6753
+ w: blockW,
6754
+ h: blockH
6755
+ });
6756
+ scanRegions.push({
6757
+ name: "右下角-大",
6758
+ x: Math.max(0, width - blockW),
6759
+ y: Math.max(0, height - blockH),
6760
+ w: blockW,
6761
+ h: blockH
6762
+ });
6763
+ if (width > mediumBlock * 1.5) {
6529
6764
  logger.debug("添加顶部/底部中间扫描区域");
6530
6765
  scanRegions.push({
6531
- name: "顶部中",
6532
- x: Math.floor((width - blockSize) / 2),
6766
+ name: "顶部中-小",
6767
+ x: Math.floor((width - smallBlock) / 2),
6533
6768
  y: 0,
6534
- w: blockSize,
6535
- h: Math.min(blockSize, height)
6769
+ w: Math.min(smallBlock, width),
6770
+ h: Math.min(smallBlock, height)
6536
6771
  });
6537
- if (height > blockSize) scanRegions.push({
6538
- name: "底部中",
6539
- x: Math.floor((width - blockSize) / 2),
6540
- y: height - blockSize,
6541
- w: blockSize,
6542
- h: blockSize
6772
+ if (height > mediumBlock * 1.5) scanRegions.push({
6773
+ name: "底部中-小",
6774
+ x: Math.floor((width - smallBlock) / 2),
6775
+ y: Math.max(0, height - smallBlock),
6776
+ w: Math.min(smallBlock, width),
6777
+ h: Math.min(smallBlock, height)
6543
6778
  });
6544
6779
  }
6545
- if (height > blockSize * 2) {
6780
+ if (height > mediumBlock * 1.5) {
6546
6781
  logger.debug("添加左右中间扫描区域");
6547
- const middleY = Math.floor((height - blockSize) / 2);
6782
+ const middleY = Math.floor((height - smallBlock) / 2);
6548
6783
  scanRegions.push({
6549
- name: "左中",
6784
+ name: "左中-小",
6550
6785
  x: 0,
6551
6786
  y: middleY,
6552
- w: Math.min(blockSize, width),
6553
- h: blockSize
6787
+ w: Math.min(smallBlock, width),
6788
+ h: Math.min(smallBlock, height)
6554
6789
  });
6555
- if (width > blockSize) scanRegions.push({
6556
- name: "右中",
6557
- x: width - blockSize,
6790
+ if (width > mediumBlock * 1.5) scanRegions.push({
6791
+ name: "右中-小",
6792
+ x: Math.max(0, width - smallBlock),
6558
6793
  y: middleY,
6559
- w: blockSize,
6560
- h: blockSize
6794
+ w: Math.min(smallBlock, width),
6795
+ h: Math.min(smallBlock, height)
6561
6796
  });
6562
6797
  }
6563
- logger.debug("添加滑动窗口扫描区域");
6564
- const step = Math.floor(blockSize / 2);
6565
- let slidingWindowCount = 0;
6566
- for (let y = 0; y < height - blockSize; y += step) {
6798
+ if (width > mediumBlock && height > mediumBlock) {
6799
+ logger.debug("添加中心区域");
6567
6800
  scanRegions.push({
6568
- name: `滑动窗口-${Math.floor(y / step)}`,
6569
- x: 0,
6570
- y,
6571
- w: Math.min(blockSize, width),
6572
- h: blockSize
6801
+ name: "中心-小",
6802
+ x: Math.floor((width - smallBlock) / 2),
6803
+ y: Math.floor((height - smallBlock) / 2),
6804
+ w: Math.min(smallBlock, width),
6805
+ h: Math.min(smallBlock, height)
6573
6806
  });
6574
- slidingWindowCount++;
6575
- if (scanRegions.length > 30) {
6576
- logger.debug(`滑动窗口数量达到上限,停止添加 (已添加 ${slidingWindowCount} 个)`);
6577
- break;
6578
- }
6579
6807
  }
6580
6808
  logger.debug(`共生成 ${scanRegions.length} 个扫描区域,开始逐个扫描`);
6581
6809
  for (let i = 0; i < scanRegions.length; i++) {
@@ -6766,8 +6994,122 @@ var init_Common = __esmMin(async () => {
6766
6994
  };
6767
6995
  Common = new Tools();
6768
6996
  });
6997
+ function getEmojiId(e, type) {
6998
+ return (PLATFORM_EMOJI_IDS[e.bot?.adapter?.platform || "other"] || PLATFORM_EMOJI_IDS.other)[type];
6999
+ }
7000
+ async function setEmojiReaction(e, emojiId, isSet = true) {
7001
+ if (!Config.app.EmojiReply) return false;
7002
+ if (e.isPrivate) return false;
7003
+ try {
7004
+ await e.bot.setMsgReaction(e.contact, e.messageId, emojiId, isSet);
7005
+ return true;
7006
+ } catch (err) {
7007
+ logger.debug("[EmojiReaction] 设置表情回复失败(已忽略):", err);
7008
+ return false;
7009
+ }
7010
+ }
7011
+ var PLATFORM_EMOJI_IDS, EmojiReactionManager;
6769
7012
  var init_EmojiReaction = __esmMin(() => {
6770
7013
  init_Config();
7014
+ PLATFORM_EMOJI_IDS = {
7015
+ qq: {
7016
+ EYES: 128064,
7017
+ PROCESSING: 366,
7018
+ SUCCESS: 389,
7019
+ ERROR: 379
7020
+ },
7021
+ wechat: {
7022
+ EYES: "WECHAT_EYES_PLACEHOLDER",
7023
+ PROCESSING: "WECHAT_PROCESSING_PLACEHOLDER",
7024
+ SUCCESS: "WECHAT_SUCCESS_PLACEHOLDER",
7025
+ ERROR: "WECHAT_ERROR_PLACEHOLDER"
7026
+ },
7027
+ telegram: {
7028
+ EYES: "TELEGRAM_EYES_PLACEHOLDER",
7029
+ PROCESSING: "TELEGRAM_PROCESSING_PLACEHOLDER",
7030
+ SUCCESS: "TELEGRAM_SUCCESS_PLACEHOLDER",
7031
+ ERROR: "TELEGRAM_ERROR_PLACEHOLDER"
7032
+ },
7033
+ discord: {
7034
+ EYES: "👀",
7035
+ PROCESSING: "⏳",
7036
+ SUCCESS: "✅",
7037
+ ERROR: "❌"
7038
+ },
7039
+ koko: {
7040
+ EYES: "KOKO_EYES_PLACEHOLDER",
7041
+ PROCESSING: "KOKO_PROCESSING_PLACEHOLDER",
7042
+ SUCCESS: "KOKO_SUCCESS_PLACEHOLDER",
7043
+ ERROR: "KOKO_ERROR_PLACEHOLDER"
7044
+ },
7045
+ other: {
7046
+ EYES: "OTHER_EYES_PLACEHOLDER",
7047
+ PROCESSING: "OTHER_PROCESSING_PLACEHOLDER",
7048
+ SUCCESS: "OTHER_SUCCESS_PLACEHOLDER",
7049
+ ERROR: "OTHER_ERROR_PLACEHOLDER"
7050
+ }
7051
+ };
7052
+ PLATFORM_EMOJI_IDS.qq;
7053
+ EmojiReactionManager = class {
7054
+ e;
7055
+ emojiIds = /* @__PURE__ */ new Set();
7056
+ constructor(e) {
7057
+ this.e = e;
7058
+ }
7059
+ getPlatformEmojiId(type) {
7060
+ return getEmojiId(this.e, type);
7061
+ }
7062
+ normalizeEmojiId(emojiId) {
7063
+ return typeof emojiId === "string" && [
7064
+ "EYES",
7065
+ "PROCESSING",
7066
+ "SUCCESS",
7067
+ "ERROR"
7068
+ ].includes(emojiId) ? this.getPlatformEmojiId(emojiId) : emojiId;
7069
+ }
7070
+ async add(emojiId) {
7071
+ const actualEmojiId = this.normalizeEmojiId(emojiId);
7072
+ const success = await setEmojiReaction(this.e, actualEmojiId, true);
7073
+ if (success) this.emojiIds.add(actualEmojiId);
7074
+ return success;
7075
+ }
7076
+ async remove(emojiId) {
7077
+ const actualEmojiId = this.normalizeEmojiId(emojiId);
7078
+ const success = await setEmojiReaction(this.e, actualEmojiId, false);
7079
+ if (success) this.emojiIds.delete(actualEmojiId);
7080
+ return success;
7081
+ }
7082
+ async replace(oldEmojiId, newEmojiId, delayMs = 2e3) {
7083
+ const addSuccess = await this.add(newEmojiId);
7084
+ await new Promise((resolve$1) => setTimeout(resolve$1, delayMs));
7085
+ await this.remove(oldEmojiId);
7086
+ return addSuccess;
7087
+ }
7088
+ async clearAll() {
7089
+ let count = 0;
7090
+ for (const emojiId of this.emojiIds) if (await setEmojiReaction(this.e, emojiId, false)) count++;
7091
+ this.emojiIds.clear();
7092
+ return count;
7093
+ }
7094
+ async keepOnly(keepEmojiIds) {
7095
+ const keepSet = new Set(keepEmojiIds);
7096
+ let removedCount = 0;
7097
+ for (const emojiId of this.emojiIds) if (!keepSet.has(emojiId)) {
7098
+ if (await this.remove(emojiId)) removedCount++;
7099
+ }
7100
+ for (const emojiId of keepEmojiIds) if (!this.emojiIds.has(emojiId)) await this.add(emojiId);
7101
+ return removedCount;
7102
+ }
7103
+ getCurrentEmojiIds() {
7104
+ return Array.from(this.emojiIds);
7105
+ }
7106
+ has(emojiId) {
7107
+ return this.emojiIds.has(emojiId);
7108
+ }
7109
+ count() {
7110
+ return this.emojiIds.size;
7111
+ }
7112
+ };
6771
7113
  });
6772
7114
  async function detectEncoder$1(codec) {
6773
7115
  if (cachedEncoders$1[codec]) return cachedEncoders$1[codec];
@@ -7468,6 +7810,12 @@ var init_danmaku = __esmMin(async () => {
7468
7810
  };
7469
7811
  MAX_OUTPUT_WIDTH = 2160;
7470
7812
  });
7813
+ async function fixM4sFile(inputPath, outputPath) {
7814
+ const result = await ffmpeg(`-y -i "${inputPath}" -c copy -movflags +faststart "${outputPath}"`);
7815
+ if (result.status) logger.debug(`m4s 文件修复成功: ${outputPath}`);
7816
+ else logger.error("m4s 文件修复失败", result);
7817
+ return result.status;
7818
+ }
7471
7819
  async function getMediaDuration(path$1) {
7472
7820
  const { stdout } = await ffprobe(`-v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${path$1}"`);
7473
7821
  return parseFloat(parseFloat(stdout.trim()).toFixed(2));
@@ -7592,7 +7940,7 @@ var init_constants = __esmMin(() => {
7592
7940
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0"
7593
7941
  };
7594
7942
  });
7595
- var getErrorDescription, sanitizeHeaders, isRecoverableNetworkError, isThrottlingError, calculateBackoffDelay, formatBytes;
7943
+ var getErrorDescription, sanitizeHeaders, isRecoverableNetworkError, isThrottlingError, calculateBackoffDelay, formatBytes, sanitizeFilename;
7596
7944
  var init_helpers = __esmMin(() => {
7597
7945
  init_constants();
7598
7946
  getErrorDescription = (error) => {
@@ -7656,6 +8004,7 @@ var init_helpers = __esmMin(() => {
7656
8004
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
7657
8005
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
7658
8006
  };
8007
+ sanitizeFilename = (filename) => filename.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").replace(/^\.+/, "").replace(/\.+$/, "").replace(/\s+/g, "_").substring(0, 200);
7659
8008
  });
7660
8009
  var ThrottleStream;
7661
8010
  var init_ThrottleStream = __esmMin(() => {
@@ -7775,6 +8124,37 @@ var init_Downloader = __esmMin(() => {
7775
8124
  });
7776
8125
  const response = await this.axiosInstance(requestConfig);
7777
8126
  clearTimeout(timeoutId);
8127
+ if (response.status === 416) {
8128
+ logger.warn("服务器返回 416,文件可能已下载完成,验证文件大小...");
8129
+ if (fs.existsSync(this.filepath)) {
8130
+ const stats = fs.statSync(this.filepath);
8131
+ logger.debug(`当前文件大小: ${formatBytes(stats.size)}`);
8132
+ if (stats.size > 1024) {
8133
+ logger.debug("文件大小合理,认为下载已完成");
8134
+ return {
8135
+ filepath: this.filepath,
8136
+ totalBytes: stats.size
8137
+ };
8138
+ } else {
8139
+ logger.warn("文件太小,删除并重新下载");
8140
+ fs.unlinkSync(this.filepath);
8141
+ return this.download(progressCallback, retryCount + 1);
8142
+ }
8143
+ }
8144
+ }
8145
+ if (response.status !== 200 && response.status !== 206) {
8146
+ logger.error(`下载失败: HTTP ${response.status}, URL: ${this.url}`);
8147
+ logger.error(`响应头: ${JSON.stringify(response.headers)}`);
8148
+ if (response.headers["content-length"] && parseInt(response.headers["content-length"]) < 10240) {
8149
+ let errorBody = "";
8150
+ response.data.on("data", (chunk) => {
8151
+ errorBody += chunk.toString();
8152
+ });
8153
+ await new Promise((resolve$1) => setTimeout(resolve$1, 100));
8154
+ logger.error(`响应内容: ${errorBody}`);
8155
+ }
8156
+ throw new Error(`HTTP ${response.status}: ${this.url}`);
8157
+ }
7778
8158
  const supportsRange = response.status === 206;
7779
8159
  if (startByte > 0 && !supportsRange) {
7780
8160
  logger.warn("服务器不支持断点续传,将重新下载整个文件");
@@ -7802,18 +8182,36 @@ var init_Downloader = __esmMin(() => {
7802
8182
  };
7803
8183
  const interval = totalBytes > 0 && totalBytes < 10 * 1024 * 1024 ? 1e3 : 500;
7804
8184
  intervalId = setInterval(printProgress, interval);
7805
- const onData = (chunk) => {
7806
- downloadedBytes += Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
7807
- };
7808
- response.data.on("data", onData);
8185
+ const counterStream = new Transform({ transform(chunk, encoding, callback) {
8186
+ downloadedBytes += chunk.length;
8187
+ callback(null, chunk);
8188
+ } });
7809
8189
  if (this.throttleConfig.enabled) {
7810
8190
  throttleStream = new ThrottleStream(this.currentSpeed);
7811
8191
  logger.debug(`启用限速下载: ${formatBytes(this.currentSpeed)}/s`);
7812
- await pipeline(response.data, throttleStream, writer);
7813
- } else await pipeline(response.data, writer);
8192
+ await pipeline(response.data, throttleStream, counterStream, writer);
8193
+ } else await pipeline(response.data, counterStream, writer);
7814
8194
  if (intervalId) clearInterval(intervalId);
7815
- response.data.off("data", onData);
7816
- writer.end();
8195
+ logger.debug("文件下载并写入完成");
8196
+ if (fs.existsSync(this.filepath)) {
8197
+ const actualSize = fs.statSync(this.filepath).size;
8198
+ const expectedSize = totalBytes > 0 ? totalBytes : downloadedBytes;
8199
+ if (actualSize < 1024 && expectedSize < 1024) {
8200
+ logger.error(`下载的文件异常小 (${formatBytes(actualSize)}),可能是错误响应`);
8201
+ try {
8202
+ const content = fs.readFileSync(this.filepath, "utf-8");
8203
+ logger.error(`文件内容: ${content}`);
8204
+ } catch {
8205
+ logger.error("无法读取文件内容(可能是二进制文件)");
8206
+ }
8207
+ throw new Error(`下载的文件异常小: ${formatBytes(actualSize)},可能是错误响应或链接失效`);
8208
+ }
8209
+ if (actualSize < expectedSize) {
8210
+ logger.warn(`文件大小不匹配: 实际 ${formatBytes(actualSize)}, 预期 ${formatBytes(expectedSize)}`);
8211
+ logger.warn(`差异: ${formatBytes(expectedSize - actualSize)} (${((expectedSize - actualSize) / expectedSize * 100).toFixed(2)}%)`);
8212
+ if ((expectedSize - actualSize) / expectedSize > .01) throw new Error(`文件下载不完整: 实际 ${formatBytes(actualSize)}, 预期 ${formatBytes(expectedSize)}`);
8213
+ } else logger.debug(`文件大小验证通过: ${formatBytes(actualSize)}`);
8214
+ }
7817
8215
  this.consecutiveResets = 0;
7818
8216
  return {
7819
8217
  filepath: this.filepath,
@@ -7874,6 +8272,77 @@ var init_Downloader = __esmMin(() => {
7874
8272
  }
7875
8273
  };
7876
8274
  });
8275
+ var ImageDownloader;
8276
+ var init_ImageDownloader = __esmMin(() => {
8277
+ init_Config();
8278
+ init_helpers();
8279
+ ImageDownloader = class {
8280
+ axiosInstance;
8281
+ tempDir;
8282
+ constructor(axiosInstance, tempDir) {
8283
+ this.axiosInstance = axiosInstance;
8284
+ this.tempDir = tempDir;
8285
+ if (!fs.existsSync(this.tempDir)) fs.mkdirSync(this.tempDir, { recursive: true });
8286
+ }
8287
+ async processImage(imageUrl, title, index) {
8288
+ if (!Config.app.downloadImageLocally) return imageUrl;
8289
+ try {
8290
+ const result = await this.downloadImage(imageUrl, title, index);
8291
+ if (result.shouldDelete) this.scheduleDelete(result.filePath);
8292
+ return result.filePath;
8293
+ } catch (error) {
8294
+ logger.error(`图片下载失败,回退到原始 URL: ${imageUrl}`, error);
8295
+ return imageUrl;
8296
+ }
8297
+ }
8298
+ async downloadImage(imageUrl, title, index) {
8299
+ const filename = this.generateFilename(imageUrl, title, index);
8300
+ const filePath = path.join(this.tempDir, filename);
8301
+ const response = await this.axiosInstance.get(imageUrl, {
8302
+ responseType: "arraybuffer",
8303
+ timeout: 3e4
8304
+ });
8305
+ fs.writeFileSync(filePath, response.data);
8306
+ logger.debug(`图片已下载: ${filePath}`);
8307
+ return {
8308
+ filePath: `file://${filePath}`,
8309
+ shouldDelete: true
8310
+ };
8311
+ }
8312
+ generateFilename(imageUrl, title, index) {
8313
+ const ext = this.getExtension(imageUrl);
8314
+ if (Config.app.removeCache) return `tmp_${Date.now()}${index !== void 0 ? `_${index}` : ""}${ext}`;
8315
+ else return `${title ? sanitizeFilename(title) : `image_${Date.now()}`}${index !== void 0 ? `_${index}` : ""}${ext}`;
8316
+ }
8317
+ getExtension(url) {
8318
+ try {
8319
+ const pathname = new URL(url).pathname;
8320
+ const ext = path.extname(pathname);
8321
+ if (ext && /^\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(ext)) return ext;
8322
+ } catch {}
8323
+ return ".jpg";
8324
+ }
8325
+ scheduleDelete(filePath) {
8326
+ const actualPath = filePath.replace(/^file:\/\//, "");
8327
+ setTimeout(() => {
8328
+ this.deleteFile(actualPath);
8329
+ }, 600 * 1e3);
8330
+ }
8331
+ deleteFile(filePath) {
8332
+ try {
8333
+ if (fs.existsSync(filePath)) {
8334
+ fs.unlinkSync(filePath);
8335
+ logger.debug(`临时图片已删除: ${filePath}`);
8336
+ }
8337
+ } catch (error) {
8338
+ logger.error(`删除临时图片失败: ${filePath}`, error);
8339
+ }
8340
+ }
8341
+ async processImages(imageUrls, title) {
8342
+ return await Promise.all(imageUrls.map((url, index) => this.processImage(url, title, index)));
8343
+ }
8344
+ };
8345
+ });
7877
8346
  var Network;
7878
8347
  var init_Network$1 = __esmMin(() => {
7879
8348
  init_constants();
@@ -8081,11 +8550,28 @@ var init_Network$1 = __esmMin(() => {
8081
8550
  var init_Network = __esmMin(() => {
8082
8551
  init_Downloader();
8083
8552
  init_helpers();
8553
+ init_ImageDownloader();
8084
8554
  init_Network$1();
8085
8555
  init_ThrottleStream();
8086
8556
  init_types();
8087
8557
  init_constants();
8088
8558
  });
8559
+ function getImageDownloader() {
8560
+ if (!imageDownloader) imageDownloader = new ImageDownloader(axios.create({
8561
+ timeout: 3e4,
8562
+ maxRedirects: 5
8563
+ }), Common.tempDri.images);
8564
+ return imageDownloader;
8565
+ }
8566
+ async function processImageUrl(imageUrl, title, index) {
8567
+ return await getImageDownloader().processImage(imageUrl, title, index);
8568
+ }
8569
+ var imageDownloader;
8570
+ var init_ImageHelper = __esmMin(async () => {
8571
+ await init_Common();
8572
+ await init_Network();
8573
+ imageDownloader = null;
8574
+ });
8089
8575
  var init_Networks = __esmMin(() => {
8090
8576
  init_Network();
8091
8577
  });
@@ -8276,6 +8762,7 @@ var init_utils$1 = __esmMin(async () => {
8276
8762
  init_Common();
8277
8763
  init_EmojiReaction();
8278
8764
  init_FFmpeg();
8765
+ init_ImageHelper();
8279
8766
  init_Networks();
8280
8767
  init_QRCodeScanner();
8281
8768
  init_Render();
@@ -10738,6 +11225,12 @@ var init_app_schema = __esmMin(() => {
10738
11225
  label: "缓存删除",
10739
11226
  description: "下载的视频缓存自动删除,非必要不修改!"
10740
11227
  },
11228
+ {
11229
+ key: "downloadImageLocally",
11230
+ type: "switch",
11231
+ label: "本地下载图片",
11232
+ description: "发送图片时由插件本地下载后使用 file 协议传递,而非直接传递 HTTP 链接给上游下载"
11233
+ },
10741
11234
  {
10742
11235
  type: "divider",
10743
11236
  title: "解析优先级设置"
@@ -10868,23 +11361,10 @@ var init_app_schema = __esmMin(() => {
10868
11361
  description: "在解析任务开始时添加表情回应,若适配器不支持需要关闭"
10869
11362
  },
10870
11363
  {
10871
- key: "EmojiReplyID",
10872
- type: "input",
10873
- inputType: "number",
10874
- label: "表情 ID",
10875
- disabled: $not("EmojiReply"),
10876
- description: "详情查看:https://koishi.js.org/QFace/#/qqnt 中调试信息的 emojiId 字段,Emoji则是 qcid",
10877
- rules: [{
10878
- min: 0,
10879
- max: 1145141919810
10880
- }]
10881
- },
10882
- {
10883
- key: "EmojiReplyIgnoreError",
11364
+ key: "parseTip",
10884
11365
  type: "switch",
10885
- label: "忽略表情回应失败",
10886
- disabled: $not("EmojiReply"),
10887
- description: "开启后表情回应失败时不会抛出错误,程序会继续执行"
11366
+ label: "解析提示",
11367
+ description: "发送提示信息:\"检测到xxx链接,开始解析\""
10888
11368
  },
10889
11369
  {
10890
11370
  key: "errorLogSendTo",
@@ -10894,15 +11374,15 @@ var init_app_schema = __esmMin(() => {
10894
11374
  orientation: "horizontal",
10895
11375
  options: [
10896
11376
  {
10897
- label: "除'console'外的第一个主人",
11377
+ label: "第一个主人",
10898
11378
  value: "master"
10899
11379
  },
10900
11380
  {
10901
- label: "所有主人(排除console)",
11381
+ label: "所有主人",
10902
11382
  value: "allMasters"
10903
11383
  },
10904
11384
  {
10905
- label: "触发者(不支持私聊)",
11385
+ label: "触发者的群聊",
10906
11386
  value: "trigger"
10907
11387
  }
10908
11388
  ]
@@ -11003,6 +11483,13 @@ var init_bilibili_schema = __esmMin(() => {
11003
11483
  description: "评论图是否显示真实评论数量,关闭则显示解析到的评论数量",
11004
11484
  disabled: $or($not("switch"), $not($includes("sendContent", "comment")))
11005
11485
  },
11486
+ {
11487
+ key: "commentImageCollection",
11488
+ type: "switch",
11489
+ label: "是否收集评论区的图片",
11490
+ description: "开启后将收集评论区的图片,以合并转发的形式返回",
11491
+ disabled: $or($not("switch"), $not($includes("sendContent", "comment")))
11492
+ },
11006
11493
  {
11007
11494
  type: "divider",
11008
11495
  title: "渲染与画质设置"
@@ -11450,7 +11937,7 @@ var init_cookies_schema = __esmMin(() => {
11450
11937
  cookiesConfigSchema = {
11451
11938
  key: "cookies",
11452
11939
  title: "Cookies 相关",
11453
- subtitle: "建议配置,否则大部分功能无法使用,此部分修改后需重启方可生效!",
11940
+ subtitle: "建议配置,否则大部分功能无法使用",
11454
11941
  fields: [
11455
11942
  {
11456
11943
  key: "douyin",
@@ -12743,6 +13230,7 @@ var init_sender = __esmMin(() => {
12743
13230
  var handleBusinessError, wrapWithErrorHandler;
12744
13231
  var init_handler = __esmMin(async () => {
12745
13232
  await init_module();
13233
+ await init_EmojiReaction();
12746
13234
  await init_render();
12747
13235
  await init_sender();
12748
13236
  await init_strategy();
@@ -12783,10 +13271,25 @@ var init_handler = __esmMin(async () => {
12783
13271
  }
12784
13272
  };
12785
13273
  wrapWithErrorHandler = (fn, options) => async (e, next) => {
13274
+ const emojiManager = e ? new EmojiReactionManager(e) : void 0;
13275
+ if (emojiManager) {
13276
+ await emojiManager.add("EYES");
13277
+ setTimeout(() => {
13278
+ emojiManager.add("PROCESSING").catch(() => {});
13279
+ }, 1500);
13280
+ }
12786
13281
  const ctx = logger.runContext(async () => fn(e, next));
12787
13282
  try {
12788
- return await ctx.run();
13283
+ const result = await ctx.run();
13284
+ if (emojiManager) setTimeout(() => {
13285
+ emojiManager.replace("PROCESSING", "SUCCESS").catch(() => {});
13286
+ }, 1500);
13287
+ return result;
12789
13288
  } catch (error) {
13289
+ if (emojiManager) {
13290
+ await emojiManager.clearAll();
13291
+ await emojiManager.add("ERROR");
13292
+ }
12790
13293
  await new Promise((resolve$1) => setTimeout(resolve$1, 100));
12791
13294
  if (await handleBusinessError(error, options, parseLogsToStructured(ctx.logs()), e) === "handled") return void 0;
12792
13295
  throw error;
@@ -12945,12 +13448,6 @@ const BilibiliWeb = (all) => [components.accordion.create("bilibili", {
12945
13448
  description: "B站解析开关,此开关为单独开关",
12946
13449
  defaultSelected: all.bilibili.switch
12947
13450
  }),
12948
- components.switch.create("tip", {
12949
- label: "解析提示",
12950
- description: "B站解析提示,发送提示信息:“检测到B站链接,开始解析”",
12951
- defaultSelected: all.bilibili.tip,
12952
- isDisabled: !all.bilibili.switch
12953
- }),
12954
13451
  components.checkbox.group("sendContent", {
12955
13452
  label: "解析时发送的内容",
12956
13453
  description: "若什么都不选,可能不会返回任何解析结果",
@@ -12984,6 +13481,12 @@ const BilibiliWeb = (all) => [components.accordion.create("bilibili", {
12984
13481
  rules: [{ min: 1 }],
12985
13482
  isDisabled: !all.bilibili.sendContent.some((content) => content === "comment") || !all.bilibili.switch
12986
13483
  }),
13484
+ components.switch.create("commentImageCollection", {
13485
+ label: "是否收集评论区的图片",
13486
+ description: "开启后将收集评论区的图片,以合并转发的形式返回",
13487
+ defaultSelected: all.bilibili.commentImageCollection,
13488
+ isDisabled: !all.bilibili.sendContent.includes("comment") || !all.bilibili.switch
13489
+ }),
12987
13490
  components.switch.create("realCommentCount", {
12988
13491
  label: "显示真实评论数量",
12989
13492
  description: "评论图是否显示真实评论数量,关闭则显示解析到的评论数量",
@@ -13422,12 +13925,6 @@ const DouyinWeb = (all) => [components.accordion.create("douyin", {
13422
13925
  description: "抖音解析开关,此开关为单独开关",
13423
13926
  defaultSelected: all.douyin.switch
13424
13927
  }),
13425
- components.switch.create("tip", {
13426
- label: "解析提示",
13427
- description: "抖音解析提示,发送提示信息:“检测到抖音链接,开始解析”",
13428
- defaultSelected: all.douyin.tip,
13429
- isDisabled: !all.douyin.switch
13430
- }),
13431
13928
  components.checkbox.group("sendContent", {
13432
13929
  label: "解析时发送的内容",
13433
13930
  description: "若什么都不选,可能不会返回任何解析结果",
@@ -13868,12 +14365,6 @@ const KuaishouWeb = (all) => [components.accordion.create("kuaishou", {
13868
14365
  description: "快手解析开关,此开关为单独开关",
13869
14366
  defaultSelected: all.kuaishou.switch
13870
14367
  }),
13871
- components.switch.create("tip", {
13872
- label: "解析提示",
13873
- description: "快手解析提示,发送提示信息:“检测到快手链接,开始解析”",
13874
- defaultSelected: all.kuaishou.tip,
13875
- isDisabled: !all.kuaishou.switch
13876
- }),
13877
14368
  components.divider.create("divider-kuaishou-comment", {
13878
14369
  description: "评论详情设置",
13879
14370
  descPosition: 20
@@ -13905,12 +14396,6 @@ const XiaohongshuWeb = (all) => [components.accordion.create("xiaohongshu", {
13905
14396
  description: "小红书解析开关,此开关为单独开关",
13906
14397
  defaultSelected: all.xiaohongshu.switch
13907
14398
  }),
13908
- components.switch.create("tip", {
13909
- label: "解析提示",
13910
- description: "小红书解析提示,发送提示信息:“检测到小红书链接,开始解析”",
13911
- defaultSelected: all.xiaohongshu.tip,
13912
- isDisabled: !all.xiaohongshu.switch
13913
- }),
13914
14399
  components.checkbox.group("sendContent", {
13915
14400
  label: "解析时发送的内容",
13916
14401
  description: "若什么都不选,可能不会返回任何解析结果",
@@ -14212,21 +14697,10 @@ const webConfig = defineConfig({
14212
14697
  description: "在解析任务开始时添加表情回应,若适配器不支持需要关闭",
14213
14698
  defaultSelected: all.app.EmojiReply
14214
14699
  }),
14215
- components.input.number("EmojiReplyID", {
14216
- label: "表情 ID",
14217
- isDisabled: !all.app.EmojiReply,
14218
- description: "详情查看:https://koishi.js.org/QFace/#/qqnt 中调试信息的 emojiId 字段,Emoji则是 qcid ",
14219
- defaultValue: all.app.EmojiReplyID.toString(),
14220
- rules: [{
14221
- min: 0,
14222
- max: 1145141919810
14223
- }]
14224
- }),
14225
- components.switch.create("EmojiReplyIgnoreError", {
14226
- label: "忽略表情回应失败",
14227
- description: "开启后表情回应失败时不会抛出错误,程序会继续执行",
14228
- defaultSelected: all.app.EmojiReplyIgnoreError,
14229
- isDisabled: !all.app.EmojiReply
14700
+ components.switch.create("parseTip", {
14701
+ label: "解析提示",
14702
+ description: "发送提示信息:\"检测到xxx链接,开始解析\"",
14703
+ defaultSelected: all.app.parseTip
14230
14704
  }),
14231
14705
  components.checkbox.group("errorLogSendTo", {
14232
14706
  label: "错误日志",
@@ -14235,15 +14709,15 @@ const webConfig = defineConfig({
14235
14709
  defaultValue: all.app.errorLogSendTo,
14236
14710
  checkbox: [
14237
14711
  components.checkbox.create("errorLogSendTo:checkbox:1", {
14238
- label: "除'console'外的第一个主人",
14712
+ label: "第一个主人",
14239
14713
  value: "master"
14240
14714
  }),
14241
14715
  components.checkbox.create("errorLogSendTo:checkbox:2", {
14242
- label: "所有主人(排除console)",
14716
+ label: "所有主人",
14243
14717
  value: "allMasters"
14244
14718
  }),
14245
14719
  components.checkbox.create("errorLogSendTo:checkbox:3", {
14246
- label: "触发者(不支持私聊)",
14720
+ label: "触发者的群聊",
14247
14721
  value: "trigger"
14248
14722
  })
14249
14723
  ]
@@ -14878,12 +15352,7 @@ var Bilibili = class extends Base {
14878
15352
  this.headers.Cookie = Config.cookies.bilibili;
14879
15353
  }
14880
15354
  async BilibiliHandler(iddata) {
14881
- if (Config.app.EmojiReply) try {
14882
- await this.e.bot.setMsgReaction(this.e.contact, this.e.messageId, Config.app.EmojiReplyID, true);
14883
- } catch (err) {
14884
- if (!Config.app.EmojiReplyIgnoreError) throw err;
14885
- }
14886
- Config.bilibili.tip && await this.e.reply("检测到B站链接,开始解析");
15355
+ Config.app.parseTip && await this.e.reply("检测到B站链接,开始解析");
14887
15356
  switch (this.Type) {
14888
15357
  case "one_video": {
14889
15358
  const infoData = await this.amagi.bilibili.fetcher.fetchVideoInfo({
@@ -14907,8 +15376,9 @@ var Bilibili = class extends Base {
14907
15376
  if (Config.bilibili.sendContent.some((content) => content === "info")) if (Config.bilibili.videoInfoMode === "text") {
14908
15377
  const replyContent = [];
14909
15378
  const { coin, like, share, view, favorite, danmaku } = infoData.data.data.stat;
15379
+ const coverUrl = await processImageUrl(infoData.data.data.pic, infoData.data.data.title);
14910
15380
  const contentMap = {
14911
- cover: segment.image(infoData.data.data.pic),
15381
+ cover: segment.image(coverUrl),
14912
15382
  title: segment.text(`\n📺 标题: ${infoData.data.data.title}\n`),
14913
15383
  author: segment.text(`\n👤 作者: ${infoData.data.data.owner.name}\n`),
14914
15384
  stats: segment.text(formatVideoStats$1(view, danmaku, like, coin, share, favorite)),
@@ -14952,7 +15422,7 @@ var Bilibili = class extends Base {
14952
15422
  videoSize = await getvideosize(correctList.videoList[0].base_url, playUrlData.data.data.dash.audio[0].base_url, infoData.data.data.bvid);
14953
15423
  } else videoSize = (nockData.data.durl[0].size / (1024 * 1024)).toFixed(2);
14954
15424
  if (Config.bilibili.sendContent.some((content) => content === "comment")) {
14955
- const commentsdata = bilibiliComments((await this.amagi.bilibili.fetcher.fetchComments({
15425
+ const { comments: commentsdata, image_urls } = bilibiliComments((await this.amagi.bilibili.fetcher.fetchComments({
14956
15426
  number: Config.bilibili.numcomment,
14957
15427
  type: 1,
14958
15428
  oid: infoData.data.data.aid.toString(),
@@ -14960,6 +15430,20 @@ var Bilibili = class extends Base {
14960
15430
  })).data, infoData.data.data.owner.mid.toString());
14961
15431
  if (!commentsdata?.length) this.e.reply("这个视频没有评论 ~");
14962
15432
  else {
15433
+ const messageElements = [];
15434
+ if (Config.bilibili.commentImageCollection && image_urls.length > 0) {
15435
+ for (const [index, v] of image_urls.entries()) {
15436
+ const imageUrl = await processImageUrl(v, infoData.data.data.title, index);
15437
+ messageElements.push(segment.image(imageUrl));
15438
+ }
15439
+ const res = common.makeForward(messageElements, this.e.sender.userId, this.e.sender.nick);
15440
+ this.e.bot.sendForwardMsg(this.e.contact, res, {
15441
+ source: "评论图片收集",
15442
+ summary: `查看${messageElements.length}张图片`,
15443
+ prompt: "B站评论解析结果",
15444
+ news: [{ text: "点击查看解析结果" }]
15445
+ });
15446
+ }
14963
15447
  img$1 = await Render("bilibili/comment", {
14964
15448
  Type: "视频",
14965
15449
  CommentsData: commentsdata,
@@ -15105,19 +15589,10 @@ var Bilibili = class extends Base {
15105
15589
  switch (dynamicInfo.data.data.item.type) {
15106
15590
  case DynamicType.DRAW: {
15107
15591
  const imgArray = [];
15108
- for (const img$2 of dynamicInfo.data.data.item.modules.module_dynamic.major.opus.pics) if (img$2.url) imgArray.push(segment.image(img$2.url));
15109
- if (Config.bilibili.sendContent.some((content) => content === "comment") && commentsData) {
15110
- const commentsdata = bilibiliComments(commentsData.data, dynamicInfo.data.data.item.modules.module_author.mid.toString());
15111
- img$1 = await Render("bilibili/comment", {
15112
- Type: "动态",
15113
- CommentsData: commentsdata,
15114
- CommentLength: String(commentsdata?.length ?? 0),
15115
- share_url: "https://t.bilibili.com/" + dynamicInfo.data.data.item.id_str,
15116
- ImageLength: dynamicInfo.data.data.item.modules?.module_dynamic?.major?.draw?.items?.length ?? 0,
15117
- shareurl: "动态分享链接",
15118
- Resolution: null
15119
- });
15120
- this.e.reply(img$1);
15592
+ const title = dynamicInfo.data.data.item.modules.module_dynamic.major.opus.title || "bilibili_dynamic";
15593
+ for (const [index, img$2] of dynamicInfo.data.data.item.modules.module_dynamic.major.opus.pics.entries()) if (img$2.url) {
15594
+ const imageUrl = await processImageUrl(img$2.url, title, index);
15595
+ imgArray.push(segment.image(imageUrl));
15121
15596
  }
15122
15597
  if (imgArray.length === 1) this.e.reply(imgArray[0]);
15123
15598
  if (imgArray.length > 1) {
@@ -15297,15 +15772,22 @@ var Bilibili = class extends Base {
15297
15772
  typeMode: "strict"
15298
15773
  });
15299
15774
  const dycrad = dynamicInfoCard.data.data.card && dynamicInfoCard.data.data.card.card && JSON.parse(dynamicInfoCard.data.data.card.card);
15300
- commentsData && Config.bilibili.sendContent.some((item) => item === "comment") && this.e.reply(await Render("bilibili/comment", {
15301
- Type: "动态",
15302
- CommentsData: bilibiliComments(commentsData.data, dynamicInfo.data.data.item.modules.module_author.mid.toString()),
15303
- CommentLength: String(bilibiliComments(commentsData.data, dynamicInfo.data.data.item.modules.module_author.mid.toString())?.length ? bilibiliComments(commentsData.data, dynamicInfo.data.data.item.modules.module_author.mid.toString()).length : 0),
15304
- share_url: "https://www.bilibili.com/video/" + bvid,
15305
- ImageLength: dynamicInfo.data.data.item.modules?.module_dynamic?.major?.draw?.items?.length ?? 0,
15306
- shareurl: "动态分享链接",
15307
- Resolution: null
15308
- }));
15775
+ let staff = void 0;
15776
+ if (INFODATA.data.data.staff && Array.isArray(INFODATA.data.data.staff)) {
15777
+ const currentMid = dynamicInfo.data.data.item.modules.module_author.mid;
15778
+ staff = INFODATA.data.data.staff.map((member) => ({
15779
+ mid: member.mid,
15780
+ title: member.title,
15781
+ name: member.name,
15782
+ face: member.face,
15783
+ follower: member.follower
15784
+ }));
15785
+ const currentUserIndex = staff.findIndex((member) => member.mid === currentMid);
15786
+ if (currentUserIndex > 0) {
15787
+ const currentUser = staff.splice(currentUserIndex, 1)[0];
15788
+ staff.unshift(currentUser);
15789
+ }
15790
+ }
15309
15791
  img$1 = await Render("bilibili/dynamic/DYNAMIC_TYPE_AV", {
15310
15792
  image_url: INFODATA.data.data.pic,
15311
15793
  text: br$3(INFODATA.data.data.title),
@@ -15317,7 +15799,7 @@ var Bilibili = class extends Base {
15317
15799
  coin: Count(dycrad.stat.coin),
15318
15800
  duration_text: dynamicInfo.data.data.item.modules.module_dynamic.major.archive.duration_text,
15319
15801
  create_time: TimeFormatter.toDateTime(INFODATA.data.data.ctime),
15320
- avatar_url: INFODATA.data.data.owner.face,
15802
+ avatar_url: userProfileData.data.data.card.face,
15321
15803
  frame: dynamicInfo.data.data.item.modules.module_author.pendant.image,
15322
15804
  share_url: "https://www.bilibili.com/video/" + bvid,
15323
15805
  username: checkvip$2(userProfileData.data.data.card),
@@ -15327,7 +15809,8 @@ var Bilibili = class extends Base {
15327
15809
  following_count: Count(userProfileData.data.data.card.attention),
15328
15810
  render_time: TimeFormatter.now(),
15329
15811
  dynamicTYPE: "视频动态",
15330
- dynamic_id: dynamicInfo.data.data.item.id_str
15812
+ dynamic_id: dynamicInfo.data.data.item.id_str,
15813
+ staff
15331
15814
  });
15332
15815
  this.e.reply(img$1);
15333
15816
  }
@@ -15366,7 +15849,11 @@ var Bilibili = class extends Base {
15366
15849
  const articleContent = articleInfo.data.data;
15367
15850
  const messageElements = [];
15368
15851
  const articleImages = extractArticleImages(articleContent);
15369
- for (const item of articleImages) messageElements.push(segment.image(item));
15852
+ const title = articleData.title || "bilibili_article";
15853
+ for (const [index, item] of articleImages.entries()) {
15854
+ const imageUrl = await processImageUrl(item, title, index);
15855
+ messageElements.push(segment.image(imageUrl));
15856
+ }
15370
15857
  if (messageElements.length === 1) this.e.reply(messageElements[0]);
15371
15858
  if (messageElements.length > 1) {
15372
15859
  const forwardMsg = common.makeForward(messageElements, this.e.userId, this.e.sender.nick);
@@ -15403,10 +15890,42 @@ var Bilibili = class extends Base {
15403
15890
  }
15404
15891
  default: {
15405
15892
  const unknownItem = dynamicInfo.data.data.item;
15406
- this.e.reply(`该动态类型「${unknownItem.type}」暂未支持解析`);
15893
+ this.e.reply(`该动态类型「${unknownItem.type}」暂未支持解析,可通过 https://github.com/ikenxuan/karin-plugin-kkk/issues/new/choose 提交反馈`);
15407
15894
  break;
15408
15895
  }
15409
15896
  }
15897
+ if (Config.bilibili.sendContent.some((content) => content === "comment") && commentsData && dynamicInfo.data.data.item.type !== DynamicType.LIVE_RCMD) {
15898
+ const { comments: commentsdata, image_urls } = bilibiliComments(commentsData.data, dynamicInfo.data.data.item.modules.module_author.mid.toString());
15899
+ if (commentsdata && commentsdata.length > 0) {
15900
+ if (Config.bilibili.commentImageCollection && image_urls.length > 0) {
15901
+ const messageElements = [];
15902
+ let title = "bilibili_dynamic";
15903
+ if (dynamicInfo.data.data.item.type === DynamicType.DRAW) title = dynamicInfo.data.data.item.modules.module_dynamic.major.opus.title || "bilibili_dynamic";
15904
+ else if (dynamicInfo.data.data.item.type === DynamicType.AV) title = dynamicInfo.data.data.item.modules.module_dynamic.major.archive.title || "bilibili_dynamic";
15905
+ for (const [index, v] of image_urls.entries()) {
15906
+ const imageUrl = await processImageUrl(v, title, index);
15907
+ messageElements.push(segment.image(imageUrl));
15908
+ }
15909
+ const res = common.makeForward(messageElements, this.e.sender.userId, this.e.sender.nick);
15910
+ this.e.bot.sendForwardMsg(this.e.contact, res, {
15911
+ source: "评论图片收集",
15912
+ summary: `查看${messageElements.length}张图片`,
15913
+ prompt: "B站评论解析结果",
15914
+ news: [{ text: "点击查看解析结果" }]
15915
+ });
15916
+ }
15917
+ const img$2 = await Render("bilibili/comment", {
15918
+ Type: "动态",
15919
+ CommentsData: commentsdata,
15920
+ CommentLength: String(commentsdata.length),
15921
+ share_url: dynamicInfo.data.data.item.type === DynamicType.AV ? `https://www.bilibili.com/video/${dynamicInfo.data.data.item.modules.module_dynamic.major.archive.bvid}` : `https://t.bilibili.com/${dynamicInfo.data.data.item.id_str}`,
15922
+ ImageLength: dynamicInfo.data.data.item.modules?.module_dynamic?.major?.draw?.items?.length ?? 0,
15923
+ shareurl: "动态分享链接",
15924
+ Resolution: null
15925
+ });
15926
+ this.e.reply(img$2);
15927
+ } else this.e.reply("这条动态暂时还没有评论~");
15928
+ }
15410
15929
  break;
15411
15930
  }
15412
15931
  case "live_room_detail": {
@@ -15450,15 +15969,39 @@ var Bilibili = class extends Base {
15450
15969
  switch (this.islogin) {
15451
15970
  case true: {
15452
15971
  logger.debug("视频 URL:", this.Type === "one_video" ? playUrlData.data?.dash?.video[0].base_url : playUrlData.result.dash.video[0].base_url);
15453
- const bmp4 = await downloadFile(this.Type === "one_video" ? playUrlData.data?.dash?.video[0].base_url : playUrlData.result.dash.video[0].base_url, {
15454
- title: `Bil_V_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.mp4`,
15455
- headers: this.headers
15972
+ const downloadHeaders = {
15973
+ ...this.headers,
15974
+ Referer: "https://www.bilibili.com"
15975
+ };
15976
+ const bmp4Raw = await downloadFile(this.Type === "one_video" ? playUrlData.data?.dash?.video[0].base_url : playUrlData.result.dash.video[0].base_url, {
15977
+ title: `Bil_V_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.m4s`,
15978
+ headers: downloadHeaders
15456
15979
  });
15980
+ const videoPath = Common.tempDri.video + `Bil_V_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.mp4`;
15981
+ if (!await fixM4sFile(bmp4Raw.filepath, videoPath)) {
15982
+ logger.error("视频文件修复失败");
15983
+ return false;
15984
+ }
15985
+ await Common.removeFile(bmp4Raw.filepath, true);
15457
15986
  logger.debug("音频 URL:", this.Type === "one_video" ? playUrlData.data?.dash?.audio[0].base_url : playUrlData.result.dash.audio[0].base_url);
15458
- const bmp3 = await downloadFile(this.Type === "one_video" ? playUrlData.data?.dash?.audio[0].base_url : playUrlData.result.dash.audio[0].base_url, {
15459
- title: `Bil_A_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.mp3`,
15460
- headers: this.headers
15987
+ const bmp3Raw = await downloadFile(this.Type === "one_video" ? playUrlData.data?.dash?.audio[0].base_url : playUrlData.result.dash.audio[0].base_url, {
15988
+ title: `Bil_A_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.m4s`,
15989
+ headers: downloadHeaders
15461
15990
  });
15991
+ const audioPath = Common.tempDri.video + `Bil_A_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.m4a`;
15992
+ if (!await fixM4sFile(bmp3Raw.filepath, audioPath)) {
15993
+ logger.error("音频文件修复失败");
15994
+ return false;
15995
+ }
15996
+ await Common.removeFile(bmp3Raw.filepath, true);
15997
+ const bmp4 = {
15998
+ filepath: videoPath,
15999
+ totalBytes: bmp4Raw.totalBytes
16000
+ };
16001
+ const bmp3 = {
16002
+ filepath: audioPath,
16003
+ totalBytes: bmp3Raw.totalBytes
16004
+ };
15462
16005
  if (bmp4.filepath && bmp3.filepath) {
15463
16006
  const hasDanmaku = (this.forceBurnDanmaku || Config.bilibili.burnDanmaku) && danmakuList.length > 0;
15464
16007
  const resultPath = Common.tempDri.video + `Bil_Result_${this.Type === "one_video" ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.mp4`;
@@ -15868,9 +16411,16 @@ await init_locale();
15868
16411
  await init_module();
15869
16412
  await init_Config();
15870
16413
  const bilibiliComments = (commentsData, host_mid) => {
15871
- if (!commentsData) return [];
16414
+ if (!commentsData) return {
16415
+ comments: [],
16416
+ image_urls: []
16417
+ };
15872
16418
  let jsonArray = [];
15873
- if (commentsData.code === 404) return [];
16419
+ const image_urls = [];
16420
+ if (commentsData.code === 404) return {
16421
+ comments: [],
16422
+ image_urls: []
16423
+ };
15874
16424
  if (commentsData.data.top && commentsData.data.top.upper) {
15875
16425
  const topReply = commentsData.data.top.upper;
15876
16426
  const ctime = getRelativeTimeFromTimestamp$3(topReply.ctime);
@@ -15885,10 +16435,57 @@ const bilibiliComments = (commentsData, host_mid) => {
15885
16435
  const like = topReply.like;
15886
16436
  const replylength = topReply.rcount;
15887
16437
  const location = topReply.reply_control?.location?.replace("IP属地:", "") ?? "";
15888
- const img_src = topReply.content && topReply.content.pictures && topReply.content.pictures.length > 0 ? topReply.content.pictures[0].img_src : null;
16438
+ const pictures = [];
16439
+ if (topReply.content && topReply.content.pictures && topReply.content.pictures.length > 0) {
16440
+ for (const picture of topReply.content.pictures) if (picture.img_src) {
16441
+ pictures.push(picture.img_src);
16442
+ image_urls.push(picture.img_src);
16443
+ }
16444
+ }
15889
16445
  const members = topReply.content.members;
15890
16446
  const isUP = topReply.mid_str === host_mid;
15891
16447
  const fanCard = extractFanCard(topReply.member);
16448
+ const subReplies = [];
16449
+ if (topReply.replies && Array.isArray(topReply.replies)) for (const subReply of topReply.replies) {
16450
+ if (!subReply.content || !subReply.member) continue;
16451
+ const subCtime = getRelativeTimeFromTimestamp$3(subReply.ctime || 0);
16452
+ const subEmote = subReply.content.emote;
16453
+ let subMessage = subReply.content.message || "";
16454
+ if (subMessage && subEmote) subMessage = emoteToUrl(subMessage, subEmote);
16455
+ const subAvatar = subReply.member.avatar || "";
16456
+ const subFrame = subReply.member.pendant?.image || "";
16457
+ const subUname = checkvip$1(subReply.member);
16458
+ const subLevel = subReply.member.level_info?.current_level || 0;
16459
+ const subVipstatus = subReply.member.vip?.vipStatus || 0;
16460
+ const subLike = subReply.like || 0;
16461
+ const subLocation = subReply.reply_control?.location?.replace("IP属地:", "") ?? "";
16462
+ const subPictures = [];
16463
+ if (subReply.content.pictures && subReply.content.pictures.length > 0) {
16464
+ for (const picture of subReply.content.pictures) if (picture.img_src) {
16465
+ subPictures.push(picture.img_src);
16466
+ image_urls.push(picture.img_src);
16467
+ }
16468
+ }
16469
+ const subMembers = subReply.content.members || [];
16470
+ const subIsUP = subReply.mid_str === host_mid;
16471
+ const subFanCard = extractFanCard(subReply.member);
16472
+ subReplies.push({
16473
+ ctime: subCtime,
16474
+ message: subMessage,
16475
+ avatar: subAvatar,
16476
+ frame: subFrame,
16477
+ uname: subUname,
16478
+ level: subLevel,
16479
+ vipstatus: subVipstatus,
16480
+ pictures: subPictures,
16481
+ location: subLocation,
16482
+ like: subLike,
16483
+ icon_big_vip: subVipstatus === 1 ? "https://i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/big-vip.svg" : null,
16484
+ members: subMembers,
16485
+ isUP: subIsUP,
16486
+ fanCard: subFanCard
16487
+ });
16488
+ }
15892
16489
  const obj = {
15893
16490
  id: 0,
15894
16491
  ctime,
@@ -15898,7 +16495,7 @@ const bilibiliComments = (commentsData, host_mid) => {
15898
16495
  uname,
15899
16496
  level,
15900
16497
  vipstatus,
15901
- img_src,
16498
+ pictures,
15902
16499
  replylength,
15903
16500
  location,
15904
16501
  like,
@@ -15906,7 +16503,8 @@ const bilibiliComments = (commentsData, host_mid) => {
15906
16503
  members,
15907
16504
  isTop: true,
15908
16505
  isUP,
15909
- fanCard
16506
+ fanCard,
16507
+ replies: subReplies
15910
16508
  };
15911
16509
  jsonArray.push(obj);
15912
16510
  }
@@ -15923,25 +16521,38 @@ const bilibiliComments = (commentsData, host_mid) => {
15923
16521
  const like = reply.like;
15924
16522
  const replylength = reply.rcount;
15925
16523
  const location = reply.reply_control?.location?.replace("IP属地:", "") ?? "";
15926
- const img_src = reply.content && reply.content.pictures && reply.content.pictures.length > 0 ? reply.content.pictures[0].img_src : null;
16524
+ const pictures = [];
16525
+ if (reply.content && reply.content.pictures && reply.content.pictures.length > 0) {
16526
+ for (const picture of reply.content.pictures) if (picture.img_src) {
16527
+ pictures.push(picture.img_src);
16528
+ image_urls.push(picture.img_src);
16529
+ }
16530
+ }
15927
16531
  const members = reply.content.members;
15928
16532
  const isUP = reply.mid_str === host_mid;
15929
16533
  const fanCard = extractFanCard(reply.member);
15930
16534
  const subReplies = [];
15931
16535
  if (reply.replies && Array.isArray(reply.replies)) for (const subReply of reply.replies) {
15932
- const subCtime = getRelativeTimeFromTimestamp$3(subReply.ctime);
16536
+ if (!subReply.content || !subReply.member) continue;
16537
+ const subCtime = getRelativeTimeFromTimestamp$3(subReply.ctime || 0);
15933
16538
  const subEmote = subReply.content.emote;
15934
- let subMessage = subReply.content.message;
16539
+ let subMessage = subReply.content.message || "";
15935
16540
  if (subMessage && subEmote) subMessage = emoteToUrl(subMessage, subEmote);
15936
- const subAvatar = subReply.member.avatar;
15937
- const subFrame = subReply.member.pendant.image;
16541
+ const subAvatar = subReply.member.avatar || "";
16542
+ const subFrame = subReply.member.pendant?.image || "";
15938
16543
  const subUname = checkvip$1(subReply.member);
15939
- const subLevel = subReply.member.level_info.current_level;
15940
- const subVipstatus = subReply.member.vip.vipStatus;
15941
- const subLike = subReply.like;
16544
+ const subLevel = subReply.member.level_info?.current_level || 0;
16545
+ const subVipstatus = subReply.member.vip?.vipStatus || 0;
16546
+ const subLike = subReply.like || 0;
15942
16547
  const subLocation = subReply.reply_control?.location?.replace("IP属地:", "") ?? "";
15943
- const subImgSrc = subReply.content && subReply.content.pictures && subReply.content.pictures.length > 0 ? subReply.content.pictures[0].img_src : null;
15944
- const subMembers = subReply.content.members;
16548
+ const subPictures = [];
16549
+ if (subReply.content.pictures && subReply.content.pictures.length > 0) {
16550
+ for (const picture of subReply.content.pictures) if (picture.img_src) {
16551
+ subPictures.push(picture.img_src);
16552
+ image_urls.push(picture.img_src);
16553
+ }
16554
+ }
16555
+ const subMembers = subReply.content.members || [];
15945
16556
  const subIsUP = subReply.mid_str === host_mid;
15946
16557
  const subFanCard = extractFanCard(subReply.member);
15947
16558
  subReplies.push({
@@ -15952,7 +16563,7 @@ const bilibiliComments = (commentsData, host_mid) => {
15952
16563
  uname: subUname,
15953
16564
  level: subLevel,
15954
16565
  vipstatus: subVipstatus,
15955
- img_src: subImgSrc,
16566
+ pictures: subPictures,
15956
16567
  location: subLocation,
15957
16568
  like: subLike,
15958
16569
  icon_big_vip: subVipstatus === 1 ? "https://i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/big-vip.svg" : null,
@@ -15970,7 +16581,7 @@ const bilibiliComments = (commentsData, host_mid) => {
15970
16581
  uname,
15971
16582
  level,
15972
16583
  vipstatus,
15973
- img_src,
16584
+ pictures,
15974
16585
  replylength,
15975
16586
  location,
15976
16587
  like,
@@ -16015,7 +16626,10 @@ const bilibiliComments = (commentsData, host_mid) => {
16015
16626
  let res;
16016
16627
  res = br$2(jsonArray);
16017
16628
  res = [...res.filter((c) => c.isTop), ...res.filter((c) => !c.isTop)].slice(0, Config.bilibili.numcomment);
16018
- return res;
16629
+ return {
16630
+ comments: res,
16631
+ image_urls
16632
+ };
16019
16633
  };
16020
16634
  var emoteToUrl = (message, emote) => {
16021
16635
  for (const key in emote) if (Object.prototype.hasOwnProperty.call(emote, key)) {
@@ -16117,6 +16731,7 @@ const checkCk = async () => {
16117
16731
  };
16118
16732
  init_src();
16119
16733
  const getBilibiliID = async (url) => {
16734
+ if (/\/read\/cv\d+/.test(url) && url.includes("opus_fallback")) url = url.split("?")[0];
16120
16735
  const resp = await axios.get(url, { headers: { "User-Agent": "Apifox/1.0.0 (https://apifox.com)" } });
16121
16736
  const longLink = resp?.request?.res?.responseUrl ?? resp?.config?.url ?? url;
16122
16737
  let result = {};
@@ -16140,6 +16755,14 @@ const getBilibiliID = async (url) => {
16140
16755
  };
16141
16756
  break;
16142
16757
  }
16758
+ case /\/read\/cv(\d+)/.test(longLink): {
16759
+ const cvMatch = /\/read\/cv(\d+)/.exec(longLink);
16760
+ result = {
16761
+ type: "dynamic_info",
16762
+ dynamic_id: cvMatch ? cvMatch[1] : void 0
16763
+ };
16764
+ break;
16765
+ }
16143
16766
  case /\/bangumi\/play\/(\w+)/.test(longLink): {
16144
16767
  const playMatch = /\/bangumi\/play\/(\w+)/.exec(longLink);
16145
16768
  const id = playMatch ? playMatch[1] : "";
@@ -16300,7 +16923,7 @@ await init_amagiClient();
16300
16923
  await init_Config();
16301
16924
  var bilibiliBaseHeaders = {
16302
16925
  ...BASE_HEADERS,
16303
- Referer: "https://api.bilibili.com/",
16926
+ Referer: "https://www.bilibili.com",
16304
16927
  Cookie: Config.cookies.bilibili
16305
16928
  };
16306
16929
  var Bilibilipush = class extends Base {
@@ -16447,6 +17070,22 @@ var Bilibilipush = class extends Base {
16447
17070
  send_video = false;
16448
17071
  logger.debug(`UP主:${INFODATA.data.data.owner.name} 的该动态类型为${logger.yellow("番剧或影视")},默认跳过不下载,直达:${logger.green(INFODATA.data.data.redirect_url)}`);
16449
17072
  }
17073
+ let staff = void 0;
17074
+ if (INFODATA.data.data.staff && Array.isArray(INFODATA.data.data.staff)) {
17075
+ const currentMid = data$1[dynamicId].host_mid;
17076
+ staff = INFODATA.data.data.staff.map((member) => ({
17077
+ mid: member.mid,
17078
+ title: member.title,
17079
+ name: member.name,
17080
+ face: member.face,
17081
+ follower: member.follower
17082
+ }));
17083
+ const currentUserIndex = staff.findIndex((member) => member.mid === currentMid);
17084
+ if (currentUserIndex > 0) {
17085
+ const currentUser = staff.splice(currentUserIndex, 1)[0];
17086
+ staff.unshift(currentUser);
17087
+ }
17088
+ }
16450
17089
  img$2 = await Render("bilibili/dynamic/DYNAMIC_TYPE_AV", {
16451
17090
  image_url: INFODATA.data.data.pic,
16452
17091
  text: br$1(INFODATA.data.data.title),
@@ -16458,7 +17097,7 @@ var Bilibilipush = class extends Base {
16458
17097
  coin: Count(dycrad.stat.coin),
16459
17098
  duration_text: data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major?.archive?.duration_text ?? "0:00",
16460
17099
  create_time: TimeFormatter.toDateTime(data$1[dynamicId].Dynamic_Data.modules.module_author.pub_ts),
16461
- avatar_url: INFODATA.data.data.owner.face,
17100
+ avatar_url: userINFO.data.data.card.face,
16462
17101
  frame: data$1[dynamicId].Dynamic_Data.modules.module_author.pendant.image,
16463
17102
  share_url: "https://www.bilibili.com/video/" + bvid,
16464
17103
  username: checkvip(userINFO.data.data.card),
@@ -16468,7 +17107,8 @@ var Bilibilipush = class extends Base {
16468
17107
  following_count: Count(userINFO.data.data.card.attention),
16469
17108
  render_time: TimeFormatter.now(),
16470
17109
  dynamicTYPE: "视频动态推送",
16471
- dynamic_id: dynamicId
17110
+ dynamic_id: dynamicId,
17111
+ staff
16472
17112
  });
16473
17113
  }
16474
17114
  break;
@@ -16617,7 +17257,7 @@ var Bilibilipush = class extends Base {
16617
17257
  }
16618
17258
  default:
16619
17259
  skip = true;
16620
- logger.warn(`UP主:${data$1[dynamicId].remark}「${data$1[dynamicId].dynamic_type}」动态类型的暂未支持推送\n动态地址:${"https://t.bilibili.com/" + data$1[dynamicId].Dynamic_Data.id_str}`);
17260
+ logger.warn(`UP主:${data$1[dynamicId].remark}「${data$1[dynamicId].dynamic_type}」动态类型的暂未支持推送\n动态地址:${"https://t.bilibili.com/" + data$1[dynamicId].Dynamic_Data.id_str}\n可通过 https://github.com/ikenxuan/karin-plugin-kkk/issues/new/choose 提交反馈`);
16621
17261
  break;
16622
17262
  }
16623
17263
  }
@@ -16708,7 +17348,12 @@ var Bilibilipush = class extends Base {
16708
17348
  break;
16709
17349
  case "DYNAMIC_TYPE_DRAW": {
16710
17350
  const imgArray = [];
16711
- for (const img2 of data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major && data$1[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.draw?.items || data$1[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.opus.pics) imgArray.push(segment.image(img2.src ?? img2.url));
17351
+ const title = data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.title || "bilibili_dynamic";
17352
+ const images = data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major && data$1[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.draw?.items || data$1[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.opus.pics;
17353
+ for (const [index, img2] of images.entries()) {
17354
+ const imageUrl = await processImageUrl(img2.src ?? img2.url, title, index);
17355
+ imgArray.push(segment.image(imageUrl));
17356
+ }
16712
17357
  const forwardMsg = common.makeForward(imgArray, botId, bot.account.name);
16713
17358
  bot.sendForwardMsg(Contact, forwardMsg, {
16714
17359
  source: "图片合集",
@@ -16725,7 +17370,11 @@ var Bilibilipush = class extends Base {
16725
17370
  });
16726
17371
  const messageElements = [];
16727
17372
  const articleImages = extractArticleImages(articleInfo.data.data);
16728
- for (const item of articleImages) messageElements.push(segment.image(item));
17373
+ const title = articleInfo.data.data.title || "bilibili_article";
17374
+ for (const [index, item] of articleImages.entries()) {
17375
+ const imageUrl = await processImageUrl(item, title, index);
17376
+ messageElements.push(segment.image(imageUrl));
17377
+ }
16729
17378
  if (messageElements.length === 1) bot.sendMsg(Contact, messageElements);
16730
17379
  if (messageElements.length > 1) {
16731
17380
  const forwardMsg = common.makeForward(messageElements, botId, bot.account.name);
@@ -16958,7 +17607,8 @@ init_danmaku$1();
16958
17607
  init_riskControl();
16959
17608
  await init_date_fns();
16960
17609
  await init_locale();
16961
- var import_heic_convert = __toESM(require_heic_convert(), 1);
17610
+ var import_heic_decode = __toESM(require_heic_decode(), 1);
17611
+ var import_jpeg_js = __toESM(require_jpeg_js(), 1);
16962
17612
  await init_utils$1();
16963
17613
  await init_amagiClient();
16964
17614
  await init_Config();
@@ -17004,13 +17654,16 @@ var processCommentImage = async (imageUrl) => {
17004
17654
  type: "arraybuffer"
17005
17655
  }).getHeaders();
17006
17656
  if (headers["content-type"] && headers["content-type"] === "image/heic") {
17007
- const jpegBuffer = await (0, import_heic_convert.default)({
17008
- buffer: (await new Network({
17009
- url: imageUrl,
17010
- type: "arraybuffer"
17011
- }).returnResult()).data,
17012
- format: "JPEG"
17013
- });
17657
+ const decoded = await (0, import_heic_decode.default)({ buffer: (await new Network({
17658
+ url: imageUrl,
17659
+ type: "arraybuffer"
17660
+ }).returnResult()).data });
17661
+ const jpegImageData = {
17662
+ data: Buffer.from(decoded.data),
17663
+ width: decoded.width,
17664
+ height: decoded.height
17665
+ };
17666
+ const jpegBuffer = import_jpeg_js.default.encode(jpegImageData, 90).data;
17014
17667
  return `data:image/jpeg;base64,${Buffer.from(jpegBuffer).toString("base64")}`;
17015
17668
  }
17016
17669
  return imageUrl;
@@ -17158,12 +17811,7 @@ var DouYin = class extends Base {
17158
17811
  this.hasProcessedLiveImage = false;
17159
17812
  }
17160
17813
  async DouyinHandler(data$1) {
17161
- if (Config.app.EmojiReply && !this.e.isPrivate) try {
17162
- await this.e.bot.setMsgReaction(this.e.contact, this.e.messageId, Config.app.EmojiReplyID, true);
17163
- } catch (err) {
17164
- if (!Config.app.EmojiReplyIgnoreError) throw err;
17165
- }
17166
- if (Config.douyin.tip) this.e.reply("检测到抖音链接,开始解析");
17814
+ Config.app.parseTip && this.e.reply("检测到抖音链接,开始解析");
17167
17815
  switch (this.type) {
17168
17816
  case "one_work": {
17169
17817
  const VideoData = await this.amagi.douyin.fetcher.parseWork({
@@ -17208,7 +17856,8 @@ var DouYin = class extends Base {
17208
17856
  imagenum++;
17209
17857
  if (imageItem.clip_type === 2 || imageItem.clip_type === void 0) {
17210
17858
  image_url = imageItem.url_list[2] || imageItem.url_list[1];
17211
- processedImages.push(segment.image(image_url));
17859
+ const imageUrl = await processImageUrl(image_url, g_title, index);
17860
+ processedImages.push(segment.image(imageUrl));
17212
17861
  if (Config.app.removeCache === false) {
17213
17862
  mkdirSync(`${Common.tempDri.images}${g_title}`);
17214
17863
  const path$1 = `${Common.tempDri.images}${g_title}/${index + 1}.png`;
@@ -17256,7 +17905,10 @@ var DouYin = class extends Base {
17256
17905
  totalBytes: 0
17257
17906
  });
17258
17907
  processedImages.push(segment.video("file://" + filePath));
17259
- if (imageItem.clip_type === 5 && imageItem.url_list?.[0]) processedImages.push(segment.image(imageItem.url_list[0]));
17908
+ if (imageItem.clip_type === 5 && imageItem.url_list?.[0]) {
17909
+ const imageUrl = await processImageUrl(imageItem.url_list[0], g_title, index);
17910
+ processedImages.push(segment.image(imageUrl));
17911
+ }
17260
17912
  } else await Common.removeFile(liveimg.filepath, true);
17261
17913
  }
17262
17914
  }
@@ -17278,7 +17930,8 @@ var DouYin = class extends Base {
17278
17930
  for (const [index, imageItem] of images.entries()) {
17279
17931
  image_url = imageItem.url_list[2] || imageItem.url_list[1];
17280
17932
  g_title = VideoData.data.aweme_detail.preview_title.substring(0, 50).replace(/[\\/:*?"<>|\r\n]/g, " ");
17281
- imageres.push(segment.image(image_url));
17933
+ const imageUrl = await processImageUrl(image_url, g_title, index);
17934
+ imageres.push(segment.image(imageUrl));
17282
17935
  imagenum++;
17283
17936
  if (Config.app.removeCache === false) {
17284
17937
  mkdirSync(`${Common.tempDri.images}${g_title}`);
@@ -17292,8 +17945,10 @@ var DouYin = class extends Base {
17292
17945
  const res = common.makeForward(imageres, this.e.sender.userId, this.e.sender.nick);
17293
17946
  image_data.push(res);
17294
17947
  image_res.push(image_data);
17295
- if (imageres.length === 1) await this.e.reply(segment.image(image_url));
17296
- else await this.e.bot.sendForwardMsg(this.e.contact, res, {
17948
+ if (imageres.length === 1) {
17949
+ const imageUrl = await processImageUrl(image_url, g_title);
17950
+ await this.e.reply(segment.image(imageUrl));
17951
+ } else await this.e.bot.sendForwardMsg(this.e.contact, res, {
17297
17952
  source: "图片合集",
17298
17953
  summary: `查看${res.length}张图片消息`,
17299
17954
  prompt: "抖音图集解析结果",
@@ -17321,10 +17976,11 @@ var DouYin = class extends Base {
17321
17976
  }
17322
17977
  const images1 = VideoData.data.aweme_detail.images ?? [];
17323
17978
  if (!images1.length) logger.debug("未获取到合辑的图片数据");
17324
- for (const item of images1) {
17979
+ for (const [index, item] of images1.entries()) {
17325
17980
  imagenum++;
17326
17981
  if (item.clip_type === 2 || item.clip_type === void 0) {
17327
- images.push(segment.image(item.url_list[0]));
17982
+ const imageUrl = await processImageUrl(item.url_list[0], g_title, index);
17983
+ images.push(segment.image(imageUrl));
17328
17984
  continue;
17329
17985
  }
17330
17986
  const liveimg = await downloadFile(`https://aweme.snssdk.com/aweme/v1/play/?video_id=${item.video.play_addr_h264.uri}&ratio=1080p&line=0`, {
@@ -17364,7 +18020,10 @@ var DouYin = class extends Base {
17364
18020
  totalBytes: 0
17365
18021
  });
17366
18022
  images.push(segment.video("file://" + filePath));
17367
- if (item.clip_type === 5 && item.url_list?.[0]) images.push(segment.image(item.url_list[0]));
18023
+ if (item.clip_type === 5 && item.url_list?.[0]) {
18024
+ const imageUrl = await processImageUrl(item.url_list[0], g_title, index);
18025
+ images.push(segment.image(imageUrl));
18026
+ }
17368
18027
  } else await Common.removeFile(liveimg.filepath, true);
17369
18028
  }
17370
18029
  }
@@ -17420,8 +18079,9 @@ var DouYin = class extends Base {
17420
18079
  if (Config.douyin.sendContent.includes("info")) if (Config.douyin.videoInfoMode === "text") {
17421
18080
  const replyContent = [];
17422
18081
  const { digg_count, share_count, collect_count, comment_count, recommend_count } = VideoData.data.aweme_detail.statistics;
18082
+ const coverUrl = await processImageUrl(this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0], VideoData.data.aweme_detail.desc);
17423
18083
  const contentMap = {
17424
- cover: segment.image(this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0]),
18084
+ cover: segment.image(coverUrl),
17425
18085
  title: segment.text(`\n📺 标题: ${VideoData.data.aweme_detail.desc}\n`),
17426
18086
  author: segment.text(`\n👤 作者: ${VideoData.data.aweme_detail.author.nickname}\n`),
17427
18087
  stats: segment.text(formatVideoStats(digg_count, share_count, collect_count, comment_count, recommend_count))
@@ -17503,7 +18163,10 @@ var DouYin = class extends Base {
17503
18163
  });
17504
18164
  const messageElements = [];
17505
18165
  if (Config.douyin.commentImageCollection && douyinCommentsRes.image_url.length > 0) {
17506
- for (const v of douyinCommentsRes.image_url) messageElements.push(segment.image(v));
18166
+ for (const [index, v] of douyinCommentsRes.image_url.entries()) {
18167
+ const imageUrl = await processImageUrl(v, VideoData.data.aweme_detail.desc, index);
18168
+ messageElements.push(segment.image(imageUrl));
18169
+ }
17507
18170
  const res = common.makeForward(messageElements, this.e.sender.userId, this.e.sender.nick);
17508
18171
  this.e.bot.sendForwardMsg(this.e.contact, res, {
17509
18172
  source: "评论图片收集",
@@ -18337,9 +19000,10 @@ var DouYinpush = class extends Base {
18337
19000
  }
18338
19001
  const images1 = Detail_Data.images ?? [];
18339
19002
  if (!images1.length) logger.debug("未获取到合辑的图片数据");
18340
- for (const item of images1) {
19003
+ for (const [index, item] of images1.entries()) {
18341
19004
  if (item.clip_type === 2 || item.clip_type === void 0) {
18342
- images.push(segment.image(item.url_list[0]));
19005
+ const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
19006
+ images.push(segment.image(imageUrl));
18343
19007
  continue;
18344
19008
  }
18345
19009
  const liveimg = await downloadFile(`https://aweme.snssdk.com/aweme/v1/play/?video_id=${item.video.play_addr_h264.uri}&ratio=1080p&line=0`, {
@@ -18379,7 +19043,10 @@ var DouYinpush = class extends Base {
18379
19043
  totalBytes: 0
18380
19044
  });
18381
19045
  images.push(segment.video("file://" + filePath));
18382
- if (item.clip_type === 5 && item.url_list?.[0]) images.push(segment.image(item.url_list[0]));
19046
+ if (item.clip_type === 5 && item.url_list?.[0]) {
19047
+ const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
19048
+ images.push(segment.image(imageUrl));
19049
+ }
18383
19050
  } else await Common.removeFile(liveimg.filepath, true);
18384
19051
  }
18385
19052
  }
@@ -18414,10 +19081,10 @@ var DouYinpush = class extends Base {
18414
19081
  temp.push(liveimgbgm);
18415
19082
  if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
18416
19083
  }
18417
- for (const item of Detail_Data.images) {
19084
+ for (const [index, item] of Detail_Data.images.entries()) {
18418
19085
  if (item.clip_type === 2 || item.clip_type === void 0) {
18419
- const image_url = item.url_list[2] ?? item.url_list[1];
18420
- processedImages.push(segment.image(image_url));
19086
+ const imageUrl = await processImageUrl(item.url_list[2] ?? item.url_list[1], Detail_Data.desc, index);
19087
+ processedImages.push(segment.image(imageUrl));
18421
19088
  continue;
18422
19089
  }
18423
19090
  const liveimg = await downloadFile(`https://aweme.snssdk.com/aweme/v1/play/?video_id=${item.video.play_addr_h264.uri}&ratio=1080p&line=0`, {
@@ -18457,7 +19124,10 @@ var DouYinpush = class extends Base {
18457
19124
  totalBytes: 0
18458
19125
  });
18459
19126
  processedImages.push(segment.video("file://" + filePath));
18460
- if (item.clip_type === 5 && item.url_list?.[0]) processedImages.push(segment.image(item.url_list[0]));
19127
+ if (item.clip_type === 5 && item.url_list?.[0]) {
19128
+ const imageUrl = await processImageUrl(item.url_list[0], Detail_Data.desc, index);
19129
+ processedImages.push(segment.image(imageUrl));
19130
+ }
18461
19131
  } else await Common.removeFile(liveimg.filepath, true);
18462
19132
  }
18463
19133
  }
@@ -18478,13 +19148,16 @@ var DouYinpush = class extends Base {
18478
19148
  } else {
18479
19149
  const imageres = [];
18480
19150
  let image_url;
18481
- for (const item of Detail_Data.images) {
19151
+ for (const [index, item] of Detail_Data.images.entries()) {
18482
19152
  image_url = item.url_list[2] ?? item.url_list[1];
18483
- imageres.push(segment.image(image_url));
19153
+ const imageUrl = await processImageUrl(image_url, Detail_Data.desc, index);
19154
+ imageres.push(segment.image(imageUrl));
18484
19155
  }
18485
19156
  const bot = karin$1.getBot(botId);
18486
- if (imageres.length === 1) await bot.sendMsg(Contact, [segment.image(image_url)]);
18487
- else {
19157
+ if (imageres.length === 1) {
19158
+ const imageUrl = await processImageUrl(image_url, Detail_Data.desc);
19159
+ await bot.sendMsg(Contact, [segment.image(imageUrl)]);
19160
+ } else {
18488
19161
  const forwardMsg = common.makeForward(imageres, botId, bot.account.name);
18489
19162
  await bot.sendForwardMsg(Contact, forwardMsg, {
18490
19163
  source: "图片合集",
@@ -18881,16 +19554,11 @@ var Kuaishou = class extends Base {
18881
19554
  this.type = iddata?.type;
18882
19555
  }
18883
19556
  async KuaishouHandler(data$1) {
18884
- if (Config.app.EmojiReply) try {
18885
- await this.e.bot.setMsgReaction(this.e.contact, this.e.messageId, Config.app.EmojiReplyID, true);
18886
- } catch (err) {
18887
- if (!Config.app.EmojiReplyIgnoreError) throw err;
18888
- }
18889
19557
  if (data$1.VideoData.data.data.visionVideoDetail.status !== 1) {
18890
19558
  await this.e.reply("不支持解析的视频");
18891
19559
  return true;
18892
19560
  }
18893
- Config.kuaishou.tip && await this.e.reply("检测到快手链接,开始解析");
19561
+ Config.app.parseTip && await this.e.reply("检测到快手链接,开始解析");
18894
19562
  const video_url = data$1.VideoData.data.data.visionVideoDetail.photo.photoUrl;
18895
19563
  const transformedData = Object.entries(data$1.EmojiData.data.data.visionBaseEmoticons.iconUrls).map(([name, path$1]) => ({
18896
19564
  name,
@@ -19280,10 +19948,15 @@ var waitQrcode = async (page) => {
19280
19948
  };
19281
19949
  await init_module();
19282
19950
  await init_Config();
19283
- const task = Config.app.removeCache && karin$1.task("[kkk-视频缓存自动删除]", "0 0 4 * * *", async () => {
19951
+ const task = Config.app.removeCache && karin$1.task("[kkk-缓存自动删除]", "0 */4 * * *", async () => {
19284
19952
  try {
19285
- await removeAllFiles(Common.tempDri.video);
19286
- logger.mark(Common.tempDri.video + "目录下所有文件已删除");
19953
+ const twoHoursAgo = Date.now() - 7200 * 1e3;
19954
+ const videoDeleted = removeOldFiles(Common.tempDri.video, twoHoursAgo);
19955
+ logger.mark(`${Common.tempDri.video} 目录下已删除 ${videoDeleted} 个文件`);
19956
+ if (Config.app.downloadImageLocally) {
19957
+ const imageDeleted = removeOldFiles(Common.tempDri.images, twoHoursAgo);
19958
+ logger.mark(`${Common.tempDri.images} 目录下已删除 ${imageDeleted} 个文件`);
19959
+ }
19287
19960
  } catch (err) {
19288
19961
  console.error("删除文件时出错:", err);
19289
19962
  }
@@ -19302,39 +19975,25 @@ const dylogin = karin$1.command(/^#?(kkk)?抖音(扫码)?登录$/, async (e) =>
19302
19975
  perm: Config.douyin.loginPerm,
19303
19976
  name: "kkk-ck管理"
19304
19977
  });
19305
- const setdyck = karin$1.command(/^#?(kkk)?s*设置抖音ck$/i, async (e) => {
19306
- const msg = await e.reply("请发在120秒内送抖音ck\n教程:https://ikenxuan.github.io/kkkkkk-10086/docs/intro/other#%E9%85%8D%E7%BD%AE%E4%B8%8D%E5%90%8C%E5%B9%B3%E5%8F%B0%E7%9A%84-cookies\n");
19307
- const context = await karin$1.ctx(e);
19308
- Config.Modify("cookies", "douyin", context.msg);
19309
- await e.bot.recallMsg(e.contact, msg.messageId);
19310
- await e.reply("设置成功!", { at: true });
19311
- return true;
19312
- }, {
19313
- perm: "master",
19314
- name: "kkk-ck管理",
19315
- event: "message.friend"
19316
- });
19317
- const setbilick = karin$1.command(/^#?(kkk)?s*设置s*(B站)ck$/i, async (e) => {
19318
- const msg = await e.reply("请发在120秒内送B站ck\n教程:https://ikenxuan.github.io/kkkkkk-10086/docs/intro/other#%E9%85%8D%E7%BD%AE%E4%B8%8D%E5%90%8C%E5%B9%B3%E5%8F%B0%E7%9A%84-cookies\n");
19319
- const context = await karin$1.ctx(e);
19320
- Config.Modify("cookies", "bilibili", context.msg);
19321
- await e.bot.recallMsg(e.contact, msg.messageId);
19322
- await e.reply("设置成功!", { at: true });
19323
- return true;
19324
- }, {
19325
- perm: "master",
19326
- name: "kkk-ck管理",
19327
- event: "message.friend"
19328
- });
19329
- const removeAllFiles = async (dir) => {
19330
- const files = await fs.promises.readdir(dir);
19331
- for (const file of files) {
19332
- const filePath = path.join(dir, file);
19333
- if ((await fs.promises.stat(filePath)).isDirectory()) {
19334
- await removeAllFiles(filePath);
19335
- await fs.promises.rmdir(filePath);
19336
- } else await fs.promises.unlink(filePath);
19978
+ const removeOldFiles = (dir, beforeTimestamp) => {
19979
+ let deletedCount = 0;
19980
+ try {
19981
+ const files = fs.readdirSync(dir);
19982
+ for (const file of files) {
19983
+ const filePath = path.join(dir, file);
19984
+ const stats = fs.statSync(filePath);
19985
+ if (stats.isDirectory()) {
19986
+ deletedCount += removeOldFiles(filePath, beforeTimestamp);
19987
+ if (fs.readdirSync(filePath).length === 0) fs.rmdirSync(filePath);
19988
+ } else if (stats.mtimeMs < beforeTimestamp) {
19989
+ fs.unlinkSync(filePath);
19990
+ deletedCount++;
19991
+ }
19992
+ }
19993
+ } catch (err) {
19994
+ logger.error(`处理目录 ${dir} 时出错:`, err);
19337
19995
  }
19996
+ return deletedCount;
19338
19997
  };
19339
19998
  await init_module();
19340
19999
  await init_Config();
@@ -19962,12 +20621,7 @@ var Xiaohongshu = class extends Base {
19962
20621
  this.type = iddata?.type;
19963
20622
  }
19964
20623
  async XiaohongshuHandler(data$1) {
19965
- if (Config.app.EmojiReply && !this.e.isPrivate) try {
19966
- await this.e.bot.setMsgReaction(this.e.contact, this.e.messageId, Config.app.EmojiReplyID, true);
19967
- } catch (err) {
19968
- if (!Config.app.EmojiReplyIgnoreError) throw err;
19969
- }
19970
- Config.xiaohongshu.tip && await this.e.reply("检测到小红书链接,开始解析");
20624
+ Config.app.parseTip && await this.e.reply("检测到小红书链接,开始解析");
19971
20625
  const NoteData = await this.amagi.xiaohongshu.fetcher.fetchNoteDetail({
19972
20626
  typeMode: "strict",
19973
20627
  note_id: data$1.note_id,
@@ -20008,10 +20662,16 @@ var Xiaohongshu = class extends Base {
20008
20662
  }
20009
20663
  if (!NoteData.data.data.items[0].note_card.video && Config.xiaohongshu.sendContent.includes("image")) {
20010
20664
  const Imgs = [];
20011
- for (const item of NoteData.data.data.items[0].note_card.image_list) Imgs.push(segment.image(item.url_default));
20665
+ const title = NoteData.data.data.items[0].note_card.title;
20666
+ for (const [index, item] of NoteData.data.data.items[0].note_card.image_list.entries()) {
20667
+ const imageUrl = await processImageUrl(item.url_default, title, index);
20668
+ Imgs.push(segment.image(imageUrl));
20669
+ }
20012
20670
  const res = common.makeForward(Imgs, this.e.sender.userId, this.e.sender.nick);
20013
- if (NoteData.data.data.items[0].note_card.image_list.length === 1) await this.e.reply(segment.image(NoteData.data.data.items[0].note_card.image_list[0].url_default));
20014
- else await this.e.bot.sendForwardMsg(this.e.contact, res, {
20671
+ if (NoteData.data.data.items[0].note_card.image_list.length === 1) {
20672
+ const imageUrl = await processImageUrl(NoteData.data.data.items[0].note_card.image_list[0].url_default, title);
20673
+ await this.e.reply(segment.image(imageUrl));
20674
+ } else await this.e.bot.sendForwardMsg(this.e.contact, res, {
20015
20675
  source: "图片合集",
20016
20676
  summary: `查看${res.length}张图片消息`,
20017
20677
  prompt: "小红书图集解析结果",
@@ -20457,4 +21117,4 @@ const update = karin$1.task("kkk-更新检测", "*/5 * * * *", Handler, {
20457
21117
  name: "kkk-更新检测",
20458
21118
  log: false
20459
21119
  });
20460
- export { init_root as A, removeAllFiles as C, webConfig as D, task as E, web_config_default as O, dylogin as S, setdyck as T, setdyPush as _, douyinAPP as a, version as b, xiaohongshuAPP as c, bilibiliPushList as d, changeBotID as f, setbiliPush as g, forcePush as h, bilibiliAPP as i, Root as k, qrLogin as l, douyinPushList as m, kkkUpdateCommand as n, kuaishouAPP as o, douyinPush as p, update as r, prefix as s, kkkUpdate as t, bilibiliPush as u, testDouyinPush as v, setbilick as w, biLogin as x, help as y };
21120
+ export { removeOldFiles as C, Root as D, web_config_default as E, init_root as O, dylogin as S, webConfig as T, setdyPush as _, douyinAPP as a, version as b, xiaohongshuAPP as c, bilibiliPushList as d, changeBotID as f, setbiliPush as g, forcePush as h, bilibiliAPP as i, qrLogin as l, douyinPushList as m, kkkUpdateCommand as n, kuaishouAPP as o, douyinPush as p, update as r, prefix as s, kkkUpdate as t, bilibiliPush as u, testDouyinPush as v, task as w, biLogin as x, help as y };