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
@@ -10,6 +10,51 @@ function register(deps) {
10
10
  const image = await renderer.renderPages(ctx, template, data);
11
11
  await (0, send_image_1.sendImageWithFallback)(session, image, fallback, `share-code:${template}`, config);
12
12
  };
13
+ // 信笺风模板(share-code-classic)的本地素材与 Wiki 性格映射。
14
+ const useClassic = config.shareCodeUiStyle !== 'panel';
15
+ const classicAssets = () => {
16
+ const root = 'render-templates/share-code-classic/assets';
17
+ const assetUrl = (rel) => deps.renderer.resourceUrl(`${root}/${rel}`);
18
+ return {
19
+ frame: assetUrl('立绘背景框.png'),
20
+ logo: assetUrl('eit-logo.png'),
21
+ rocoIcon: assetUrl('roco_icon.png'),
22
+ star: assetUrl('img_xingxingdi.png'),
23
+ wax: assetUrl('火漆印.png'),
24
+ typeIcon: name => assetUrl(`宠物属性/${name}.png`),
25
+ bloodIcon: element => assetUrl(`血脉/${element}.png`),
26
+ };
27
+ };
28
+ const classicNatureLookup = async (id) => {
29
+ try {
30
+ const payload = await deps.wikiService.getOptionsPayload();
31
+ const item = (payload?.pet?.natures || []).find((entry) => String(entry?.id ?? '') === id);
32
+ if (!item)
33
+ return {};
34
+ return { name: String(item.name || ''), up: String(item.up || ''), down: String(item.down || ''), summary: String(item.summary || '') };
35
+ }
36
+ catch {
37
+ return {};
38
+ }
39
+ };
40
+ // 性格 id 全量缓存,避免每只精灵各查一次。
41
+ const classicNatureMap = new Map();
42
+ const classicLookup = async (id) => {
43
+ if (!classicNatureMap.has(id))
44
+ classicNatureMap.set(id, await classicNatureLookup(id));
45
+ return classicNatureMap.get(id) || {};
46
+ };
47
+ const sendShareView = async (session, view) => {
48
+ const fallback = (0, share_code_service_1.buildShareCodeText)(view);
49
+ if (!useClassic) {
50
+ await send(session, 'share-code-team', view, fallback);
51
+ return;
52
+ }
53
+ const ids = (view.teams || []).map((team) => String(team.personality?.id ?? ''));
54
+ await Promise.all(ids.map(id => classicLookup(id)));
55
+ const classic = (0, share_code_service_1.buildShareCodeClassicView)(view, classicAssets(), id2 => classicNatureMap.get(id2) || {});
56
+ await send(session, 'share-code-classic', classic, fallback);
57
+ };
13
58
  const command = ctx.command('阵容码', '阵容分享码工具');
14
59
  command.subcommand('解析 <input:text>', '解析分享码或链接')
15
60
  .alias('阵容码解析')
@@ -31,7 +76,7 @@ function register(deps) {
31
76
  const data = (0, share_code_service_1.buildShareCodeView)(payload, 'parse', undefined, baseUrl);
32
77
  if (!data.teams.length)
33
78
  return '分享码中没有可展示的精灵数据。';
34
- await send(session, 'share-code-team', data, (0, share_code_service_1.buildShareCodeText)(data));
79
+ await sendShareView(session, data);
35
80
  });
36
81
  command.subcommand('查询 <input:text>', '查询分享码历史记录')
37
82
  .alias('阵容码查询')
@@ -61,6 +106,6 @@ function register(deps) {
61
106
  }
62
107
  if (!data.teams.length)
63
108
  return '已找到分享码记录,但无法还原阵容详情,请稍后重试。';
64
- await send(session, 'share-code-team', data, (0, share_code_service_1.buildShareCodeText)(data));
109
+ await sendShareView(session, data);
65
110
  });
66
111
  }
@@ -66,7 +66,7 @@ function announcementSubscriptionKey(session) {
66
66
  const privateChat = (0, permissions_1.isDirectSession)(session);
67
67
  return [session?.platform || '', session?.channelId || 'private', privateChat ? (session?.userId || '') : '', 'announcement'].join(':');
68
68
  }
69
- // 上游 v3.2.0:轮询最新公告,向订阅会话推送新公告提醒。
69
+ // 轮询最新公告,向订阅会话推送新公告提醒。
70
70
  async function checkAnnouncementSubscriptions(deps, signal) {
71
71
  const { ctx, client, announcementSubMgr } = deps;
72
72
  const subs = announcementSubMgr.getAll();
@@ -128,7 +128,7 @@ function register(deps) {
128
128
  let lastPercent = -20;
129
129
  try {
130
130
  const { imageCount, totalBytes } = await atlasService.download(ctx, async (percent, stage) => {
131
- // 20% 粒度进度提醒(对应上游行为)
131
+ // 20% 粒度进度提醒
132
132
  if (percent - lastPercent >= 20 || percent >= 100) {
133
133
  lastPercent = percent;
134
134
  await session?.send?.(`图鉴下载进度 ${percent}%:${stage}`);
@@ -829,7 +829,7 @@ class EggService {
829
829
  copyright: 'Koishi & WeGame 洛克王国插件',
830
830
  };
831
831
  }
832
- // ─── 后端 Egg API 转换(上游 v3.8.0)───
832
+ // ─── 后端 Egg API 转换───
833
833
  buildCandidatesFromEggApi(keyword, candidates) {
834
834
  return {
835
835
  keyword,
@@ -0,0 +1,3 @@
1
+ export declare const ELEMENT_COLORS: Record<string, string>;
2
+ export declare function elementColor(name: string): string;
3
+ export declare function bloodlineElement(name: string): string;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ELEMENT_COLORS = void 0;
4
+ exports.elementColor = elementColor;
5
+ exports.bloodlineElement = bloodlineElement;
6
+ // 元素配色表,供信笺风阵容分享与家园详情 classic 模板共用。
7
+ exports.ELEMENT_COLORS = {
8
+ 草: '#4ebc73',
9
+ 火: '#db5525',
10
+ 水: '#3f89b4',
11
+ 光: '#6aa9fe',
12
+ 地: '#9a7e3f',
13
+ 冰: '#63aeda',
14
+ 龙: '#ed4962',
15
+ 电: '#e7c506',
16
+ 毒: '#ba62e0',
17
+ 虫: '#96ca0f',
18
+ 武: '#ff9636',
19
+ 翼: '#3ec7ca',
20
+ 萌: '#fc74a7',
21
+ 幽: '#9446ec',
22
+ 恶: '#cf467a',
23
+ 机械: '#40cba9',
24
+ 幻: '#9fa7f8',
25
+ 普通: '#babbc6',
26
+ 无: '#babbc6',
27
+ };
28
+ function elementColor(name) {
29
+ return exports.ELEMENT_COLORS[String(name || '').trim()] || '#a687d5';
30
+ }
31
+ // 「烈焰血脉」→「火」这类血脉名到元素名的归一。
32
+ function bloodlineElement(name) {
33
+ return String(name || '').replace(/系血脉$/, '').replace(/血脉$/, '').trim() || '普通';
34
+ }
@@ -0,0 +1,32 @@
1
+ export interface ImageContentBox {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export interface ImageMetrics {
8
+ width: number;
9
+ height: number;
10
+ content: ImageContentBox;
11
+ }
12
+ export interface IconFit {
13
+ scale: number;
14
+ offsetX: number;
15
+ offsetY: number;
16
+ }
17
+ /** 读一张图的尺寸与内容框;无法识别时返回 null。 */
18
+ export declare function readImageMetrics(buffer: Buffer): ImageMetrics | null;
19
+ export interface IconFitBox {
20
+ width: number;
21
+ height: number;
22
+ targetArea: number;
23
+ }
24
+ /**
25
+ * 把「等面积归一」算成 CSS transform 参数。
26
+ * `.goods-img` 本身 object-fit: contain 按整张画布贴边居中,这里再叠一层
27
+ * 以框中心为原点的 translate(...) scale(...):
28
+ * - scale 把不透明内容缩放到 targetArea 对应面积,上限是不溢出图标框;
29
+ * - translate 写在 scale 前,矩阵乘法上位移不会被 scale 再放大,
30
+ * 用于把不在画布中心的内容挪回框中心。
31
+ */
32
+ export declare function buildIconFit(metrics: ImageMetrics | null | undefined, options: IconFitBox): IconFit | null;
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ // 图片尺寸与「不透明内容框」测量,用于商品图标等面积归一。
3
+ // 商品图标画布大小、四周留白、内容长宽比都不一致,object-fit: contain 按整张画布贴边,
4
+ // 会导致同一张卡片里圆球占满、细长商品偏小。这里量出不透明内容包围盒供缩放换算。
5
+ // PNG 只精确支持 8bit 非隔行且带独立 alpha 的变体;WebP 只读画布尺寸;其余返回 null 按原样渲染。
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.readImageMetrics = readImageMetrics;
11
+ exports.buildIconFit = buildIconFit;
12
+ const node_zlib_1 = __importDefault(require("node:zlib"));
13
+ const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
14
+ /** PNG 颜色类型 → 每像素通道数 */
15
+ const PNG_CHANNELS = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
16
+ /** 带独立 alpha 通道的颜色类型 → alpha 在像素里的下标 */
17
+ const PNG_ALPHA_INDEX = { 4: 1, 6: 3 };
18
+ function paeth(a, b, c) {
19
+ const p = a + b - c;
20
+ const pa = Math.abs(p - a);
21
+ const pb = Math.abs(p - b);
22
+ const pc = Math.abs(p - c);
23
+ if (pa <= pb && pa <= pc)
24
+ return a;
25
+ if (pb <= pc)
26
+ return b;
27
+ return c;
28
+ }
29
+ /** 按 PNG 的 5 种行过滤器还原一行原始像素 */
30
+ function unfilterScanline(src, dest, prev, filter, bpp) {
31
+ const length = src.length;
32
+ if (filter === 0) {
33
+ src.copy(dest, 0, 0, length);
34
+ return true;
35
+ }
36
+ if (filter === 1) {
37
+ for (let i = 0; i < length; i += 1) {
38
+ dest[i] = (src[i] + (i >= bpp ? dest[i - bpp] : 0)) & 0xff;
39
+ }
40
+ return true;
41
+ }
42
+ if (filter === 2) {
43
+ for (let i = 0; i < length; i += 1) {
44
+ dest[i] = (src[i] + prev[i]) & 0xff;
45
+ }
46
+ return true;
47
+ }
48
+ if (filter === 3) {
49
+ for (let i = 0; i < length; i += 1) {
50
+ const left = i >= bpp ? dest[i - bpp] : 0;
51
+ dest[i] = (src[i] + ((left + prev[i]) >> 1)) & 0xff;
52
+ }
53
+ return true;
54
+ }
55
+ if (filter === 4) {
56
+ for (let i = 0; i < length; i += 1) {
57
+ const left = i >= bpp ? dest[i - bpp] : 0;
58
+ const up = prev[i];
59
+ const upLeft = i >= bpp ? prev[i - bpp] : 0;
60
+ dest[i] = (src[i] + paeth(left, up, upLeft)) & 0xff;
61
+ }
62
+ return true;
63
+ }
64
+ return false;
65
+ }
66
+ function readPngHeader(buffer) {
67
+ if (buffer.length < 33)
68
+ return null;
69
+ if (buffer.toString('ascii', 12, 16) !== 'IHDR')
70
+ return null;
71
+ const width = buffer.readUInt32BE(16);
72
+ const height = buffer.readUInt32BE(20);
73
+ if (width <= 0 || height <= 0)
74
+ return null;
75
+ return {
76
+ width,
77
+ height,
78
+ bitDepth: buffer[24],
79
+ colorType: buffer[25],
80
+ interlace: buffer[28],
81
+ };
82
+ }
83
+ /** 量 PNG 的不透明内容框;只处理 8bit 非隔行带独立 alpha 的图,其余返回 null(整张都算内容)。 */
84
+ function readPngContentBox(buffer, header) {
85
+ const alphaIndex = PNG_ALPHA_INDEX[header.colorType];
86
+ if (alphaIndex === undefined)
87
+ return null;
88
+ if (header.bitDepth !== 8 || header.interlace !== 0)
89
+ return null;
90
+ const channels = PNG_CHANNELS[header.colorType];
91
+ const stride = header.width * channels;
92
+ if (stride <= 0)
93
+ return null;
94
+ const idatParts = [];
95
+ let offset = 8;
96
+ while (offset + 8 <= buffer.length) {
97
+ const length = buffer.readUInt32BE(offset);
98
+ const type = buffer.toString('ascii', offset + 4, offset + 8);
99
+ const dataStart = offset + 8;
100
+ if (dataStart + length > buffer.length)
101
+ break;
102
+ if (type === 'IDAT')
103
+ idatParts.push(buffer.subarray(dataStart, dataStart + length));
104
+ if (type === 'IEND')
105
+ break;
106
+ offset = dataStart + length + 4;
107
+ }
108
+ if (idatParts.length === 0)
109
+ return null;
110
+ let raw;
111
+ try {
112
+ raw = node_zlib_1.default.inflateSync(Buffer.concat(idatParts));
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ let current = Buffer.alloc(stride);
118
+ let previous = Buffer.alloc(stride);
119
+ let cursor = 0;
120
+ let minX = header.width;
121
+ let minY = header.height;
122
+ let maxX = -1;
123
+ let maxY = -1;
124
+ for (let y = 0; y < header.height; y += 1) {
125
+ if (cursor + 1 + stride > raw.length)
126
+ return null;
127
+ const filter = raw[cursor];
128
+ cursor += 1;
129
+ const line = raw.subarray(cursor, cursor + stride);
130
+ cursor += stride;
131
+ if (!unfilterScanline(line, current, previous, filter, channels))
132
+ return null;
133
+ for (let x = 0; x < header.width; x += 1) {
134
+ if (current[(x * channels) + alphaIndex] === 0)
135
+ continue;
136
+ if (x < minX)
137
+ minX = x;
138
+ if (x > maxX)
139
+ maxX = x;
140
+ if (y < minY)
141
+ minY = y;
142
+ if (y > maxY)
143
+ maxY = y;
144
+ }
145
+ const swap = previous;
146
+ previous = current;
147
+ current = swap;
148
+ }
149
+ if (maxX < 0)
150
+ return null;
151
+ return {
152
+ x: minX,
153
+ y: minY,
154
+ width: (maxX - minX) + 1,
155
+ height: (maxY - minY) + 1,
156
+ };
157
+ }
158
+ /** 读 WebP 画布尺寸(VP8X / VP8L / VP8) */
159
+ function readWebpSize(buffer) {
160
+ if (buffer.length < 30)
161
+ return null;
162
+ if (buffer.toString('ascii', 0, 4) !== 'RIFF')
163
+ return null;
164
+ if (buffer.toString('ascii', 8, 12) !== 'WEBP')
165
+ return null;
166
+ const chunk = buffer.toString('ascii', 12, 16);
167
+ if (chunk === 'VP8X') {
168
+ const width = 1 + (buffer[24] | (buffer[25] << 8) | (buffer[26] << 16));
169
+ const height = 1 + (buffer[27] | (buffer[28] << 8) | (buffer[29] << 16));
170
+ return { width, height };
171
+ }
172
+ if (chunk === 'VP8L') {
173
+ const bits = buffer.readUInt32LE(21);
174
+ return {
175
+ width: (bits & 0x3fff) + 1,
176
+ height: ((bits >> 14) & 0x3fff) + 1,
177
+ };
178
+ }
179
+ if (chunk === 'VP8 ') {
180
+ return {
181
+ width: buffer.readUInt16LE(26) & 0x3fff,
182
+ height: buffer.readUInt16LE(28) & 0x3fff,
183
+ };
184
+ }
185
+ return null;
186
+ }
187
+ /** 读一张图的尺寸与内容框;无法识别时返回 null。 */
188
+ function readImageMetrics(buffer) {
189
+ if (!Buffer.isBuffer(buffer) || buffer.length < 16)
190
+ return null;
191
+ if (buffer.subarray(0, 8).equals(PNG_SIGNATURE)) {
192
+ const header = readPngHeader(buffer);
193
+ if (!header)
194
+ return null;
195
+ return {
196
+ width: header.width,
197
+ height: header.height,
198
+ content: readPngContentBox(buffer, header) || {
199
+ x: 0,
200
+ y: 0,
201
+ width: header.width,
202
+ height: header.height,
203
+ },
204
+ };
205
+ }
206
+ const webp = readWebpSize(buffer);
207
+ if (webp) {
208
+ return {
209
+ width: webp.width,
210
+ height: webp.height,
211
+ content: { x: 0, y: 0, width: webp.width, height: webp.height },
212
+ };
213
+ }
214
+ return null;
215
+ }
216
+ function round4(value) {
217
+ // +0 归一掉 -0,避免模板里出现 "scale(-0)" 之类的输出
218
+ return Math.round(value * 10000) / 10000 + 0;
219
+ }
220
+ /**
221
+ * 把「等面积归一」算成 CSS transform 参数。
222
+ * `.goods-img` 本身 object-fit: contain 按整张画布贴边居中,这里再叠一层
223
+ * 以框中心为原点的 translate(...) scale(...):
224
+ * - scale 把不透明内容缩放到 targetArea 对应面积,上限是不溢出图标框;
225
+ * - translate 写在 scale 前,矩阵乘法上位移不会被 scale 再放大,
226
+ * 用于把不在画布中心的内容挪回框中心。
227
+ */
228
+ function buildIconFit(metrics, options) {
229
+ const boxWidth = Number(options.width) || 0;
230
+ const boxHeight = Number(options.height) || 0;
231
+ const targetArea = Number(options.targetArea) || 0;
232
+ if (boxWidth <= 0 || boxHeight <= 0 || targetArea <= 0)
233
+ return null;
234
+ const width = Number(metrics?.width) || 0;
235
+ const height = Number(metrics?.height) || 0;
236
+ const content = metrics?.content;
237
+ const contentWidth = Number(content?.width) || 0;
238
+ const contentHeight = Number(content?.height) || 0;
239
+ if (width <= 0 || height <= 0 || contentWidth <= 0 || contentHeight <= 0)
240
+ return null;
241
+ const containScale = Math.min(boxWidth / width, boxHeight / height);
242
+ if (!Number.isFinite(containScale) || containScale <= 0)
243
+ return null;
244
+ const contentScale = Math.min(Math.sqrt(targetArea / (contentWidth * contentHeight)), boxWidth / contentWidth, boxHeight / contentHeight);
245
+ if (!Number.isFinite(contentScale) || contentScale <= 0)
246
+ return null;
247
+ const contentCenterX = (Number(content?.x) || 0) + (contentWidth / 2);
248
+ const contentCenterY = (Number(content?.y) || 0) + (contentHeight / 2);
249
+ const scale = contentScale / containScale;
250
+ return {
251
+ scale: round4(scale),
252
+ offsetX: round4(-contentScale * (contentCenterX - (width / 2))),
253
+ offsetY: round4(-contentScale * (contentCenterY - (height / 2))),
254
+ };
255
+ }
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Context, Schema } from 'koishi';
2
+ import { ImageFormat } from './render';
2
3
  export declare const name = "rocom";
3
4
  export declare const inject: {
4
5
  required: string[];
@@ -13,11 +14,14 @@ export interface Config {
13
14
  merchantSubscriptionEnabled: boolean;
14
15
  merchantSubscriptionItems: string[];
15
16
  merchantUiStyle: 'new' | 'old';
17
+ shareCodeUiStyle: 'panel' | 'classic';
18
+ homeDetailUiStyle: 'panel' | 'classic';
16
19
  merchantPrivateSubscriptionEnabled: boolean;
17
20
  merchantCheckMode: 'interval' | 'times';
18
21
  merchantCheckInterval: number;
19
22
  merchantCheckTimes: string[];
20
23
  merchantTimezone: string;
24
+ merchantAllDayOnceEnabled: boolean;
21
25
  subscriptionGroupAdminEnabled: boolean;
22
26
  subscriptionBotAdminEnabled: boolean;
23
27
  subscriptionBotAdminAuthority: number;
@@ -32,6 +36,7 @@ export interface Config {
32
36
  renderTimeout: number;
33
37
  atlasZipUrls: string[];
34
38
  atlasGitUrl: string;
39
+ imageFormat: ImageFormat;
35
40
  imageCompressionEnabled: boolean;
36
41
  imageCompressionMinBytes: number;
37
42
  imageCompressionLevel: number;