koishi-plugin-rocom 1.0.13 → 1.0.17
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/atlas-service.d.ts +33 -0
- package/lib/atlas-service.js +439 -0
- package/lib/client.d.ts +19 -0
- package/lib/client.js +101 -1
- package/lib/commands/account.js +1 -0
- package/lib/commands/merchant.js +11 -2
- package/lib/commands/query.js +141 -9
- package/lib/commands/tools.js +104 -0
- package/lib/commands/wiki.js +167 -6
- package/lib/egg-service.d.ts +2 -0
- package/lib/egg-service.js +50 -15
- package/lib/index.d.ts +5 -0
- package/lib/index.js +3153 -188
- package/lib/pet-data-service.d.ts +62 -0
- package/lib/pet-data-service.js +468 -0
- package/lib/render-templates/pet-data/index.html +172 -0
- package/lib/render-templates/pet-data/style.css +553 -0
- package/lib/render-templates/wiki/detail/index.html +84 -0
- package/lib/render-templates/wiki/list/index.html +84 -0
- package/lib/render-templates/wiki/menu/index.html +65 -0
- package/lib/render-templates/wiki/pet/index.html +158 -0
- package/lib/render-templates/wiki/pet/style.css +393 -0
- package/lib/render-templates/wiki/shared/style.css +431 -0
- package/lib/render-templates/wiki/skill/index.html +79 -0
- package/lib/render-templates/wiki/skill/style.css +233 -0
- package/lib/render-templates/wiki/suggestions/index.html +84 -0
- package/lib/render.js +37 -8
- package/lib/types.d.ts +9 -1
- package/lib/user.d.ts +23 -1
- package/lib/user.js +32 -1
- package/lib/wiki-catalog.d.ts +19 -0
- package/lib/wiki-catalog.js +124 -0
- package/lib/wiki-service.d.ts +220 -0
- package/lib/wiki-service.js +1463 -0
- package/package.json +1 -1
- package/readme.md +4 -5
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=1280, initial-scale=1">
|
|
6
|
+
<title>{{title}}</title>
|
|
7
|
+
<link rel="stylesheet" href="{{_res_path}}render/wiki/shared/style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body class="wiki-body">
|
|
10
|
+
<div class="wiki-page wiki-suggestions-page">
|
|
11
|
+
<div class="wiki-content">
|
|
12
|
+
<header class="hero">
|
|
13
|
+
<div>
|
|
14
|
+
<div class="kicker">ROCOM WIKI</div>
|
|
15
|
+
<div class="subtitle">{{subtitle}}</div>
|
|
16
|
+
<h1>{{title}}</h1>
|
|
17
|
+
<p class="hero-summary">{{summary}}</p>
|
|
18
|
+
<div class="badge-row">
|
|
19
|
+
{{each badges badge}}
|
|
20
|
+
<span>{{badge}}</span>
|
|
21
|
+
{{/each}}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</header>
|
|
25
|
+
|
|
26
|
+
{{if facts && facts.length}}
|
|
27
|
+
<div class="facts-grid">
|
|
28
|
+
{{each facts fact}}
|
|
29
|
+
<div class="fact"><span>{{fact.label}}</span><strong>{{fact.value}}</strong></div>
|
|
30
|
+
{{/each}}
|
|
31
|
+
</div>
|
|
32
|
+
{{/if}}
|
|
33
|
+
|
|
34
|
+
{{if actionHint}}
|
|
35
|
+
<section class="query-guide">
|
|
36
|
+
<div class="guide-title">查看具体条目</div>
|
|
37
|
+
<div class="guide-text">{{actionHint}}</div>
|
|
38
|
+
{{if actionExamples && actionExamples.length}}
|
|
39
|
+
<div class="guide-examples">
|
|
40
|
+
{{each actionExamples example}}
|
|
41
|
+
<span>{{example}}</span>
|
|
42
|
+
{{/each}}
|
|
43
|
+
</div>
|
|
44
|
+
{{/if}}
|
|
45
|
+
</section>
|
|
46
|
+
{{/if}}
|
|
47
|
+
|
|
48
|
+
<section class="panel">
|
|
49
|
+
<div class="panel-title">可能相关 <small>{{cards.length}}</small></div>
|
|
50
|
+
<div class="card-grid">
|
|
51
|
+
{{each cards card}}
|
|
52
|
+
<article class="wiki-card">
|
|
53
|
+
{{if card.image}}
|
|
54
|
+
<div class="card-image"><img src="{{card.image}}" alt="{{card.title}}" onerror="this.parentElement.style.display='none'"></div>
|
|
55
|
+
{{/if}}
|
|
56
|
+
<h2>{{card.title}}</h2>
|
|
57
|
+
{{if card.summary}}<p>{{card.summary}}</p>{{/if}}
|
|
58
|
+
{{if card.badges && card.badges.length}}
|
|
59
|
+
<div class="card-badges">
|
|
60
|
+
{{each card.badges badge}}
|
|
61
|
+
<span>{{badge}}</span>
|
|
62
|
+
{{/each}}
|
|
63
|
+
</div>
|
|
64
|
+
{{/if}}
|
|
65
|
+
{{if card.meta && card.meta.length}}
|
|
66
|
+
<div class="meta-grid">
|
|
67
|
+
{{each card.meta row}}
|
|
68
|
+
<div class="meta-item"><span>{{row.label}}</span><b>{{row.value}}</b></div>
|
|
69
|
+
{{/each}}
|
|
70
|
+
</div>
|
|
71
|
+
{{/if}}
|
|
72
|
+
</article>
|
|
73
|
+
{{/each}}
|
|
74
|
+
</div>
|
|
75
|
+
</section>
|
|
76
|
+
|
|
77
|
+
<footer class="footer">
|
|
78
|
+
<span>{{commandHint}}</span>
|
|
79
|
+
<span>{{copyright}}</span>
|
|
80
|
+
</footer>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
package/lib/render.js
CHANGED
|
@@ -145,14 +145,18 @@ class Renderer {
|
|
|
145
145
|
try {
|
|
146
146
|
await page.evaluate(async () => {
|
|
147
147
|
const images = Array.from(document.images);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
// 限制远程图片等待时间,避免慢速资源拖垮整次渲染(对应上游 v3.7.1)。
|
|
149
|
+
await Promise.race([
|
|
150
|
+
Promise.all(images.map((img) => {
|
|
151
|
+
if (img.complete)
|
|
152
|
+
return Promise.resolve();
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
img.onload = () => resolve();
|
|
155
|
+
img.onerror = () => resolve();
|
|
156
|
+
});
|
|
157
|
+
})),
|
|
158
|
+
new Promise(resolve => setTimeout(resolve, 10000)),
|
|
159
|
+
]);
|
|
156
160
|
const fonts = document.fonts;
|
|
157
161
|
if (fonts?.ready) {
|
|
158
162
|
await fonts.ready;
|
|
@@ -170,6 +174,8 @@ class Renderer {
|
|
|
170
174
|
'.searcheggs-cont',
|
|
171
175
|
'.bwiki-shell',
|
|
172
176
|
'.skill-shell',
|
|
177
|
+
'.wiki-page',
|
|
178
|
+
'.pet-data-page',
|
|
173
179
|
'.lineup-page',
|
|
174
180
|
'.lineup-detail-page',
|
|
175
181
|
'.page-section-main',
|
|
@@ -218,6 +224,29 @@ class Renderer {
|
|
|
218
224
|
deviceScaleFactor: initialViewport.deviceScaleFactor,
|
|
219
225
|
});
|
|
220
226
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
227
|
+
// 居中(margin auto)模板在视口调整后 x/y 会变化,截图前需要重新测量,
|
|
228
|
+
// 否则 clip 会带着旧偏移导致画面左侧被截断(对应上游 v3.7.5 修复)。
|
|
229
|
+
try {
|
|
230
|
+
const remeasured = await page.evaluate((el, visualBounds) => {
|
|
231
|
+
const rect = el.getBoundingClientRect();
|
|
232
|
+
const element = el;
|
|
233
|
+
return {
|
|
234
|
+
x: rect.left + window.scrollX,
|
|
235
|
+
y: rect.top + window.scrollY,
|
|
236
|
+
width: visualBounds ? rect.width : Math.max(rect.width, element.scrollWidth, element.offsetWidth),
|
|
237
|
+
height: visualBounds ? rect.height : Math.max(rect.height, element.scrollHeight, element.offsetHeight),
|
|
238
|
+
};
|
|
239
|
+
}, target, useVisualBounds);
|
|
240
|
+
if (remeasured && remeasured.width > 0 && remeasured.height > 0) {
|
|
241
|
+
elementMetrics.x = remeasured.x;
|
|
242
|
+
elementMetrics.y = remeasured.y;
|
|
243
|
+
elementMetrics.width = remeasured.width;
|
|
244
|
+
elementMetrics.height = remeasured.height;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
logger.warn(`element remeasure failed for ${templateName}: ${err}`);
|
|
249
|
+
}
|
|
221
250
|
const hasOverflow = elementMetrics.width > box.width + 0.5 ||
|
|
222
251
|
elementMetrics.height > box.height + 0.5;
|
|
223
252
|
if (capturePadding.left || capturePadding.right || capturePadding.top || capturePadding.bottom || hasOverflow) {
|
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Context } from 'koishi';
|
|
2
2
|
import { RocomClient } from './client';
|
|
3
|
-
import { UserManager, MerchantSubscriptionManager, HomeSubscriptionManager } from './user';
|
|
3
|
+
import { UserManager, MerchantSubscriptionManager, HomeSubscriptionManager, AnnouncementSubscriptionManager } from './user';
|
|
4
4
|
import { EggService } from './egg-service';
|
|
5
5
|
import { Renderer } from './render';
|
|
6
|
+
import { WikiService } from './wiki-service';
|
|
7
|
+
import { AtlasService } from './atlas-service';
|
|
6
8
|
export interface PluginConfig {
|
|
7
9
|
apiBaseUrl: string;
|
|
8
10
|
wegameApiKey: string;
|
|
@@ -19,9 +21,12 @@ export interface PluginConfig {
|
|
|
19
21
|
merchantCheckTimes: string[];
|
|
20
22
|
homeSubscriptionEnabled: boolean;
|
|
21
23
|
homeSubscriptionIntervalMinutes: number;
|
|
24
|
+
announcementSubscriptionEnabled: boolean;
|
|
25
|
+
announcementPollIntervalMinutes: number;
|
|
22
26
|
homeQueryWaitMs: number;
|
|
23
27
|
homeQueryPollIntervalMs: number;
|
|
24
28
|
homeQueryTimeoutMs: number;
|
|
29
|
+
lowBandwidthMode: boolean;
|
|
25
30
|
imageCompressionEnabled: boolean;
|
|
26
31
|
imageCompressionMinBytes: number;
|
|
27
32
|
imageCompressionLevel: number;
|
|
@@ -33,6 +38,9 @@ export interface PluginDeps {
|
|
|
33
38
|
userMgr: UserManager;
|
|
34
39
|
merchantSubMgr: MerchantSubscriptionManager;
|
|
35
40
|
homeSubMgr: HomeSubscriptionManager;
|
|
41
|
+
announcementSubMgr: AnnouncementSubscriptionManager;
|
|
36
42
|
eggService: EggService;
|
|
37
43
|
renderer: Renderer;
|
|
44
|
+
wikiService: WikiService;
|
|
45
|
+
atlasService: AtlasService;
|
|
38
46
|
}
|
package/lib/user.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface MerchantSubscription {
|
|
|
21
21
|
}
|
|
22
22
|
export interface HomeSubscription {
|
|
23
23
|
key: string;
|
|
24
|
-
kind: 'garden' | 'inspiration';
|
|
24
|
+
kind: 'garden' | 'inspiration' | 'egg';
|
|
25
25
|
uid: string;
|
|
26
26
|
channel_id?: string;
|
|
27
27
|
platform?: string;
|
|
@@ -66,3 +66,25 @@ export declare class HomeSubscriptionManager {
|
|
|
66
66
|
}, kind?: string, uid?: string): number;
|
|
67
67
|
getAll(): Record<string, HomeSubscription>;
|
|
68
68
|
}
|
|
69
|
+
export interface AnnouncementSubscription {
|
|
70
|
+
key: string;
|
|
71
|
+
platform?: string;
|
|
72
|
+
channel_id?: string;
|
|
73
|
+
guild_id?: string;
|
|
74
|
+
user_id?: string;
|
|
75
|
+
updated_by: string;
|
|
76
|
+
last_id?: string;
|
|
77
|
+
since_ts?: number;
|
|
78
|
+
updated_at: number;
|
|
79
|
+
}
|
|
80
|
+
export declare class AnnouncementSubscriptionManager {
|
|
81
|
+
private store;
|
|
82
|
+
constructor(dataDir: string);
|
|
83
|
+
upsert(key: string, sub: AnnouncementSubscription): void;
|
|
84
|
+
deleteMatching(target: {
|
|
85
|
+
platform?: string;
|
|
86
|
+
channelId?: string;
|
|
87
|
+
userId?: string;
|
|
88
|
+
}): number;
|
|
89
|
+
getAll(): Record<string, AnnouncementSubscription>;
|
|
90
|
+
}
|
package/lib/user.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.HomeSubscriptionManager = exports.MerchantSubscriptionManager = exports.UserManager = void 0;
|
|
6
|
+
exports.AnnouncementSubscriptionManager = exports.HomeSubscriptionManager = exports.MerchantSubscriptionManager = exports.UserManager = void 0;
|
|
7
7
|
const koishi_1 = require("koishi");
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -174,3 +174,34 @@ class HomeSubscriptionManager {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
exports.HomeSubscriptionManager = HomeSubscriptionManager;
|
|
177
|
+
class AnnouncementSubscriptionManager {
|
|
178
|
+
store;
|
|
179
|
+
constructor(dataDir) {
|
|
180
|
+
this.store = new JsonStore(dataDir, 'rocom_announcement_subscriptions.json', {});
|
|
181
|
+
}
|
|
182
|
+
upsert(key, sub) {
|
|
183
|
+
const data = this.store.get();
|
|
184
|
+
data[key] = { ...sub };
|
|
185
|
+
this.store.set(data);
|
|
186
|
+
}
|
|
187
|
+
deleteMatching(target) {
|
|
188
|
+
const data = this.store.get();
|
|
189
|
+
let deleted = 0;
|
|
190
|
+
for (const [key, sub] of Object.entries(data)) {
|
|
191
|
+
const sameTarget = sub.platform === target.platform
|
|
192
|
+
&& (sub.channel_id || '') === (target.channelId || '')
|
|
193
|
+
&& (sub.user_id || '') === (target.userId || '');
|
|
194
|
+
if (!sameTarget)
|
|
195
|
+
continue;
|
|
196
|
+
delete data[key];
|
|
197
|
+
deleted++;
|
|
198
|
+
}
|
|
199
|
+
if (deleted)
|
|
200
|
+
this.store.set(data);
|
|
201
|
+
return deleted;
|
|
202
|
+
}
|
|
203
|
+
getAll() {
|
|
204
|
+
return JSON.parse(JSON.stringify(this.store.get()));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.AnnouncementSubscriptionManager = AnnouncementSubscriptionManager;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface WikiCatalog {
|
|
2
|
+
key: string;
|
|
3
|
+
title: string;
|
|
4
|
+
aliases: string[];
|
|
5
|
+
list_path?: string;
|
|
6
|
+
detail_path: string;
|
|
7
|
+
id_fields: string[];
|
|
8
|
+
/** null 表示以后端 filters 是否带 q 为准 */
|
|
9
|
+
search: boolean | null;
|
|
10
|
+
_backend?: any;
|
|
11
|
+
}
|
|
12
|
+
export declare const WIKI_CATALOG_ROUTES: WikiCatalog[];
|
|
13
|
+
export declare const WIKI_CATALOG_ROUTES_BY_KEY: Map<string, WikiCatalog>;
|
|
14
|
+
export declare const WIKI_CATALOG_ROUTES_BY_ALIAS: Map<string, WikiCatalog>;
|
|
15
|
+
export declare function wikiGlobalCatalogPriority(catalog: {
|
|
16
|
+
key?: string;
|
|
17
|
+
} | null | undefined): number;
|
|
18
|
+
export declare function wikiLabelForKey(key: string): string;
|
|
19
|
+
export declare function wikiSectionTitle(key: string): string;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 新版 RoCom Wiki 目录路由表(移植自上游 astrbot v3.6.0 core/wiki_catalog.py)。
|
|
3
|
+
// list_path 由后端 /wiki/catalogs 实时下发;这里只维护详情路径、ID 字段与别名。
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.WIKI_CATALOG_ROUTES_BY_ALIAS = exports.WIKI_CATALOG_ROUTES_BY_KEY = exports.WIKI_CATALOG_ROUTES = void 0;
|
|
6
|
+
exports.wikiGlobalCatalogPriority = wikiGlobalCatalogPriority;
|
|
7
|
+
exports.wikiLabelForKey = wikiLabelForKey;
|
|
8
|
+
exports.wikiSectionTitle = wikiSectionTitle;
|
|
9
|
+
function route(key, title, aliases, detailPath = '', idFields = [], search = null) {
|
|
10
|
+
return { key, title, aliases, detail_path: detailPath, id_fields: idFields, search };
|
|
11
|
+
}
|
|
12
|
+
exports.WIKI_CATALOG_ROUTES = [
|
|
13
|
+
route('pets', '精灵', ['精灵', '宠物', 'pet', 'pets'], '/api/v1/games/rocom/wiki/pets/{pet_id}', ['pet_id']),
|
|
14
|
+
route('pet-skills', '精灵技能', ['精灵技能', '宠物技能'], '/api/v1/games/rocom/wiki/pets/{pet_id}/skills', ['pet_id'], false),
|
|
15
|
+
route('pet-features', '精灵特性', ['精灵特性', '宠物特性'], '/api/v1/games/rocom/wiki/pets/{pet_id}/features', ['pet_id'], false),
|
|
16
|
+
route('pet-family', '精灵家族', ['精灵家族', '进化链', '形态链'], '/api/v1/games/rocom/wiki/pets/{pet_id}/family', ['pet_id'], false),
|
|
17
|
+
route('pet-egg', '精灵蛋详情', ['精灵蛋详情', '宠物蛋详情'], '/api/v1/games/rocom/wiki/pets/{pet_id}/egg', ['pet_id'], false),
|
|
18
|
+
route('pet-handbook', '精灵图鉴课题', ['精灵图鉴课题', '图鉴课题'], '/api/v1/games/rocom/wiki/pets/{pet_id}/handbook', ['pet_id'], false),
|
|
19
|
+
route('pet-talents', '精灵天赋', ['精灵天赋', '同乘天赋'], '/api/v1/games/rocom/wiki/pets/{pet_id}/talents', ['pet_id'], false),
|
|
20
|
+
route('pet-natures', '精灵性格', ['精灵性格', '性格'], '/api/v1/games/rocom/wiki/pets/{pet_id}/natures', ['pet_id'], false),
|
|
21
|
+
route('pet-profile', '精灵资料', ['精灵资料', '资料页'], '/api/v1/games/rocom/wiki/pets/{pet_id}/profile', ['pet_id'], false),
|
|
22
|
+
route('pet-fruit-section', '精灵关联果实', ['精灵关联果实'], '/api/v1/games/rocom/wiki/pets/{pet_id}/fruit', ['pet_id'], false),
|
|
23
|
+
route('pet-ride', '精灵骑乘', ['精灵骑乘', '骑乘'], '/api/v1/games/rocom/wiki/pets/{pet_id}/ride', ['pet_id'], false),
|
|
24
|
+
route('pet-ecology', '精灵生态', ['精灵生态', '生态'], '/api/v1/games/rocom/wiki/pets/{pet_id}/ecology', ['pet_id'], false),
|
|
25
|
+
route('pet-stories', '精灵故事', ['精灵故事', '故事'], '/api/v1/games/rocom/wiki/pets/{pet_id}/stories', ['pet_id'], false),
|
|
26
|
+
route('pet-acquisition', '精灵获取方式', ['精灵获取', '获取方式'], '/api/v1/games/rocom/wiki/pets/{pet_id}/acquisition', ['pet_id'], false),
|
|
27
|
+
route('skills', '技能', ['技能', 'skill', 'skills'], '/api/v1/games/rocom/wiki/skills/{skill_id}', ['skill_id']),
|
|
28
|
+
route('skill-pets', '技能可用精灵', ['技能可用精灵', '技能精灵'], '/api/v1/games/rocom/wiki/skills/{skill_id}/pets', ['skill_id'], false),
|
|
29
|
+
route('skill-stones', '技能石', ['技能石', 'skillstone', 'skill-stone'], '/api/v1/games/rocom/wiki/skill-stones/{item_id}', ['item_id']),
|
|
30
|
+
route('skill-stone-recipes', '技能石配方', ['技能石配方', '技能配方', 'skillstonerecipe'], '/api/v1/games/rocom/wiki/skill-stone-recipes/{item_id}', ['item_id']),
|
|
31
|
+
route('items', '物品', ['物品', '道具', 'item', 'items'], '/api/v1/games/rocom/wiki/items/{item_kind}/{item_id}', ['item_kind', 'item_id']),
|
|
32
|
+
route('egg-items', '蛋物品', ['蛋物品', '蛋道具', 'egg-item'], '/api/v1/games/rocom/wiki/egg-items/{item_id}', ['item_id']),
|
|
33
|
+
route('pet-eggs', '精灵蛋', ['精灵蛋', '宠物蛋', '蛋图鉴', 'pet-egg'], '/api/v1/games/rocom/wiki/pet-eggs/{egg_conf_id}', ['egg_conf_id']),
|
|
34
|
+
route('random-eggs', '随机蛋', ['随机蛋', '随机蛋池', 'random-egg'], '/api/v1/games/rocom/wiki/random-eggs/{random_egg_id}', ['random_egg_id']),
|
|
35
|
+
route('pet-fruits', '精灵果实', ['精灵果实', '果实', 'pet-fruit'], '/api/v1/games/rocom/wiki/pet-fruits/{item_id}', ['item_id']),
|
|
36
|
+
route('pet-foods', '精灵食物', ['精灵食物', '食物', 'pet-food'], '/api/v1/games/rocom/wiki/pet-foods/{item_id}', ['item_id']),
|
|
37
|
+
route('pet-gifts', '精灵礼物', ['精灵礼物', '礼物', 'pet-gift'], '/api/v1/games/rocom/wiki/pet-gifts/{item_id}', ['item_id']),
|
|
38
|
+
route('pet-close-level-effects', '亲密等级效果', ['亲密等级', '亲密效果'], '', ['level'], false),
|
|
39
|
+
route('pet-action-close-exp', '行为亲密经验', ['行为亲密', '亲密经验'], '', [], false),
|
|
40
|
+
route('home-egg-lay-rates', '家园产蛋概率', ['家园产蛋', '产蛋概率'], '', [], false),
|
|
41
|
+
route('home-progression', '家园成长', ['家园成长', '家园升级'], '', [], false),
|
|
42
|
+
route('pet-levels', '精灵等级经验', ['精灵等级', '等级经验', 'pet-level'], '/api/v1/games/rocom/wiki/pet-levels/{level}', ['level'], false),
|
|
43
|
+
route('handbook-areas', '图鉴区域', ['图鉴区域', '区域图鉴'], '', ['id'], false),
|
|
44
|
+
route('fruit-trees', '果实树', ['果实树', '树'], '/api/v1/games/rocom/wiki/fruit-trees/{tree_id}', ['tree_id']),
|
|
45
|
+
route('fruit-refresh', '果实刷新精灵', ['果实刷新', '刷新精灵'], '/api/v1/games/rocom/wiki/fruit-refresh/{fruit_item_id}', ['fruit_item_id'], false),
|
|
46
|
+
route('medals', '奖章', ['奖章', '勋章', 'medal'], '/api/v1/games/rocom/wiki/medals/{medal_id}', ['medal_id']),
|
|
47
|
+
route('recipes', '食谱', ['食谱', '料理', 'recipe'], '/api/v1/games/rocom/wiki/recipes/{item_id}', ['item_id']),
|
|
48
|
+
route('balls', '咕噜球', ['咕噜球', '球', 'ball'], '/api/v1/games/rocom/wiki/balls/{ball_id}', ['ball_id']),
|
|
49
|
+
route('plants', '种植', ['种植', '作物', '植物', 'plant'], '/api/v1/games/rocom/wiki/plants/{plant_id}', ['plant_id']),
|
|
50
|
+
route('pet-carryons', '精灵携带物', ['精灵携带物', '携带物', 'carryon'], '/api/v1/games/rocom/wiki/pet-carryons/{carryon_id}', ['carryon_id']),
|
|
51
|
+
route('furniture', '家具', ['家具', 'furniture'], '/api/v1/games/rocom/wiki/furniture/{furniture_id}', ['furniture_id']),
|
|
52
|
+
route('fashion', '服装套装', ['服装', '套装', 'fashion'], '/api/v1/games/rocom/wiki/fashion/{suit_id}', ['suit_id']),
|
|
53
|
+
route('regions', '地区', ['地区', '区域', 'region'], '/api/v1/games/rocom/wiki/regions/{region_id}', ['region_id']),
|
|
54
|
+
route('dungeons', '副本', ['副本', 'dungeon'], '/api/v1/games/rocom/wiki/dungeons/{dungeon_id}', ['dungeon_id']),
|
|
55
|
+
route('mails', '邮件模板', ['邮件', '邮件模板', 'mail'], '/api/v1/games/rocom/wiki/mails/{mail_id}', ['mail_id']),
|
|
56
|
+
route('music', '音乐', ['音乐', 'music'], '/api/v1/games/rocom/wiki/music/{music_id}', ['music_id']),
|
|
57
|
+
route('profile-assets', '个人名片资产', ['名片', '名片资产', 'profile-asset'], '/api/v1/games/rocom/wiki/profile-assets/{asset_type}/{asset_id}', ['asset_type', 'asset_id']),
|
|
58
|
+
route('chat-emojis', '聊天表情', ['表情', '聊天表情', 'emoji'], '/api/v1/games/rocom/wiki/chat-emojis/{emoji_id}', ['emoji_id']),
|
|
59
|
+
route('photo-actions', '拍照动作', ['拍照动作', '动作', 'photo-action'], '/api/v1/games/rocom/wiki/photo-actions/{action_type}/{action_id}', ['action_type', 'action_id']),
|
|
60
|
+
route('tasks', '任务', ['任务', 'task'], '/api/v1/games/rocom/wiki/tasks/{task_id}', ['task_id']),
|
|
61
|
+
route('task-summaries', '剧情摘要', ['剧情', '剧情摘要', 'task-summary'], '/api/v1/games/rocom/wiki/task-summaries/{summary_id}', ['summary_id']),
|
|
62
|
+
route('shops', '商店', ['商店', 'shop'], '/api/v1/games/rocom/wiki/shops/{shop_id}', ['shop_id']),
|
|
63
|
+
route('exchanges', '兑换', ['兑换', 'exchange'], '/api/v1/games/rocom/wiki/exchanges/{exchange_id}', ['exchange_id']),
|
|
64
|
+
];
|
|
65
|
+
exports.WIKI_CATALOG_ROUTES_BY_KEY = new Map(exports.WIKI_CATALOG_ROUTES.map(item => [item.key, item]));
|
|
66
|
+
exports.WIKI_CATALOG_ROUTES_BY_ALIAS = new Map();
|
|
67
|
+
for (const item of exports.WIKI_CATALOG_ROUTES) {
|
|
68
|
+
for (const alias of [item.key, item.title, ...item.aliases]) {
|
|
69
|
+
exports.WIKI_CATALOG_ROUTES_BY_ALIAS.set(alias.toLowerCase(), item);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// 全局搜索时各目录的展示优先级(越小越靠前)。
|
|
73
|
+
const WIKI_GLOBAL_PRIORITIES = {
|
|
74
|
+
'pets': 0, 'skills': 0,
|
|
75
|
+
'balls': 1, 'pet-carryons': 1, 'medals': 1, 'plants': 1, 'pet-fruits': 1, 'recipes': 1,
|
|
76
|
+
'pet-eggs': 1, 'random-eggs': 1, 'egg-items': 1, 'skill-stones': 1, 'skill-stone-recipes': 1,
|
|
77
|
+
'pet-foods': 1, 'pet-gifts': 1, 'furniture': 1, 'fashion': 1, 'regions': 1, 'dungeons': 1,
|
|
78
|
+
'tasks': 1, 'task-summaries': 1, 'shops': 1, 'exchanges': 1,
|
|
79
|
+
'mails': 2, 'music': 2, 'chat-emojis': 2, 'photo-actions': 2,
|
|
80
|
+
'items': 3,
|
|
81
|
+
'profile-assets': 4,
|
|
82
|
+
};
|
|
83
|
+
function wikiGlobalCatalogPriority(catalog) {
|
|
84
|
+
const key = String(catalog?.key || '');
|
|
85
|
+
return WIKI_GLOBAL_PRIORITIES[key] ?? 2;
|
|
86
|
+
}
|
|
87
|
+
const WIKI_KEY_LABELS = {
|
|
88
|
+
pet_id: '精灵ID', skill_id: '技能ID', item_id: '物品ID', item_kind: '物品类型',
|
|
89
|
+
egg_conf_id: '蛋配置ID', random_egg_id: '随机蛋ID', tree_id: '果实树ID', medal_id: '奖章ID',
|
|
90
|
+
ball_id: '咕噜球ID', plant_id: '种植ID', carryon_id: '携带物ID', furniture_id: '家具ID',
|
|
91
|
+
suit_id: '套装ID', region_id: '地区ID', dungeon_id: '副本ID', mail_id: '邮件ID',
|
|
92
|
+
music_id: '音乐ID', asset_type: '资产类型', asset_id: '资产ID', emoji_id: '表情ID',
|
|
93
|
+
action_type: '动作类型', action_id: '动作ID', task_id: '任务ID', summary_id: '剧情ID',
|
|
94
|
+
shop_id: '商店ID', exchange_id: '兑换ID', handbook_no: '图鉴编号', stage: '阶段',
|
|
95
|
+
quality: '品质', cost: '能量', power: '威力', level: '等级', total_exp: '累计经验',
|
|
96
|
+
next_level_exp: '升级所需', next_level_total_exp: '下级累计', hatch_label: '孵化时间',
|
|
97
|
+
hatch_seconds: '孵化秒数', egg_type: '蛋类型', egg_size: '蛋尺寸', body_size: '体型',
|
|
98
|
+
type: '类型', skill_type: '技能类型', damage_type: '伤害类型', element_type: '属性',
|
|
99
|
+
label_type: '标签', families: '技能族', pet_count: '可用精灵', learnable_pet_count: '可学习精灵',
|
|
100
|
+
food_close_exp: '亲密经验', close_exp: '亲密经验', home_exp_num: '家园经验',
|
|
101
|
+
furniture_coin_num: '家具币', need_time_label: '制作时间', interaction_count: '交互次数',
|
|
102
|
+
comfort: '舒适度', footprint: '占地', gender: '性别', grade: '品级', bond: '羁绊',
|
|
103
|
+
category: '分类', area: '区域', source: '来源', source_label: '来源', summary: '摘要',
|
|
104
|
+
description: '说明', flavor_text: '背景说明', enabled: '启用', banned: '禁用',
|
|
105
|
+
shareable: '可分享', common: '通用', nest_num: '巢穴数', egg_laying_nest_num: '产蛋巢数',
|
|
106
|
+
pet_lay_egg_rate_percent: '产蛋概率', close_level: '亲密等级', action_label: '行为',
|
|
107
|
+
};
|
|
108
|
+
function wikiLabelForKey(key) {
|
|
109
|
+
return WIKI_KEY_LABELS[String(key || '')] || String(key || '').replace(/_/g, ' ');
|
|
110
|
+
}
|
|
111
|
+
const WIKI_SECTION_TITLES = {
|
|
112
|
+
items: '条目', members: '家族成员', probabilities: '概率信息', variants: '蛋型变体',
|
|
113
|
+
voice_percent: '语音概率', topics: '图鉴课题', rewards: '奖励', reward_items: '奖励',
|
|
114
|
+
acquire_methods: '获取方式', acquisition: '获取方式', methods: '获取方式', sources: '来源',
|
|
115
|
+
world_maps: '世界地图', habitats: '栖息地', effects: '效果', rules: '规则', parts: '套装部件',
|
|
116
|
+
tags: '标签', goods: '商品', mall_items: '商城商品', get_items: '获得物品', cost_items: '消耗物品',
|
|
117
|
+
comfort_levels: '舒适度收益', levels: '等级成长', pet_bond_counts: '精灵羁绊',
|
|
118
|
+
pet_home_limits: '入驻上限', settled_bonuses: '入驻加成', enjoy_field_types: '喜欢场景',
|
|
119
|
+
hate_field_types: '讨厌场景', npc_reactions: 'NPC 反应', movements: '骑乘动作',
|
|
120
|
+
blood: '血脉技能', level: '升级习得', machine: '技能石习得', field: '场景习得',
|
|
121
|
+
};
|
|
122
|
+
function wikiSectionTitle(key) {
|
|
123
|
+
return WIKI_SECTION_TITLES[String(key || '')] || wikiLabelForKey(key);
|
|
124
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { RocomClient } from './client';
|
|
3
|
+
import { WikiCatalog } from './wiki-catalog';
|
|
4
|
+
export declare function normalizeQueryText(text: any): string;
|
|
5
|
+
export declare function wikiNamedValue(value: any): string;
|
|
6
|
+
export declare function wikiNames(values: any): string[];
|
|
7
|
+
export declare function wikiRangeLabel(data: any, keyMin: string, keyMax: string, unit: string): string;
|
|
8
|
+
export declare function similarityRatio(a: string, b: string): number;
|
|
9
|
+
export declare function wikiSimilarityScore(query: string, item: any): number;
|
|
10
|
+
export declare function wikiGenericValue(value: any, depth?: number): string;
|
|
11
|
+
export declare function wikiPickImage(item: any): string;
|
|
12
|
+
export declare function wikiTitleForItem(item: any, fallback?: string): string;
|
|
13
|
+
export declare function wikiMetaForItem(item: any, limit?: number, catalogKey?: string): Array<{
|
|
14
|
+
label: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function wikiCardForItem(item: any, fallback: string, catalog?: WikiCatalog | null): {
|
|
18
|
+
title: string;
|
|
19
|
+
image: string;
|
|
20
|
+
summary: string;
|
|
21
|
+
badges: string[];
|
|
22
|
+
meta: {
|
|
23
|
+
label: string;
|
|
24
|
+
value: string;
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
27
|
+
export declare function wikiSectionsForPayload(payload: any, catalog?: WikiCatalog | null): any[];
|
|
28
|
+
export declare function findExactWikiMatch(results: any[], query: string): any | null;
|
|
29
|
+
export declare function wikiCandidateText(query: string, items: any[], kind: string): string;
|
|
30
|
+
export declare function buildWikiPetRenderData(overview: any, profile: any, skills: any, family: any, handbook: any, query: string): {
|
|
31
|
+
name: any;
|
|
32
|
+
query: string;
|
|
33
|
+
pet_id: any;
|
|
34
|
+
number: any;
|
|
35
|
+
form: any;
|
|
36
|
+
formLine: string;
|
|
37
|
+
quality: any;
|
|
38
|
+
stage: any;
|
|
39
|
+
pet_icon: any;
|
|
40
|
+
main_image: any;
|
|
41
|
+
type_names: string[];
|
|
42
|
+
egg_groups: string[];
|
|
43
|
+
description: any;
|
|
44
|
+
classis: string;
|
|
45
|
+
feature_name: any;
|
|
46
|
+
feature_desc: any;
|
|
47
|
+
ride_talent: string;
|
|
48
|
+
height_label: string;
|
|
49
|
+
weight_label: string;
|
|
50
|
+
gender_ratio: string;
|
|
51
|
+
move_type: any;
|
|
52
|
+
habitats: string[];
|
|
53
|
+
habitatsLabel: string;
|
|
54
|
+
ecology: string;
|
|
55
|
+
type_effectiveness: {
|
|
56
|
+
label: string;
|
|
57
|
+
value: string;
|
|
58
|
+
}[];
|
|
59
|
+
total_stats: any;
|
|
60
|
+
pet_stats: {
|
|
61
|
+
label: string;
|
|
62
|
+
value: number;
|
|
63
|
+
color: string;
|
|
64
|
+
percent: number;
|
|
65
|
+
}[];
|
|
66
|
+
skill_groups: any[];
|
|
67
|
+
family_members: any[];
|
|
68
|
+
handbook_topics: any[];
|
|
69
|
+
areas: string[];
|
|
70
|
+
areasLabel: string;
|
|
71
|
+
commandHint: string;
|
|
72
|
+
copyright: string;
|
|
73
|
+
};
|
|
74
|
+
export declare function buildWikiSkillRenderData(item: any, petsData: any, query: string): {
|
|
75
|
+
name: any;
|
|
76
|
+
skill_id: any;
|
|
77
|
+
query: string;
|
|
78
|
+
icon: any;
|
|
79
|
+
type: string;
|
|
80
|
+
skill_type: string;
|
|
81
|
+
damage_type: string;
|
|
82
|
+
element_type: string;
|
|
83
|
+
cost: any;
|
|
84
|
+
power: any;
|
|
85
|
+
families: any;
|
|
86
|
+
description: any;
|
|
87
|
+
flavor_text: any;
|
|
88
|
+
tags: any;
|
|
89
|
+
pets: any;
|
|
90
|
+
pet_total: any;
|
|
91
|
+
commandHint: string;
|
|
92
|
+
copyright: string;
|
|
93
|
+
};
|
|
94
|
+
export declare function splitWikiCommandParts(text: string): {
|
|
95
|
+
parts: string[];
|
|
96
|
+
pageNo: number;
|
|
97
|
+
};
|
|
98
|
+
export declare function wikiCatalogByToken(token: string): WikiCatalog | null;
|
|
99
|
+
export declare class WikiService {
|
|
100
|
+
private ctx;
|
|
101
|
+
private client;
|
|
102
|
+
private catalogsCache;
|
|
103
|
+
private catalogsCacheTs;
|
|
104
|
+
private optionsCache;
|
|
105
|
+
private optionsCacheTs;
|
|
106
|
+
private skillDetailCache;
|
|
107
|
+
private petDetailCache;
|
|
108
|
+
constructor(ctx: Context, client: RocomClient);
|
|
109
|
+
getCatalogsPayload(force?: boolean): Promise<any>;
|
|
110
|
+
getOptionsPayload(force?: boolean): Promise<any>;
|
|
111
|
+
backendCatalogItems(payload: any): any[];
|
|
112
|
+
catalogFromBackendItem(item: any): WikiCatalog;
|
|
113
|
+
catalogForKeyFromPayload(key: string, catalogsPayload: any): WikiCatalog | null;
|
|
114
|
+
catalogsFromPayload(catalogsPayload: any): WikiCatalog[];
|
|
115
|
+
getCatalogByKey(key: string): Promise<WikiCatalog | null>;
|
|
116
|
+
dynamicCatalogByToken(token: string, catalogsPayload: any): WikiCatalog | null;
|
|
117
|
+
parseWikiCommand(text: string): {
|
|
118
|
+
catalog: WikiCatalog | null;
|
|
119
|
+
query: string;
|
|
120
|
+
pageNo: number;
|
|
121
|
+
};
|
|
122
|
+
catalogUsageText(): string;
|
|
123
|
+
buildCatalogRenderData(catalogsPayload: any, optionsPayload: any): {
|
|
124
|
+
title: string;
|
|
125
|
+
subtitle: string;
|
|
126
|
+
summary: string;
|
|
127
|
+
badges: any[];
|
|
128
|
+
facts: any[];
|
|
129
|
+
primary: any[];
|
|
130
|
+
groups: {
|
|
131
|
+
title: string;
|
|
132
|
+
desc: string;
|
|
133
|
+
items: {
|
|
134
|
+
title: string;
|
|
135
|
+
key: string;
|
|
136
|
+
summary: string;
|
|
137
|
+
desc: string;
|
|
138
|
+
children: string;
|
|
139
|
+
}[];
|
|
140
|
+
total: number;
|
|
141
|
+
}[];
|
|
142
|
+
commandHint: string;
|
|
143
|
+
copyright: string;
|
|
144
|
+
};
|
|
145
|
+
globalSearchCatalogs(catalogsPayload: any): WikiCatalog[];
|
|
146
|
+
resultCommandExamples(items: any[], catalog?: WikiCatalog | null, limit?: number): string[];
|
|
147
|
+
wikiPathParamsFromItem(catalog: WikiCatalog, item: any, rawText?: string): Record<string, string>;
|
|
148
|
+
fillDetailPath(catalog: WikiCatalog, params: Record<string, string>): string;
|
|
149
|
+
itemMatchesQueryExact(catalog: WikiCatalog, item: any, query: string): boolean;
|
|
150
|
+
findCatalogMatch(catalog: WikiCatalog, items: any[], query: string, allowSingle?: boolean): any | null;
|
|
151
|
+
suggestCatalogItems(catalog: WikiCatalog | null, query: string, limit?: number): Promise<any[]>;
|
|
152
|
+
buildGenericRenderData(catalog: WikiCatalog | {
|
|
153
|
+
title: string;
|
|
154
|
+
key: string;
|
|
155
|
+
}, payload: any, query: string, mode: string, pageNo?: number): {
|
|
156
|
+
title: string;
|
|
157
|
+
subtitle: string;
|
|
158
|
+
image: string;
|
|
159
|
+
summary: any;
|
|
160
|
+
badges: string[];
|
|
161
|
+
facts: {
|
|
162
|
+
label: string;
|
|
163
|
+
value: string;
|
|
164
|
+
}[];
|
|
165
|
+
actionHint: string;
|
|
166
|
+
actionExamples: string[];
|
|
167
|
+
cards: any;
|
|
168
|
+
sections: any[];
|
|
169
|
+
commandHint: string;
|
|
170
|
+
copyright: string;
|
|
171
|
+
} | {
|
|
172
|
+
title: string;
|
|
173
|
+
subtitle: string;
|
|
174
|
+
image: string;
|
|
175
|
+
summary: string;
|
|
176
|
+
badges: string[];
|
|
177
|
+
facts: {
|
|
178
|
+
label: string;
|
|
179
|
+
value: string;
|
|
180
|
+
}[];
|
|
181
|
+
cards: any[];
|
|
182
|
+
sections: any[];
|
|
183
|
+
commandHint: string;
|
|
184
|
+
copyright: string;
|
|
185
|
+
actionHint?: undefined;
|
|
186
|
+
actionExamples?: undefined;
|
|
187
|
+
};
|
|
188
|
+
private mergeDetailPayloads;
|
|
189
|
+
private itemDetailCompanionKeys;
|
|
190
|
+
enrichItemDetail(catalog: WikiCatalog, detail: any): Promise<any>;
|
|
191
|
+
resolveWikiPet(query: string): Promise<{
|
|
192
|
+
detail: any;
|
|
193
|
+
candidates: any[];
|
|
194
|
+
error: string;
|
|
195
|
+
}>;
|
|
196
|
+
resolveWikiSkill(query: string): Promise<{
|
|
197
|
+
detail: any;
|
|
198
|
+
candidates: any[];
|
|
199
|
+
error: string;
|
|
200
|
+
}>;
|
|
201
|
+
fetchWikiPetSections(petId: any): Promise<{
|
|
202
|
+
profile: any;
|
|
203
|
+
skills: any;
|
|
204
|
+
family: any;
|
|
205
|
+
handbook: any;
|
|
206
|
+
}>;
|
|
207
|
+
getSkillDetailCached(skillId: string): Promise<any | null>;
|
|
208
|
+
getPetDetailCached(petId: string): Promise<any | null>;
|
|
209
|
+
fetchGenericCatalog(catalog: WikiCatalog, query: string, pageNo: number): Promise<{
|
|
210
|
+
payload: any;
|
|
211
|
+
mode: string;
|
|
212
|
+
error: string;
|
|
213
|
+
}>;
|
|
214
|
+
private fetchDetailForCatalogItem;
|
|
215
|
+
fetchGlobalSearch(query: string, pageNo: number): Promise<{
|
|
216
|
+
payload: any;
|
|
217
|
+
mode: string;
|
|
218
|
+
error: string;
|
|
219
|
+
}>;
|
|
220
|
+
}
|