koishi-plugin-rocom 1.0.18 → 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 (216) hide show
  1. package/lib/announcement-service.d.ts +21 -0
  2. package/lib/announcement-service.js +63 -0
  3. package/lib/async-control.d.ts +3 -0
  4. package/lib/async-control.js +48 -0
  5. package/lib/atlas-service.js +1 -1
  6. package/lib/client.d.ts +58 -7
  7. package/lib/client.js +282 -127
  8. package/lib/commands/egg.js +66 -4
  9. package/lib/commands/merchant.d.ts +105 -0
  10. package/lib/commands/merchant.js +600 -182
  11. package/lib/commands/query.d.ts +18 -0
  12. package/lib/commands/query.js +585 -145
  13. package/lib/commands/ranking.d.ts +2 -0
  14. package/lib/commands/ranking.js +31 -0
  15. package/lib/commands/share-code.d.ts +2 -0
  16. package/lib/commands/share-code.js +111 -0
  17. package/lib/commands/tools.js +48 -60
  18. package/lib/commands/wiki.js +1 -1
  19. package/lib/egg-service.d.ts +82 -8
  20. package/lib/egg-service.js +235 -19
  21. package/lib/element-colors.d.ts +3 -0
  22. package/lib/element-colors.js +34 -0
  23. package/lib/icon-metrics.d.ts +32 -0
  24. package/lib/icon-metrics.js +255 -0
  25. package/lib/img//345/274/202/350/211/262.png +0 -0
  26. package/lib/img//347/202/253/345/275/251.png +0 -0
  27. package/lib/index.d.ts +10 -0
  28. package/lib/index.js +11857 -175
  29. package/lib/merchant-catalog.d.ts +47 -0
  30. package/lib/merchant-catalog.js +193 -0
  31. package/lib/merchant-clock.d.ts +22 -0
  32. package/lib/merchant-clock.js +95 -0
  33. package/lib/merchant-price-log.d.ts +63 -0
  34. package/lib/merchant-price-log.js +187 -0
  35. package/lib/merchant-service.d.ts +38 -0
  36. package/lib/merchant-service.js +216 -0
  37. package/lib/permissions.d.ts +16 -0
  38. package/lib/permissions.js +66 -0
  39. package/lib/pet-data-service.d.ts +13 -0
  40. package/lib/pet-data-service.js +117 -10
  41. package/lib/pet-size.d.ts +13 -0
  42. package/lib/pet-size.js +54 -0
  43. package/lib/player-service.d.ts +37 -0
  44. package/lib/player-service.js +201 -0
  45. package/lib/ranking-service.d.ts +46 -0
  46. package/lib/ranking-service.js +137 -0
  47. package/lib/render-templates/announcement/detail.html +8 -0
  48. package/lib/render-templates/announcement/list.html +5 -0
  49. package/lib/render-templates/announcement/style.css +1 -0
  50. package/lib/render-templates/home/style.css +15 -14
  51. package/lib/render-templates/home-pets/index.html +80 -0
  52. package/lib/render-templates/home-pets/style.css +338 -0
  53. package/lib/render-templates/pet-detail-classic/index.html +151 -0
  54. package/lib/render-templates/pet-detail-classic/style.css +503 -0
  55. package/lib/render-templates/pet-detail-classic/texture2D/a_title.png +0 -0
  56. package/lib/render-templates/pet-detail-classic/texture2D/bg.jpg +0 -0
  57. package/lib/render-templates/pet-detail-classic/texture2D/footer.png +0 -0
  58. package/lib/render-templates/pet-detail-classic/texture2D/icon.png +0 -0
  59. package/lib/render-templates/pet-detail-classic/texture2D/img_head.png +0 -0
  60. package/lib/render-templates/pet-detail-classic/texture2D/jinhua_bg.png +0 -0
  61. package/lib/render-templates/pet-detail-classic/texture2D/level_icon.png +0 -0
  62. package/lib/render-templates/pet-detail-classic/texture2D/line.png +0 -0
  63. package/lib/render-templates/pet-detail-classic/texture2D/mask_bar.png +0 -0
  64. package/lib/render-templates/pet-detail-classic/texture2D/pet_bg.png +0 -0
  65. package/lib/render-templates/pet-detail-classic/texture2D/pet_rocom.png +0 -0
  66. package/lib/render-templates/pet-detail-classic/texture2D/pet_rocom_bg.png +0 -0
  67. package/lib/render-templates/pet-detail-classic/texture2D/poro_bar.png +0 -0
  68. package/lib/render-templates/pet-detail-classic/texture2D/right_jinhua.png +0 -0
  69. package/lib/render-templates/pet-detail-classic/texture2D/skill_bg.png +0 -0
  70. package/lib/render-templates/pet-detail-classic/texture2D/skill_mask.png +0 -0
  71. package/lib/render-templates/pet-detail-classic/texture2D/star.png +0 -0
  72. package/lib/render-templates/pet-detail-classic/texture2D/star_1.png +0 -0
  73. package/lib/render-templates/pet-detail-classic/texture2D/star_8.png +0 -0
  74. package/lib/render-templates/pet-detail-classic/texture2D/star_9.png +0 -0
  75. package/lib/render-templates/pet-detail-classic/texture2D/table.png +0 -0
  76. package/lib/render-templates/pet-detail-classic/texture2D/tags.png +0 -0
  77. package/lib/render-templates/pet-detail-classic/texture2D/title.png +0 -0
  78. package/lib/render-templates/pet-detail-classic/texture2D/top_bg.png +0 -0
  79. package/lib/render-templates/pet-detail-classic/texture2D/xuancai_overlay.png +0 -0
  80. package/lib/render-templates/pet-detail-classic/texture2D/yise_overlay.png +0 -0
  81. package/lib/render-templates/pet-detail-classic/texture2D/ys_bar.png +0 -0
  82. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/10.png +0 -0
  83. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/11.png +0 -0
  84. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/12.png +0 -0
  85. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/13.png +0 -0
  86. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/14.png +0 -0
  87. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/15.png +0 -0
  88. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/16.png +0 -0
  89. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/17.png +0 -0
  90. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/18.png +0 -0
  91. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/19.png +0 -0
  92. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/2.png +0 -0
  93. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/20.png +0 -0
  94. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/23.png +0 -0
  95. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/3.png +0 -0
  96. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/4.png +0 -0
  97. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/5.png +0 -0
  98. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/6.png +0 -0
  99. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/8.png +0 -0
  100. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/9.png +0 -0
  101. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/205/211.png +0 -0
  102. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/206/260.png +0 -0
  103. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/234/260.png +0 -0
  104. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/271/273.png +0 -0
  105. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/271/275.png +0 -0
  106. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/201/266.png +0 -0
  107. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/227/240.png +0 -0
  108. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/231/256/351/200/232.png +0 -0
  109. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/234/272/346/242/260.png +0 -0
  110. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/255/246.png +0 -0
  111. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/257/222.png +0 -0
  112. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/260/264.png +0 -0
  113. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/201/253.png +0 -0
  114. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/224/265.png +0 -0
  115. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/277/274.png +0 -0
  116. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/215/211.png +0 -0
  117. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/220/214.png +0 -0
  118. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/231/253.png +0 -0
  119. package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//351/276/231.png +0 -0
  120. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/1.png +0 -0
  121. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/10.png +0 -0
  122. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/11.png +0 -0
  123. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/12.png +0 -0
  124. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/13.png +0 -0
  125. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/14.png +0 -0
  126. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/15.png +0 -0
  127. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/16.png +0 -0
  128. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/17.png +0 -0
  129. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/18.png +0 -0
  130. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/19.png +0 -0
  131. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/2.png +0 -0
  132. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/21.png +0 -0
  133. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/23.png +0 -0
  134. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/24.png +0 -0
  135. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/3.png +0 -0
  136. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/4.png +0 -0
  137. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/5.png +0 -0
  138. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/6.png +0 -0
  139. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/7.png +0 -0
  140. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/8.png +0 -0
  141. package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/9.png +0 -0
  142. package/lib/render-templates/pet-ranking/index.html +99 -0
  143. package/lib/render-templates/pet-ranking/style.css +338 -0
  144. package/lib/render-templates/player-search/index.html +7 -0
  145. package/lib/render-templates/searcheggs/index.html +1 -1
  146. package/lib/render-templates/searcheggs/size.html +70 -68
  147. package/lib/render-templates/share-code-classic/assets/eit-logo.png +0 -0
  148. package/lib/render-templates/share-code-classic/assets/img_xingxingdi.png +0 -0
  149. package/lib/render-templates/share-code-classic/assets/roco_icon.png +0 -0
  150. package/lib/render-templates/share-code-classic/assets//345/255/227/344/275/223/Rocofontblack-Regular .ttf +0 -0
  151. 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
  152. 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
  153. 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
  154. 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
  155. 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
  156. 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
  157. 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
  158. 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
  159. 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
  160. 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
  161. 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
  162. 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
  163. 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
  164. 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
  165. 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
  166. 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
  167. 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
  168. 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
  169. 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
  170. package/lib/render-templates/share-code-classic/assets//347/201/253/346/274/206/345/215/260.png +0 -0
  171. 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
  172. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/205/211.png +0 -0
  173. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/206/260.png +0 -0
  174. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/234/260.png +0 -0
  175. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/245/207/345/274/202.png +0 -0
  176. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/271/273.png +0 -0
  177. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/271/275.png +0 -0
  178. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/201/266.png +0 -0
  179. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/231/256/351/200/232.png +0 -0
  180. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/234/272/346/242/260.png +0 -0
  181. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/255/246.png +0 -0
  182. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/257/222.png +0 -0
  183. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/260/264.png +0 -0
  184. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/261/241/346/237/223.png +0 -0
  185. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/201/253.png +0 -0
  186. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/224/265.png +0 -0
  187. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/277/274.png +0 -0
  188. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/215/211.png +0 -0
  189. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/220/214.png +0 -0
  190. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/231/253.png +0 -0
  191. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//351/246/226/351/242/206.png +0 -0
  192. package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//351/276/231.png +0 -0
  193. package/lib/render-templates/share-code-classic/index.html +119 -0
  194. package/lib/render-templates/share-code-classic/style.css +646 -0
  195. package/lib/render-templates/share-code-team/index.html +105 -0
  196. package/lib/render-templates/share-code-team/style.css +345 -0
  197. package/lib/render-templates/yuanxing-shangren/merchant.html +479 -461
  198. package/lib/render-templates/yuanxing-shangren/today.html +407 -461
  199. package/lib/render.d.ts +18 -2
  200. package/lib/render.js +138 -39
  201. package/lib/send-image.d.ts +1 -1
  202. package/lib/send-image.js +22 -1
  203. package/lib/share-code-service.d.ts +68 -0
  204. package/lib/share-code-service.js +257 -0
  205. package/lib/subscription-runner.d.ts +6 -0
  206. package/lib/subscription-runner.js +21 -0
  207. package/lib/subscription-send.d.ts +4 -2
  208. package/lib/subscription-send.js +45 -20
  209. package/lib/ttf/rocom_origin.ttf +0 -0
  210. package/lib/types.d.ts +14 -1
  211. package/lib/user.d.ts +11 -0
  212. package/lib/wiki-catalog.js +1 -1
  213. package/lib/wiki-service.js +1 -1
  214. package/package.json +5 -1
  215. package/readme.md +125 -124
  216. package/lib/ttf/rocom_skill_origin.ttf +0 -0
package/lib/render.d.ts CHANGED
@@ -1,11 +1,27 @@
1
1
  import { Context } from 'koishi';
2
+ export type ImageFormat = 'jpeg' | 'png' | 'webp';
3
+ export declare function normalizeImageFormat(format: unknown): ImageFormat;
4
+ export interface RenderOptions {
5
+ signal?: AbortSignal;
6
+ timeoutMs?: number;
7
+ imageWaitMs?: number;
8
+ fontWaitMs?: number;
9
+ deviceScaleFactor?: number;
10
+ imageFormat?: ImageFormat;
11
+ jpegQuality?: number;
12
+ maxPageHeight?: number;
13
+ }
14
+ export declare const lowBandwidthRenderOptions: RenderOptions;
2
15
  export declare class Renderer {
3
16
  private resPath;
4
- constructor(resPath: string);
17
+ private defaults;
18
+ constructor(resPath: string, defaults?: RenderOptions);
5
19
  resourceUrl(relativePath: string): string;
6
20
  private getPreferredResourceRoot;
7
21
  private getTemplateCandidateRoots;
8
22
  private resolveTemplatePath;
9
23
  private getStylePath;
10
- renderHtml(ctx: Context, templateName: string, data: any): Promise<Buffer | null>;
24
+ renderHtml(ctx: Context, templateName: string, data: any, options?: RenderOptions): Promise<Buffer | null>;
25
+ renderPages(ctx: Context, templateName: string, data: any, options?: RenderOptions): Promise<Buffer[] | null>;
26
+ private capture;
11
27
  }
package/lib/render.js CHANGED
@@ -36,22 +36,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Renderer = void 0;
39
+ exports.Renderer = exports.lowBandwidthRenderOptions = void 0;
40
+ exports.normalizeImageFormat = normalizeImageFormat;
40
41
  const koishi_1 = require("koishi");
41
42
  const template = __importStar(require("art-template"));
42
43
  const node_fs_1 = __importDefault(require("node:fs"));
43
44
  const node_os_1 = __importDefault(require("node:os"));
44
45
  const node_path_1 = __importDefault(require("node:path"));
45
46
  const node_url_1 = require("node:url");
47
+ const async_control_1 = require("./async-control");
46
48
  const logger = new koishi_1.Logger('rocom-render');
47
49
  const TEMPLATE_CAPTURE_PADDING = {
48
50
  package: { left: 0, right: 0, top: 0, bottom: 0 },
49
51
  };
50
52
  const TEMPLATE_VIEWPORTS = {
51
53
  activities: { width: 1600, height: 1200, deviceScaleFactor: 2 },
54
+ // 商人卡片模板按原生尺寸渲染:视口 960 + deviceScaleFactor 4 → 3840 物理像素输出
55
+ 'yuanxing-shangren/merchant': { width: 960, height: 1600, deviceScaleFactor: 4 },
56
+ 'yuanxing-shangren/today': { width: 960, height: 1600, deviceScaleFactor: 4 },
52
57
  };
53
58
  const VISUAL_BOUNDS_TEMPLATES = new Set([
54
59
  'home',
60
+ 'home-pets',
55
61
  'yuanxing-shangren/merchant',
56
62
  'yuanxing-shangren/today',
57
63
  ]);
@@ -59,6 +65,19 @@ const DEFAULT_SCREENSHOT_OPTIONS = {
59
65
  type: 'jpeg',
60
66
  quality: 82,
61
67
  };
68
+ const IMAGE_FORMATS = new Set(['jpeg', 'png', 'webp']);
69
+ function normalizeImageFormat(format) {
70
+ return typeof format === 'string' && IMAGE_FORMATS.has(format) ? format : 'jpeg';
71
+ }
72
+ // 家园详情低带宽建议初值,需用真实截图校准。
73
+ exports.lowBandwidthRenderOptions = {
74
+ timeoutMs: 30000,
75
+ imageWaitMs: 3000,
76
+ fontWaitMs: 1500,
77
+ deviceScaleFactor: 1,
78
+ jpegQuality: 65,
79
+ maxPageHeight: 12000,
80
+ };
62
81
  function toDirectoryFileUrl(dirPath) {
63
82
  const href = (0, node_url_1.pathToFileURL)(dirPath).href;
64
83
  return href.endsWith('/') ? href : `${href}/`;
@@ -68,8 +87,10 @@ function normalizeTemplateResourcePaths(content) {
68
87
  }
69
88
  class Renderer {
70
89
  resPath;
71
- constructor(resPath) {
90
+ defaults;
91
+ constructor(resPath, defaults = {}) {
72
92
  this.resPath = resPath;
93
+ this.defaults = defaults;
73
94
  }
74
95
  resourceUrl(relativePath) {
75
96
  return (0, node_url_1.pathToFileURL)(node_path_1.default.join(this.getPreferredResourceRoot(), relativePath)).href;
@@ -107,7 +128,62 @@ class Renderer {
107
128
  return '';
108
129
  return node_path_1.default.join(resolved.resourceRoot, 'render-templates', templateName, 'style.css');
109
130
  }
110
- async renderHtml(ctx, templateName, data) {
131
+ async renderHtml(ctx, templateName, data, options = {}) {
132
+ const pages = await this.renderPages(ctx, templateName, data, { ...options, maxPageHeight: 0 });
133
+ return pages?.[0] ?? null;
134
+ }
135
+ async renderPages(ctx, templateName, data, options = {}) {
136
+ options = { maxPageHeight: 6000, ...this.defaults, ...options };
137
+ let page;
138
+ let tempDir = '';
139
+ let stopped = false;
140
+ let closing;
141
+ const close = () => {
142
+ if (page && !closing)
143
+ closing = Promise.resolve().then(() => page.close()).catch(() => { });
144
+ return closing;
145
+ };
146
+ try {
147
+ return await (0, async_control_1.withDeadline)(options.timeoutMs || 30000, options.signal, async (signal) => {
148
+ const acquire = async () => {
149
+ page = await ctx.puppeteer.page();
150
+ if (stopped || signal.aborted) {
151
+ await close();
152
+ signal.throwIfAborted();
153
+ }
154
+ return page;
155
+ };
156
+ return this.capture(ctx, templateName, data, { ...options, signal }, acquire, dir => { tempDir = dir; });
157
+ }, () => { stopped = true; void close(); });
158
+ }
159
+ catch (error) {
160
+ logger.warn('render interrupted for ' + templateName + ': ' + error);
161
+ return null;
162
+ }
163
+ finally {
164
+ stopped = true;
165
+ // A stalled browser must not block the request's deadline indefinitely.
166
+ void close();
167
+ if (tempDir) {
168
+ try {
169
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
170
+ }
171
+ catch { }
172
+ }
173
+ }
174
+ }
175
+ async capture(ctx, templateName, data, options, acquire, rememberDirectory) {
176
+ const timeoutMs = options.timeoutMs && options.timeoutMs > 0 ? options.timeoutMs : 30000;
177
+ const deadline = Date.now() + timeoutMs;
178
+ const imageWaitMs = options.imageWaitMs && options.imageWaitMs > 0 ? options.imageWaitMs : 10000;
179
+ const fontWaitMs = options.fontWaitMs && options.fontWaitMs > 0 ? options.fontWaitMs : 3000;
180
+ const jpegQuality = options.jpegQuality && options.jpegQuality > 0 ? options.jpegQuality : DEFAULT_SCREENSHOT_OPTIONS.quality;
181
+ const imageFormat = normalizeImageFormat(options.imageFormat ?? DEFAULT_SCREENSHOT_OPTIONS.type);
182
+ // quality 仅对有损格式(jpeg/webp)有意义,png 截图忽略该参数
183
+ const screenshotOptions = imageFormat === 'png'
184
+ ? { type: imageFormat }
185
+ : { type: imageFormat, quality: jpegQuality };
186
+ const remainingMs = () => { options.signal?.throwIfAborted(); return Math.max(1, deadline - Date.now()); };
111
187
  try {
112
188
  const resolvedTemplate = this.resolveTemplatePath(templateName);
113
189
  if (!resolvedTemplate) {
@@ -125,27 +201,35 @@ class Renderer {
125
201
  logger.error('puppeteer service is unavailable');
126
202
  return null;
127
203
  }
128
- const page = await ctx.puppeteer.page();
129
- const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'rocom-render-'));
130
- const tempHtmlPath = node_path_1.default.join(tempDir, `${templateName.replace(/[\\/]/g, '_')}.html`);
204
+ // Puppeteer 服务由 Koishi 共享,只能关闭本次 page,不能关闭共享 browser。
205
+ const page = await acquire();
206
+ let tempDir = '';
131
207
  try {
208
+ options.signal?.throwIfAborted();
209
+ tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'rocom-render-'));
210
+ rememberDirectory(tempDir);
211
+ const tempHtmlPath = node_path_1.default.join(tempDir, `${templateName.replace(/[\\/]/g, '_')}.html`);
132
212
  await page.setCacheEnabled(false);
133
213
  node_fs_1.default.writeFileSync(tempHtmlPath, html, 'utf-8');
134
- const initialViewport = TEMPLATE_VIEWPORTS[templateName] || { width: 1280, height: 768, deviceScaleFactor: 2 };
214
+ const templateViewport = TEMPLATE_VIEWPORTS[templateName] || { width: 1280, height: 768, deviceScaleFactor: 2 };
215
+ const initialViewport = {
216
+ ...templateViewport,
217
+ deviceScaleFactor: options.deviceScaleFactor ?? templateViewport.deviceScaleFactor,
218
+ };
135
219
  await page.setViewport(initialViewport);
136
220
  try {
137
221
  await page.goto((0, node_url_1.pathToFileURL)(tempHtmlPath).href, {
138
222
  waitUntil: 'networkidle0',
139
- timeout: 15000,
223
+ timeout: Math.min(15000, remainingMs()),
140
224
  });
141
225
  }
142
226
  catch (err) {
143
227
  logger.warn(`page.goto failed for ${templateName}: ${err}`);
144
228
  }
145
229
  try {
146
- await page.evaluate(async () => {
230
+ await page.evaluate(async (waitImageMs, waitFontMs) => {
147
231
  const images = Array.from(document.images);
148
- // 限制远程图片等待时间,避免慢速资源拖垮整次渲染(对应上游 v3.7.1)。
232
+ // 限制远程图片等待时间,避免慢速资源拖垮整次渲染。
149
233
  await Promise.race([
150
234
  Promise.all(images.map((img) => {
151
235
  if (img.complete)
@@ -155,18 +239,22 @@ class Renderer {
155
239
  img.onerror = () => resolve();
156
240
  });
157
241
  })),
158
- new Promise(resolve => setTimeout(resolve, 10000)),
242
+ new Promise(resolve => setTimeout(resolve, waitImageMs)),
159
243
  ]);
160
244
  const fonts = document.fonts;
161
245
  if (fonts?.ready) {
162
- await fonts.ready;
246
+ // 字体等待单独设上限,避免字体服务异常时无限等待。
247
+ await Promise.race([
248
+ fonts.ready,
249
+ new Promise(resolve => setTimeout(resolve, waitFontMs)),
250
+ ]);
163
251
  }
164
- });
252
+ }, Math.min(imageWaitMs, remainingMs()), Math.min(fontWaitMs, remainingMs()));
165
253
  }
166
254
  catch (err) {
167
255
  logger.warn(`asset wait failed for ${templateName}: ${err}`);
168
256
  }
169
- await new Promise(resolve => setTimeout(resolve, 300));
257
+ await (0, async_control_1.delay)(300, options.signal);
170
258
  const selectors = [
171
259
  '.exchange-page',
172
260
  '.record-page',
@@ -190,9 +278,16 @@ class Renderer {
190
278
  '.merchant-page',
191
279
  '.page',
192
280
  '.home-page',
281
+ '.home-pets-page',
193
282
  '.pet-panel-page',
194
283
  '.pet-detail-page',
195
284
  '.activity-calendar-page',
285
+ '.pet-ranking-page',
286
+ '.share-code-page',
287
+ '.share-page',
288
+ '.pet-page',
289
+ '.announcement-list-page',
290
+ '.announcement-detail-page',
196
291
  ];
197
292
  let target = null;
198
293
  for (const selector of selectors) {
@@ -220,12 +315,12 @@ class Renderer {
220
315
  const capturePadding = TEMPLATE_CAPTURE_PADDING[templateName] || { left: 0, right: 0, top: 0, bottom: 0 };
221
316
  await page.setViewport({
222
317
  width: Math.max(Math.ceil(elementMetrics.x + elementMetrics.width + capturePadding.right) + 8, 200),
223
- height: Math.max(Math.ceil(elementMetrics.y + elementMetrics.height + capturePadding.bottom) + 8, 200),
318
+ height: Math.min(6000, Math.max(Math.ceil(elementMetrics.y + elementMetrics.height + capturePadding.bottom) + 8, 200)),
224
319
  deviceScaleFactor: initialViewport.deviceScaleFactor,
225
320
  });
226
- await new Promise(resolve => setTimeout(resolve, 100));
321
+ await (0, async_control_1.delay)(100, options.signal);
227
322
  // 居中(margin auto)模板在视口调整后 x/y 会变化,截图前需要重新测量,
228
- // 否则 clip 会带着旧偏移导致画面左侧被截断(对应上游 v3.7.5 修复)。
323
+ // 否则 clip 会带着旧偏移导致画面左侧被截断。
229
324
  try {
230
325
  const remeasured = await page.evaluate((el, visualBounds) => {
231
326
  const rect = el.getBoundingClientRect();
@@ -247,30 +342,27 @@ class Renderer {
247
342
  catch (err) {
248
343
  logger.warn(`element remeasure failed for ${templateName}: ${err}`);
249
344
  }
250
- const hasOverflow = elementMetrics.width > box.width + 0.5 ||
251
- elementMetrics.height > box.height + 0.5;
252
- if (capturePadding.left || capturePadding.right || capturePadding.top || capturePadding.bottom || hasOverflow) {
253
- const clipX = Math.max(0, elementMetrics.x - capturePadding.left);
254
- const clipY = Math.max(0, elementMetrics.y - capturePadding.top);
255
- const clipWidth = elementMetrics.width + capturePadding.left + capturePadding.right;
256
- const clipHeight = elementMetrics.height + capturePadding.top + capturePadding.bottom;
257
- const screenshot = await page.screenshot({
258
- ...DEFAULT_SCREENSHOT_OPTIONS,
259
- clip: {
260
- x: clipX,
261
- y: clipY,
262
- width: clipWidth,
263
- height: clipHeight,
264
- },
265
- });
266
- return Buffer.isBuffer(screenshot) ? screenshot : Buffer.from(screenshot);
345
+ const clipX = Math.max(0, elementMetrics.x - capturePadding.left);
346
+ const clipY = Math.max(0, elementMetrics.y - capturePadding.top);
347
+ const width = elementMetrics.width + capturePadding.left + capturePadding.right;
348
+ const height = elementMetrics.height + capturePadding.top + capturePadding.bottom;
349
+ const maxHeight = Math.max(1, options.maxPageHeight || height);
350
+ // Evenly distribute the last few pixels instead of emitting a tiny blank final page.
351
+ const step = Math.ceil(height / Math.ceil(height / maxHeight));
352
+ const pages = [];
353
+ for (let offset = 0; offset < height; offset += step) {
354
+ options.signal?.throwIfAborted();
355
+ const screenshot = await page.screenshot({ ...screenshotOptions, captureBeyondViewport: true,
356
+ clip: { x: clipX, y: clipY + offset, width, height: Math.min(step, height - offset) } });
357
+ pages.push(Buffer.from(screenshot));
267
358
  }
359
+ return pages;
268
360
  }
269
- const screenshot = await target.screenshot(DEFAULT_SCREENSHOT_OPTIONS);
270
- return Buffer.isBuffer(screenshot) ? screenshot : Buffer.from(screenshot);
361
+ const screenshot = await target.screenshot(screenshotOptions);
362
+ return [Buffer.from(screenshot)];
271
363
  }
272
- const screenshot = await page.screenshot({ ...DEFAULT_SCREENSHOT_OPTIONS, fullPage: true });
273
- return Buffer.isBuffer(screenshot) ? screenshot : Buffer.from(screenshot);
364
+ const screenshot = await page.screenshot({ ...screenshotOptions, fullPage: true });
365
+ return [Buffer.from(screenshot)];
274
366
  }
275
367
  finally {
276
368
  try {
@@ -279,7 +371,14 @@ class Renderer {
279
371
  catch {
280
372
  // ignore
281
373
  }
282
- node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
374
+ if (tempDir) {
375
+ try {
376
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
377
+ }
378
+ catch {
379
+ // ignore
380
+ }
381
+ }
283
382
  }
284
383
  }
285
384
  catch (e) {
@@ -1,4 +1,4 @@
1
1
  import { PluginConfig } from './types';
2
2
  export declare function detectImageMime(image: Buffer): string;
3
3
  export declare function compressPngImage(image: Buffer, config: Pick<PluginConfig, 'imageCompressionEnabled' | 'imageCompressionMinBytes' | 'imageCompressionLevel'>): Buffer;
4
- export declare function sendImageWithFallback(session: any, image: Buffer | null, fallbackText: string, scene: string, compressionConfig?: Pick<PluginConfig, 'imageCompressionEnabled' | 'imageCompressionMinBytes' | 'imageCompressionLevel'>): Promise<void>;
4
+ export declare function sendImageWithFallback(session: any, image: Buffer | Buffer[] | null, fallbackText: string, scene: string, compressionConfig?: Pick<PluginConfig, 'imageCompressionEnabled' | 'imageCompressionMinBytes' | 'imageCompressionLevel'>): Promise<boolean>;
package/lib/send-image.js CHANGED
@@ -11,6 +11,8 @@ const node_zlib_1 = __importDefault(require("node:zlib"));
11
11
  const logger = new koishi_1.Logger('rocom-send');
12
12
  const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
13
13
  const JPEG_SIGNATURE = Buffer.from([0xff, 0xd8, 0xff]);
14
+ const WEBP_RIFF_SIGNATURE = Buffer.from('RIFF', 'ascii');
15
+ const WEBP_MARK = Buffer.from('WEBP', 'ascii');
14
16
  function formatSessionContext(session) {
15
17
  if (!session)
16
18
  return 'session=<null>';
@@ -53,11 +55,19 @@ function isPng(buffer) {
53
55
  function isJpeg(buffer) {
54
56
  return buffer.length > JPEG_SIGNATURE.length && buffer.subarray(0, JPEG_SIGNATURE.length).equals(JPEG_SIGNATURE);
55
57
  }
58
+ function isWebp(buffer) {
59
+ // WebP: "RIFF"<4 字节长度>"WEBP"
60
+ return buffer.length >= 12
61
+ && buffer.subarray(0, 4).equals(WEBP_RIFF_SIGNATURE)
62
+ && buffer.subarray(8, 12).equals(WEBP_MARK);
63
+ }
56
64
  function detectImageMime(image) {
57
65
  if (isPng(image))
58
66
  return 'image/png';
59
67
  if (isJpeg(image))
60
68
  return 'image/jpeg';
69
+ if (isWebp(image))
70
+ return 'image/webp';
61
71
  return 'image/png';
62
72
  }
63
73
  function compressPngImage(image, config) {
@@ -114,6 +124,16 @@ function compressPngImage(image, config) {
114
124
  return image;
115
125
  }
116
126
  async function sendImageWithFallback(session, image, fallbackText, scene, compressionConfig) {
127
+ if (Array.isArray(image)) {
128
+ if (!image.length)
129
+ return sendImageWithFallback(session, null, fallbackText, scene, compressionConfig);
130
+ for (const page of image) {
131
+ const ok = await sendImageWithFallback(session, page, fallbackText, scene, compressionConfig);
132
+ if (!ok)
133
+ return false;
134
+ }
135
+ return true;
136
+ }
117
137
  const ctxInfo = formatSessionContext(session);
118
138
  if (!image) {
119
139
  logger.warn(`[${scene}] render returned empty image, fallback to text | ${ctxInfo}`);
@@ -132,8 +152,9 @@ async function sendImageWithFallback(session, image, fallbackText, scene, compre
132
152
  try {
133
153
  const result = await session.send(koishi_1.h.image(outputImage, detectImageMime(outputImage)));
134
154
  if (!hasSendResult(result)) {
135
- logger.warn(`[${scene}] image send returned empty result | size=${outputImage.length}B | ${ctxInfo}`);
155
+ throw new Error('图片发送未返回消息 ID');
136
156
  }
157
+ return true;
137
158
  }
138
159
  catch (e) {
139
160
  logger.error(`[${scene}] image send failed | size=${outputImage.length}B | ${ctxInfo} | ${e}`);
@@ -0,0 +1,68 @@
1
+ export declare const SHARE_CODE_MAX_LENGTH: number;
2
+ export type ShareCodeSource = 'parse' | 'record';
3
+ export declare function extractShareCode(input: string): string;
4
+ export declare function parsedRecord(record: any): any | null;
5
+ export declare function buildShareCodeView(payload: any, source: ShareCodeSource, record?: any, baseUrl?: string): {
6
+ source: ShareCodeSource;
7
+ sourceLabel: string;
8
+ shareCode: string;
9
+ shareCodePreview: string;
10
+ version: number;
11
+ spriteCount: number;
12
+ mode: {
13
+ id: number;
14
+ name: string;
15
+ icon: string;
16
+ configured: boolean;
17
+ };
18
+ magic: {
19
+ id: number;
20
+ name: string;
21
+ icon: string;
22
+ configured: boolean;
23
+ };
24
+ teams: any;
25
+ parseCount: number;
26
+ firstSeen: string;
27
+ lastSeen: string;
28
+ shareCodeHash: string;
29
+ commandHint: string;
30
+ copyright: string;
31
+ };
32
+ export declare function buildShareCodeText(data: any): string;
33
+ export interface ShareCodeClassicAssets {
34
+ frame: string;
35
+ logo: string;
36
+ rocoIcon: string;
37
+ star: string;
38
+ wax: string;
39
+ typeIcon: (name: string) => string;
40
+ bloodIcon: (element: string) => string;
41
+ }
42
+ export interface ClassicNatureInfo {
43
+ name?: string;
44
+ up?: string;
45
+ down?: string;
46
+ summary?: string;
47
+ }
48
+ export declare function parseNaturePair(summary: unknown): {
49
+ up: string;
50
+ down: string;
51
+ };
52
+ export declare function buildShareCodeClassicView(view: any, assets: ShareCodeClassicAssets, natureLookup?: (id: string) => ClassicNatureInfo): {
53
+ team: {
54
+ mode_name: any;
55
+ share_code: any;
56
+ sprite_count: any;
57
+ count_label: string;
58
+ magic_icon: any;
59
+ teams: any;
60
+ };
61
+ shareAssets: {
62
+ frame: string;
63
+ logo: string;
64
+ rocoIcon: string;
65
+ star: string;
66
+ wax: string;
67
+ };
68
+ };