koishi-plugin-dew-bot 1.0.261 → 1.0.263

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.
Files changed (30) hide show
  1. package/lib/index.js +316 -105
  2. package/lib/plugin/characterCardAssets.d.ts +3 -1
  3. package/lib/plugin/characterCardStyles.d.ts +1 -1
  4. package/lib/plugin/serverHandel/imageMessageFollowup.d.ts +9 -0
  5. package/lib/plugin/serverHandel/qqMessageButtons.d.ts +27 -0
  6. package/lib/plugin/serverHandel/qqMessagePreview.d.ts +3 -0
  7. package/lib/plugin/serverHandel/qqPageMessage.d.ts +2 -1
  8. package/lib/shared/bot/analysisMsg.d.ts +5 -0
  9. package/lib/shared/bot/inlineCommands.d.ts +11 -0
  10. package/lib/shared/face/index.d.ts +16 -1
  11. package/package.json +1 -1
  12. package/src/plugin/bloodlineCardTemplate.ts +2 -4
  13. package/src/plugin/cardLayout.ts +15 -1
  14. package/src/plugin/characterCardAssets.ts +9 -1
  15. package/src/plugin/characterCardStyles.ts +6 -4
  16. package/src/plugin/characterCardTemplate.ts +9 -8
  17. package/src/plugin/serverHandel/image.ts +5 -5
  18. package/src/plugin/serverHandel/imageMessageFollowup.ts +23 -0
  19. package/src/plugin/serverHandel/qqCommandMessage.ts +10 -3
  20. package/src/plugin/serverHandel/qqMessageButtons.ts +38 -0
  21. package/src/plugin/serverHandel/qqMessagePreview.ts +19 -8
  22. package/src/plugin/serverHandel/qqPageMessage.ts +9 -21
  23. package/src/plugin/serverHandel/qqRewardMessage.ts +2 -0
  24. package/src/plugin/serverHandel/qqStoryMessage.ts +4 -1
  25. package/src/plugin/serverHandel/qqTransactionMessage.ts +4 -1
  26. package/src/plugin/serverHandel/text.ts +1 -1
  27. package/src/shared/bot/analysisMsg.ts +38 -1
  28. package/src/shared/bot/inlineCommands.ts +39 -0
  29. package/src/shared/bot/serviceProto.ts +80 -1
  30. package/src/shared/face/index.ts +18 -1
package/lib/index.js CHANGED
@@ -67,7 +67,7 @@ var serviceProto;
67
67
  var init_serviceProto = __esm({
68
68
  "src/shared/bot/serviceProto.ts"() {
69
69
  serviceProto = {
70
- "version": 37,
70
+ "version": 39,
71
71
  "services": [
72
72
  {
73
73
  "id": 0,
@@ -295,6 +295,18 @@ var init_serviceProto = __esm({
295
295
  "target": "messageFace/TitleCardData"
296
296
  },
297
297
  "optional": true
298
+ },
299
+ {
300
+ "id": 17,
301
+ "name": "buttons",
302
+ "type": {
303
+ "type": "Array",
304
+ "elementType": {
305
+ "type": "Reference",
306
+ "target": "messageFace/MessageButtonRow"
307
+ }
308
+ },
309
+ "optional": true
298
310
  }
299
311
  ]
300
312
  },
@@ -751,6 +763,14 @@ var init_serviceProto = __esm({
751
763
  }
752
764
  },
753
765
  "optional": true
766
+ },
767
+ {
768
+ "id": 14,
769
+ "name": "explorationRank",
770
+ "type": {
771
+ "type": "String"
772
+ },
773
+ "optional": true
754
774
  }
755
775
  ]
756
776
  },
@@ -1891,6 +1911,65 @@ var init_serviceProto = __esm({
1891
1911
  }
1892
1912
  ]
1893
1913
  },
1914
+ "messageFace/MessageButtonRow": {
1915
+ "type": "Interface",
1916
+ "properties": [
1917
+ {
1918
+ "id": 0,
1919
+ "name": "buttons",
1920
+ "type": {
1921
+ "type": "Array",
1922
+ "elementType": {
1923
+ "type": "Reference",
1924
+ "target": "messageFace/MessageButton"
1925
+ }
1926
+ }
1927
+ }
1928
+ ]
1929
+ },
1930
+ "messageFace/MessageButton": {
1931
+ "type": "Interface",
1932
+ "properties": [
1933
+ {
1934
+ "id": 0,
1935
+ "name": "label",
1936
+ "type": {
1937
+ "type": "String"
1938
+ }
1939
+ },
1940
+ {
1941
+ "id": 1,
1942
+ "name": "command",
1943
+ "type": {
1944
+ "type": "String"
1945
+ }
1946
+ },
1947
+ {
1948
+ "id": 2,
1949
+ "name": "style",
1950
+ "type": {
1951
+ "type": "Union",
1952
+ "members": [
1953
+ {
1954
+ "id": 0,
1955
+ "type": {
1956
+ "type": "Literal",
1957
+ "literal": "primary"
1958
+ }
1959
+ },
1960
+ {
1961
+ "id": 1,
1962
+ "type": {
1963
+ "type": "Literal",
1964
+ "literal": "secondary"
1965
+ }
1966
+ }
1967
+ ]
1968
+ },
1969
+ "optional": true
1970
+ }
1971
+ ]
1972
+ },
1894
1973
  "PtlMessage/ReqMessage": {
1895
1974
  "type": "Interface",
1896
1975
  "extends": [
@@ -2660,15 +2739,17 @@ var characterCardStyles = `
2660
2739
  .character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}
2661
2740
  .character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}
2662
2741
  .character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 "Microsoft YaHei",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}
2663
- .character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
2742
+ .character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
2664
2743
  .character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt="称号图片占位"]{max-width:min(244px,100%);max-height:min(44px,100%)}
2665
2744
  .character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,"Microsoft YaHei",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}
2666
2745
  .gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}
2667
2746
  .gene-panel:before,.gene-panel:after{content:"";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}
2668
- .combat-row{display:flex;align-items:center;gap:8px;margin:14px 0 12px;min-width:0}.power-icon{flex:0 0 42px;height:42px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--accent);box-shadow:inset 0 0 0 3px #fffef3b5;color:var(--accent);background:radial-gradient(ellipse at 38% 25%,#fffdf0,color-mix(in srgb,var(--accent) 14%,var(--panel)))}.power-icon svg{width:27px;height:27px;stroke:currentColor;fill:currentColor;stroke-width:2;stroke-linejoin:round}.power-label{font-size:21px;white-space:nowrap}.power-value{min-width:0;flex:1;text-align:right;overflow-wrap:anywhere;font:35px/1.2 Georgia,"Microsoft YaHei",serif;color:var(--accent);letter-spacing:-1px}
2747
+ .exploration-rank-row{display:flex;align-items:center;justify-content:center;gap:7px;margin:10px 0 0;font:16px/1.4 "KaiTi","STKaiti","Microsoft YaHei",sans-serif;letter-spacing:1px;color:var(--subtle)}.exploration-rank-row>svg{width:17px;height:17px;flex-shrink:0;fill:none;stroke:var(--accent);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}.exploration-rank-row>span{white-space:nowrap}.exploration-rank-row strong{font-size:18px;font-weight:700;color:var(--ink);overflow-wrap:anywhere;min-width:0}
2748
+ .combat-row{display:flex;align-items:center;justify-content:center;gap:8px;margin:12px 0 0;padding:0 6px;min-width:0;font:28px/1.2 "KaiTi","STKaiti","Microsoft YaHei",sans-serif;position:relative;z-index:4}.power-icon{flex:0 0 34px;height:34px;display:grid;place-items:center;color:var(--accent)}.power-icon svg{display:block;width:34px;height:34px;overflow:visible}.power-label{font:inherit;white-space:nowrap}.power-value{min-width:0;flex:0 1 auto;text-align:center;overflow-wrap:anywhere;font:inherit;color:var(--accent);letter-spacing:0}
2669
2749
  .companion-resource{font-family:"Microsoft YaHei",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}
2670
- .resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource .resource-heading{color:var(--subtle)}.exp-resource .resource-label svg{fill:var(--radar)}.exp-resource .resource-track{height:16px;border-color:color-mix(in srgb,var(--radar) 60%,#fff);background:var(--paper)}.exp-resource .resource-fill{background:linear-gradient(var(--leaf-light),var(--radar) 56%,var(--leaf))}
2671
- .companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.ornament-heading{display:flex;align-items:center;justify-content:center;gap:7px;margin:0 0 3px;font-size:22px;font-weight:700;letter-spacing:2px;line-height:1.3;color:var(--ink)}.ornament-heading:before,.ornament-heading:after{content:"";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--edge))}.ornament-heading:after{transform:rotate(180deg)}.heading-star{font-size:12px;color:var(--accent);letter-spacing:0}.companion-radar{width:100%;height:260px;display:block;overflow:visible}
2750
+ .resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource{width:calc(100% - 20px);align-self:center;margin:0 0 14px}.exp-resource .resource-track{height:6px;margin-top:0;border-color:#d5c48b;border-radius:4px;background:#eee9d7;box-shadow:inset 0 1px 1px #9d82351a}.exp-resource .resource-fill{border-radius:3px;border-right:0;background:linear-gradient(#fff0a0,#e8bd48 60%,#d6a934)}
2751
+ .hp-resource{display:flex;align-items:center;margin-top:16px;margin-bottom:0}.hp-resource .resource-track{flex:1;min-width:0;height:20px;margin-top:0}.hp-bar-value{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:0 8px;white-space:nowrap;font:12px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443,0 0 2px #435443}
2752
+ .companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.attribute-divider{height:28px;margin:0 0 3px;display:flex;align-items:center;justify-content:center}.attribute-divider svg{display:block;width:100%;height:36px}.companion-radar{width:100%;height:260px;display:block;overflow:visible}
2672
2753
  .radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px "KaiTi","STKaiti","Microsoft YaHei",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,"Microsoft YaHei",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 "Microsoft YaHei",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 "Microsoft YaHei",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}
2673
2754
  `;
2674
2755
 
@@ -2721,6 +2802,12 @@ function characterGeneCrest(tier) {
2721
2802
  return `<svg viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="m30 4 20 10v20L30 55 10 34V14Z" fill="var(--paper)" stroke="currentColor" stroke-width="1.6"/><path d="m30 9 15 8v16L30 49 15 33V17Z" fill="none" stroke="var(--light-edge)" stroke-width="1"/><path d="M23 27v-6a7 7 0 0 1 14 0v6" fill="none" stroke="currentColor" stroke-width="2.5"/><rect x="20" y="26" width="20" height="15" rx="3" fill="currentColor"/><circle cx="30" cy="32" r="2" fill="var(--light-edge)"/><path d="M30 33v4" stroke="var(--light-edge)" stroke-width="1.8"/>${tier >= 3 ? '<path d="m30 2 3 4-3 4-3-4Z" fill="var(--gem)" stroke="var(--light-edge)"/>' : ""}${tier >= 5 ? '<path d="m18 12 1-7 6 4 5-7 5 7 6-4 1 7Z" fill="var(--accent)" stroke="var(--light-edge)" stroke-width=".7"/>' : ""}</svg>`;
2722
2803
  }
2723
2804
  __name(characterGeneCrest, "characterGeneCrest");
2805
+ var characterAttributeDivider = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 310 36">
2806
+ ${ornamentPaint}
2807
+ <g fill="none" stroke="var(--edge)" stroke-width=".8"><path d="M5 18h91m118 0h91"/><path d="M75 18c18 0 29-8 47-8m66 0c18 0 29 8 47 8"/><path d="M102 18c12 0 21 6 31 6m44 0c10 0 19-6 31-6"/></g>
2808
+ <g fill="url(#character-leaf)" stroke="var(--light-edge)" stroke-width=".6"><path d="M120 11c-10-1-14-6-16-10 10 0 17 3 16 10Zm14 12c-10 2-18-1-22-6 10-2 18-1 22 6Zm56-12c10-1 14-6 16-10-10 0-17 3-16 10Zm-14 12c10 2 18-1 22-6-10-2-18-1-22 6Z"/></g>
2809
+ ${flower(155, 18, 0.5)}<g fill="var(--accent)"><path d="m94 18 3-3 3 3-3 3Zm116 0 3-3 3 3-3 3Z"/></g>
2810
+ </svg>`;
2724
2811
 
2725
2812
  // src/plugin/characterCardTemplate.ts
2726
2813
  var finite = /* @__PURE__ */ __name((value) => Number.isFinite(value) ? value : 0, "finite");
@@ -2735,7 +2822,6 @@ var primaryAttributes = [
2735
2822
  ["resistance", "抗性"]
2736
2823
  ];
2737
2824
  var primaryIds = new Set(primaryAttributes.map(([id]) => id));
2738
- var heading = /* @__PURE__ */ __name((label) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(label)}<span class="heading-star" aria-hidden="true">✦</span></h2>`, "heading");
2739
2825
  var corners = /* @__PURE__ */ __name((tier, positions = ["tl", "tr", "bl", "br"]) => positions.map((position) => `<span class="panel-corner corner-${position}" data-decoration-level="${tier}">${characterTierOrnament(tier)}</span>`).join(""), "corners");
2740
2826
  function radar(attributes) {
2741
2827
  if (!attributes.length) return '<p class="empty-attributes">暂无一级属性</p>';
@@ -2781,14 +2867,16 @@ function characterCardHtml(card, artwork, _frame, defaultArtwork, titleArtwork,
2781
2867
  const hpPercent = ratio(card.hp, card.maxHp) * 100;
2782
2868
  const experience = Math.max(0, Math.min(100, finite(card.experiencePercent)));
2783
2869
  const geneRank = card.geneLockRankName || "基因锁未开启";
2870
+ const explorationRank = card.explorationRank?.trim();
2784
2871
  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${characterCardStyles}</style></head><body><main id="capture" class="companion-sheet character-adventure character-sheet gene-tier-${tier}" data-gene-lock-tier="${tier}" data-decoration-level="${tier}"><span class="character-ambient" aria-hidden="true">${characterFloralWatermark}</span><div class="character-card companion-main">
2785
2872
  <div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? "")}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div>
2786
- <div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? "称号图片" : "称号图片占位"}"></div></div></div>
2787
- <section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ["tr", "br"])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number2(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div></div>
2788
- <div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg viewBox="0 0 42 42"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number2(card.power)}</strong></div>
2789
- <div class="resource companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="hp-numbers" class="resource-number">${number2(card.hp)} / ${number2(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span class="bar-percent resource-percent">${number2(hpPercent)}%</span></div></div>
2790
- <div class="resource companion-resource exp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="m16 2 4.5 9.1 10 1.4-7.3 7.1 1.7 10-8.9-4.7-8.9 4.7 1.7-10-7.3-7.1 10-1.4Z"/></svg>经验进度</span><span id="exp-number" class="resource-number">${number2(experience)}%</span></div><div class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span><span class="resource-percent">${number2(experience)}%</span></div></div>
2791
- <section class="attribute-panel companion-attributes" aria-label="一级属性">${heading("一级属性")}${radar(card.attributes)}</section></section>
2873
+ ${titleArtwork || card.titleImageUrl ? `<div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? "称号图片" : "称号图片占位"}"></div>` : ""}</div>
2874
+ <div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg class="crossed-weapons" viewBox="0 0 42 42"><g fill="currentColor" stroke="var(--panel)" stroke-width=".9" stroke-linejoin="round"><g transform="rotate(-45 21 21)"><path d="M21 2 26 9v14l-5 4-5-4V9Z"/><path d="M12 26h18l-1 4H13Zm7 4h4v8h-4Zm-1 8h6v3h-6Z"/><path d="M21 7v15" fill="none" stroke="var(--light-edge)" stroke-width="1.1"/></g><g transform="rotate(45 21 21)"><path d="M21 2 26 9v14l-5 4-5-4V9Z"/><path d="M12 26h18l-1 4H13Zm7 4h4v8h-4Zm-1 8h6v3h-6Z"/><path d="M21 7v15" fill="none" stroke="var(--light-edge)" stroke-width="1.1"/></g></g></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number2(card.power)}</strong></div>
2875
+ </div>
2876
+ <section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ["tr", "br"])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number2(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div><p class="exploration-rank-row"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19.8 7.7A8 8 0 1 0 20 16M20 3v5h-5M15.8 12a3.8 3.8 0 1 1-3.8-3.8"/></svg><span>轮回等级</span> <strong id="exploration-rank">${cardEscape(explorationRank ? explorationRank + "级" : "—")}</strong></p></div>
2877
+ <div class="resource companion-resource hp-resource"><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}" aria-valuetext="${number2(card.hp)} / ${number2(card.maxHp)}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span id="hp-numbers" class="hp-bar-value">${number2(card.hp)} / ${number2(card.maxHp)}</span></div></div>
2878
+ <div class="resource companion-resource exp-resource"><div id="experience-track" class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span></div></div>
2879
+ <section class="attribute-panel companion-attributes" aria-label="一级属性"><div class="attribute-divider" aria-hidden="true">${characterAttributeDivider}</div>${radar(card.attributes)}</section></section>
2792
2880
  </div></main></body></html>`;
2793
2881
  }
2794
2882
  __name(characterCardHtml, "characterCardHtml");
@@ -2854,7 +2942,7 @@ var primaryAttributes2 = [
2854
2942
  ];
2855
2943
  var primaryIds2 = new Set(primaryAttributes2.map(([id]) => id));
2856
2944
  var corners2 = /* @__PURE__ */ __name((positions = ["tl", "tr", "bl", "br"]) => positions.map((position) => `<span class="panel-corner corner-${position}">${companionCornerDecoration}</span>`).join(""), "corners");
2857
- var heading2 = /* @__PURE__ */ __name((title) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(title)}<span class="heading-star" aria-hidden="true">✦</span></h2>`, "heading");
2945
+ var heading = /* @__PURE__ */ __name((title) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(title)}<span class="heading-star" aria-hidden="true">✦</span></h2>`, "heading");
2858
2946
  var paw = '<svg viewBox="0 0 28 28" aria-hidden="true"><ellipse cx="5" cy="10" rx="3" ry="4"/><ellipse cx="11" cy="5" rx="3" ry="4"/><ellipse cx="18" cy="5" rx="3" ry="4"/><ellipse cx="24" cy="10" rx="3" ry="4"/><path d="M5 21c0-5 4-4 6-9 1-3 5-3 6 0 2 5 6 4 6 9 0 5-6 4-9 2-3 2-9 3-9-2Z"/></svg>';
2859
2947
  var crest = '<svg viewBox="0 0 32 32" aria-hidden="true" fill="currentColor"><path d="M15 28C5 24 3 16 4 7c8 0 11 9 11 21ZM17 28c10-4 12-12 11-21-8 0-11 9-11 21ZM16 18C9 13 10 7 16 2c6 5 7 11 0 16Z"/></svg>';
2860
2948
  var sword = '<svg viewBox="0 0 42 42" aria-hidden="true"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg>';
@@ -2898,13 +2986,13 @@ function radar2(card) {
2898
2986
  }
2899
2987
  __name(radar2, "radar");
2900
2988
  function bloodlines(card, icons) {
2901
- return `<section class="companion-panel bloodline-panel" aria-label="血统">${heading2("血统")}${card.bloodlines.length ? `<div class="bloodline-list">${card.bloodlines.map((bloodline, index) => `<div class="bloodline-row"><span class="bloodline-crest${icons?.bloodlines?.[index] ? " has-entry-artwork" : ""}" aria-hidden="true">${bloodline.emoji ? cardEscape(bloodline.emoji) : crest}${icons?.bloodlines?.[index] ? `<img class="bloodline-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.bloodlines[index])}" alt="">` : ""}</span><span class="bloodline-name">${cardEscape(bloodline.name)}</span><span class="bloodline-quality">${cardEscape(bloodline.quality)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无血统</p>'}</section>`;
2989
+ return `<section class="companion-panel bloodline-panel" aria-label="血统">${heading("血统")}${card.bloodlines.length ? `<div class="bloodline-list">${card.bloodlines.map((bloodline, index) => `<div class="bloodline-row"><span class="bloodline-crest${icons?.bloodlines?.[index] ? " has-entry-artwork" : ""}" aria-hidden="true">${bloodline.emoji ? cardEscape(bloodline.emoji) : crest}${icons?.bloodlines?.[index] ? `<img class="bloodline-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.bloodlines[index])}" alt="">` : ""}</span><span class="bloodline-name">${cardEscape(bloodline.name)}</span><span class="bloodline-quality">${cardEscape(bloodline.quality)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无血统</p>'}</section>`;
2902
2990
  }
2903
2991
  __name(bloodlines, "bloodlines");
2904
2992
  function skills(card, icons) {
2905
2993
  const active = '<path d="m8 24 16-16 2-3 4 4-3 2-16 16M7 21l9 9M7 31l5-5M5 33l2-2"/>';
2906
2994
  const passive = '<path d="M17 30V14M17 24C7 22 4 14 5 8c9 1 13 7 12 16ZM17 19c1-9 5-14 13-15 0 8-4 14-13 15Z"/>';
2907
- return `<section class="companion-panel companion-skills" aria-label="技能">${corners2()}${heading2("技能")}${card.skills.length ? `<div class="skill-grid">${card.skills.map((skill, index) => `<div class="companion-skill" data-skill-id="${cardEscape(skill.id)}"><span class="skill-icon" aria-hidden="true"><svg viewBox="0 0 36 36">${skill.type === "passive" ? passive : active}</svg>${icons?.skills?.[index] ? `<img class="skill-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.skills[index])}" alt="">` : ""}</span><span class="skill-name">${cardEscape(skill.name)}</span><span class="skill-level">Lv.${number3(skill.rank)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无技能</p>'}</section>`;
2995
+ return `<section class="companion-panel companion-skills" aria-label="技能">${corners2()}${heading("技能")}${card.skills.length ? `<div class="skill-grid">${card.skills.map((skill, index) => `<div class="companion-skill" data-skill-id="${cardEscape(skill.id)}"><span class="skill-icon" aria-hidden="true"><svg viewBox="0 0 36 36">${skill.type === "passive" ? passive : active}</svg>${icons?.skills?.[index] ? `<img class="skill-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.skills[index])}" alt="">` : ""}</span><span class="skill-name">${cardEscape(skill.name)}</span><span class="skill-level">Lv.${number3(skill.rank)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无技能</p>'}</section>`;
2908
2996
  }
2909
2997
  __name(skills, "skills");
2910
2998
  function companionCardHtml(card, artwork, icons) {
@@ -2920,7 +3008,7 @@ function companionCardHtml(card, artwork, icons) {
2920
3008
  <div class="companion-power-row"><span class="power-icon" aria-hidden="true">${sword}</span><span class="companion-power-label">战力</span><strong id="companion-power" class="companion-power">${number3(card.power)}</strong></div>
2921
3009
  <div class="companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="companion-hp" class="resource-number">${number3(card.hp)} / ${number3(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${healthPercent}"><span class="resource-fill" style="width:${healthPercent}%"></span><span class="resource-percent">${number3(healthPercent)}%</span></div></div>
2922
3010
  ${pet ? `<div class="companion-resource exp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="m16 2 4.5 9.1 10 1.4-7.3 7.1 1.7 10-8.9-4.7-8.9 4.7 1.7-10-7.3-7.1 10-1.4Z"/></svg>经验进度</span><span class="resource-number">${number3(experience)}%</span></div><div class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span><span class="resource-percent">${number3(experience)}%</span></div></div>` : ""}
2923
- <section class="companion-attributes" aria-label="一级属性">${heading2("一级属性")}${radar2(card)}</section></section>
3011
+ <section class="companion-attributes" aria-label="一级属性">${heading("一级属性")}${radar2(card)}</section></section>
2924
3012
  </div>${skills(card, icons)}</main></body></html>`;
2925
3013
  }
2926
3014
  __name(companionCardHtml, "companionCardHtml");
@@ -3134,6 +3222,18 @@ var import_node_crypto = require("node:crypto");
3134
3222
  // src/plugin/cardLayout.ts
3135
3223
  async function layoutCard(document2 = globalThis.document) {
3136
3224
  await document2.fonts.ready;
3225
+ for (const name3 of document2.querySelectorAll("#character-name,#companion-name")) {
3226
+ const style = document2.defaultView.getComputedStyle(name3);
3227
+ const maximum = parseFloat(style.fontSize);
3228
+ const spacing = parseFloat(style.letterSpacing) || 0;
3229
+ name3.style.whiteSpace = "nowrap";
3230
+ for (let size = maximum; size >= 18; size--) {
3231
+ name3.style.fontSize = size + "px";
3232
+ name3.style.letterSpacing = spacing * size / maximum + "px";
3233
+ if (name3.scrollWidth <= name3.clientWidth) break;
3234
+ }
3235
+ if (name3.scrollWidth > name3.clientWidth) name3.style.whiteSpace = "normal";
3236
+ }
3137
3237
  const titleBanner = document2.querySelector(".title-banner-artwork");
3138
3238
  if (titleBanner) {
3139
3239
  try {
@@ -3419,15 +3519,13 @@ function radar4(card) {
3419
3519
  const attribute = card.attributes.find((value) => value.id === id);
3420
3520
  return { name: attribute?.name?.trim() || name2, value: finite4(attribute?.value ?? 0) };
3421
3521
  });
3422
- const max = Math.max(1, ...attributes.map((attribute) => Math.max(0, attribute.value)));
3423
- const magnitude = 10 ** Math.floor(Math.log10(max));
3424
- const ceiling = Math.ceil(max / magnitude) * magnitude;
3522
+ const ceiling = Math.max(0, ...attributes.map((attribute) => attribute.value));
3425
3523
  const point = /* @__PURE__ */ __name((index, radius) => {
3426
3524
  const angle = -Math.PI / 2 + index * Math.PI / 3;
3427
3525
  return [320 + Math.cos(angle) * radius, 190 + Math.sin(angle) * radius];
3428
3526
  }, "point");
3429
3527
  const polygon = /* @__PURE__ */ __name((radius) => attributes.map((_, index) => point(index, radius).join(",")).join(" "), "polygon");
3430
- const data = attributes.map((attribute, index) => point(index, 122 * Math.max(0, Math.min(1, attribute.value / ceiling))));
3528
+ const data = attributes.map((attribute, index) => point(index, 122 * (ceiling > 0 ? Math.max(0, attribute.value / ceiling) : 0)));
3431
3529
  return `<div class="radar-panel"><svg class="bloodline-radar" viewBox="0 0 640 396" role="img" aria-label="血统六维属性加成雷达图" xmlns="http://www.w3.org/2000/svg">
3432
3530
  <defs><linearGradient id="bloodline-radar-fill" x1="0" y1="0" x2="1" y2="1"><stop stop-color="var(--quality-accent)" stop-opacity=".35"/><stop offset="1" stop-color="var(--quality-accent)" stop-opacity=".12"/></linearGradient></defs>
3433
3531
  ${[0.2, 0.4, 0.6, 0.8, 1].map((scale) => `<polygon points="${polygon(122 * scale)}" fill="none" stroke="#b9bdad" stroke-width="1.2"/>`).join("")}
@@ -3929,6 +4027,143 @@ var handel_base = class {
3929
4027
  // src/plugin/serverHandel/text.ts
3930
4028
  init_sessions();
3931
4029
 
4030
+ // src/shared/bot/inlineCommands.ts
4031
+ function inlineCommandSpans(content) {
4032
+ const spans = [];
4033
+ let depth = 0, start = 0, labelStart = 0;
4034
+ for (let index = 0; index < content.length; index++) {
4035
+ if (content[index] === "[") {
4036
+ if (depth === 0) {
4037
+ start = index > 0 && content[index - 1] === "\\" ? index - 1 : index;
4038
+ labelStart = index + 1;
4039
+ }
4040
+ depth++;
4041
+ } else if (content[index] === "]" && depth > 0 && --depth === 0) {
4042
+ const label = content.slice(labelStart, index).replace(/\\$/, "");
4043
+ if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue;
4044
+ const alias = /^\(command:([^()[\]\r\n]+)\)/.exec(content.slice(index + 1));
4045
+ const command = alias ? alias[1].trim() : label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, "");
4046
+ if (!command) continue;
4047
+ const end = index + 1 + (alias?.[0].length ?? 0);
4048
+ spans.push({ start, end, label, command, ...alias ? { aliased: true } : {} });
4049
+ if (alias) index = end - 1;
4050
+ }
4051
+ }
4052
+ return spans;
4053
+ }
4054
+ __name(inlineCommandSpans, "inlineCommandSpans");
4055
+ function renderPlainInlineCommandAliases(content) {
4056
+ let result = "", offset = 0;
4057
+ for (const span of inlineCommandSpans(content)) {
4058
+ if (!span.aliased) continue;
4059
+ result += content.slice(offset, span.start) + (span.label.trim() === span.command ? `[${span.command}]` : `${span.label}[${span.command}]`);
4060
+ offset = span.end;
4061
+ }
4062
+ return result + content.slice(offset);
4063
+ }
4064
+ __name(renderPlainInlineCommandAliases, "renderPlainInlineCommandAliases");
4065
+
4066
+ // src/shared/bot/analysisMsg.ts
4067
+ function messageButtonFallbackLines(data) {
4068
+ return [...new Set((data.buttons ?? []).flatMap((row) => row.buttons.map((button) => "[" + button.command + "]" + button.label)))];
4069
+ }
4070
+ __name(messageButtonFallbackLines, "messageButtonFallbackLines");
4071
+ function matchesButtonFallback(content, fallback) {
4072
+ const bracket = fallback.indexOf("]");
4073
+ const prefix = fallback.slice(0, bracket + 1);
4074
+ const line = content.trim();
4075
+ return line.startsWith(prefix) && line.slice(prefix.length).trim() === fallback.slice(prefix.length).trim();
4076
+ }
4077
+ __name(matchesButtonFallback, "matchesButtonFallback");
4078
+ function appendMessageButtonFallbacks(content, data) {
4079
+ const lines = content.replace(/\r\n?/g, "\n").split("\n");
4080
+ const missing = messageButtonFallbackLines(data).filter((fallback) => !lines.some((line) => matchesButtonFallback(line, fallback)));
4081
+ if (!missing.length) return content;
4082
+ return content + (content && !content.endsWith("\n") ? "\n" : "") + missing.join("\n") + "\n";
4083
+ }
4084
+ __name(appendMessageButtonFallbacks, "appendMessageButtonFallbacks");
4085
+ function withoutMessageButtonFallbacks(data) {
4086
+ const fallbacks = messageButtonFallbackLines(data);
4087
+ if (!fallbacks.length) return data;
4088
+ return { ...data, line: data.line.map((item) => {
4089
+ if (item.type !== 1 /* line */ || item.codeBlock || item.reward || item.mailPart !== void 0) return item;
4090
+ const lines = item.content.replace(/\r\n?/g, "\n").split("\n");
4091
+ const kept = lines.filter((line) => !fallbacks.some((fallback) => matchesButtonFallback(line, fallback)));
4092
+ return kept.length === lines.length ? item : { ...item, content: kept.join("\n") };
4093
+ }) };
4094
+ }
4095
+ __name(withoutMessageButtonFallbacks, "withoutMessageButtonFallbacks");
4096
+ var AnalysisMsg = class {
4097
+ static {
4098
+ __name(this, "AnalysisMsg");
4099
+ }
4100
+ parse(data) {
4101
+ let msg = "";
4102
+ let haveTitle = false;
4103
+ const commands = new Map(data.pageNav?.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
4104
+ const labeledEntries = /* @__PURE__ */ new Map();
4105
+ for (const entry of data.pageNav?.entries ?? []) {
4106
+ if (entry.label === void 0) continue;
4107
+ const row = labeledEntries.get(entry.lineIndex) ?? [];
4108
+ row.push(entry);
4109
+ labeledEntries.set(entry.lineIndex, row);
4110
+ }
4111
+ const withCommandHint = /* @__PURE__ */ __name((content, command) => {
4112
+ const hint = command && !content.includes(`[${command}]`) && content.trim() !== command ? ` [${command}]` : "";
4113
+ return content + hint;
4114
+ }, "withCommandHint");
4115
+ for (const [lineIndex, item] of data.line.entries()) {
4116
+ switch (item.type) {
4117
+ case 0 /* title */:
4118
+ haveTitle = true;
4119
+ msg += `┄━═${item.content}═━┄
4120
+ `;
4121
+ break;
4122
+ case 1 /* line */:
4123
+ const labeledRow = labeledEntries.get(lineIndex);
4124
+ const content = labeledRow ? labeledRow.map((entry) => entry.label + (entry.description ? " · " + entry.description : "") + ` [${entry.viewCommand}]`).join("") : withCommandHint(item.content, commands.get(lineIndex));
4125
+ const plainContent = item.codeBlock || item.mailPart === "body" || labeledRow ? content : renderPlainInlineCommandAliases(content);
4126
+ msg += `${plainContent}
4127
+ `;
4128
+ break;
4129
+ case 2 /* error */:
4130
+ msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者
4131
+ `;
4132
+ break;
4133
+ default:
4134
+ break;
4135
+ }
4136
+ }
4137
+ msg = appendMessageButtonFallbacks(msg, data);
4138
+ if (haveTitle) {
4139
+ msg += "┄━════════━┄";
4140
+ }
4141
+ return msg;
4142
+ }
4143
+ };
4144
+
4145
+ // src/plugin/serverHandel/qqMessageButtons.ts
4146
+ function renderQQKeyboard(data) {
4147
+ const rows = data.buttons;
4148
+ if (!rows?.length) return void 0;
4149
+ if (rows.length > 5 || rows.some((row) => !row.buttons.length || row.buttons.length > 5)) {
4150
+ throw new Error("QQ 按钮最多 5 行,每行必须有 1–5 个按钮");
4151
+ }
4152
+ return { content: { rows: rows.map((row, rowIndex) => ({
4153
+ buttons: row.buttons.map((button, buttonIndex) => {
4154
+ if (!button.label.trim() || !button.command.trim() || /[\r\n\0]/.test(button.label + button.command) || button.command.length > 4e3 || button.style !== void 0 && button.style !== "primary" && button.style !== "secondary") {
4155
+ throw new Error("QQ 按钮文案和指令必须是非空单行文本,样式必须为 primary 或 secondary");
4156
+ }
4157
+ return {
4158
+ id: "action-" + rowIndex + "-" + buttonIndex,
4159
+ render_data: { label: button.label, visited_label: button.label, style: button.style === "primary" ? 1 : 0 },
4160
+ action: { type: 2, permission: { type: 2 }, data: button.command, enter: true, reply: false, unsupport_tips: "请发送:" + button.command }
4161
+ };
4162
+ })
4163
+ })) } };
4164
+ }
4165
+ __name(renderQQKeyboard, "renderQQKeyboard");
4166
+
3932
4167
  // src/plugin/serverHandel/qqMarkdownHelpers.ts
3933
4168
  function escapeMarkdown(value) {
3934
4169
  return value.replace(/[\\`*_{}\[\]()#+\-.!|>~<&]/g, "\\$&");
@@ -4051,6 +4286,7 @@ function renderQQMailMarkdown(data) {
4051
4286
  }
4052
4287
  __name(renderQQMailMarkdown, "renderQQMailMarkdown");
4053
4288
  function renderQQRewardMarkdown(data, showGreeting) {
4289
+ data = withoutMessageButtonFallbacks(data);
4054
4290
  if (data.mail) return appendMissingBattleLogLinks(renderQQMailMarkdown(data), data);
4055
4291
  const rewards = data.line.filter((item) => item.type === 1 /* line */ && item.reward).map((item) => renderRewardLine(item.content)).filter(Boolean);
4056
4292
  if (rewards.length && !data.mail) return appendMissingBattleLogLinks("🎁获得奖励\n\n" + rewards.join(" \n"), data);
@@ -4099,36 +4335,21 @@ function inlineCommandUrl(command) {
4099
4335
  return `mqqapi://aio/inlinecmd?command=${encoded}&enter=true&reply=false`;
4100
4336
  }
4101
4337
  __name(inlineCommandUrl, "inlineCommandUrl");
4102
- function inlineCommandSpans(content) {
4103
- const spans = [];
4104
- let depth = 0, start = 0, labelStart = 0;
4105
- for (let index = 0; index < content.length; index++) {
4106
- if (content[index] === "[") {
4107
- if (depth === 0) {
4108
- start = index > 0 && content[index - 1] === "\\" ? index - 1 : index;
4109
- labelStart = index + 1;
4110
- }
4111
- depth++;
4112
- } else if (content[index] === "]" && depth > 0 && --depth === 0) {
4113
- const label = content.slice(labelStart, index).replace(/\\$/, "");
4114
- if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue;
4115
- const command = label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, "");
4116
- if (command) spans.push({ start, end: index + 1, label, command });
4117
- }
4118
- }
4338
+ function inlineCommandSpans2(content) {
4339
+ const spans = inlineCommandSpans(content);
4119
4340
  const logLinks = battleLogLinkSpans(content);
4120
4341
  return spans.filter((span) => !logLinks.some((link) => span.start < link.end && span.end > link.start));
4121
4342
  }
4122
- __name(inlineCommandSpans, "inlineCommandSpans");
4343
+ __name(inlineCommandSpans2, "inlineCommandSpans");
4123
4344
  function hasInlineCommands(content) {
4124
- return inlineCommandSpans(content).length > 0;
4345
+ return inlineCommandSpans2(content).length > 0;
4125
4346
  }
4126
4347
  __name(hasInlineCommands, "hasInlineCommands");
4127
4348
  function renderInlineCommands(content) {
4128
4349
  const parts = [];
4129
4350
  let offset = 0;
4130
4351
  const spans = [
4131
- ...inlineCommandSpans(content).map((span) => ({ ...span, rendered: "[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")" })),
4352
+ ...inlineCommandSpans2(content).map((span) => ({ ...span, rendered: "[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")" })),
4132
4353
  ...battleLogLinkSpans(content).map((span) => ({ ...span, rendered: renderBattleLogLink(span) }))
4133
4354
  ].sort((a, b) => a.start - b.start);
4134
4355
  for (const span of spans) {
@@ -4255,6 +4476,7 @@ function renderQQInventoryMarkdown(data, showGreeting) {
4255
4476
  }
4256
4477
  __name(renderQQInventoryMarkdown, "renderQQInventoryMarkdown");
4257
4478
  function renderQQPageMarkdown(data, showGreeting) {
4479
+ data = withoutMessageButtonFallbacks(data);
4258
4480
  const hasEntryLabels = data.pageNav?.entries?.some((entry) => entry.label !== void 0) ?? false;
4259
4481
  if (data.task && !hasEntryLabels) return renderQQTaskMarkdown(data);
4260
4482
  if (!hasEntryLabels && data.pageNav && data.line.some((item) => item.type === 0 /* title */ && /^(?:💼)?我的背包$/u.test(compactDisplayText(item.content))))
@@ -4380,7 +4602,7 @@ function canSendQQPage(session, data) {
4380
4602
  return !!internal?.sendMessage && !!internal.sendPrivateMessage;
4381
4603
  }
4382
4604
  __name(canSendQQPage, "canSendQQPage");
4383
- async function sendQQMarkdown(session, markdown) {
4605
+ async function sendQQMarkdown(session, markdown, keyboard) {
4384
4606
  const internal = session.bot.internal;
4385
4607
  const request = {
4386
4608
  // With msg_type=2, QQ requires the ordinary content field to be empty.
@@ -4388,6 +4610,7 @@ async function sendQQMarkdown(session, markdown) {
4388
4610
  msg_type: 2,
4389
4611
  markdown: { content: markdown }
4390
4612
  };
4613
+ if (keyboard) request.keyboard = keyboard;
4391
4614
  const age = Date.now() - session.timestamp;
4392
4615
  if (session.messageId && Number.isFinite(age) && age >= 0 && age < 5 * 60 * 1e3 - 2e3) {
4393
4616
  const sequenced = session;
@@ -4401,68 +4624,21 @@ async function sendQQMarkdown(session, markdown) {
4401
4624
  }
4402
4625
  __name(sendQQMarkdown, "sendQQMarkdown");
4403
4626
 
4404
- // src/shared/bot/analysisMsg.ts
4405
- var AnalysisMsg = class {
4406
- static {
4407
- __name(this, "AnalysisMsg");
4408
- }
4409
- parse(data) {
4410
- let msg = "";
4411
- let haveTitle = false;
4412
- const commands = new Map(data.pageNav?.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
4413
- const labeledEntries = /* @__PURE__ */ new Map();
4414
- for (const entry of data.pageNav?.entries ?? []) {
4415
- if (entry.label === void 0) continue;
4416
- const row = labeledEntries.get(entry.lineIndex) ?? [];
4417
- row.push(entry);
4418
- labeledEntries.set(entry.lineIndex, row);
4419
- }
4420
- const withCommandHint = /* @__PURE__ */ __name((content, command) => {
4421
- const hint = command && !content.includes(`[${command}]`) && content.trim() !== command ? ` [${command}]` : "";
4422
- return content + hint;
4423
- }, "withCommandHint");
4424
- for (const [lineIndex, item] of data.line.entries()) {
4425
- switch (item.type) {
4426
- case 0 /* title */:
4427
- haveTitle = true;
4428
- msg += `┄━═${item.content}═━┄
4429
- `;
4430
- break;
4431
- case 1 /* line */:
4432
- const labeledRow = labeledEntries.get(lineIndex);
4433
- const content = labeledRow ? labeledRow.map((entry) => entry.label + (entry.description ? " · " + entry.description : "") + ` [${entry.viewCommand}]`).join("") : withCommandHint(item.content, commands.get(lineIndex));
4434
- msg += `${content}
4435
- `;
4436
- break;
4437
- case 2 /* error */:
4438
- msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者
4439
- `;
4440
- break;
4441
- default:
4442
- break;
4443
- }
4444
- }
4445
- if (haveTitle) {
4446
- msg += "┄━════════━┄";
4447
- }
4448
- return msg;
4449
- }
4450
- };
4451
-
4452
4627
  // src/plugin/serverHandel/qqCommandMessage.ts
4453
4628
  function renderQQCommandMarkdown(data, showGreeting) {
4629
+ data = withoutMessageButtonFallbacks(data);
4454
4630
  if (data.notification) {
4455
4631
  const onlyLine = data.line.length === 1 ? data.line[0] : void 0;
4456
4632
  if (onlyLine?.type === 1 /* line */ && /^🔔系统提示:[^\r\n]+$/.test(onlyLine.content)) {
4457
4633
  return renderInlineCommands(onlyLine.content);
4458
4634
  }
4459
4635
  const title = data.line.find((item) => item.type === 0 /* title */)?.content;
4460
- const heading3 = title === void 0 ? "**🔔** 系统提示" : `**${escapeMarkdown(title)}**`;
4636
+ const heading2 = title === void 0 ? "**🔔** 系统提示" : `**${escapeMarkdown(title)}**`;
4461
4637
  const body = data.line.filter((item) => item.type !== 0 /* title */).flatMap((item) => {
4462
4638
  const content = item.type === 2 /* error */ ? `❌ ${item.content}` : item.content;
4463
4639
  return content.replace(/\r\n?/g, "\n").split("\n").map((line) => "> " + renderInlineCommands(line));
4464
4640
  }).join(" \n");
4465
- return heading3 + (body ? "\n\n" + body : "");
4641
+ return heading2 + (body ? "\n\n" + body : "");
4466
4642
  }
4467
4643
  const rewardMessage = isRewardMessage(data);
4468
4644
  if (!data.commandHelp && !data.pageNav && data.line.some((item) => item.type === 1 /* line */ && !item.reward && /^\[\d+\]\s*[^\r\n]+$/.test(item.content))) {
@@ -4494,8 +4670,10 @@ function renderQQCommandMarkdown(data, showGreeting) {
4494
4670
  break;
4495
4671
  }
4496
4672
  flushRewards();
4497
- if (!item.content.trim()) flushBody();
4498
- else bodyLines.push(renderInlineCommands(item.content));
4673
+ for (const line of item.content.replace(/\r\n?/g, "\n").split("\n")) {
4674
+ if (!line.trim()) flushBody();
4675
+ else bodyLines.push(renderInlineCommands(line));
4676
+ }
4499
4677
  break;
4500
4678
  case 2 /* error */:
4501
4679
  flushBody();
@@ -4512,6 +4690,7 @@ __name(renderQQCommandMarkdown, "renderQQCommandMarkdown");
4512
4690
 
4513
4691
  // src/plugin/serverHandel/qqTransactionMessage.ts
4514
4692
  function renderQQTransactionMarkdown(data, showGreeting) {
4693
+ data = withoutMessageButtonFallbacks(data);
4515
4694
  const rewardMessage = isRewardMessage(data);
4516
4695
  const shortageMessage = data.transaction === true && data.line.some((item) => item.type === 0 /* title */ && item.content === "❌材料不足");
4517
4696
  const sections = [];
@@ -4605,6 +4784,7 @@ function fencedStory(content) {
4605
4784
  }
4606
4785
  __name(fencedStory, "fencedStory");
4607
4786
  function renderQQStoryMarkdown(data, showGreeting) {
4787
+ data = withoutMessageButtonFallbacks(data);
4608
4788
  if (data.reward === true && data.line.some((item) => item.type === 1 /* line */ && item.reward === true && item.content.trim())) {
4609
4789
  const stories = data.line.filter((item) => item.type === 1 /* line */ && item.codeBlock === true).map((item) => fencedStory(item.content));
4610
4790
  return [...stories, renderQQRewardMarkdown(data, false)].join("\n\n");
@@ -4663,15 +4843,29 @@ __name(renderQQStoryMarkdown, "renderQQStoryMarkdown");
4663
4843
  function renderQQMessagePreview(data, showGreeting = false) {
4664
4844
  const plain = new AnalysisMsg().parse(data);
4665
4845
  const text = data.notification ? plain.trimEnd() : plain;
4666
- if (data.notification) return { kind: "command", markdown: renderQQCommandMarkdown(data, false), text };
4667
- if (hasStoryCodeBlock(data)) return { kind: "story", markdown: renderQQStoryMarkdown(data, showGreeting), text };
4668
- if (data.commandHelp) return { kind: "command", markdown: renderQQCommandMarkdown(data, showGreeting), text };
4669
- if (data.transaction) return { kind: "transaction", markdown: renderQQTransactionMarkdown(data, showGreeting), text };
4670
- if (data.pageNav) return { kind: "page", markdown: renderQQPageMarkdown(data, showGreeting), text };
4671
- if (isRewardMessage(data)) return { kind: "reward", markdown: renderQQRewardMarkdown(data, showGreeting), text };
4846
+ let keyboard;
4847
+ try {
4848
+ keyboard = renderQQKeyboard(data);
4849
+ } catch {
4850
+ return { kind: "text", text };
4851
+ }
4852
+ data = withoutMessageButtonFallbacks(data);
4853
+ const native = /* @__PURE__ */ __name((kind, markdown) => ({
4854
+ kind,
4855
+ markdown,
4856
+ text,
4857
+ ...keyboard ? { keyboard } : {}
4858
+ }), "native");
4859
+ if (data.notification) return native("command", renderQQCommandMarkdown(data, false));
4860
+ if (hasStoryCodeBlock(data)) return native("story", renderQQStoryMarkdown(data, showGreeting));
4861
+ if (data.commandHelp) return native("command", renderQQCommandMarkdown(data, showGreeting));
4862
+ if (data.transaction) return native("transaction", renderQQTransactionMarkdown(data, showGreeting));
4863
+ if (data.pageNav) return native("page", renderQQPageMarkdown(data, showGreeting));
4864
+ if (isRewardMessage(data)) return native("reward", renderQQRewardMarkdown(data, showGreeting));
4672
4865
  if (battleLogMessageLinks(data).length || data.line.some((item) => (item.type === 1 /* line */ || item.type === 2 /* error */) && hasInlineCommands(item.content))) {
4673
- return { kind: "command", markdown: renderQQCommandMarkdown(data, showGreeting), text };
4866
+ return native("command", renderQQCommandMarkdown(data, showGreeting));
4674
4867
  }
4868
+ if (keyboard) return native("command", renderQQCommandMarkdown(data, showGreeting));
4675
4869
  return { kind: "text", text };
4676
4870
  }
4677
4871
  __name(renderQQMessagePreview, "renderQQMessagePreview");
@@ -4688,7 +4882,7 @@ var handelr_text = class extends handel_base {
4688
4882
  if (preview.markdown !== void 0 && canSendQQPage(session, data)) {
4689
4883
  const sendStartedAt = this.timing ? performance.now() : 0;
4690
4884
  try {
4691
- await sendQQMarkdown(session, preview.markdown);
4885
+ await sendQQMarkdown(session, preview.markdown, preview.keyboard);
4692
4886
  if (this.timing) {
4693
4887
  this.timing.delivery = "qq-markdown";
4694
4888
  this.timing.sendMs = performance.now() - sendStartedAt;
@@ -4733,6 +4927,23 @@ function titleCardFollowup(data) {
4733
4927
  }
4734
4928
  __name(titleCardFollowup, "titleCardFollowup");
4735
4929
 
4930
+ // src/plugin/serverHandel/imageMessageFollowup.ts
4931
+ function renderQQImageFollowup(data) {
4932
+ const title = titleCardFollowup(data);
4933
+ const actionLines = messageButtonFallbackLines(data);
4934
+ if (!title && !actionLines.length) return void 0;
4935
+ const text = appendMessageButtonFallbacks(title?.text ?? "", data).trimEnd();
4936
+ let keyboard;
4937
+ try {
4938
+ keyboard = renderQQKeyboard(data);
4939
+ } catch {
4940
+ return { text };
4941
+ }
4942
+ const markdown = titleCardFollowup(withoutMessageButtonFallbacks(data))?.markdown ?? " ";
4943
+ return { text, markdown, ...keyboard ? { keyboard } : {} };
4944
+ }
4945
+ __name(renderQQImageFollowup, "renderQQImageFollowup");
4946
+
4736
4947
  // src/plugin/serverHandel/image.ts
4737
4948
  var ImageHandler = class extends handel_base {
4738
4949
  static {
@@ -4763,16 +4974,16 @@ var ImageHandler = class extends handel_base {
4763
4974
  }
4764
4975
  const sendStartedAt = this.timing ? performance.now() : 0;
4765
4976
  await this.send(data, content, "image");
4766
- const followup = titleCardFollowup(data);
4977
+ const followup = renderQQImageFollowup(data);
4767
4978
  if (!followup) return;
4768
4979
  const session = sessions_default.get(sessionKey(data.botId, data.userId, data.channelId));
4769
4980
  let sentMarkdown = false;
4770
- if (canSendQQPage(session, data)) {
4981
+ if (followup.markdown !== void 0 && canSendQQPage(session, data)) {
4771
4982
  try {
4772
- await sendQQMarkdown(session, followup.markdown);
4983
+ await sendQQMarkdown(session, followup.markdown, followup.keyboard);
4773
4984
  sentMarkdown = true;
4774
4985
  } catch (error) {
4775
- this.ctx.logger.warn("称号操作 Markdown 发送失败,改用普通文字", error instanceof Error ? error.message : error);
4986
+ this.ctx.logger.warn("图片操作 Markdown 发送失败,改用普通文字", error instanceof Error ? error.message : error);
4776
4987
  }
4777
4988
  }
4778
4989
  if (!sentMarkdown) await this.send({ ...data, notification: true }, followup.text);