karin-plugin-kkk 2.45.0-beta.3 → 2.45.0-beta.4

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,14 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [2.45.0-beta.4](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.45.0-beta.3...v2.45.0-beta.4) (2026-10-06)
6
+
7
+
8
+ ### 🐛 错误修复
9
+
10
+ * 优化更新检测逻辑,支持逐渠道版本锁定与提醒 ([ddfd011](https://github.com/ikenxuan/karin-plugin-kkk/commit/ddfd011ac3bcd49789870a2e154003973c52cea9))
11
+ * 优化更新逻辑以支持金丝雀用户的版本解锁和范围起点解析 ([4a28ce5](https://github.com/ikenxuan/karin-plugin-kkk/commit/4a28ce55b82115683713ad6985d456751f151c23))
12
+
5
13
  ## [2.45.0-beta.3](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.45.0-beta.2...v2.45.0-beta.3) (2026-10-06)
6
14
 
7
15
 
@@ -1,10 +1,10 @@
1
1
  {
2
- "version": "2.45.0-beta.3",
3
- "buildTime": "2026-10-06T06:00:56.704Z",
4
- "buildTimestamp": 1791266456705,
2
+ "version": "2.45.0-beta.4",
3
+ "buildTime": "2026-10-06T07:34:14.563Z",
4
+ "buildTimestamp": 1791272054563,
5
5
  "name": "karin-plugin-kkk",
6
6
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
7
7
  "homepage": "https://github.com/ikenxuan/karin-plugin-kkk",
8
- "commitHash": "030579c9a3c0615a5bb50526953874b9109ba9ad",
9
- "shortCommitHash": "030579c9"
8
+ "commitHash": "e6ff6df77f6305256a35f531a2f0c26f95fa3341",
9
+ "shortCommitHash": "e6ff6df7"
10
10
  }
@@ -7841,8 +7841,17 @@ var Render = async (event, path, data) => {
7841
7841
  const templateType = path.split("/")[0];
7842
7842
  let hasUpdate = false;
7843
7843
  try {
7844
- const lockedVersion = await db.get("kkk:update:lock");
7845
- if (typeof lockedVersion === "string" && lockedVersion.length > 0) hasUpdate = isSemverGreater(lockedVersion, Root.pluginVersion);
7844
+ for (const channel of [
7845
+ "stable",
7846
+ "beta",
7847
+ "rc"
7848
+ ]) {
7849
+ const lockedVersion = await db.get(`kkk:update:lock:${channel}`);
7850
+ if (typeof lockedVersion === "string" && lockedVersion.length > 0 && isSemverGreater(lockedVersion, Root.pluginVersion)) {
7851
+ hasUpdate = true;
7852
+ break;
7853
+ }
7854
+ }
7846
7855
  } catch {}
7847
7856
  const useDarkTheme = await resolveUseDarkTheme(path, data);
7848
7857
  const result = await renderTemplate(path, data, {
@@ -24561,6 +24570,15 @@ var updateHelp_default = defineTemplate({
24561
24570
  active: true
24562
24571
  };
24563
24572
  const order = semverCompare(channel.version ?? "0.0.0", data.currentVersion);
24573
+ if (channel.hasUpdate !== void 0) return channel.hasUpdate ? {
24574
+ dot: palette.success,
24575
+ text: "有更新可升级",
24576
+ active: true
24577
+ } : {
24578
+ dot: palette.faint,
24579
+ text: order < 0 ? "低于当前版本" : "已是最新",
24580
+ active: false
24581
+ };
24564
24582
  if (order > 0) return {
24565
24583
  dot: palette.success,
24566
24584
  text: "有更新可升级",
@@ -40321,6 +40339,23 @@ var getLagVersionCount = (changelog, localVersion, remoteVersion) => {
40321
40339
  }).length;
40322
40340
  };
40323
40341
  /**
40342
+ * 金丝雀用户的 range 起点解析:semver 的 ASCII 序(beta < canary)无法表达
40343
+ * 「发布晚于本机构建」——改按 pkg.pr.new 发布历史的时间线,取本机构建时间
40344
+ * 之前的最后一个发布版本作为 range 起点。失败返回 undefined(回退 semver)。
40345
+ */
40346
+ var resolveCanaryRangeStart = async (buildTimestamp) => {
40347
+ if (!buildTimestamp) return void 0;
40348
+ try {
40349
+ const nodes = (await axios.get("https://pkg.pr.new/api/repo/commits?owner=ikenxuan&repo=karin-plugin-kkk", { timeout: 1e4 })).data?.target?.history?.nodes;
40350
+ if (!Array.isArray(nodes)) return void 0;
40351
+ for (const n of nodes) {
40352
+ const t = n.authoredDate ? new Date(n.authoredDate).getTime() : 0;
40353
+ const v = /v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/.exec(n.message ?? "")?.[1];
40354
+ if (t > 0 && t <= buildTimestamp && v) return v;
40355
+ }
40356
+ } catch {}
40357
+ };
40358
+ /**
40324
40359
  * 获取远程构建元数据
40325
40360
  * @param version 远程版本号
40326
40361
  * @returns 构建元数据对象,如果获取失败则返回 null
@@ -40393,9 +40428,10 @@ var getChangelogImage = async (ctx, props) => {
40393
40428
  lagVersionCount = getLagVersionCount(changelog, props.localVersion, props.remoteVersion);
40394
40429
  const remoteMeta = await getRemoteBuildMetadata(props.remoteVersion);
40395
40430
  if (remoteMeta?.buildTime) buildTime = formatBuildTime(remoteMeta.buildTime);
40431
+ const rangeStart = parseReleaseChannel(props.localVersion) === "Canary" ? await resolveCanaryRangeStart(getBuildMetadata()?.buildTimestamp) ?? props.localVersion : props.localVersion;
40396
40432
  changelog = range({
40397
40433
  data: changelog,
40398
- startVersion: props.localVersion,
40434
+ startVersion: rangeStart,
40399
40435
  endVersion: versionCore(props.remoteVersion),
40400
40436
  compare: "semver"
40401
40437
  });
@@ -40403,9 +40439,10 @@ var getChangelogImage = async (ctx, props) => {
40403
40439
  try {
40404
40440
  changelog = fs.readFileSync(Root.pluginPath + "/CHANGELOG.md", "utf8");
40405
40441
  lagVersionCount = getLagVersionCount(changelog, props.localVersion, props.remoteVersion);
40442
+ const rangeStart = parseReleaseChannel(props.localVersion) === "Canary" ? await resolveCanaryRangeStart(getBuildMetadata()?.buildTimestamp) ?? props.localVersion : props.localVersion;
40406
40443
  changelog = range({
40407
40444
  data: changelog,
40408
- startVersion: props.localVersion,
40445
+ startVersion: rangeStart,
40409
40446
  endVersion: versionCore(props.remoteVersion),
40410
40447
  compare: "semver"
40411
40448
  });
@@ -40428,45 +40465,55 @@ var getChangelogImage = async (ctx, props) => {
40428
40465
  };
40429
40466
  //#endregion
40430
40467
  //#region src/apps/update.ts
40431
- var UPDATE_LOCK_KEY = "kkk:update:lock";
40468
+ /** 更新提醒的订阅渠道(与面板 UpdateNotifyChannels 可选项一致) */
40469
+ var UPDATE_CHANNELS = [
40470
+ "stable",
40471
+ "beta",
40472
+ "rc"
40473
+ ];
40474
+ /** 各渠道更新提醒锁:记录该渠道上次已推送的版本,检测到新版本推送一次后上锁 */
40475
+ var updateLockKey = (channel) => `kkk:update:lock:${channel}`;
40432
40476
  var UPDATE_MSGID_KEY = "kkk:update:msgid";
40433
40477
  /**
40434
40478
  * 定时更新检测处理器
40435
- * 按订阅渠道检查远程版本(latest 恒参与 —— 装 prerelease 的用户也要收到转正提醒),
40436
- * 候选取 semver 最大且大于本地者,渲染变更日志并私聊通知所有主人。
40479
+ * 逐渠道检查远程 dist-tag 版本:检测到该渠道有新版本就推送一次然后上锁,
40480
+ * 各渠道锁相互独立、互不挤占;金丝雀安装按构建时间线判定新版本。
40437
40481
  *
40438
40482
  * @returns 是否继续后续任务
40439
40483
  */
40440
40484
  var Handler = async () => {
40441
- const tags = ["latest", ...(Config.app.UpdateNotifyChannels?.length ? Config.app.UpdateNotifyChannels : ["stable"]).filter((c) => c !== "stable")];
40485
+ const channels = Config.app.UpdateNotifyChannels?.length ? Config.app.UpdateNotifyChannels : ["stable"];
40486
+ const tagOf = (c) => c === "stable" ? "latest" : c;
40442
40487
  const installedIsCanary = parseReleaseChannel(Root.pluginVersion) === "Canary";
40443
40488
  const installedBuildTime = getBuildMetadata()?.buildTimestamp;
40444
- const remotes = await Promise.all(tags.map(async (tag) => {
40489
+ const tagVersions = /* @__PURE__ */ new Map();
40490
+ await Promise.all([...new Set(channels.map(tagOf))].map(async (tag) => {
40445
40491
  try {
40446
- return await getRemotePkgVersion(Root.pluginName, tag) || null;
40492
+ tagVersions.set(tag, await getRemotePkgVersion(Root.pluginName, tag) || null);
40447
40493
  } catch {
40448
- return null;
40494
+ tagVersions.set(tag, null);
40449
40495
  }
40450
40496
  }));
40451
40497
  const canaryInfo = installedIsCanary ? await fetchCanaryInfo() : null;
40452
- const remote = [...new Set(remotes.filter((v) => !!v))].filter((version) => {
40498
+ const isNewVersion = (version) => {
40453
40499
  if (installedIsCanary && installedBuildTime !== void 0) {
40454
40500
  const releaseNode = canaryInfo?.nodes.find((n) => n.message.includes(version));
40455
40501
  if (releaseNode) return releaseNode.time > installedBuildTime;
40456
40502
  }
40457
40503
  return isSemverGreater(version, Root.pluginVersion);
40458
- }).sort((a, b) => isSemverGreater(b, a) ? 1 : -1)[0];
40459
- if (!remote) return true;
40460
- try {
40461
- const lockedVersion = await db.get(UPDATE_LOCK_KEY);
40462
- if (typeof lockedVersion === "string" && lockedVersion.length > 0) {
40463
- if (!isSemverGreater(lockedVersion, Root.pluginVersion)) await db.del(UPDATE_LOCK_KEY);
40464
- else if (!isSemverGreater(remote, lockedVersion)) return true;
40465
- }
40466
- } catch {}
40467
- try {
40468
- await db.set(UPDATE_LOCK_KEY, remote);
40469
- } catch {}
40504
+ };
40505
+ const pending = /* @__PURE__ */ new Set();
40506
+ for (const channel of channels) {
40507
+ const version = tagVersions.get(tagOf(channel)) ?? null;
40508
+ if (!version || !isNewVersion(version)) continue;
40509
+ try {
40510
+ const locked = await db.get(updateLockKey(channel));
40511
+ if (typeof locked === "string" && locked === version) continue;
40512
+ await db.set(updateLockKey(channel), version);
40513
+ } catch {}
40514
+ pending.add(version);
40515
+ }
40516
+ if (pending.size === 0) return true;
40470
40517
  const masters = config.master().filter((id) => id !== "console");
40471
40518
  if (masters.length === 0) return true;
40472
40519
  const botItems = karin.getAllBotList().filter((b) => b.bot.account.name !== "console");
@@ -40486,12 +40533,16 @@ var Handler = async () => {
40486
40533
  const botToImage = /* @__PURE__ */ new Map();
40487
40534
  for (const item of botItems) {
40488
40535
  if (!Array.from(masterToBot.entries()).some(([, b]) => b.account.selfId === item.bot.account.selfId)) continue;
40489
- const img = await getChangelogImage({ bot: item.bot }, {
40490
- localVersion: Root.pluginVersion,
40491
- remoteVersion: remote,
40492
- Tip: true
40493
- });
40494
- if (img && img.length > 0) botToImage.set(item.bot.account.selfId, [segment.text("karin-plugin-kkk 有新的更新!"), ...img]);
40536
+ const elements = [segment.text("karin-plugin-kkk 有新的更新!")];
40537
+ for (const version of pending) {
40538
+ const img = await getChangelogImage({ bot: item.bot }, {
40539
+ localVersion: Root.pluginVersion,
40540
+ remoteVersion: version,
40541
+ Tip: true
40542
+ });
40543
+ if (img && img.length > 0) elements.push(...img);
40544
+ }
40545
+ if (elements.length > 1) botToImage.set(item.bot.account.selfId, elements);
40495
40546
  }
40496
40547
  let storedMsgId;
40497
40548
  for (const owner of masters) {
@@ -40516,7 +40567,7 @@ var handleUpdateHook = wrapWithErrorHandler(async (e) => {
40516
40567
  const msgResult = await e.reply(`${Root.pluginName} 更新成功!\n${result.local} -> ${result.remote}\n开始执行重启......`);
40517
40568
  if (msgResult.messageId) try {
40518
40569
  await db.del(UPDATE_MSGID_KEY);
40519
- await db.del(UPDATE_LOCK_KEY);
40570
+ for (const ch of UPDATE_CHANNELS) await db.del(updateLockKey(ch));
40520
40571
  } catch {}
40521
40572
  await restart(e.selfId, e.contact, msgResult.messageId);
40522
40573
  } else e.reply(`${Root.pluginName} 更新失败: ${result.data ?? "更新执行失败"}`);
@@ -40586,7 +40637,7 @@ var installAndRestart = async (e, version) => {
40586
40637
  const msgResult = await e.reply(`${Root.pluginName} 更新成功!→ ${version}\n开始执行重启......`);
40587
40638
  if (msgResult.messageId) try {
40588
40639
  await db.del(UPDATE_MSGID_KEY);
40589
- await db.del(UPDATE_LOCK_KEY);
40640
+ for (const ch of UPDATE_CHANNELS) await db.del(updateLockKey(ch));
40590
40641
  } catch {}
40591
40642
  await restart(e.selfId, e.contact, msgResult.messageId);
40592
40643
  };
@@ -40743,7 +40794,7 @@ var kkkUpdateCommand = karin.command(/^#?kkk更新(?:\s*(\S+))?$/, handleKkkUpda
40743
40794
  });
40744
40795
  var kkkUpdateTest = process.env.NODE_ENV === "development" && karin.command("test", async (_e, next) => {
40745
40796
  await db.del(UPDATE_MSGID_KEY);
40746
- await db.del(UPDATE_LOCK_KEY);
40797
+ for (const ch of UPDATE_CHANNELS) await db.del(updateLockKey(ch));
40747
40798
  await Handler();
40748
40799
  next();
40749
40800
  }, { name: "kkk-更新检测-测试" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karin-plugin-kkk",
3
- "version": "2.45.0-beta.3",
3
+ "version": "2.45.0-beta.4",
4
4
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
5
5
  "keywords": [
6
6
  "karin",
@@ -83,7 +83,7 @@
83
83
  "@lobehub/webfont-harmony-sans-sc": "^1.0.0",
84
84
  "emoji-datasource-apple": "16.0.0"
85
85
  },
86
- "timestamp": "2026-10-06T05:59:30.186Z",
86
+ "timestamp": "2026-10-06T07:32:55.907Z",
87
87
  "engines": {
88
88
  "node": ">=18.0.0"
89
89
  }