karin-plugin-kkk 2.21.0 → 2.21.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 +7 -0
- package/config/default_config/app.yaml +0 -3
- package/config/default_config/upload.yaml +6 -0
- package/lib/apps/admin.js +1 -1
- package/lib/apps/help.js +1 -1
- package/lib/apps/push.js +1 -1
- package/lib/apps/qrlogin.js +1 -1
- package/lib/apps/tools.js +1 -1
- package/lib/apps/update.js +1 -1
- package/lib/build-metadata.json +5 -5
- package/lib/core_chunk/{main-eqK3KHoJ.js → main-B-QVNIL7.js} +110 -21
- package/lib/index.js +1 -1
- package/lib/root.js +1 -1
- package/lib/web.config.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [2.21.1](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.21.0...v2.21.1) (2026-03-02)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### 🐛 错误修复
|
|
9
|
+
|
|
10
|
+
* 在线图片的上传方式配置 ([5d80aa5](https://github.com/ikenxuan/karin-plugin-kkk/commit/5d80aa571095e3851daa7120c724b75b38df83e7))
|
|
11
|
+
|
|
5
12
|
## [2.21.0](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.20.3...v2.21.0) (2026-03-02)
|
|
6
13
|
|
|
7
14
|
|
|
@@ -22,6 +22,12 @@ usegroupfile: false
|
|
|
22
22
|
# 群文件上传阈值,当文件大小超过该值时将使用群文件上传,单位:MB,「使用群文件上传」开启后才会生效
|
|
23
23
|
groupfilevalue: 100
|
|
24
24
|
|
|
25
|
+
# 网络图片发送方式,可选值:url / file / base64
|
|
26
|
+
# url: 直接传递 HTTP 链接给上游下载(可能因上游网络问题导致下载超时)
|
|
27
|
+
# file: 下载到本地使用 file 协议发送(需 Karin 与协议端在同一系统)
|
|
28
|
+
# base64: 下载后转换为 base64 发送(传输数据增大约 1/3,不在同一网络环境可能导致额外带宽成本)
|
|
29
|
+
imageSendMode: url
|
|
30
|
+
|
|
25
31
|
# 下载限速开关,开启后会限制下载速度,避免触发服务器风控导致连接被重置(ECONNRESET)
|
|
26
32
|
# 如果你的网络带宽很大且下载时经常报错"连接被重置",建议开启此选项
|
|
27
33
|
downloadThrottle: false
|
package/lib/apps/admin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
|
|
2
|
-
import { C as removeOldFiles, S as dylogin, w as task, x as biLogin } from "../core_chunk/main-
|
|
2
|
+
import { C as removeOldFiles, S as dylogin, w as task, x as biLogin } from "../core_chunk/main-B-QVNIL7.js";
|
|
3
3
|
import "../core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "../core_chunk/template-n3eb7E79.js";
|
|
5
5
|
export { biLogin, dylogin, removeOldFiles, 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-
|
|
2
|
+
import { b as version, y as help } from "../core_chunk/main-B-QVNIL7.js";
|
|
3
3
|
import "../core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "../core_chunk/template-n3eb7E79.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-
|
|
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-B-QVNIL7.js";
|
|
3
3
|
import "../core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "../core_chunk/template-n3eb7E79.js";
|
|
5
5
|
export { bilibiliPush, bilibiliPushList, changeBotID, douyinPush, douyinPushList, forcePush, setbiliPush, setdyPush, testDouyinPush };
|
package/lib/apps/qrlogin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
|
|
2
|
-
import { l as qrLogin } from "../core_chunk/main-
|
|
2
|
+
import { l as qrLogin } from "../core_chunk/main-B-QVNIL7.js";
|
|
3
3
|
import "../core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "../core_chunk/template-n3eb7E79.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-
|
|
2
|
+
import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-B-QVNIL7.js";
|
|
3
3
|
import "../core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "../core_chunk/template-n3eb7E79.js";
|
|
5
5
|
export { bilibiliAPP, douyinAPP, kuaishouAPP, prefix, xiaohongshuAPP };
|
package/lib/apps/update.js
CHANGED
|
@@ -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-
|
|
2
|
+
import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-B-QVNIL7.js";
|
|
3
3
|
import "../core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "../core_chunk/template-n3eb7E79.js";
|
|
5
5
|
export { kkkUpdate, kkkUpdateCommand, update };
|
package/lib/build-metadata.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.21.
|
|
3
|
-
"buildTime": "2026-03-
|
|
4
|
-
"buildTimestamp":
|
|
2
|
+
"version": "2.21.1",
|
|
3
|
+
"buildTime": "2026-03-02T12:56:19.212Z",
|
|
4
|
+
"buildTimestamp": 1772456179213,
|
|
5
5
|
"name": "karin-plugin-kkk",
|
|
6
6
|
"description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
|
|
7
7
|
"homepage": "https://github.com/ikenxuan/karin-plugin-kkk",
|
|
8
|
-
"commitHash": "
|
|
9
|
-
"shortCommitHash": "
|
|
8
|
+
"commitHash": "e43173f44582ab306a63ac13df2c7a3c4fb33200",
|
|
9
|
+
"shortCommitHash": "e43173f4"
|
|
10
10
|
}
|
|
@@ -6225,7 +6225,7 @@ var init_Base = __esmMin(() => {
|
|
|
6225
6225
|
} catch (error) {
|
|
6226
6226
|
if (options && options.active === false) await event.reply("视频文件上传失败" + JSON.stringify(error, null, 2));
|
|
6227
6227
|
logger.error("视频文件上传错误," + String(error));
|
|
6228
|
-
|
|
6228
|
+
throw error;
|
|
6229
6229
|
} finally {
|
|
6230
6230
|
const filePath = file.filepath;
|
|
6231
6231
|
logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/api/kkk/video/${encodeURIComponent(filePath.split("/").pop() ?? "")}`);
|
|
@@ -8279,29 +8279,55 @@ var init_ImageDownloader = __esmMin(() => {
|
|
|
8279
8279
|
ImageDownloader = class {
|
|
8280
8280
|
axiosInstance;
|
|
8281
8281
|
tempDir;
|
|
8282
|
+
maxRetries = 3;
|
|
8283
|
+
retryDelay = 1e3;
|
|
8282
8284
|
constructor(axiosInstance, tempDir) {
|
|
8283
8285
|
this.axiosInstance = axiosInstance;
|
|
8284
8286
|
this.tempDir = tempDir;
|
|
8285
8287
|
if (!fs.existsSync(this.tempDir)) fs.mkdirSync(this.tempDir, { recursive: true });
|
|
8286
8288
|
}
|
|
8287
8289
|
async processImage(imageUrl, title, index) {
|
|
8288
|
-
|
|
8290
|
+
switch (Config.upload.imageSendMode) {
|
|
8291
|
+
case "base64": try {
|
|
8292
|
+
return await this.downloadAndConvertToBase64(imageUrl);
|
|
8293
|
+
} catch (error) {
|
|
8294
|
+
logger.error(`图片转换 base64 失败,回退到原始 URL: ${imageUrl}`, error);
|
|
8295
|
+
return imageUrl;
|
|
8296
|
+
}
|
|
8297
|
+
case "file": try {
|
|
8298
|
+
const result = await this.downloadImage(imageUrl, title, index);
|
|
8299
|
+
if (result.shouldDelete) this.scheduleDelete(result.filePath);
|
|
8300
|
+
return result.filePath;
|
|
8301
|
+
} catch (error) {
|
|
8302
|
+
logger.error(`图片下载失败,回退到原始 URL: ${imageUrl}`, error);
|
|
8303
|
+
return imageUrl;
|
|
8304
|
+
}
|
|
8305
|
+
case "url":
|
|
8306
|
+
default: return imageUrl;
|
|
8307
|
+
}
|
|
8308
|
+
}
|
|
8309
|
+
async downloadAndConvertToBase64(imageUrl) {
|
|
8310
|
+
const filename = this.generateFilename(imageUrl);
|
|
8311
|
+
const filePath = path.join(this.tempDir, filename);
|
|
8289
8312
|
try {
|
|
8290
|
-
const
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8313
|
+
const response = await this.downloadWithRetry(imageUrl);
|
|
8314
|
+
fs.writeFileSync(filePath, response.data);
|
|
8315
|
+
const base64 = fs.readFileSync(filePath).toString("base64");
|
|
8316
|
+
logger.debug(`图片已下载并转换为 base64: ${imageUrl.substring(0, 50)}...`);
|
|
8317
|
+
return `base64://${base64}`;
|
|
8318
|
+
} finally {
|
|
8319
|
+
if (Config.app.removeCache && fs.existsSync(filePath)) try {
|
|
8320
|
+
fs.unlinkSync(filePath);
|
|
8321
|
+
logger.debug(`临时文件已删除: ${filePath}`);
|
|
8322
|
+
} catch (error) {
|
|
8323
|
+
logger.error(`删除临时文件失败: ${filePath}`, error);
|
|
8324
|
+
}
|
|
8296
8325
|
}
|
|
8297
8326
|
}
|
|
8298
8327
|
async downloadImage(imageUrl, title, index) {
|
|
8299
8328
|
const filename = this.generateFilename(imageUrl, title, index);
|
|
8300
8329
|
const filePath = path.join(this.tempDir, filename);
|
|
8301
|
-
const response = await this.
|
|
8302
|
-
responseType: "arraybuffer",
|
|
8303
|
-
timeout: 3e4
|
|
8304
|
-
});
|
|
8330
|
+
const response = await this.downloadWithRetry(imageUrl);
|
|
8305
8331
|
fs.writeFileSync(filePath, response.data);
|
|
8306
8332
|
logger.debug(`图片已下载: ${filePath}`);
|
|
8307
8333
|
return {
|
|
@@ -8309,6 +8335,28 @@ var init_ImageDownloader = __esmMin(() => {
|
|
|
8309
8335
|
shouldDelete: true
|
|
8310
8336
|
};
|
|
8311
8337
|
}
|
|
8338
|
+
async downloadWithRetry(imageUrl, retryCount = 0) {
|
|
8339
|
+
try {
|
|
8340
|
+
const response = await this.axiosInstance.get(imageUrl, {
|
|
8341
|
+
responseType: "arraybuffer",
|
|
8342
|
+
timeout: 3e4
|
|
8343
|
+
});
|
|
8344
|
+
if (retryCount > 0) logger.info(`图片下载成功(重试 ${retryCount} 次后): ${imageUrl.substring(0, 50)}...`);
|
|
8345
|
+
return response;
|
|
8346
|
+
} catch (error) {
|
|
8347
|
+
if (retryCount < this.maxRetries) {
|
|
8348
|
+
const delay$1 = this.retryDelay * Math.pow(2, retryCount);
|
|
8349
|
+
logger.warn(`图片下载失败,${delay$1}ms 后进行第 ${retryCount + 1}/${this.maxRetries} 次重试: ${imageUrl.substring(0, 50)}...`, error);
|
|
8350
|
+
await this.sleep(delay$1);
|
|
8351
|
+
return this.downloadWithRetry(imageUrl, retryCount + 1);
|
|
8352
|
+
}
|
|
8353
|
+
logger.error(`图片下载失败,已重试 ${this.maxRetries} 次: ${imageUrl}`, error);
|
|
8354
|
+
throw error;
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8357
|
+
sleep(ms) {
|
|
8358
|
+
return new Promise((resolve$1) => setTimeout(resolve$1, ms));
|
|
8359
|
+
}
|
|
8312
8360
|
generateFilename(imageUrl, title, index) {
|
|
8313
8361
|
const ext = this.getExtension(imageUrl);
|
|
8314
8362
|
if (Config.app.removeCache) return `tmp_${Date.now()}${index !== void 0 ? `_${index}` : ""}${ext}`;
|
|
@@ -11225,12 +11273,6 @@ var init_app_schema = __esmMin(() => {
|
|
|
11225
11273
|
label: "缓存删除",
|
|
11226
11274
|
description: "下载的视频缓存自动删除,非必要不修改!"
|
|
11227
11275
|
},
|
|
11228
|
-
{
|
|
11229
|
-
key: "downloadImageLocally",
|
|
11230
|
-
type: "switch",
|
|
11231
|
-
label: "本地下载图片",
|
|
11232
|
-
description: "发送图片时由插件本地下载后使用 file 协议传递,而非直接传递 HTTP 链接给上游下载"
|
|
11233
|
-
},
|
|
11234
11276
|
{
|
|
11235
11277
|
type: "divider",
|
|
11236
11278
|
title: "解析优先级设置"
|
|
@@ -12622,6 +12664,26 @@ var init_upload_schema = __esmMin(() => {
|
|
|
12622
12664
|
disabled: $or($not("usegroupfile"), $var("sendbase64")),
|
|
12623
12665
|
rules: [{ min: 1 }]
|
|
12624
12666
|
},
|
|
12667
|
+
{
|
|
12668
|
+
key: "imageSendMode",
|
|
12669
|
+
type: "radio",
|
|
12670
|
+
label: "网络图片发送方式",
|
|
12671
|
+
description: "选择发送网络图片的方式:\n• URL - 直接传递链接给上游(可能因上游网络问题超时)\n• File - 下载后用 file 协议发送(需 Karin 与协议端同系统)\n• Base64 - 转 base64 发送(传输数据增大 1/3,不在同一网络环境可能导致额外带宽成本)",
|
|
12672
|
+
options: [
|
|
12673
|
+
{
|
|
12674
|
+
label: "URL 链接(直接传递)",
|
|
12675
|
+
value: "url"
|
|
12676
|
+
},
|
|
12677
|
+
{
|
|
12678
|
+
label: "File 协议(本地文件)",
|
|
12679
|
+
value: "file"
|
|
12680
|
+
},
|
|
12681
|
+
{
|
|
12682
|
+
label: "Base64(编码传输)",
|
|
12683
|
+
value: "base64"
|
|
12684
|
+
}
|
|
12685
|
+
]
|
|
12686
|
+
},
|
|
12625
12687
|
{
|
|
12626
12688
|
type: "divider",
|
|
12627
12689
|
title: "上传拦截配置"
|
|
@@ -13272,22 +13334,27 @@ var init_handler = __esmMin(async () => {
|
|
|
13272
13334
|
};
|
|
13273
13335
|
wrapWithErrorHandler = (fn, options) => async (e, next) => {
|
|
13274
13336
|
const emojiManager = e ? new EmojiReactionManager(e) : void 0;
|
|
13337
|
+
let processingTimer = null;
|
|
13338
|
+
let successTimer = null;
|
|
13275
13339
|
if (emojiManager) {
|
|
13276
13340
|
await emojiManager.add("EYES");
|
|
13277
|
-
setTimeout(() => {
|
|
13341
|
+
processingTimer = setTimeout(() => {
|
|
13278
13342
|
emojiManager.add("PROCESSING").catch(() => {});
|
|
13279
13343
|
}, 1500);
|
|
13280
13344
|
}
|
|
13281
13345
|
const ctx = logger.runContext(async () => fn(e, next));
|
|
13282
13346
|
try {
|
|
13283
13347
|
const result = await ctx.run();
|
|
13284
|
-
if (emojiManager) setTimeout(() => {
|
|
13348
|
+
if (emojiManager) successTimer = setTimeout(() => {
|
|
13285
13349
|
emojiManager.replace("PROCESSING", "SUCCESS").catch(() => {});
|
|
13286
13350
|
}, 1500);
|
|
13287
13351
|
return result;
|
|
13288
13352
|
} catch (error) {
|
|
13353
|
+
if (processingTimer) clearTimeout(processingTimer);
|
|
13354
|
+
if (successTimer) clearTimeout(successTimer);
|
|
13289
13355
|
if (emojiManager) {
|
|
13290
|
-
|
|
13356
|
+
const processingEmojiId = emojiManager["getPlatformEmojiId"]("PROCESSING");
|
|
13357
|
+
if (emojiManager.has(processingEmojiId)) await emojiManager.remove("PROCESSING");
|
|
13291
13358
|
await emojiManager.add("ERROR");
|
|
13292
13359
|
}
|
|
13293
13360
|
await new Promise((resolve$1) => setTimeout(resolve$1, 100));
|
|
@@ -14787,6 +14854,28 @@ const webConfig = defineConfig({
|
|
|
14787
14854
|
rules: [{ min: 1 }],
|
|
14788
14855
|
isDisabled: !all.upload.usegroupfile || all.upload.sendbase64
|
|
14789
14856
|
}),
|
|
14857
|
+
components.radio.group("imageSendMode", {
|
|
14858
|
+
label: "网络图片发送方式",
|
|
14859
|
+
orientation: "vertical",
|
|
14860
|
+
defaultValue: all.upload.imageSendMode,
|
|
14861
|
+
radio: [
|
|
14862
|
+
components.radio.create("imageSendMode:radio-1", {
|
|
14863
|
+
label: "URL 链接(直接传递)",
|
|
14864
|
+
description: "直接传递 HTTP 链接给上游下载,可能因上游网络问题导致下载超时",
|
|
14865
|
+
value: "url"
|
|
14866
|
+
}),
|
|
14867
|
+
components.radio.create("imageSendMode:radio-2", {
|
|
14868
|
+
label: "File 协议(本地文件)",
|
|
14869
|
+
description: "下载到本地后使用 file 协议发送,需 Karin 与协议端在同一系统",
|
|
14870
|
+
value: "file"
|
|
14871
|
+
}),
|
|
14872
|
+
components.radio.create("imageSendMode:radio-3", {
|
|
14873
|
+
label: "Base64(编码传输)",
|
|
14874
|
+
description: "下载后转换为 base64 发送,传输数据增大约 1/3,不在同一网络环境可能导致额外带宽成本",
|
|
14875
|
+
value: "base64"
|
|
14876
|
+
})
|
|
14877
|
+
]
|
|
14878
|
+
}),
|
|
14790
14879
|
components.divider.create("divider-upload-limit", {
|
|
14791
14880
|
description: "上传拦截配置",
|
|
14792
14881
|
descPosition: 20
|
package/lib/index.js
CHANGED
package/lib/root.js
CHANGED
package/lib/web.config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
|
|
2
|
-
import { E as web_config_default, T as webConfig } from "./core_chunk/main-
|
|
2
|
+
import { E as web_config_default, T as webConfig } from "./core_chunk/main-B-QVNIL7.js";
|
|
3
3
|
import "./core_chunk/vendor-CYCcUtqE.js";
|
|
4
4
|
import "./core_chunk/template-n3eb7E79.js";
|
|
5
5
|
export { web_config_default as default, webConfig };
|