koishi-plugin-cfmrmod 1.0.7 → 1.0.9

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.
@@ -1893,6 +1893,28 @@ async function fetchCurseForgeDetail(id, apiKey, timeout, cfUrl = null) {
1893
1893
  }
1894
1894
  // 搜索入口 (MR)
1895
1895
  async function searchModrinth(query, type, timeout) {
1896
+ // 先尝试直接通过ID/slug获取项目详情
1897
+ try {
1898
+ const project = await fetchJson(`${MR_BASE}/project/${encodeURIComponent(query)}`, {}, timeout);
1899
+ if (project && project.slug) {
1900
+ // 成功获取到项目,返回单个结果
1901
+ return [{
1902
+ platform: 'Modrinth',
1903
+ id: project.slug,
1904
+ name: project.title,
1905
+ author: project.author || 'Unknown',
1906
+ summary: project.description,
1907
+ type,
1908
+ icon: project.icon_url,
1909
+ downloads: project.downloads || 0,
1910
+ updated: new Date(project.updated || project.published).toLocaleDateString()
1911
+ }];
1912
+ }
1913
+ }
1914
+ catch (e) {
1915
+ // ID/slug获取失败,继续使用搜索API
1916
+ }
1917
+ // 使用搜索API
1896
1918
  const facet = MR_FACET_MAP[type];
1897
1919
  const url = `${MR_BASE}/search?query=${encodeURIComponent(query)}&facets=[["${facet}"]]&limit=20`;
1898
1920
  const json = await fetchJson(url, {}, timeout);
@@ -2029,8 +2051,10 @@ function apply(ctx, config) {
2029
2051
  '\n请输入序号查看详情 (p/n 翻页)';
2030
2052
  };
2031
2053
  const handleSearch = async (session, platform, type, keyword) => {
2032
- if (!keyword)
2033
- return session.send('请输入关键词');
2054
+ if (!keyword) {
2055
+ await session.send('请输入关键词');
2056
+ return;
2057
+ }
2034
2058
  let results = [];
2035
2059
  try {
2036
2060
  if (platform === 'mr')
@@ -2039,10 +2063,13 @@ function apply(ctx, config) {
2039
2063
  results = await searchCurseForge(keyword, type, config.curseforgeApiKey, config.requestTimeout, config.curseforgeGameId);
2040
2064
  }
2041
2065
  catch (e) {
2042
- return session.send(`搜索出错: ${e.message}`);
2066
+ await session.send(`搜索出错: ${e.message}`);
2067
+ return;
2068
+ }
2069
+ if (!results.length) {
2070
+ await session.send('未找到结果');
2071
+ return;
2043
2072
  }
2044
- if (!results.length)
2045
- return session.send('未找到结果');
2046
2073
  if (results.length === 1) {
2047
2074
  const item = results[0];
2048
2075
  try {
@@ -2653,7 +2653,7 @@ function apply(ctx, config) {
2653
2653
  const sentMessageIds = await session.send(listText);
2654
2654
  searchStates.set(session.cid, {
2655
2655
  results,
2656
- page: 0,
2656
+ pageIndex: 0,
2657
2657
  type: 'mod',
2658
2658
  messageIds: Array.isArray(sentMessageIds) ? sentMessageIds : [sentMessageIds],
2659
2659
  timer: setTimeout(() => {
@@ -2686,6 +2686,7 @@ function apply(ctx, config) {
2686
2686
  if (input === 'p' || input === 'n') {
2687
2687
  // 加入队列处理翻页,防止并发
2688
2688
  enqueue(session, 'page-turn', async () => {
2689
+ var _a, _b;
2689
2690
  // 重新获取状态,防止排队期间状态丢失
2690
2691
  const currentState = searchStates.get(session.cid);
2691
2692
  if (!currentState)
@@ -2693,10 +2694,11 @@ function apply(ctx, config) {
2693
2694
  clearTimeout(currentState.timer);
2694
2695
  currentState.timer = setTimeout(() => searchStates.delete(session.cid), TIMEOUT_MS);
2695
2696
  const total = Math.ceil(currentState.results.length / PAGE_SIZE);
2696
- let newIndex = currentState.pageIndex;
2697
- if (input === 'n' && currentState.pageIndex < total - 1)
2697
+ const currentPage = Number((_b = (_a = currentState.pageIndex) !== null && _a !== void 0 ? _a : currentState.page) !== null && _b !== void 0 ? _b : 0) || 0;
2698
+ let newIndex = currentPage;
2699
+ if (input === 'n' && currentPage < total - 1)
2698
2700
  newIndex++;
2699
- else if (input === 'p' && currentState.pageIndex > 0)
2701
+ else if (input === 'p' && currentPage > 0)
2700
2702
  newIndex--;
2701
2703
  else {
2702
2704
  await session.send('没有更多页面了。');
@@ -2705,8 +2707,8 @@ function apply(ctx, config) {
2705
2707
  // 撤回旧列表(可选,为了整洁)
2706
2708
  await tryWithdraw(session, currentState.messageIds);
2707
2709
  currentState.pageIndex = newIndex;
2708
- const newMsgIds = await session.send(formatListPage(currentState.results, currentState.pageIndex, currentState.type));
2709
- currentState.messageIds = newMsgIds;
2710
+ const newMsgIds = await session.send(formatListPage(currentState.results, newIndex, currentState.type));
2711
+ currentState.messageIds = Array.isArray(newMsgIds) ? newMsgIds : [newMsgIds];
2710
2712
  });
2711
2713
  return;
2712
2714
  }
@@ -2715,12 +2717,13 @@ function apply(ctx, config) {
2715
2717
  if (!isNaN(choice) && choice >= 1) {
2716
2718
  // 加入队列处理生成卡片
2717
2719
  enqueue(session, 'select-item', async () => {
2718
- var _a;
2720
+ var _a, _b, _c;
2719
2721
  const currentState = searchStates.get(session.cid);
2720
2722
  if (!currentState)
2721
2723
  return; // 状态可能已过期
2722
2724
  const idx = choice - 1;
2723
- const pageStart = currentState.pageIndex * PAGE_SIZE;
2725
+ const currentPage = Number((_b = (_a = currentState.pageIndex) !== null && _a !== void 0 ? _a : currentState.page) !== null && _b !== void 0 ? _b : 0) || 0;
2726
+ const pageStart = currentPage * PAGE_SIZE;
2724
2727
  const pageEnd = Math.min(pageStart + PAGE_SIZE, currentState.results.length);
2725
2728
  if (choice < pageStart + 1 || choice > pageEnd) {
2726
2729
  // 如果序号不在当前页,忽略或提示
@@ -2738,7 +2741,7 @@ function apply(ctx, config) {
2738
2741
  if (currentState.type === 'author')
2739
2742
  img = await drawAuthorCard(item.link);
2740
2743
  else if (currentState.type === 'user') {
2741
- const uid = ((_a = item.link.match(/\/(\d+)(?:\.html|\/)?$/)) === null || _a === void 0 ? void 0 : _a[1]) || '0';
2744
+ const uid = ((_c = item.link.match(/\/(\d+)(?:\.html|\/)?$/)) === null || _c === void 0 ? void 0 : _c[1]) || '0';
2742
2745
  img = await drawCenterCardImpl(uid, logger);
2743
2746
  }
2744
2747
  else if (currentState.type === 'mod' || currentState.type === 'pack')
package/dist/notify.js CHANGED
@@ -513,10 +513,53 @@ function apply(ctx, config, options) {
513
513
  await updateState(sub.channelId, sub.platform, sub.projectId, latest.version || '');
514
514
  return { sent: true, updated: true };
515
515
  };
516
- if (config.enabled) {
517
- const tick = Math.max(60 * 1000, Number(config.interval) || 30 * 60 * 1000);
518
- ctx.setInterval(() => checkOnce().catch(() => null), tick);
519
- }
516
+ // 自动检查更新定时器
517
+ const startAutoCheck = async () => {
518
+ // 首先加载配置文件
519
+ await loadConfigFromFile();
520
+ // 计算轮询间隔:取所有订阅中最小的 interval,最小不低于 1 分钟
521
+ const getMinInterval = () => {
522
+ const subs = getConfigSubs();
523
+ if (!subs.length)
524
+ return Number(config.interval) || 30 * 60 * 1000;
525
+ const intervals = subs.map(s => s.interval);
526
+ return Math.min(...intervals);
527
+ };
528
+ // 使用较短的基准轮询间隔(1分钟),让 checkOnce 内部判断每个订阅是否到期
529
+ // 这样可以支持每个订阅的独立 interval
530
+ const baseTick = 60 * 1000; // 1 分钟基准轮询
531
+ // 自动轮询函数:每次都检查 config.enabled
532
+ const autoCheckLoop = async () => {
533
+ try {
534
+ await loadConfigFromFile();
535
+ if (config.enabled) {
536
+ const subs = getConfigSubs();
537
+ if (subs.length > 0) {
538
+ logger.debug(`自动检查更新开始,共 ${subs.length} 个订阅...`);
539
+ const stats = await checkOnce();
540
+ if (stats) {
541
+ logger.debug(`自动检查完成: checked=${stats.checked}, updated=${stats.updated}, skipped=${stats.skipped}, failed=${stats.failed}`);
542
+ }
543
+ }
544
+ }
545
+ }
546
+ catch (e) {
547
+ logger.warn(`自动检查更新失败: ${e.message}`);
548
+ }
549
+ };
550
+ // 启动时延迟执行一次初始检查(给 bot 连接时间)
551
+ ctx.setTimeout(async () => {
552
+ await autoCheckLoop();
553
+ }, 10 * 1000);
554
+ // 设置定时器,每分钟轮询一次,由 checkOnce 内部判断哪些订阅到期
555
+ ctx.setInterval(autoCheckLoop, baseTick);
556
+ const minInterval = getMinInterval();
557
+ logger.info(`自动更新检查已启动,基准轮询间隔: 1 分钟,最短订阅间隔: ${Math.round(minInterval / 60000)} 分钟`);
558
+ };
559
+ // 使用 ctx.on('ready') 确保在 Koishi 完全就绪后启动
560
+ ctx.on('ready', () => {
561
+ startAutoCheck().catch(e => logger.warn(`启动自动检查失败: ${e.message}`));
562
+ });
520
563
  ctx.command('notify.add <platform> <projectId>', '添加更新订阅')
521
564
  .action(async ({ session }, platform, projectId) => {
522
565
  await loadConfigFromFile();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-cfmrmod",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Koishi 插件:搜索 CurseForge/Modrinth/MCMod 并渲染图片卡片",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -39,8 +39,8 @@
39
39
  "pub": "npm run build && npm publish --access public"
40
40
  },
41
41
  "peerDependencies": {
42
- "koishi": "^4.0.0",
43
- "@ltxhhz/koishi-plugin-skia-canvas": "^0.0.10"
42
+ "@ltxhhz/koishi-plugin-skia-canvas": "^0.0.10",
43
+ "koishi": "^4.0.0"
44
44
  },
45
45
  "dependencies": {
46
46
  "cheerio": "^1.0.0-rc.12",
@@ -55,7 +55,10 @@
55
55
  "zh": "从 CurseForge/Modrinth/MCMod 搜索模组/整合包/光影等内容,并生成图片卡片。"
56
56
  },
57
57
  "service": {
58
- "required": ["skia", "database"]
58
+ "required": [
59
+ "skia",
60
+ "database"
61
+ ]
59
62
  }
60
63
  }
61
64
  }