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.
- package/lib/announcement-service.d.ts +21 -0
- package/lib/announcement-service.js +63 -0
- package/lib/async-control.d.ts +3 -0
- package/lib/async-control.js +48 -0
- package/lib/atlas-service.js +1 -1
- package/lib/client.d.ts +58 -7
- package/lib/client.js +282 -127
- package/lib/commands/egg.js +66 -4
- package/lib/commands/merchant.d.ts +105 -0
- package/lib/commands/merchant.js +600 -182
- package/lib/commands/query.d.ts +18 -0
- package/lib/commands/query.js +585 -145
- package/lib/commands/ranking.d.ts +2 -0
- package/lib/commands/ranking.js +31 -0
- package/lib/commands/share-code.d.ts +2 -0
- package/lib/commands/share-code.js +111 -0
- package/lib/commands/tools.js +48 -60
- package/lib/commands/wiki.js +1 -1
- package/lib/egg-service.d.ts +82 -8
- package/lib/egg-service.js +235 -19
- package/lib/element-colors.d.ts +3 -0
- package/lib/element-colors.js +34 -0
- package/lib/icon-metrics.d.ts +32 -0
- package/lib/icon-metrics.js +255 -0
- package/lib/img//345/274/202/350/211/262.png +0 -0
- package/lib/img//347/202/253/345/275/251.png +0 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +11857 -175
- package/lib/merchant-catalog.d.ts +47 -0
- package/lib/merchant-catalog.js +193 -0
- package/lib/merchant-clock.d.ts +22 -0
- package/lib/merchant-clock.js +95 -0
- package/lib/merchant-price-log.d.ts +63 -0
- package/lib/merchant-price-log.js +187 -0
- package/lib/merchant-service.d.ts +38 -0
- package/lib/merchant-service.js +216 -0
- package/lib/permissions.d.ts +16 -0
- package/lib/permissions.js +66 -0
- package/lib/pet-data-service.d.ts +13 -0
- package/lib/pet-data-service.js +117 -10
- package/lib/pet-size.d.ts +13 -0
- package/lib/pet-size.js +54 -0
- package/lib/player-service.d.ts +37 -0
- package/lib/player-service.js +201 -0
- package/lib/ranking-service.d.ts +46 -0
- package/lib/ranking-service.js +137 -0
- package/lib/render-templates/announcement/detail.html +8 -0
- package/lib/render-templates/announcement/list.html +5 -0
- package/lib/render-templates/announcement/style.css +1 -0
- package/lib/render-templates/home/style.css +15 -14
- package/lib/render-templates/home-pets/index.html +80 -0
- package/lib/render-templates/home-pets/style.css +338 -0
- package/lib/render-templates/pet-detail-classic/index.html +151 -0
- package/lib/render-templates/pet-detail-classic/style.css +503 -0
- package/lib/render-templates/pet-detail-classic/texture2D/a_title.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/bg.jpg +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/footer.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/icon.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/img_head.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/jinhua_bg.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/level_icon.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/line.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/mask_bar.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/pet_bg.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/pet_rocom.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/pet_rocom_bg.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/poro_bar.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/right_jinhua.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/skill_bg.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/skill_mask.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/star.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/star_1.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/star_8.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/star_9.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/table.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/tags.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/title.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/top_bg.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/xuancai_overlay.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/yise_overlay.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D/ys_bar.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/10.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/11.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/12.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/13.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/14.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/15.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/16.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/17.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/18.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/19.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/2.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/20.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/23.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/3.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/4.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/5.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/6.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/8.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247/9.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/205/211.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/206/260.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/234/260.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/271/273.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//345/271/275.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/201/266.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/227/240.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/231/256/351/200/232.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/234/272/346/242/260.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/255/246.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/257/222.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//346/260/264.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/201/253.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/224/265.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//347/277/274.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/215/211.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/220/214.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//350/231/253.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//345/261/236/346/200/247//351/276/231.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/1.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/10.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/11.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/12.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/13.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/14.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/15.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/16.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/17.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/18.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/19.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/2.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/21.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/23.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/24.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/3.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/4.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/5.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/6.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/7.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/8.png +0 -0
- package/lib/render-templates/pet-detail-classic/texture2D//350/241/200/350/204/211/9.png +0 -0
- package/lib/render-templates/pet-ranking/index.html +99 -0
- package/lib/render-templates/pet-ranking/style.css +338 -0
- package/lib/render-templates/player-search/index.html +7 -0
- package/lib/render-templates/searcheggs/index.html +1 -1
- package/lib/render-templates/searcheggs/size.html +70 -68
- package/lib/render-templates/share-code-classic/assets/eit-logo.png +0 -0
- package/lib/render-templates/share-code-classic/assets/img_xingxingdi.png +0 -0
- package/lib/render-templates/share-code-classic/assets/roco_icon.png +0 -0
- package/lib/render-templates/share-code-classic/assets//345/255/227/344/275/223/Rocofontblack-Regular .ttf +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/lib/render-templates/share-code-classic/assets//347/201/253/346/274/206/345/215/260.png +0 -0
- 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
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/205/211.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/206/260.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/234/260.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/245/207/345/274/202.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/271/273.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//345/271/275.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/201/266.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/231/256/351/200/232.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/234/272/346/242/260.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/255/246.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/257/222.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/260/264.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//346/261/241/346/237/223.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/201/253.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/224/265.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//347/277/274.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/215/211.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/220/214.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//350/231/253.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//351/246/226/351/242/206.png +0 -0
- package/lib/render-templates/share-code-classic/assets//350/241/200/350/204/211//351/276/231.png +0 -0
- package/lib/render-templates/share-code-classic/index.html +119 -0
- package/lib/render-templates/share-code-classic/style.css +646 -0
- package/lib/render-templates/share-code-team/index.html +105 -0
- package/lib/render-templates/share-code-team/style.css +345 -0
- package/lib/render-templates/yuanxing-shangren/merchant.html +479 -461
- package/lib/render-templates/yuanxing-shangren/today.html +407 -461
- package/lib/render.d.ts +18 -2
- package/lib/render.js +138 -39
- package/lib/send-image.d.ts +1 -1
- package/lib/send-image.js +22 -1
- package/lib/share-code-service.d.ts +68 -0
- package/lib/share-code-service.js +257 -0
- package/lib/subscription-runner.d.ts +6 -0
- package/lib/subscription-runner.js +21 -0
- package/lib/subscription-send.d.ts +4 -2
- package/lib/subscription-send.js +45 -20
- package/lib/ttf/rocom_origin.ttf +0 -0
- package/lib/types.d.ts +14 -1
- package/lib/user.d.ts +11 -0
- package/lib/wiki-catalog.js +1 -1
- package/lib/wiki-service.js +1 -1
- package/package.json +5 -1
- package/readme.md +125 -124
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
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
|
|
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
|
-
//
|
|
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,
|
|
242
|
+
new Promise(resolve => setTimeout(resolve, waitImageMs)),
|
|
159
243
|
]);
|
|
160
244
|
const fonts = document.fonts;
|
|
161
245
|
if (fonts?.ready) {
|
|
162
|
-
|
|
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
|
|
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
|
|
321
|
+
await (0, async_control_1.delay)(100, options.signal);
|
|
227
322
|
// 居中(margin auto)模板在视口调整后 x/y 会变化,截图前需要重新测量,
|
|
228
|
-
// 否则 clip
|
|
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
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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(
|
|
270
|
-
return Buffer.
|
|
361
|
+
const screenshot = await target.screenshot(screenshotOptions);
|
|
362
|
+
return [Buffer.from(screenshot)];
|
|
271
363
|
}
|
|
272
|
-
const screenshot = await page.screenshot({ ...
|
|
273
|
-
return Buffer.
|
|
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
|
-
|
|
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) {
|
package/lib/send-image.d.ts
CHANGED
|
@@ -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<
|
|
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
|
-
|
|
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
|
+
};
|