koishi-plugin-rocom 1.0.19 → 1.0.24

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 (183) hide show
  1. package/lib/atlas-service.js +1 -1
  2. package/lib/client.d.ts +10 -0
  3. package/lib/client.js +44 -8
  4. package/lib/commands/egg.js +1 -1
  5. package/lib/commands/merchant.d.ts +105 -0
  6. package/lib/commands/merchant.js +474 -54
  7. package/lib/commands/query.d.ts +18 -0
  8. package/lib/commands/query.js +446 -37
  9. package/lib/commands/share-code.js +47 -2
  10. package/lib/commands/tools.js +1 -1
  11. package/lib/commands/wiki.js +1 -1
  12. package/lib/egg-service.js +1 -1
  13. package/lib/element-colors.d.ts +3 -0
  14. package/lib/element-colors.js +34 -0
  15. package/lib/icon-metrics.d.ts +32 -0
  16. package/lib/icon-metrics.js +255 -0
  17. package/lib/index.d.ts +5 -0
  18. package/lib/index.js +11857 -188
  19. package/lib/merchant-catalog.d.ts +47 -0
  20. package/lib/merchant-catalog.js +193 -0
  21. package/lib/merchant-price-log.d.ts +63 -0
  22. package/lib/merchant-price-log.js +187 -0
  23. package/lib/merchant-service.d.ts +17 -3
  24. package/lib/merchant-service.js +79 -11
  25. package/lib/pet-data-service.d.ts +13 -0
  26. package/lib/pet-data-service.js +111 -2
  27. package/lib/player-service.js +2 -2
  28. package/lib/ranking-service.js +1 -1
  29. package/lib/render-templates/home/style.css +15 -14
  30. package/lib/render-templates/home-pets/index.html +80 -0
  31. package/lib/render-templates/home-pets/style.css +338 -0
  32. package/lib/render-templates/pet-detail-classic/index.html +151 -0
  33. package/lib/render-templates/pet-detail-classic/style.css +503 -0
  34. package/lib/render-templates/pet-detail-classic/texture2D/a_title.png +0 -0
  35. package/lib/render-templates/pet-detail-classic/texture2D/bg.jpg +0 -0
  36. package/lib/render-templates/pet-detail-classic/texture2D/footer.png +0 -0
  37. package/lib/render-templates/pet-detail-classic/texture2D/icon.png +0 -0
  38. package/lib/render-templates/pet-detail-classic/texture2D/img_head.png +0 -0
  39. package/lib/render-templates/pet-detail-classic/texture2D/jinhua_bg.png +0 -0
  40. package/lib/render-templates/pet-detail-classic/texture2D/level_icon.png +0 -0
  41. package/lib/render-templates/pet-detail-classic/texture2D/line.png +0 -0
  42. package/lib/render-templates/pet-detail-classic/texture2D/mask_bar.png +0 -0
  43. package/lib/render-templates/pet-detail-classic/texture2D/pet_bg.png +0 -0
  44. package/lib/render-templates/pet-detail-classic/texture2D/pet_rocom.png +0 -0
  45. package/lib/render-templates/pet-detail-classic/texture2D/pet_rocom_bg.png +0 -0
  46. package/lib/render-templates/pet-detail-classic/texture2D/poro_bar.png +0 -0
  47. package/lib/render-templates/pet-detail-classic/texture2D/right_jinhua.png +0 -0
  48. package/lib/render-templates/pet-detail-classic/texture2D/skill_bg.png +0 -0
  49. package/lib/render-templates/pet-detail-classic/texture2D/skill_mask.png +0 -0
  50. package/lib/render-templates/pet-detail-classic/texture2D/star.png +0 -0
  51. package/lib/render-templates/pet-detail-classic/texture2D/star_1.png +0 -0
  52. package/lib/render-templates/pet-detail-classic/texture2D/star_8.png +0 -0
  53. package/lib/render-templates/pet-detail-classic/texture2D/star_9.png +0 -0
  54. package/lib/render-templates/pet-detail-classic/texture2D/table.png +0 -0
  55. package/lib/render-templates/pet-detail-classic/texture2D/tags.png +0 -0
  56. package/lib/render-templates/pet-detail-classic/texture2D/title.png +0 -0
  57. package/lib/render-templates/pet-detail-classic/texture2D/top_bg.png +0 -0
  58. package/lib/render-templates/pet-detail-classic/texture2D/xuancai_overlay.png +0 -0
  59. package/lib/render-templates/pet-detail-classic/texture2D/yise_overlay.png +0 -0
  60. package/lib/render-templates/pet-detail-classic/texture2D/ys_bar.png +0 -0
  61. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/10.png +0 -0
  62. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/11.png +0 -0
  63. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/12.png +0 -0
  64. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/13.png +0 -0
  65. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/14.png +0 -0
  66. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/15.png +0 -0
  67. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/16.png +0 -0
  68. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/17.png +0 -0
  69. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/18.png +0 -0
  70. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/19.png +0 -0
  71. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/2.png +0 -0
  72. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/20.png +0 -0
  73. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/23.png +0 -0
  74. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/3.png +0 -0
  75. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/4.png +0 -0
  76. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/5.png +0 -0
  77. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/6.png +0 -0
  78. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/8.png +0 -0
  79. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/9.png +0 -0
  80. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/205/211.png +0 -0
  81. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/206/260.png +0 -0
  82. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/234/260.png +0 -0
  83. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/271/273.png +0 -0
  84. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/271/275.png +0 -0
  85. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/201/266.png +0 -0
  86. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/227/240.png +0 -0
  87. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/231/256/351/200/232.png +0 -0
  88. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/234/272/346/242/260.png +0 -0
  89. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/255/246.png +0 -0
  90. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/257/222.png +0 -0
  91. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/260/264.png +0 -0
  92. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/201/253.png +0 -0
  93. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/224/265.png +0 -0
  94. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/277/274.png +0 -0
  95. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/215/211.png +0 -0
  96. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/220/214.png +0 -0
  97. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/231/253.png +0 -0
  98. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//351/276/231.png +0 -0
  99. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/1.png +0 -0
  100. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/10.png +0 -0
  101. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/11.png +0 -0
  102. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/12.png +0 -0
  103. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/13.png +0 -0
  104. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/14.png +0 -0
  105. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/15.png +0 -0
  106. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/16.png +0 -0
  107. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/17.png +0 -0
  108. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/18.png +0 -0
  109. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/19.png +0 -0
  110. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/2.png +0 -0
  111. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/21.png +0 -0
  112. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/23.png +0 -0
  113. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/24.png +0 -0
  114. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/3.png +0 -0
  115. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/4.png +0 -0
  116. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/5.png +0 -0
  117. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/6.png +0 -0
  118. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/7.png +0 -0
  119. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/8.png +0 -0
  120. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/9.png +0 -0
  121. package/lib/render-templates/share-code-classic/assets/eit-logo.png +0 -0
  122. package/lib/render-templates/share-code-classic/assets/img_xingxingdi.png +0 -0
  123. package/lib/render-templates/share-code-classic/assets/roco_icon.png +0 -0
  124. package/lib/render-templates/share-code-classic/assets//345/255/227/344/275/223/Rocofontblack-Regular .ttf +0 -0
  125. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//345/205/211.png +0 -0
  126. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//345/206/260.png +0 -0
  127. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//345/234/260.png +0 -0
  128. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//345/271/273.png +0 -0
  129. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//345/271/275.png +0 -0
  130. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/201/266.png +0 -0
  131. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/227/240.png +0 -0
  132. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/231/256/351/200/232.png +0 -0
  133. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/234/272/346/242/260.png +0 -0
  134. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/255/246.png +0 -0
  135. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/257/222.png +0 -0
  136. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//346/260/264.png +0 -0
  137. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//347/201/253.png +0 -0
  138. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//347/224/265.png +0 -0
  139. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//347/277/274.png +0 -0
  140. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//350/215/211.png +0 -0
  141. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//350/220/214.png +0 -0
  142. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//350/231/253.png +0 -0
  143. package/lib/render-templates/share-code-classic/assets//345/256/240/347/211/251/345/261/236/346/200/247//351/276/231.png +0 -0
  144. package/lib/render-templates/share-code-classic/assets//347/201/253/346/274/206/345/215/260.png +0 -0
  145. package/lib/render-templates/share-code-classic/assets//347/253/213/347/273/230/350/203/214/346/231/257/346/241/206.png +0 -0
  146. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/205/211.png +0 -0
  147. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/206/260.png +0 -0
  148. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/234/260.png +0 -0
  149. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/245/207/345/274/202.png +0 -0
  150. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/271/273.png +0 -0
  151. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/271/275.png +0 -0
  152. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/201/266.png +0 -0
  153. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/231/256/351/200/232.png +0 -0
  154. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/234/272/346/242/260.png +0 -0
  155. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/255/246.png +0 -0
  156. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/257/222.png +0 -0
  157. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/260/264.png +0 -0
  158. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/261/241/346/237/223.png +0 -0
  159. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/201/253.png +0 -0
  160. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/224/265.png +0 -0
  161. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/277/274.png +0 -0
  162. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/215/211.png +0 -0
  163. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/220/214.png +0 -0
  164. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/231/253.png +0 -0
  165. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//351/246/226/351/242/206.png +0 -0
  166. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//351/276/231.png +0 -0
  167. package/lib/render-templates/share-code-classic/index.html +119 -0
  168. package/lib/render-templates/share-code-classic/style.css +646 -0
  169. package/lib/render-templates/yuanxing-shangren/merchant.html +479 -461
  170. package/lib/render-templates/yuanxing-shangren/today.html +407 -461
  171. package/lib/render.d.ts +3 -0
  172. package/lib/render.js +19 -3
  173. package/lib/send-image.js +10 -0
  174. package/lib/share-code-service.d.ts +36 -0
  175. package/lib/share-code-service.js +73 -0
  176. package/lib/ttf/rocom_origin.ttf +0 -0
  177. package/lib/types.d.ts +9 -1
  178. package/lib/user.d.ts +6 -0
  179. package/lib/wiki-catalog.js +1 -1
  180. package/lib/wiki-service.js +1 -1
  181. package/package.json +2 -1
  182. package/readme.md +3 -23
  183. package/lib/ttf/rocom_skill_origin.ttf +0 -0
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.numberOrNull = void 0;
4
4
  exports.livePrice = livePrice;
5
5
  exports.merchantTimestampMs = merchantTimestampMs;
6
+ exports.isAllDayMerchantProduct = isAllDayMerchantProduct;
7
+ exports.wikiItemAssetUrl = wikiItemAssetUrl;
8
+ exports.collectAllDayProductNames = collectAllDayProductNames;
6
9
  exports.normalizeLiveMerchant = normalizeLiveMerchant;
7
10
  exports.normalizeLegacyMerchant = normalizeLegacyMerchant;
8
11
  exports.isLiveMerchantPayload = isLiveMerchantPayload;
@@ -15,7 +18,7 @@ const numberOrNull = (v) => {
15
18
  return Number.isFinite(n) ? n : null;
16
19
  };
17
20
  exports.numberOrNull = numberOrNull;
18
- // 价格可能直接是数字,也可能是 { real, origin } 或 { amount }(上游 v4.1.0)。
21
+ // 价格可能直接是数字,也可能是 { real, origin } 或 { amount }。
19
22
  function livePrice(value) {
20
23
  const selected = value && typeof value === 'object'
21
24
  ? value.real ?? value.origin : value;
@@ -31,6 +34,63 @@ function merchantTimestampMs(value) {
31
34
  return null;
32
35
  return Math.abs(timestamp) < 100_000_000_000 ? timestamp * 1000 : timestamp;
33
36
  }
37
+ const MERCHANT_ROUND_SPAN_MS = 4 * 3600_000;
38
+ // 按接口真实字段判定商品是否全天在售,而非 4 小时轮次商品。
39
+ // - 实时接口带 next_refresh_time 的商品随轮次刷新,属于 4 小时商品;
40
+ // - 旧接口 round 字段:0 表示全天在售,1/2/3/4 表示对应 4 小时轮次;
41
+ // - 已知起止时间时,有效时长超过单轮 4 小时即视为全天商品;
42
+ // - 仅知结束时间时(实时接口全天商品只有 disable_time),结束时刻落在
43
+ // 当日 24:00 边界(23:59:58 之后或次日 00:00:01 之内)视为全天商品。
44
+ function isAllDayMerchantProduct(refreshAt, startsAt, endsAt, roundValue, secondsOfDay) {
45
+ if (refreshAt !== null)
46
+ return false;
47
+ if (roundValue !== null && roundValue !== undefined && roundValue !== '') {
48
+ const round = Number(roundValue);
49
+ if (Number.isFinite(round))
50
+ return round === 0;
51
+ }
52
+ if (startsAt !== null && endsAt !== null)
53
+ return endsAt - startsAt > MERCHANT_ROUND_SPAN_MS;
54
+ if (endsAt !== null && secondsOfDay) {
55
+ const second = secondsOfDay(endsAt);
56
+ return second >= 23 * 3600 + 59 * 60 + 58 || second <= 1;
57
+ }
58
+ return false;
59
+ }
60
+ // 实时接口不返回商品图片地址时,按 goods_mapping 的 item_id/item_kind
61
+ // 使用 Wiki 公共物品图,避免商品图片 404。
62
+ function wikiItemAssetUrl(baseUrl, itemId, itemKind) {
63
+ const idText = String(itemId ?? '').trim();
64
+ const kindText = String(itemKind ?? '').trim() || 'bag';
65
+ if (!baseUrl || !idText || !/^\d+$/.test(idText))
66
+ return '';
67
+ return `${baseUrl.replace(/\/+$/, '')}/api/v1/resources/wiki/assets/items/${kindText}/${idText}.png`;
68
+ }
69
+ function resolveMerchantIcon(direct, itemId, itemKind, options) {
70
+ const url = String(direct || '').trim();
71
+ if (url)
72
+ return url;
73
+ const wikiUrl = wikiItemAssetUrl(options.assetBaseUrl, itemId, itemKind);
74
+ return wikiUrl || String(options.fallbackIcon || '');
75
+ }
76
+ // 同一商品可能同时出现在多个分组,只有全部出现均为全天时才按全天去重。
77
+ function collectAllDayProductNames(items) {
78
+ const flags = new Map();
79
+ for (const item of items) {
80
+ const name = String(item?.name || '').trim();
81
+ if (!name)
82
+ continue;
83
+ const seen = flags.get(name) || [];
84
+ seen.push(Boolean(item.allDay));
85
+ flags.set(name, seen);
86
+ }
87
+ const names = new Set();
88
+ for (const [name, seen] of flags) {
89
+ if (seen.length && seen.every(Boolean))
90
+ names.add(name);
91
+ }
92
+ return names;
93
+ }
34
94
  function mappingById(payload) {
35
95
  const mapping = payload?._goods_mapping ?? payload?.goods_mapping;
36
96
  const list = Array.isArray(mapping) ? mapping : [];
@@ -48,11 +108,8 @@ function liveGoodsName(meta, item, goodsId) {
48
108
  return name;
49
109
  return goodsId !== undefined && goodsId !== null && goodsId !== '' ? `商品 ${goodsId}` : '未知商品';
50
110
  }
51
- function liveGoodsIcon(meta, item) {
52
- return String(item?.icon_url || item?.iconUrl || meta?.icon_url || meta?.icon || '').trim();
53
- }
54
111
  // 实时商人 goods 结构:按 mapping 补齐名称/图标,递归 sub_goods,按“商品 ID + 时间窗口 + 父商品”去重。
55
- function normalizeLiveMerchant(payload, nowMs = Date.now()) {
112
+ function normalizeLiveMerchant(payload, nowMs = Date.now(), options = {}) {
56
113
  const map = mappingById(payload);
57
114
  const items = [];
58
115
  const seen = new Set();
@@ -63,15 +120,18 @@ function normalizeLiveMerchant(payload, nowMs = Date.now()) {
63
120
  const meta = map.get(String(goodsId)) || {};
64
121
  const startsAt = merchantTimestampMs(raw.start_time ?? payload?.start_time);
65
122
  const endsAt = merchantTimestampMs(raw.disable_time ?? raw.end_time);
123
+ const refreshAt = merchantTimestampMs(raw.next_refresh_time);
66
124
  const key = `${String(goodsId)}|${startsAt ?? ''}|${endsAt ?? ''}|${parentId ?? ''}`;
67
125
  if (seen.has(key))
68
126
  return;
69
127
  seen.add(key);
128
+ const itemId = meta.item_id ?? raw.item_id;
129
+ const itemKind = meta.item_kind ?? raw.item_kind ?? 'bag';
70
130
  items.push({
71
131
  id: String(goodsId ?? ''),
72
132
  parentId,
73
133
  name: liveGoodsName(meta, raw, goodsId),
74
- icon: liveGoodsIcon(meta, raw),
134
+ icon: resolveMerchantIcon(raw.icon_url || raw.iconUrl || meta.icon_url || meta.icon, itemId, itemKind, options),
75
135
  price: livePrice(raw.price) ?? (0, exports.numberOrNull)(meta.price),
76
136
  limit: (0, exports.numberOrNull)(raw.limit_buy_num ?? raw.buy_limit_num ?? meta.buy_limit_num),
77
137
  startsAt,
@@ -79,6 +139,9 @@ function normalizeLiveMerchant(payload, nowMs = Date.now()) {
79
139
  active: (startsAt === null || nowMs >= startsAt) && (endsAt === null || nowMs < endsAt),
80
140
  category: parentId ? '子商品' : '商品',
81
141
  source: 'live',
142
+ allDay: isAllDayMerchantProduct(refreshAt, startsAt, endsAt, undefined, options.secondsOfDay),
143
+ itemId: itemId !== undefined && itemId !== null ? String(itemId) : undefined,
144
+ itemKind: String(itemKind || 'bag'),
82
145
  });
83
146
  const children = Array.isArray(raw.sub_goods) ? raw.sub_goods : [];
84
147
  for (const child of children)
@@ -90,7 +153,7 @@ function normalizeLiveMerchant(payload, nowMs = Date.now()) {
90
153
  return items;
91
154
  }
92
155
  // 旧接口:merchantActivities 下 products/get_props/get_extra_props/get_pets 合并,random_goods 补价格/限购。
93
- function normalizeLegacyMerchant(payload, nowMs = Date.now()) {
156
+ function normalizeLegacyMerchant(payload, nowMs = Date.now(), options = {}) {
94
157
  const activities = payload?.merchantActivities || payload?.merchant_activities || [];
95
158
  const activity = activities[0] || {};
96
159
  const randomGoods = Array.isArray(payload?.random_goods) ? payload.random_goods : [];
@@ -121,10 +184,12 @@ function normalizeLegacyMerchant(payload, nowMs = Date.now()) {
121
184
  continue;
122
185
  seen.add(key);
123
186
  const meta = metaByName.get(name) || {};
187
+ const itemId = raw.item_id ?? meta.item_id;
188
+ const itemKind = raw.item_kind ?? meta.item_kind ?? 'bag';
124
189
  items.push({
125
190
  id: String(raw.id ?? ''),
126
191
  name,
127
- icon: String(raw.icon_url || raw.iconUrl || meta.icon_url || meta.icon || '').trim(),
192
+ icon: resolveMerchantIcon(raw.icon_url || raw.iconUrl || meta.icon_url || meta.icon, itemId, itemKind, options),
128
193
  price: (0, exports.numberOrNull)(raw.price) ?? (0, exports.numberOrNull)(meta.price),
129
194
  limit: (0, exports.numberOrNull)(raw.buy_limit_num ?? raw.limit_buy_num) ?? (0, exports.numberOrNull)(meta.buy_limit_num),
130
195
  startsAt,
@@ -132,6 +197,9 @@ function normalizeLegacyMerchant(payload, nowMs = Date.now()) {
132
197
  active: (startsAt === null || nowMs >= startsAt) && (endsAt === null || nowMs < endsAt),
133
198
  category,
134
199
  source: 'legacy',
200
+ allDay: isAllDayMerchantProduct(merchantTimestampMs(raw.next_refresh_time), startsAt, endsAt, raw.round, options.secondsOfDay),
201
+ itemId: itemId !== undefined && itemId !== null ? String(itemId) : undefined,
202
+ itemKind: String(itemKind || 'bag'),
135
203
  });
136
204
  }
137
205
  }
@@ -140,9 +208,9 @@ function normalizeLegacyMerchant(payload, nowMs = Date.now()) {
140
208
  function isLiveMerchantPayload(payload) {
141
209
  return (0, client_1.isObject)(payload) && Array.isArray(payload.goods);
142
210
  }
143
- function normalizeMerchant(payload, nowMs = Date.now()) {
211
+ function normalizeMerchant(payload, nowMs = Date.now(), options = {}) {
144
212
  if (isLiveMerchantPayload(payload)) {
145
- return { source: 'live', items: normalizeLiveMerchant(payload, nowMs) };
213
+ return { source: 'live', items: normalizeLiveMerchant(payload, nowMs, options) };
146
214
  }
147
- return { source: 'legacy', items: normalizeLegacyMerchant(payload, nowMs) };
215
+ return { source: 'legacy', items: normalizeLegacyMerchant(payload, nowMs, options) };
148
216
  }
@@ -36,6 +36,7 @@ export declare function petDataAttributes(pet: any): Array<{
36
36
  percent: number;
37
37
  }>;
38
38
  export declare function petDataSkillItems(pet: any): any[];
39
+ export declare function extractRawItems(payload: any): any[];
39
40
  export declare function petDataSkillIdsFromPayload(payload: any): string[];
40
41
  export declare function petDataPetIdsFromPayload(payload: any): string[];
41
42
  export declare function petDataSkillIconUrl(baseUrl: string, skillId: any, detail?: any): string;
@@ -60,3 +61,15 @@ export declare function buildPetDataRenderData(res: any, uid: string, options?:
60
61
  lowBandwidthMode?: boolean;
61
62
  baseUrl?: string;
62
63
  }): any;
64
+ export interface PetDetailClassicOptions {
65
+ optionsPayload?: any;
66
+ skillLookup?: Record<string, any>;
67
+ baseUrl?: string;
68
+ loadSkillIcons?: boolean;
69
+ variantIconPath?: (mutationType: number) => string;
70
+ }
71
+ export declare function buildPetDetailClassicView(res: any, uid: string, options?: PetDetailClassicOptions): {
72
+ uid: string;
73
+ emptyText: string;
74
+ pets: any[];
75
+ };
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // 家园详情(ingame pet/data)渲染数据构造(移植自上游 astrbot v3.6.1/v3.7.0)。
2
+ // 家园详情(ingame pet/data)渲染数据构造。
3
3
  // 纯函数模块,便于单元测试。
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.petDataDisplay = petDataDisplay;
@@ -17,6 +17,7 @@ exports.petDataLookup = petDataLookup;
17
17
  exports.petDataVariant = petDataVariant;
18
18
  exports.petDataAttributes = petDataAttributes;
19
19
  exports.petDataSkillItems = petDataSkillItems;
20
+ exports.extractRawItems = extractRawItems;
20
21
  exports.petDataSkillIdsFromPayload = petDataSkillIdsFromPayload;
21
22
  exports.petDataPetIdsFromPayload = petDataPetIdsFromPayload;
22
23
  exports.petDataSkillIconUrl = petDataSkillIconUrl;
@@ -24,7 +25,9 @@ exports.petDataSkills = petDataSkills;
24
25
  exports.petDataCardItems = petDataCardItems;
25
26
  exports.petDataExtractItems = petDataExtractItems;
26
27
  exports.buildPetDataRenderData = buildPetDataRenderData;
28
+ exports.buildPetDetailClassicView = buildPetDetailClassicView;
27
29
  const pet_size_1 = require("./pet-size");
30
+ const element_colors_1 = require("./element-colors");
28
31
  const CN_TIME_ZONE = 'Asia/Shanghai';
29
32
  function petDataDisplay(value, defaultText = '--') {
30
33
  if (value === undefined || value === null || value === '')
@@ -106,7 +109,7 @@ function petDataKgCompact(grams) {
106
109
  const text = (number / 1000).toFixed(2).replace(/0+$/, '').replace(/\.$/, '');
107
110
  return `${text}kg`;
108
111
  }
109
- // 上游 v3.7.0:接入 Wiki 精灵体重范围,与查蛋共用同一判定。
112
+ // 接入 Wiki 精灵体重范围,与查蛋共用同一判定。
110
113
  function petDataWeightSizeInfo(pet, wikiPet) {
111
114
  const bodySize = wikiPet && typeof wikiPet === 'object' ? wikiPet.body_size : null;
112
115
  const weightRange = bodySize && typeof bodySize === 'object' ? bodySize.weight : null;
@@ -464,3 +467,109 @@ function buildPetDataRenderData(res, uid, options = {}) {
464
467
  copyright: 'Koishi & WeGame 洛克王国插件',
465
468
  };
466
469
  }
470
+ function classicMutationType(pet, raw) {
471
+ const direct = Number(pet?.mutation_type ?? raw?.mutation_type ?? NaN);
472
+ if (Number.isFinite(direct) && [1, 8, 9].includes(direct))
473
+ return direct;
474
+ const values = [];
475
+ for (const value of [pet?.real_speciality_ids, raw?.real_speciality_ids, pet?.speciality_id, raw?.speciality_id]) {
476
+ if (Array.isArray(value))
477
+ values.push(...value);
478
+ else if (value !== undefined && value !== null && value !== '')
479
+ values.push(value);
480
+ }
481
+ const ids = new Set(values.map(value => String(value).trim()));
482
+ if (ids.has('103') && ids.has('502'))
483
+ return 9;
484
+ if (ids.has('103'))
485
+ return 1;
486
+ if (ids.has('502'))
487
+ return 8;
488
+ return 0;
489
+ }
490
+ function classicTypeNames(pet) {
491
+ const fromPet = Array.isArray(pet?.unit_type) ? pet.unit_type : [];
492
+ const fromInfo = Array.isArray(pet?.pet_types_info)
493
+ ? pet.pet_types_info.map((item) => item?.name || item?.type_name).filter(Boolean)
494
+ : [];
495
+ const source = fromPet.length > 0 ? fromPet : fromInfo;
496
+ const names = source.map((name) => String(name ?? '').trim()).filter(Boolean);
497
+ return [...new Set(names)];
498
+ }
499
+ function buildPetDetailClassicView(res, uid, options = {}) {
500
+ let payload = res || {};
501
+ if (payload.result && typeof payload.result === 'object')
502
+ payload = payload.result;
503
+ const optionMaps = petDataOptionMaps(options.optionsPayload);
504
+ const pets = [];
505
+ extractRawItems(payload).forEach((raw, index) => {
506
+ const npcPet = raw?.npc_pet && typeof raw.npc_pet === 'object' ? raw.npc_pet : {};
507
+ const pet = npcPet?.pet && typeof npcPet.pet === 'object' ? npcPet.pet : {};
508
+ const status = String(raw?.status || (Object.keys(pet).length ? 'ok' : 'unknown'));
509
+ const baseId = pet.base_conf_id || raw.pet_cfg_id || pet.catch_base_id || pet.conf_id;
510
+ const name = pet.name || pet.pet_default_name || raw.pet_default_name || raw.name || `精灵 ${baseId || index + 1}`;
511
+ const gender = Number(pet.gender ?? 0) || 0;
512
+ const mutationType = classicMutationType(pet, raw);
513
+ const bloodName = petDataLookup(optionMaps.bloodlines, pet.blood_id ?? pet.bloodline_id, '');
514
+ const bloodElement = (0, element_colors_1.bloodlineElement)(bloodName || '普通');
515
+ const typeNames = classicTypeNames(pet);
516
+ const typeTags = typeNames.map(tagName => ({ name: tagName, icon: tagName, color: (0, element_colors_1.elementColor)(tagName) }));
517
+ const bloodTag = bloodName
518
+ ? { name: bloodName, icon: bloodElement, color: (0, element_colors_1.elementColor)(bloodElement) }
519
+ : null;
520
+ const voiceInfo = petDataVoiceInfo(pet.voice);
521
+ const natureText = petDataLookup(optionMaps.natures, pet.nature, '');
522
+ const skillItems = petDataSkills(pet, options.skillLookup || {}, {
523
+ loadSkillIcons: options.loadSkillIcons !== false,
524
+ baseUrl: options.baseUrl || '',
525
+ });
526
+ const classicSkill = (skill) => ({
527
+ name: skill.name,
528
+ iconUrl: skill.icon || '',
529
+ fallbackIconUrl: '',
530
+ placeholderIconUrl: '',
531
+ typeIcon: skill.element || '无',
532
+ typeColor: (0, element_colors_1.elementColor)(skill.element),
533
+ families: skill.type || '',
534
+ cost: skill.cost && skill.cost !== '--' ? skill.cost : '0',
535
+ });
536
+ const featureRaw = pet.feature && typeof pet.feature === 'object'
537
+ ? pet.feature
538
+ : (pet.feature_info && typeof pet.feature_info === 'object' ? pet.feature_info : null);
539
+ pets.push({
540
+ name: String(name),
541
+ level: petDataDisplay(pet.level),
542
+ iconUrl: petDataImageUrl(baseId, 'image'),
543
+ fallbackIconUrl: petDataImageUrl(baseId, 'icon'),
544
+ fallbackIconUrl2: '',
545
+ genderText: gender === 1 ? '♂ 雄性' : gender === 2 ? '♀ 雌性' : '未知',
546
+ natureText: natureText && natureText !== '--' ? natureText : '',
547
+ // 标签(婉转声/粗嗓门)由 voiceLabel 单独承载,这里只放纯分贝文本避免重复。
548
+ voiceText: voiceInfo.value === '--' ? '' : petDataVoiceText(pet.voice),
549
+ voiceLabel: voiceInfo.className === 'voice-soft' ? '婉转声' : (voiceInfo.className === 'voice-rough' ? '粗嗓门' : ''),
550
+ variantIcon: mutationType && options.variantIconPath ? options.variantIconPath(mutationType) : '',
551
+ typeColor: typeTags[0]?.color || (bloodTag ? bloodTag.color : '#a687d5'),
552
+ bloodTag,
553
+ typeTags,
554
+ feature: featureRaw && (featureRaw.name || featureRaw.desc)
555
+ ? { name: String(featureRaw.name || '未知特性'), desc: String(featureRaw.desc || featureRaw.description || '') }
556
+ : null,
557
+ featureIcon: String(featureRaw?.icon || ''),
558
+ fallbackFeatureIcon: '',
559
+ attributes: petDataAttributes(pet).map(attr => ({
560
+ label: attr.label,
561
+ value: attr.value,
562
+ talent: attr.talent,
563
+ effort: attr.race,
564
+ })),
565
+ equipSkills: skillItems.filter(skill => skill.statusClass === 'equipped').map(classicSkill),
566
+ learnedSkills: skillItems.filter(skill => skill.statusClass !== 'equipped').map(classicSkill),
567
+ errorText: petDataDisplay(raw.error || raw.message || npcPet.error_message, ''),
568
+ });
569
+ });
570
+ return {
571
+ uid: petDataDisplay(payload.uin || uid),
572
+ emptyText: '未获取到家园精灵数据。请确认目标玩家在线、家园可访问,或稍后重试。',
573
+ pets,
574
+ };
575
+ }
@@ -11,7 +11,7 @@ exports.mergePlayerPayloads = mergePlayerPayloads;
11
11
  exports.playerPayloadUid = playerPayloadUid;
12
12
  exports.buildPlayerText = buildPlayerText;
13
13
  const client_1 = require("./client");
14
- // 相对资源地址转绝对地址(上游 v4.1.0);非字符串/非法协议返回空串,避免渲染裂图。
14
+ // 相对资源地址转绝对地址;非字符串/非法协议返回空串,避免渲染裂图。
15
15
  function resourceUrl(value, apiBaseUrl) {
16
16
  if (typeof value !== 'string' || !value.trim())
17
17
  return '';
@@ -148,7 +148,7 @@ function playerField(parsed, field, defaultValue = '未设置') {
148
148
  const value = cleanPlayerFieldValue(field, raw);
149
149
  return value && value !== '-' && value !== '未设置' ? value : defaultValue;
150
150
  }
151
- // 合并搜索结果与名片结果;名片失败时保留基础信息(上游 v4.1.0)。
151
+ // 合并搜索结果与名片结果;名片失败时保留基础信息。
152
152
  function buildPlayerView(searchPayload, cardPayload, uid, apiBaseUrl) {
153
153
  const merged = mergePlayerPayloads(searchPayload, cardPayload);
154
154
  const parsed = parseIngamePlayerPayload(merged, uid);
@@ -59,7 +59,7 @@ function toInt(value, fallback = 0) {
59
59
  const n = Number(value);
60
60
  return Number.isFinite(n) ? Math.trunc(n) : fallback;
61
61
  }
62
- // 无时区的时间字符串按 +08:00 解释(与上游一致)。
62
+ // 无时区的时间字符串按 +08:00 解释。
63
63
  function formatRankingTime(value) {
64
64
  const text = String(value ?? '').trim();
65
65
  if (!text)
@@ -6,7 +6,7 @@
6
6
 
7
7
  @font-face {
8
8
  font-family: "rocom-skill";
9
- src: url("../../ttf/rocom_skill_origin.ttf") format("truetype");
9
+ src: url("../../ttf/fzlant.D5FI9Et0.ttf") format("truetype");
10
10
  font-display: swap;
11
11
  }
12
12
 
@@ -15,18 +15,18 @@
15
15
  }
16
16
 
17
17
  html,
18
- body {
19
- margin: 0;
20
- padding: 0;
21
- display: inline-block;
22
- width: fit-content;
23
- min-width: 0;
24
- height: auto;
25
- min-height: 0;
26
- background: transparent;
27
- color: #ffffff;
28
- font-family: "rocom-home", "Microsoft YaHei", "PingFang SC", sans-serif;
29
- }
18
+ body {
19
+ margin: 0;
20
+ padding: 0;
21
+ display: inline-block;
22
+ width: fit-content;
23
+ min-width: 0;
24
+ height: auto;
25
+ min-height: 0;
26
+ background: transparent;
27
+ color: #ffffff;
28
+ font-family: "rocom-home", "Microsoft YaHei", "PingFang SC", sans-serif;
29
+ }
30
30
 
31
31
  .home-page {
32
32
  position: relative;
@@ -42,7 +42,8 @@ body {
42
42
  inset: 0 auto auto 0;
43
43
  z-index: 0;
44
44
  width: 1000px;
45
- min-height: 100%;
45
+ /* height 而非 min-height:背景图按容器高度裁切,避免大图溢出被算进 scrollHeight 导致截图尾部大片空白 */
46
+ height: 100%;
46
47
  object-fit: cover;
47
48
  object-position: top center;
48
49
  pointer-events: none;
@@ -0,0 +1,80 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=1000, initial-scale=1">
6
+ <link rel="stylesheet" href="{{_res_path}}render-templates/home-pets/style.css">
7
+ <title>家园精灵</title>
8
+ </head>
9
+ <body>
10
+ <div class="home-pets-page">
11
+ <img class="page-bg-img" src="{{_res_path}}render-templates/home/img/rocomuid/bg.jpg" alt="">
12
+ <header class="home-top">
13
+ <img class="top-bg" src="{{_res_path}}render-templates/home/img/rocomuid/top_bg.png" alt="">
14
+ <img class="title-fg" src="{{_res_path}}render-templates/home/img/rocomuid/title_fg.png" alt="">
15
+ <div class="avatar-wrap">
16
+ <img class="avatar-img" src="{{_res_path}}render-templates/home/img/rocomuid/img_head.png" alt="">
17
+ </div>
18
+ <div class="home-profile">
19
+ <div class="home-owner">{{homeName}}的家园精灵</div>
20
+ <div class="home-uid">学号 {{uid}}</div>
21
+ </div>
22
+ {{if updatedAt}}
23
+ <div class="update-time">{{updatedAt}}</div>
24
+ {{/if}}
25
+ </header>
26
+
27
+ <section class="pets-summary">
28
+ <span>共 <strong>{{total}}</strong> 只精灵</span>
29
+ {{if eggCount > 0}}
30
+ <span class="egg-summary">可收蛋 <strong>{{eggCount}}</strong> 只</span>
31
+ {{/if}}
32
+ </section>
33
+
34
+ <main class="pets-content">
35
+ {{if pets.length}}
36
+ <div class="pet-grid">
37
+ {{each pets item}}
38
+ <article class="pet-card">
39
+ <img class="card-bg" src="{{_res_path}}render-templates/home/img/rocomuid/pet_bg.png" alt="">
40
+ <div class="pet-avatar-box">
41
+ {{if item.iconUrl}}
42
+ <img class="pet-avatar" src="{{item.iconUrl}}" alt="">
43
+ {{else}}
44
+ <div class="pet-avatar pet-empty">无图</div>
45
+ {{/if}}
46
+ {{if item.starIconUrl}}
47
+ <img class="pet-star" src="{{item.starIconUrl}}" alt="">
48
+ {{/if}}
49
+ <div class="level-badge">
50
+ <img src="{{_res_path}}render-templates/home/img/rocomuid/level_icon.png" alt="">
51
+ <span>Lv{{item.level}}</span>
52
+ </div>
53
+ </div>
54
+ <div class="pet-info">
55
+ <div class="pet-name">
56
+ <span class="pet-name-text">{{item.name}}</span>
57
+ {{if item.eggReady}}<em class="egg-badge">可收蛋</em>{{/if}}
58
+ </div>
59
+ <div class="pet-attrs">
60
+ <span class="attr"><i>性别</i>{{item.genderText}}</span>
61
+ <span class="attr"><i>体重</i>{{item.weightText}}</span>
62
+ <span class="attr"><i>声音</i>{{item.voiceText}}</span>
63
+ </div>
64
+ {{if item.variantText}}<div class="pet-variant">{{item.variantText}}</div>{{/if}}
65
+ {{if item.eggText}}<div class="pet-egg">{{item.eggText}}</div>{{/if}}
66
+ </div>
67
+ </article>
68
+ {{/each}}
69
+ </div>
70
+ {{else}}
71
+ <div class="empty-state">{{emptyText}}</div>
72
+ {{/if}}
73
+ </main>
74
+
75
+ <footer class="pets-footer">
76
+ <span>WeGame · 洛克家园精灵</span>
77
+ </footer>
78
+ </div>
79
+ </body>
80
+ </html>