karin-plugin-kkk 2.19.1 → 2.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [2.20.1](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.20.0...v2.20.1) (2026-02-23)
6
+
7
+
8
+ ### 🐛 错误修复
9
+
10
+ * 抖音图集解析中,图片类型识别错误 ([3a1d57d](https://github.com/ikenxuan/karin-plugin-kkk/commit/3a1d57d7e7f7c68ed3cdae813d0a83b5bfc4fbb9))
11
+ * 获取封面图空指针错误 ([d8434c7](https://github.com/ikenxuan/karin-plugin-kkk/commit/d8434c746ee11671656bda17ddb754d2aa2a6422))
12
+
13
+
14
+ ### ✨ 细节优化
15
+
16
+ * 兼容性提高 ([3ea0cf7](https://github.com/ikenxuan/karin-plugin-kkk/commit/3ea0cf7706bdef6a3d3ef0e121c0991782162728))
17
+
18
+ ## [2.20.0](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.19.1...v2.20.0) (2026-02-23)
19
+
20
+
21
+ ### ✨ 新功能
22
+
23
+ * **core:** 引用解析支持识别图片中的二维码进行解析 ([82e7878](https://github.com/ikenxuan/karin-plugin-kkk/commit/82e78788a53332b605a5fc5280d824eefc81b0f2))
24
+
25
+
26
+ ### 💄 UI 优化
27
+
28
+ * **douyin:** 视频信息的字体优化 ([3d39d6c](https://github.com/ikenxuan/karin-plugin-kkk/commit/3d39d6c86dd7ced63d948e08e8ff3b6c8165bd3e))
29
+
30
+
31
+ ### ✨ 细节优化
32
+
33
+ * 提高二维码识别兼容性 ([28ead9d](https://github.com/ikenxuan/karin-plugin-kkk/commit/28ead9da322af913e0defa1ac0f7118f44e9edb1))
34
+ * 细优 ([1f45d0b](https://github.com/ikenxuan/karin-plugin-kkk/commit/1f45d0b81c9dfd906e1be227ddbe799ec26377e4))
35
+
5
36
  ## [2.19.1](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.19.0...v2.19.1) (2026-02-21)
6
37
 
7
38
 
package/lib/apps/admin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { C as removeAllFiles, E as task, S as dylogin, T as setdyck, w as setbilick, x as biLogin } from "../core_chunk/main-BZ80CSSD.js";
3
- import "../core_chunk/vendor-BsdYKPEs.js";
4
- import "../core_chunk/template-BjkVe9U9.js";
2
+ import { C as removeAllFiles, E as task, S as dylogin, T as setdyck, w as setbilick, x as biLogin } from "../core_chunk/main-C6bm6hYZ.js";
3
+ import "../core_chunk/vendor-B3KhGHI_.js";
4
+ import "../core_chunk/template-CQ-u6h09.js";
5
5
  export { biLogin, dylogin, removeAllFiles, setbilick, setdyck, task };
package/lib/apps/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { b as version, y as help } from "../core_chunk/main-BZ80CSSD.js";
3
- import "../core_chunk/vendor-BsdYKPEs.js";
4
- import "../core_chunk/template-BjkVe9U9.js";
2
+ import { b as version, y as help } from "../core_chunk/main-C6bm6hYZ.js";
3
+ import "../core_chunk/vendor-B3KhGHI_.js";
4
+ import "../core_chunk/template-CQ-u6h09.js";
5
5
  export { help, version };
package/lib/apps/push.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { _ as setdyPush, d as bilibiliPushList, f as changeBotID, g as setbiliPush, h as forcePush, m as douyinPushList, p as douyinPush, u as bilibiliPush, v as testDouyinPush } from "../core_chunk/main-BZ80CSSD.js";
3
- import "../core_chunk/vendor-BsdYKPEs.js";
4
- import "../core_chunk/template-BjkVe9U9.js";
2
+ import { _ as setdyPush, d as bilibiliPushList, f as changeBotID, g as setbiliPush, h as forcePush, m as douyinPushList, p as douyinPush, u as bilibiliPush, v as testDouyinPush } from "../core_chunk/main-C6bm6hYZ.js";
3
+ import "../core_chunk/vendor-B3KhGHI_.js";
4
+ import "../core_chunk/template-CQ-u6h09.js";
5
5
  export { bilibiliPush, bilibiliPushList, changeBotID, douyinPush, douyinPushList, forcePush, setbiliPush, setdyPush, testDouyinPush };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { l as qrLogin } from "../core_chunk/main-BZ80CSSD.js";
3
- import "../core_chunk/vendor-BsdYKPEs.js";
4
- import "../core_chunk/template-BjkVe9U9.js";
2
+ import { l as qrLogin } from "../core_chunk/main-C6bm6hYZ.js";
3
+ import "../core_chunk/vendor-B3KhGHI_.js";
4
+ import "../core_chunk/template-CQ-u6h09.js";
5
5
  export { qrLogin };
package/lib/apps/tools.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-BZ80CSSD.js";
3
- import "../core_chunk/vendor-BsdYKPEs.js";
4
- import "../core_chunk/template-BjkVe9U9.js";
2
+ import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-C6bm6hYZ.js";
3
+ import "../core_chunk/vendor-B3KhGHI_.js";
4
+ import "../core_chunk/template-CQ-u6h09.js";
5
5
  export { bilibiliAPP, douyinAPP, kuaishouAPP, prefix, xiaohongshuAPP };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-BZ80CSSD.js";
3
- import "../core_chunk/vendor-BsdYKPEs.js";
4
- import "../core_chunk/template-BjkVe9U9.js";
2
+ import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-C6bm6hYZ.js";
3
+ import "../core_chunk/vendor-B3KhGHI_.js";
4
+ import "../core_chunk/template-CQ-u6h09.js";
5
5
  export { kkkUpdate, kkkUpdateCommand, update };
@@ -1,10 +1,10 @@
1
1
  {
2
- "version": "2.19.1",
3
- "buildTime": "2026-02-21T13:10:10.770Z",
4
- "buildTimestamp": 1771679410770,
2
+ "version": "2.20.1",
3
+ "buildTime": "2026-02-23T16:01:34.942Z",
4
+ "buildTimestamp": 1771862494942,
5
5
  "name": "karin-plugin-kkk",
6
6
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
7
7
  "homepage": "https://github.com/ikenxuan/karin-plugin-kkk",
8
- "commitHash": "3792c959012fb3f06e765807dde8e599da849563",
9
- "shortCommitHash": "3792c959"
8
+ "commitHash": "182d64bef47a9e02ded26f9bc17270cab8993411",
9
+ "shortCommitHash": "182d64be"
10
10
  }
@@ -1,6 +1,6 @@
1
1
  import { n as __esmMin, o as __toESM, r as __export } from "./rolldown-runtime-BMXAG3ag.js";
2
- import { D as init_locale, O as zhCN, _n as init_zod, a as require_lib, at as format, c as Window, cn as require_express, dn as init_source, fn as AxiosError$1, gn as init_esm, hn as Xhshow, i as require_dist, it as formatDistanceToNow, ln as require_protobufjs, mn as axios_default, n as require_jsQR, nt as init_date_fns, o as require_qr_code_styling, ot as differenceInSeconds, pn as init_axios, r as require_heic_convert, rt as fromUnixTime, s as init_lib, t as require_png, un as Chalk, vn as zod_default } from "./vendor-BsdYKPEs.js";
3
- import { n as init_client, r as reactServerRender } from "./template-BjkVe9U9.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";
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,8 +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 { pipeline } from "node:stream/promises";
16
+ import { Transformer } from "@napi-rs/image";
17
17
  import axios, { AxiosError } from "node-karin/axios";
18
+ import { pipeline } from "node:stream/promises";
18
19
  import { Transform } from "node:stream";
19
20
  import express from "node-karin/express";
20
21
  import template from "node-karin/template";
@@ -6386,9 +6387,273 @@ var init_build_metadata = __esmMin(() => {
6386
6387
  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")}`;
6387
6388
  };
6388
6389
  });
6390
+ var import_jsQR, QRCodeScanner;
6391
+ var init_QRCodeScanner = __esmMin(() => {
6392
+ import_jsQR = __toESM(require_jsQR(), 1);
6393
+ QRCodeScanner = class {
6394
+ static async scanFromUrl(imageUrl) {
6395
+ try {
6396
+ const response = await axios.get(imageUrl, { responseType: "arraybuffer" });
6397
+ const buffer = Buffer.from(response.data);
6398
+ return this.scanFromBuffer(buffer);
6399
+ } catch (error) {
6400
+ logger.error("识别二维码时发生错误:", error);
6401
+ return null;
6402
+ }
6403
+ }
6404
+ static extractRegion(imageData, x, y, width, height) {
6405
+ const newData = new Uint8ClampedArray(width * height * 4);
6406
+ for (let dy = 0; dy < height; dy++) for (let dx = 0; dx < width; dx++) {
6407
+ const srcX = x + dx;
6408
+ const srcY = y + dy;
6409
+ if (srcX >= imageData.width || srcY >= imageData.height) continue;
6410
+ const srcIndex = (srcY * imageData.width + srcX) * 4;
6411
+ const dstIndex = (dy * width + dx) * 4;
6412
+ newData[dstIndex] = imageData.data[srcIndex];
6413
+ newData[dstIndex + 1] = imageData.data[srcIndex + 1];
6414
+ newData[dstIndex + 2] = imageData.data[srcIndex + 2];
6415
+ newData[dstIndex + 3] = imageData.data[srcIndex + 3];
6416
+ }
6417
+ return {
6418
+ width,
6419
+ height,
6420
+ data: newData
6421
+ };
6422
+ }
6423
+ static enhanceContrast(imageData) {
6424
+ const { width, height, data: data$1 } = imageData;
6425
+ const newData = new Uint8ClampedArray(data$1.length);
6426
+ const histogram = new Array(256).fill(0);
6427
+ for (let i = 0; i < data$1.length; i += 4) {
6428
+ const gray = Math.floor(.299 * data$1[i] + .587 * data$1[i + 1] + .114 * data$1[i + 2]);
6429
+ histogram[gray]++;
6430
+ }
6431
+ const cdf = new Array(256).fill(0);
6432
+ cdf[0] = histogram[0];
6433
+ for (let i = 1; i < 256; i++) cdf[i] = cdf[i - 1] + histogram[i];
6434
+ const totalPixels = width * height;
6435
+ const cdfMin = cdf.find((v) => v > 0) || 0;
6436
+ for (let i = 0; i < data$1.length; i += 4) {
6437
+ const gray = Math.floor(.299 * data$1[i] + .587 * data$1[i + 1] + .114 * data$1[i + 2]);
6438
+ const newGray = Math.floor((cdf[gray] - cdfMin) / (totalPixels - cdfMin) * 255);
6439
+ newData[i] = newGray;
6440
+ newData[i + 1] = newGray;
6441
+ newData[i + 2] = newGray;
6442
+ newData[i + 3] = data$1[i + 3];
6443
+ }
6444
+ return {
6445
+ width,
6446
+ height,
6447
+ data: newData
6448
+ };
6449
+ }
6450
+ static tryRecognizeInRegion(imageData, regionName) {
6451
+ for (const strategy of [
6452
+ {
6453
+ name: "默认",
6454
+ enhance: false,
6455
+ options: void 0
6456
+ },
6457
+ {
6458
+ name: "增强对比度",
6459
+ enhance: true,
6460
+ options: void 0
6461
+ },
6462
+ {
6463
+ name: "attemptBoth",
6464
+ enhance: false,
6465
+ options: { inversionAttempts: "attemptBoth" }
6466
+ },
6467
+ {
6468
+ name: "增强+attemptBoth",
6469
+ enhance: true,
6470
+ options: { inversionAttempts: "attemptBoth" }
6471
+ }
6472
+ ]) try {
6473
+ logger.debug(` 尝试策略: ${strategy.name}`);
6474
+ const processedData = strategy.enhance ? this.enhanceContrast(imageData) : imageData;
6475
+ const code = (0, import_jsQR.default)(processedData.data, processedData.width, processedData.height, strategy.options);
6476
+ if (code && code.data) {
6477
+ logger.debug(`✓ 成功识别二维码 [区域: ${regionName}] [策略: ${strategy.name}]`);
6478
+ logger.debug(` 二维码内容: ${code.data}`);
6479
+ return code.data;
6480
+ } else logger.debug(` 策略 ${strategy.name} 未识别到二维码`);
6481
+ } catch (err) {
6482
+ logger.debug(` 策略 ${strategy.name} 执行失败: ${err}`);
6483
+ }
6484
+ logger.debug(` 区域 ${regionName} 识别失败,尝试下一个区域`);
6485
+ return null;
6486
+ }
6487
+ static parseImageBuffer(buffer) {
6488
+ try {
6489
+ const transformer = new Transformer(buffer);
6490
+ const metadata = transformer.metadataSync();
6491
+ const width = metadata.width;
6492
+ const height = metadata.height;
6493
+ const rawPixels = transformer.rawPixelsSync();
6494
+ logger.debug(`图片解析成功: ${width}x${height}, 格式: ${metadata.format}`);
6495
+ logger.debug(`原始像素数据长度: ${rawPixels.length}, 预期长度(RGBA): ${width * height * 4}`);
6496
+ const expectedRGBA = width * height * 4;
6497
+ const expectedRGB = width * height * 3;
6498
+ if (rawPixels.length === expectedRGB) {
6499
+ logger.debug("检测到 RGB 格式,转换为 RGBA");
6500
+ const rgbaData = new Uint8ClampedArray(expectedRGBA);
6501
+ for (let i = 0, j = 0; i < rawPixels.length; i += 3, j += 4) {
6502
+ rgbaData[j] = rawPixels[i];
6503
+ rgbaData[j + 1] = rawPixels[i + 1];
6504
+ rgbaData[j + 2] = rawPixels[i + 2];
6505
+ rgbaData[j + 3] = 255;
6506
+ }
6507
+ return {
6508
+ width,
6509
+ height,
6510
+ data: rgbaData
6511
+ };
6512
+ } else if (rawPixels.length === expectedRGBA) {
6513
+ logger.debug("检测到 RGBA 格式");
6514
+ return {
6515
+ width,
6516
+ height,
6517
+ data: Uint8ClampedArray.from(rawPixels)
6518
+ };
6519
+ } else {
6520
+ logger.warn(`像素数据长度不匹配: ${rawPixels.length}, 预期 RGB: ${expectedRGB}, RGBA: ${expectedRGBA}`);
6521
+ return null;
6522
+ }
6523
+ } catch (err) {
6524
+ logger.warn("图片解析失败:", err);
6525
+ return null;
6526
+ }
6527
+ }
6528
+ static scanFromBuffer(buffer) {
6529
+ try {
6530
+ const imageData = this.parseImageBuffer(buffer);
6531
+ if (!imageData) return null;
6532
+ const { width, height } = imageData;
6533
+ const dataSizeMB = (width * height * 4 / 1024 / 1024).toFixed(2);
6534
+ logger.debug(`图片数据: ${width}x${height}, 内存占用: ${dataSizeMB}MB`);
6535
+ if (width <= 2048 && height <= 2048) {
6536
+ logger.debug("图片尺寸适中,使用全图识别策略");
6537
+ const result = this.tryRecognizeInRegion(imageData, "全图");
6538
+ if (result) return result;
6539
+ }
6540
+ logger.debug(`图片尺寸较大 (${width}x${height}),使用分块扫描策略`);
6541
+ const scanRegions = [];
6542
+ const blockSize = 1024;
6543
+ logger.debug("添加四角扫描区域");
6544
+ scanRegions.push({
6545
+ name: "左上角",
6546
+ x: 0,
6547
+ y: 0,
6548
+ w: Math.min(blockSize, width),
6549
+ h: Math.min(blockSize, height)
6550
+ });
6551
+ if (width > blockSize) scanRegions.push({
6552
+ name: "右上角",
6553
+ x: width - Math.min(blockSize, width),
6554
+ y: 0,
6555
+ w: Math.min(blockSize, width),
6556
+ h: Math.min(blockSize, height)
6557
+ });
6558
+ if (height > blockSize) scanRegions.push({
6559
+ name: "左下角",
6560
+ x: 0,
6561
+ y: height - Math.min(blockSize, height),
6562
+ w: Math.min(blockSize, width),
6563
+ h: Math.min(blockSize, height)
6564
+ });
6565
+ if (width > blockSize && height > blockSize) scanRegions.push({
6566
+ name: "右下角",
6567
+ x: width - Math.min(blockSize, width),
6568
+ y: height - Math.min(blockSize, height),
6569
+ w: Math.min(blockSize, width),
6570
+ h: Math.min(blockSize, height)
6571
+ });
6572
+ if (width > blockSize * 2) {
6573
+ logger.debug("添加顶部/底部中间扫描区域");
6574
+ scanRegions.push({
6575
+ name: "顶部中",
6576
+ x: Math.floor((width - blockSize) / 2),
6577
+ y: 0,
6578
+ w: blockSize,
6579
+ h: Math.min(blockSize, height)
6580
+ });
6581
+ if (height > blockSize) scanRegions.push({
6582
+ name: "底部中",
6583
+ x: Math.floor((width - blockSize) / 2),
6584
+ y: height - blockSize,
6585
+ w: blockSize,
6586
+ h: blockSize
6587
+ });
6588
+ }
6589
+ if (height > blockSize * 2) {
6590
+ logger.debug("添加左右中间扫描区域");
6591
+ const middleY = Math.floor((height - blockSize) / 2);
6592
+ scanRegions.push({
6593
+ name: "左中",
6594
+ x: 0,
6595
+ y: middleY,
6596
+ w: Math.min(blockSize, width),
6597
+ h: blockSize
6598
+ });
6599
+ if (width > blockSize) scanRegions.push({
6600
+ name: "右中",
6601
+ x: width - blockSize,
6602
+ y: middleY,
6603
+ w: blockSize,
6604
+ h: blockSize
6605
+ });
6606
+ }
6607
+ logger.debug("添加滑动窗口扫描区域");
6608
+ const step = Math.floor(blockSize / 2);
6609
+ let slidingWindowCount = 0;
6610
+ for (let y = 0; y < height - blockSize; y += step) {
6611
+ scanRegions.push({
6612
+ name: `滑动窗口-${Math.floor(y / step)}`,
6613
+ x: 0,
6614
+ y,
6615
+ w: Math.min(blockSize, width),
6616
+ h: blockSize
6617
+ });
6618
+ slidingWindowCount++;
6619
+ if (scanRegions.length > 30) {
6620
+ logger.debug(`滑动窗口数量达到上限,停止添加 (已添加 ${slidingWindowCount} 个)`);
6621
+ break;
6622
+ }
6623
+ }
6624
+ logger.debug(`共生成 ${scanRegions.length} 个扫描区域,开始逐个扫描`);
6625
+ for (let i = 0; i < scanRegions.length; i++) {
6626
+ const region = scanRegions[i];
6627
+ logger.debug(`[${i + 1}/${scanRegions.length}] 扫描区域: ${region.name} (位置: ${region.x},${region.y}, 尺寸: ${region.w}x${region.h})`);
6628
+ const regionData = this.extractRegion(imageData, region.x, region.y, region.w, region.h);
6629
+ const result = this.tryRecognizeInRegion(regionData, region.name);
6630
+ if (result) {
6631
+ logger.debug(`二维码识别完成,共扫描了 ${i + 1}/${scanRegions.length} 个区域`);
6632
+ return result;
6633
+ }
6634
+ }
6635
+ logger.warn(`图片中未识别到二维码,已扫描所有 ${scanRegions.length} 个区域`);
6636
+ return null;
6637
+ } catch (error) {
6638
+ logger.error("解析图片时发生错误:", error);
6639
+ return null;
6640
+ }
6641
+ }
6642
+ static isSupportedPlatform(qrContent) {
6643
+ return [
6644
+ /(https?:\/\/)?(www|v|jx|m|jingxuan)\.(douyin|iesdouyin)\.com/i,
6645
+ /https:\/\/aweme\.snssdk\.com\/aweme\/v1\/play/i,
6646
+ /(bilibili\.com|b23\.tv|t\.bilibili\.com|bili2233\.cn|\bBV[1-9a-zA-Z]{10}\b|\bav\d+\b)/i,
6647
+ /(快手.*快手|v\.kuaishou\.com|kuaishou\.com)/,
6648
+ /(xiaohongshu\.com|xhslink\.com)/
6649
+ ].some((pattern) => pattern.test(qrContent));
6650
+ }
6651
+ };
6652
+ });
6389
6653
  var Tools, Common;
6390
6654
  var init_Common = __esmMin(async () => {
6391
6655
  await init_Config();
6656
+ await init_QRCodeScanner();
6392
6657
  await init_root();
6393
6658
  await init_module();
6394
6659
  Tools = class {
@@ -6405,6 +6670,19 @@ var init_Common = __esmMin(async () => {
6405
6670
  const reply = await e.bot.getMsg(e.contact, e.replyId);
6406
6671
  for (const v of reply.elements) if (v.type === "text") return v.text;
6407
6672
  else if (v.type === "json") return v.data;
6673
+ else if (v.type === "image") try {
6674
+ logger.debug("检测到引用消息为图片,尝试识别二维码...");
6675
+ const imageUrl = v.file;
6676
+ if (imageUrl) {
6677
+ const qrContent = await QRCodeScanner.scanFromUrl(imageUrl);
6678
+ if (qrContent && QRCodeScanner.isSupportedPlatform(qrContent)) {
6679
+ logger.debug(`从图片二维码中识别到支持的平台链接: ${qrContent}`);
6680
+ return qrContent;
6681
+ } else if (qrContent) logger.debug(`识别到二维码内容但不是支持的平台: ${qrContent}`);
6682
+ }
6683
+ } catch (error) {
6684
+ logger.error("识别图片二维码时发生错误:", error);
6685
+ }
6408
6686
  }
6409
6687
  return "";
6410
6688
  }
@@ -8063,6 +8341,7 @@ var utils_exports = __export({
8063
8341
  Downloader: () => Downloader,
8064
8342
  Network: () => Network,
8065
8343
  Networks: () => Network,
8344
+ QRCodeScanner: () => QRCodeScanner,
8066
8345
  Render: () => Render,
8067
8346
  Root: () => Root,
8068
8347
  ThrottleStream: () => ThrottleStream,
@@ -8097,6 +8376,7 @@ var init_utils$1 = __esmMin(async () => {
8097
8376
  init_Common();
8098
8377
  init_FFmpeg();
8099
8378
  init_Networks();
8379
+ init_QRCodeScanner();
8100
8380
  init_Render();
8101
8381
  });
8102
8382
  var BilibiliDBBase;
@@ -12685,15 +12965,16 @@ var init_setup = __esmMin(async () => {
12685
12965
  });
12686
12966
  await init_module();
12687
12967
  await init_semver();
12688
- var requireVersion = "1.14.1";
12968
+ var requireVersion = "1.14.4";
12689
12969
  if (process.env.NODE_ENV !== "development" && isSemverGreater(requireVersion, Root.karinVersion)) {
12690
12970
  const msg = `[karin-plugin-kkk] 插件构建时的 karin 版本 (${requireVersion}) 高于当前运行版本 (${Root.karinVersion}),可能会出现兼容性问题!`;
12691
12971
  logger.warn(msg);
12692
12972
  const notifiedSet = /* @__PURE__ */ new Set();
12693
12973
  karin$1.on(BOT_CONNECT, async (bot) => {
12974
+ const botId = bot.selfId;
12975
+ if (botId === "console") return;
12694
12976
  await new Promise((resolve$1) => setTimeout(resolve$1, 2e3));
12695
12977
  const masters = config.master();
12696
- const botId = bot.selfId;
12697
12978
  logger.info(`[karin-plugin-kkk] 监测到 Bot 连接: ${botId}, 准备发送版本警告`);
12698
12979
  let warningImage = null;
12699
12980
  try {
@@ -16992,7 +17273,7 @@ var DouYin = class extends Base {
16992
17273
  if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
16993
17274
  for (const [index, imageItem] of images.entries()) {
16994
17275
  imagenum++;
16995
- if (imageItem.clip_type === 2) {
17276
+ if (imageItem.clip_type === 2 || imageItem.clip_type === void 0) {
16996
17277
  image_url = imageItem.url_list[2] || imageItem.url_list[1];
16997
17278
  processedImages.push(segment.image(image_url));
16998
17279
  if (Config.app.removeCache === false) {
@@ -17230,7 +17511,7 @@ var DouYin = class extends Base {
17230
17511
  gender: userProfile.data.user.gender ?? 0,
17231
17512
  user_age: userProfile.data.user.user_age ?? 0
17232
17513
  } : void 0,
17233
- image_url: 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],
17514
+ image_url: this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
17234
17515
  cover_size: this.is_mp4 ? VideoData.data.aweme_detail.video.cover ? {
17235
17516
  width: VideoData.data.aweme_detail.video.cover_original_scale.width,
17236
17517
  height: VideoData.data.aweme_detail.video.cover_original_scale.height
@@ -17982,7 +18263,7 @@ var DouYinpush = class extends Base {
17982
18263
  if (pushItem.pushType === "favorite") {
17983
18264
  const authorUserInfo = "author_user_info" in Detail_Data ? Detail_Data.author_user_info : void 0;
17984
18265
  img$2 = await Render("douyin/favorite-list", {
17985
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
18266
+ image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
17986
18267
  desc: this.desc(Detail_Data, Detail_Data.desc),
17987
18268
  dianzan: this.count(Detail_Data.statistics.digg_count),
17988
18269
  pinglun: this.count(Detail_Data.statistics.comment_count),
@@ -18001,7 +18282,7 @@ var DouYinpush = class extends Base {
18001
18282
  } else if (pushItem.pushType === "recommend") {
18002
18283
  const authorUserInfo = "author_user_info" in Detail_Data ? Detail_Data.author_user_info : void 0;
18003
18284
  img$2 = await Render("douyin/recommend-list", {
18004
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
18285
+ image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
18005
18286
  desc: this.desc(Detail_Data, Detail_Data.desc),
18006
18287
  dianzan: this.count(Detail_Data.statistics.digg_count),
18007
18288
  pinglun: this.count(Detail_Data.statistics.comment_count),
@@ -18112,7 +18393,7 @@ var DouYinpush = class extends Base {
18112
18393
  bgmContext = await createLiveImageContext$1(liveimgbgm.filepath);
18113
18394
  }
18114
18395
  for (const item of images1) {
18115
- if (item.clip_type === 2) {
18396
+ if (item.clip_type === 2 || item.clip_type === void 0) {
18116
18397
  images.push(segment.image(item.url_list[0]));
18117
18398
  continue;
18118
18399
  }
@@ -18698,8 +18979,6 @@ var Kuaishou = class extends Base {
18698
18979
  return true;
18699
18980
  }
18700
18981
  };
18701
- var import_jsQR = __toESM(require_jsQR(), 1);
18702
- var import_png = require_png();
18703
18982
  await init_module();
18704
18983
  await init_Config();
18705
18984
  var safeScreenshot = async (page, screenshotPath) => {
@@ -19039,12 +19318,11 @@ var waitQrcode = async (page) => {
19039
19318
  const response = await fetch(originalImage);
19040
19319
  imageBuffer = Buffer.from(await response.arrayBuffer());
19041
19320
  }
19042
- const png = import_png.PNG.sync.read(imageBuffer);
19043
- const code = (0, import_jsQR.default)(Uint8ClampedArray.from(png.data), png.width, png.height);
19044
- if (code && code.data) {
19045
- logger.mark("二维码解码成功:", code.data);
19321
+ const qrContent = QRCodeScanner.scanFromBuffer(imageBuffer);
19322
+ if (qrContent) {
19323
+ logger.mark("二维码解码成功:", qrContent);
19046
19324
  return {
19047
- url: code.data,
19325
+ url: qrContent,
19048
19326
  originalImage
19049
19327
  };
19050
19328
  }
@@ -19133,8 +19411,8 @@ var HELP_MENU_CONFIG = [
19133
19411
  icon: "Link",
19134
19412
  roles: ["member", "master"]
19135
19413
  }, {
19136
- title: "「#解析」「#kkk解析」",
19137
- description: "在解析功能关闭的情况下,可对引用消息进行解析",
19414
+ title: "「#解析」「#kkk解析」「#弹幕解析」",
19415
+ description: "在解析功能关闭的情况下,可对引用消息进行解析;弹幕解析仅使用于「抖音」「哔哩哔哩」",
19138
19416
  icon: "Sparkles",
19139
19417
  roles: ["member", "master"]
19140
19418
  }]
@@ -19942,6 +20220,7 @@ await init_Config();
19942
20220
  await init_ErrorHandler();
19943
20221
  var reg = {
19944
20222
  douyin: /(https?:\/\/)?(www|v|jx|m|jingxuan)\.(douyin|iesdouyin)\.com/i,
20223
+ douyinCDN: /https:\/\/aweme\.snssdk\.com\/aweme\/v1\/play/i,
19945
20224
  bilibili: /(bilibili\.com|b23\.tv|t\.bilibili\.com|bili2233\.cn|\bBV[1-9a-zA-Z]{10}\b|\bav\d+\b)/i,
19946
20225
  kuaishou: /(快手.*快手|v\.kuaishou\.com|kuaishou\.com)/,
19947
20226
  xiaohongshu: /(xiaohongshu\.com|xhslink\.com)/
@@ -19997,7 +20276,19 @@ var handlePrefix = wrapWithErrorHandler(async (e, next) => {
19997
20276
  const originalMsg = e.msg;
19998
20277
  e.msg = await Common.getReplyMessage(e);
19999
20278
  if (/^#?弹幕解析/.test(originalMsg)) e.msg = "#弹幕解析 " + e.msg;
20000
- if (reg.douyin.test(e.msg)) return await handleDouyin(e, next);
20279
+ if (reg.douyinCDN.test(e.msg)) {
20280
+ logger.debug("检测到抖音 CDN 下载链接,直接下载视频");
20281
+ const videoIdMatch = e.msg.match(/video_id=([^&]+)/);
20282
+ const videoId = videoIdMatch ? videoIdMatch[1] : Date.now().toString();
20283
+ await downloadVideo(e, {
20284
+ video_url: e.msg,
20285
+ title: {
20286
+ timestampTitle: `tmp_${Date.now()}.mp4`,
20287
+ originTitle: `抖音视频_${videoId}.mp4`
20288
+ }
20289
+ });
20290
+ return true;
20291
+ } else if (reg.douyin.test(e.msg)) return await handleDouyin(e, next);
20001
20292
  else if (reg.bilibili.test(e.msg)) return await handleBilibili(e, next);
20002
20293
  else if (reg.kuaishou.test(e.msg)) return await handleKuaishou(e, next);
20003
20294
  else if (reg.xiaohongshu.test(e.msg)) return await handleXiaohongshu(e, next);
@@ -1,5 +1,5 @@
1
1
  import { n as __esmMin, o as __toESM, r as __export } from "./rolldown-runtime-BMXAG3ag.js";
2
- import { $ as init_ri, $t as ChartColumn, A as RiBellFill, At as Quote, B as RiMessage3Fill, Bt as Gift, C as init_fa6, Ct as Star, D as init_locale, Dt as Share2, E as init_ai, Et as ShieldCheck, F as RiHeart3Line, Ft as MessageCircle, G as RiShareForwardFill, Gt as Crown, H as RiRefreshLine, Ht as FileText, I as RiLinkM, It as MapPin, J as RiStarLine, Jt as Clock, K as RiSparkling2Fill, Kt as CornerDownLeft, L as RiListCheck2, Lt as Info, M as RiHashtag, Mt as Plug2, N as RiHeart2Line, Nt as Play, O as zhCN, Ot as ScanLine, P as RiHeart3Fill, Pt as Music, Q as RiVideoLine, Qt as CircleAlert, R as RiLiveLine, Rt as Heart, S as FaCommentDots, St as Terminal, T as AiFillStar, Tt as Shield, U as RiRobot2Fill, Ut as Eye, V as RiQuestionFill, Vt as Gamepad2, W as RiSendPlaneFill, Wt as ExternalLink, X as RiTiktokFill, Xt as CircleEllipsis, Y as RiThumbUpFill, Yt as CircleFadingArrowUp, Z as RiUserFollowLine, Zt as CircleCheckBig, _ as TbScan, _t as Users, an as init_clsx, at as format, b as init_hi, bt as TriangleAlert, ct as code_default, d as init_react_markdown, dt as HeroUIProvider, en as Calendar, et as LuFullscreen, f as Markdown, ft as require_jsx_runtime, g as init_si, gt as Zap, h as SiBilibili, ht as init_lucide_react, in as clsx_default, it as formatDistanceToNow, j as RiGroupLine, jt as QrCode, k as RiArrowRightFill, kt as Radio, l as init_rehype_highlight, lt as chip_default, m as init_md, mt as init_io5, nn as BookOpen, nt as init_date_fns, on as require_server_node, p as MdAccessTime, pt as IoSearch, q as RiStarFill, qt as Coins, rn as Bell, sn as require_react, st as init_dist, tn as Bookmark, tt as init_lu, u as rehypeHighlight, ut as button_default, v as init_tb, vt as User, w as AiFillHeart, wt as Smartphone, x as FaCodeBranch, xt as ThumbsUp, y as HiOutlineMenuAlt2, yt as UserPlus, z as RiLoginCircleFill, zt as Hash } from "./vendor-BsdYKPEs.js";
2
+ import { $ as init_lu, $t as Bookmark, A as RiHashtag, At as Plug2, B as RiRefreshLine, Bt as FileText, C as AiFillStar, Ct as Shield, D as RiArrowRightFill, Dt as Radio, E as zhCN, Et as ScanLine, F as RiListCheck2, Ft as Info, G as RiStarFill, Gt as Coins, H as RiSendPlaneFill, Ht as ExternalLink, I as RiLiveLine, It as Heart, J as RiTiktokFill, Jt as CircleEllipsis, K as RiStarLine, Kt as Clock, L as RiLoginCircleFill, Lt as Hash, M as RiHeart3Fill, Mt as Music, N as RiHeart3Line, Nt as MessageCircle, O as RiBellFill, Ot as Quote, P as RiLinkM, Pt as MapPin, Q as LuFullscreen, Qt as Calendar, R as RiMessage3Fill, Rt as Gift, S as AiFillHeart, St as Smartphone, T as init_locale, Tt as Share2, U as RiShareForwardFill, Ut as Crown, V as RiRobot2Fill, Vt as Eye, W as RiSparkling2Fill, Wt as CornerDownLeft, X as RiVideoLine, Xt as CircleAlert, Y as RiUserFollowLine, Yt as CircleCheckBig, Z as init_ri, Zt as ChartColumn, _ as HiOutlineMenuAlt2, _t as UserPlus, an as require_react, at as init_dist, b as FaCommentDots, bt as Terminal, c as rehypeHighlight, ct as button_default, d as MdAccessTime, dt as IoSearch, en as BookOpen, et as init_date_fns, f as init_md, ft as init_io5, g as init_tb, gt as User, h as TbScan, ht as Users, in as require_server_node, j as RiHeart2Line, jt as Play, k as RiGroupLine, kt as QrCode, l as init_react_markdown, lt as HeroUIProvider, m as init_si, mt as Zap, nn as clsx_default, nt as formatDistanceToNow, ot as code_default, p as SiBilibili, pt as init_lucide_react, q as RiThumbUpFill, qt as CircleFadingArrowUp, rn as init_clsx, rt as format, s as init_rehype_highlight, st as chip_default, tn as Bell, u as Markdown, ut as require_jsx_runtime, v as init_hi, vt as TriangleAlert, w as init_ai, wt as ShieldCheck, x as init_fa6, xt as Star, y as FaCodeBranch, yt as ThumbsUp, z as RiQuestionFill, zt as Gamepad2 } from "./vendor-B3KhGHI_.js";
3
3
  import { logger as logger$1 } from "node-karin";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
@@ -3634,7 +3634,7 @@ var init_videoInfo$1 = __esmMin(() => {
3634
3634
  className: "w-full h-full object-cover scale-150 blur-[120px] saturate-[1.8] opacity-50"
3635
3635
  }), (0, import_jsx_runtime$22.jsx)("div", { className: "absolute inset-0 bg-black/50" })]
3636
3636
  }), (0, import_jsx_runtime$22.jsx)("div", {
3637
- className: "relative w-full overflow-hidden text-white font-sans",
3637
+ className: "relative w-full overflow-hidden text-white",
3638
3638
  children: (0, import_jsx_runtime$22.jsxs)("div", {
3639
3639
  className: "relative z-10",
3640
3640
  children: [
@@ -3692,7 +3692,7 @@ var init_videoInfo$1 = __esmMin(() => {
3692
3692
  children: [(0, import_jsx_runtime$22.jsxs)("div", {
3693
3693
  className: "flex items-center justify-between mb-10",
3694
3694
  children: [duration ? (0, import_jsx_runtime$22.jsx)("div", {
3695
- className: "px-6 py-4 rounded-2xl bg-white/20 backdrop-blur-2xl border border-white/30 shadow-lg text-white text-3xl font-semibold tracking-wider",
3695
+ className: "px-6 py-4 rounded-2xl bg-white/20 backdrop-blur-2xl border border-white/30 shadow-lg text-white text-3xl tracking-wider",
3696
3696
  children: duration
3697
3697
  }) : (0, import_jsx_runtime$22.jsx)("div", {}), props.data.music && (0, import_jsx_runtime$22.jsxs)("div", {
3698
3698
  className: "flex items-center gap-6 p-4 rounded-3xl bg-white/20 backdrop-blur-2xl border border-white/30 shadow-lg overflow-hidden",
@@ -3734,7 +3734,7 @@ var init_videoInfo$1 = __esmMin(() => {
3734
3734
  className: "text-white/60",
3735
3735
  children: icon
3736
3736
  }), (0, import_jsx_runtime$22.jsx)("span", {
3737
- className: "text-4xl font-bold text-white/90 tabular-nums whitespace-nowrap",
3737
+ className: "text-4xl text-white/90 tabular-nums whitespace-nowrap",
3738
3738
  children: formatNumber$3(value)
3739
3739
  })]
3740
3740
  });
@@ -1,4 +1,4 @@
1
1
  import "./rolldown-runtime-BMXAG3ag.js";
2
- import "./vendor-BsdYKPEs.js";
3
- import { r as reactServerRender, t as template_default } from "./template-BjkVe9U9.js";
2
+ import "./vendor-B3KhGHI_.js";
3
+ import { r as reactServerRender, t as template_default } from "./template-CQ-u6h09.js";
4
4
  export { template_default as default, reactServerRender };