koishi-plugin-rocom 1.0.17 → 1.0.19
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/client.d.ts +49 -9
- package/lib/client.js +250 -141
- package/lib/commands/egg.js +101 -9
- package/lib/commands/merchant.js +144 -146
- package/lib/commands/query.js +148 -117
- 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 +66 -0
- package/lib/commands/tools.js +47 -59
- package/lib/egg-service.d.ts +125 -20
- package/lib/egg-service.js +364 -96
- 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 +5 -0
- package/lib/index.js +193 -8740
- package/lib/merchant-clock.d.ts +22 -0
- package/lib/merchant-clock.js +95 -0
- package/lib/merchant-service.d.ts +24 -0
- package/lib/merchant-service.js +148 -0
- package/lib/permissions.d.ts +16 -0
- package/lib/permissions.js +66 -0
- package/lib/pet-data-service.js +7 -9
- 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/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-team/index.html +105 -0
- package/lib/render-templates/share-code-team/style.css +345 -0
- package/lib/render.d.ts +15 -2
- package/lib/render.js +120 -37
- package/lib/send-image.d.ts +1 -1
- package/lib/send-image.js +12 -1
- package/lib/share-code-service.d.ts +32 -0
- package/lib/share-code-service.js +184 -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/types.d.ts +5 -0
- package/lib/user.d.ts +5 -0
- package/package.json +4 -1
- package/readme.md +145 -124
package/lib/egg-service.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.formatEggGroups = formatEggGroups;
|
|
|
9
9
|
const koishi_1 = require("koishi");
|
|
10
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const pet_size_1 = require("./pet-size");
|
|
12
13
|
const logger = new koishi_1.Logger('rocom-egg');
|
|
13
14
|
exports.EGG_GROUP_META = {
|
|
14
15
|
1: { label: '未发现', desc: '不能和任何精灵生蛋' },
|
|
@@ -70,6 +71,8 @@ function fmtRange(lo, hi, u) {
|
|
|
70
71
|
return `${lo ?? hi}${u}`;
|
|
71
72
|
}
|
|
72
73
|
function num(value) {
|
|
74
|
+
if (value === null || value === undefined || value === '')
|
|
75
|
+
return null;
|
|
73
76
|
const parsed = Number(value);
|
|
74
77
|
return Number.isFinite(parsed) ? parsed : null;
|
|
75
78
|
}
|
|
@@ -275,33 +278,60 @@ class EggService {
|
|
|
275
278
|
weight_min: weightMin,
|
|
276
279
|
weight_max: weightMax,
|
|
277
280
|
weight_label: fmtRange(weightMin, weightMax, 'kg'),
|
|
281
|
+
...(0, pet_size_1.sizeVariantPayload)(queryWeight, pet?.breeding?.weight_low == null ? null : pet.breeding.weight_low / 1000, pet?.breeding?.weight_high == null ? null : pet.breeding.weight_high / 1000),
|
|
278
282
|
probability,
|
|
279
283
|
match_count: matchCount,
|
|
280
284
|
match_info_label: this.formatMatchSummary(probability, matchCount),
|
|
281
285
|
};
|
|
282
286
|
}
|
|
283
|
-
formatSizeApiCard(item) {
|
|
284
|
-
//
|
|
287
|
+
formatSizeApiCard(item, queryWeight) {
|
|
288
|
+
// 兼容 /wiki/pet-size/query 的 items[].pet/egg_size/match 结构与 /egg/search 的
|
|
289
|
+
// height_range_m/cm、weight_range_kg/g、r_value/range_area 平铺结构。
|
|
285
290
|
const petInfo = item?.pet && typeof item.pet === 'object' ? item.pet : {};
|
|
286
291
|
const eggSize = item?.egg_size && typeof item.egg_size === 'object' ? item.egg_size : {};
|
|
287
292
|
const heightRange = eggSize?.height && typeof eggSize.height === 'object' ? eggSize.height : {};
|
|
288
293
|
const weightRange = eggSize?.weight && typeof eggSize.weight === 'object' ? eggSize.weight : {};
|
|
289
294
|
const match = item?.match && typeof item.match === 'object' ? item.match : {};
|
|
290
|
-
const petId = petInfo?.pet_id || item?.petId || item?.pet_id || '-';
|
|
291
|
-
const
|
|
295
|
+
const petId = petInfo?.pet_id || petInfo?.id || item?.petId || item?.pet_id || item?.id || '-';
|
|
296
|
+
const petForm = String(petInfo?.form || item?.form || '').trim();
|
|
297
|
+
let petName = petInfo?.name || (typeof item?.pet === 'string' ? item.pet : '') || item?.name || item?.pet_name || '未知精灵';
|
|
298
|
+
if (petForm && !String(petName).includes(petForm))
|
|
299
|
+
petName = `${petName}(${petForm})`;
|
|
292
300
|
const typeNames = Array.isArray(petInfo?.type_names) ? petInfo.type_names.filter(Boolean) : [];
|
|
293
301
|
const eggGroupNames = Array.isArray(petInfo?.egg_group_names) ? petInfo.egg_group_names.filter(Boolean) : [];
|
|
302
|
+
const [heightRangeMMin, heightRangeMMax] = this.rangePair(item?.height_range_m);
|
|
303
|
+
const [heightRangeCmMin, heightRangeCmMax] = this.rangePair(item?.height_range_cm);
|
|
304
|
+
const [weightRangeKgMin, weightRangeKgMax] = this.rangePair(item?.weight_range_kg);
|
|
305
|
+
const [weightRangeGMin, weightRangeGMax] = this.rangePair(item?.weight_range_g);
|
|
294
306
|
const probability = num(item?.probability) ?? num(match?.percent);
|
|
295
307
|
const matchCount = num(item?.matchCount);
|
|
296
|
-
const heightMin = num(heightRange?.min_m ??
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const
|
|
308
|
+
const heightMin = num(heightRange?.min_m) ?? heightRangeMMin
|
|
309
|
+
?? (heightRangeCmMin == null ? null : heightRangeCmMin / 100)
|
|
310
|
+
?? num(item?.diameterMin);
|
|
311
|
+
const heightMax = num(heightRange?.max_m) ?? heightRangeMMax
|
|
312
|
+
?? (heightRangeCmMax == null ? null : heightRangeCmMax / 100)
|
|
313
|
+
?? num(item?.diameterMax);
|
|
314
|
+
const weightMin = num(weightRange?.min_kg) ?? weightRangeKgMin
|
|
315
|
+
?? (weightRangeGMin == null ? null : weightRangeGMin / 1000)
|
|
316
|
+
?? num(item?.weightMin);
|
|
317
|
+
const weightMax = num(weightRange?.max_kg) ?? weightRangeKgMax
|
|
318
|
+
?? (weightRangeGMax == null ? null : weightRangeGMax / 1000)
|
|
319
|
+
?? num(item?.weightMax);
|
|
320
|
+
const rValue = num(item?.r_value);
|
|
321
|
+
const rangeArea = num(item?.range_area);
|
|
322
|
+
const sizeVariant = (0, pet_size_1.sizeVariantPayload)(queryWeight, weightMin, weightMax, fmtRange(weightMin, weightMax, 'kg'));
|
|
323
|
+
const fallbackMatchLabel = [
|
|
324
|
+
rValue != null ? `R值 ${formatNumber(rValue, 3)}` : '',
|
|
325
|
+
rangeArea != null ? `范围面积 ${formatNumber(rangeArea, 0)}` : '',
|
|
326
|
+
].filter(Boolean).join(' / ');
|
|
327
|
+
const matchInfoLabel = String(match?.match_percent_text || '')
|
|
328
|
+
|| fallbackMatchLabel
|
|
329
|
+
|| this.formatMatchSummary(probability, matchCount);
|
|
300
330
|
return {
|
|
301
331
|
id: petId,
|
|
302
332
|
name: petName,
|
|
303
|
-
icon: petInfo?.icon || item?.petIcon || petIconUrl(petId),
|
|
304
|
-
image: petInfo?.small_icon || item?.petImage || petImageUrl(petId),
|
|
333
|
+
icon: petInfo?.icon || item?.pet_icon_url || item?.petIcon || petIconUrl(petId),
|
|
334
|
+
image: petInfo?.small_icon || item?.pet_img_url || item?.petImage || petImageUrl(petId),
|
|
305
335
|
type_label: typeNames.length ? typeNames.join(' / ') : '后端未提供',
|
|
306
336
|
egg_group_ids: [],
|
|
307
337
|
egg_groups_label: eggGroupNames.length ? eggGroupNames.join(' / ') : '后端未提供',
|
|
@@ -311,11 +341,18 @@ class EggService {
|
|
|
311
341
|
weight_min: weightMin,
|
|
312
342
|
weight_max: weightMax,
|
|
313
343
|
weight_label: fmtRange(weightMin, weightMax, 'kg'),
|
|
344
|
+
query_weight: queryWeight,
|
|
345
|
+
...sizeVariant,
|
|
314
346
|
probability,
|
|
315
347
|
match_count: matchCount,
|
|
316
|
-
match_info_label:
|
|
348
|
+
match_info_label: matchInfoLabel,
|
|
317
349
|
};
|
|
318
350
|
}
|
|
351
|
+
rangePair(value) {
|
|
352
|
+
if (Array.isArray(value) && value.length >= 2)
|
|
353
|
+
return [num(value[0]), num(value[1])];
|
|
354
|
+
return [null, null];
|
|
355
|
+
}
|
|
319
356
|
// 新版尺寸反查按 match.layer 划分完美/范围匹配。
|
|
320
357
|
splitNewSizeApiItems(items) {
|
|
321
358
|
const perfect = [];
|
|
@@ -341,42 +378,6 @@ class EggService {
|
|
|
341
378
|
candidates: Array.isArray(results?.candidates) ? results.candidates : [],
|
|
342
379
|
};
|
|
343
380
|
}
|
|
344
|
-
formatEggSearchCard(item) {
|
|
345
|
-
const eggGroups = Array.isArray(item?.egg_groups)
|
|
346
|
-
? item.egg_groups
|
|
347
|
-
.map((group) => group?.official_name || group?.display_name || `蛋组${group?.group_id}`)
|
|
348
|
-
.filter(Boolean)
|
|
349
|
-
: [];
|
|
350
|
-
const heightRange = Array.isArray(item?.height_range_m) ? item.height_range_m : [];
|
|
351
|
-
const weightRange = Array.isArray(item?.weight_range_kg) ? item.weight_range_kg : [];
|
|
352
|
-
const typeLabel = Array.isArray(item?.unit_type) && item.unit_type.length
|
|
353
|
-
? item.unit_type.join(' / ')
|
|
354
|
-
: '未知';
|
|
355
|
-
return {
|
|
356
|
-
id: item?.id || '-',
|
|
357
|
-
name: item?.name || '未知精灵',
|
|
358
|
-
icon: item?.pet_icon_url || petIconUrl(item?.id),
|
|
359
|
-
image: item?.pet_img_url || petImageUrl(item?.id),
|
|
360
|
-
type_label: typeLabel,
|
|
361
|
-
egg_group_ids: Array.isArray(item?.egg_groups)
|
|
362
|
-
? item.egg_groups.map((group) => Number(group?.group_id)).filter((value) => Number.isFinite(value))
|
|
363
|
-
: [],
|
|
364
|
-
egg_groups_label: eggGroups.length ? eggGroups.join(' / ') : '暂无蛋组数据',
|
|
365
|
-
height_min: num(heightRange[0]),
|
|
366
|
-
height_max: num(heightRange[1]),
|
|
367
|
-
height_label: fmtRange(num(heightRange[0]), num(heightRange[1]), 'm'),
|
|
368
|
-
weight_min: num(weightRange[0]),
|
|
369
|
-
weight_max: num(weightRange[1]),
|
|
370
|
-
weight_label: fmtRange(num(weightRange[0]), num(weightRange[1]), 'kg'),
|
|
371
|
-
probability: null,
|
|
372
|
-
match_count: null,
|
|
373
|
-
match_info_label: '',
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
formatEggSearchTextLine(item) {
|
|
377
|
-
const card = this.formatEggSearchCard(item);
|
|
378
|
-
return `${card.name} (#${card.id}) — ${card.height_label} / ${card.weight_label} · ${card.egg_groups_label}`;
|
|
379
|
-
}
|
|
380
381
|
mergeCardsByName(perfect, ranged) {
|
|
381
382
|
const perfectMap = new Map();
|
|
382
383
|
const rangedMap = new Map();
|
|
@@ -509,7 +510,7 @@ class EggService {
|
|
|
509
510
|
lines.push(`✅ 完美匹配 ${condStr} 的精灵(共 ${results.perfect.length} 只):`);
|
|
510
511
|
results.perfect.slice(0, 10).forEach((p, i) => {
|
|
511
512
|
const br = p.breeding || {};
|
|
512
|
-
lines.push(` ${i + 1}. ${petName(p)} (#${p.id}) — ${fmtRange(ht(br.height_low), ht(br.height_high), 'm')} / ${fmtRange(wt(br.weight_low), wt(br.weight_high), 'kg')} · ${formatEggGroups(this.getEggGroups(p))}`);
|
|
513
|
+
lines.push(` ${i + 1}. ${petName(p)} (#${p.id}) — ${fmtRange(ht(br.height_low), ht(br.height_high), 'm')} / ${fmtRange(wt(br.weight_low), wt(br.weight_high), 'kg')} · ${formatEggGroups(this.getEggGroups(p))}${this.formatPetCard(p, height, weight).size_variant_label ? ' · 【' + this.formatPetCard(p, height, weight).size_variant_label + '】' : ''}`);
|
|
513
514
|
});
|
|
514
515
|
}
|
|
515
516
|
if (results.range.length) {
|
|
@@ -518,7 +519,7 @@ class EggService {
|
|
|
518
519
|
lines.push(`🔍 范围匹配 ${condStr} 的精灵(共 ${results.range.length} 只,容差±15%):`);
|
|
519
520
|
results.range.slice(0, 10).forEach((p, i) => {
|
|
520
521
|
const br = p.breeding || {};
|
|
521
|
-
lines.push(` ${i + 1}. ${petName(p)} (#${p.id}) — ${fmtRange(ht(br.height_low), ht(br.height_high), 'm')} / ${fmtRange(wt(br.weight_low), wt(br.weight_high), 'kg')} · ${formatEggGroups(this.getEggGroups(p))}`);
|
|
522
|
+
lines.push(` ${i + 1}. ${petName(p)} (#${p.id}) — ${fmtRange(ht(br.height_low), ht(br.height_high), 'm')} / ${fmtRange(wt(br.weight_low), wt(br.weight_high), 'kg')} · ${formatEggGroups(this.getEggGroups(p))}${this.formatPetCard(p, height, weight).size_variant_label ? ' · 【' + this.formatPetCard(p, height, weight).size_variant_label + '】' : ''}`);
|
|
522
523
|
});
|
|
523
524
|
}
|
|
524
525
|
return lines.join('\n');
|
|
@@ -537,8 +538,8 @@ class EggService {
|
|
|
537
538
|
if (exact.length) {
|
|
538
539
|
lines.push(`✅ 完美匹配 ${condStr} 的精灵(共 ${exact.length} 只):`);
|
|
539
540
|
exact.slice(0, 10).forEach((item, i) => {
|
|
540
|
-
const card = this.formatSizeApiCard(item);
|
|
541
|
-
lines.push(` ${i + 1}. ${card.name} (#${card.id}) — ${card.height_label} / ${card.weight_label}`);
|
|
541
|
+
const card = this.formatSizeApiCard(item, weight);
|
|
542
|
+
lines.push(` ${i + 1}. ${card.name} (#${card.id}) — ${card.height_label} / ${card.weight_label}${card.size_variant_label ? ` · 【${card.size_variant_label}】` : ''}`);
|
|
542
543
|
});
|
|
543
544
|
}
|
|
544
545
|
if (candidates.length) {
|
|
@@ -546,32 +547,12 @@ class EggService {
|
|
|
546
547
|
lines.push('');
|
|
547
548
|
lines.push(`🔍 范围匹配 ${condStr} 的精灵(共 ${candidates.length} 只):`);
|
|
548
549
|
candidates.slice(0, 10).forEach((item, i) => {
|
|
549
|
-
const card = this.formatSizeApiCard(item);
|
|
550
|
-
lines.push(` ${i + 1}. ${card.name} (#${card.id}) — ${card.height_label} / ${card.weight_label}`);
|
|
550
|
+
const card = this.formatSizeApiCard(item, weight);
|
|
551
|
+
lines.push(` ${i + 1}. ${card.name} (#${card.id}) — ${card.height_label} / ${card.weight_label}${card.size_variant_label ? ` · 【${card.size_variant_label}】` : ''}`);
|
|
551
552
|
});
|
|
552
553
|
}
|
|
553
554
|
return lines.join('\n');
|
|
554
555
|
}
|
|
555
|
-
buildEggSearchText(heightMeters, weight, results, heightDisplay) {
|
|
556
|
-
const cond = [];
|
|
557
|
-
if (heightMeters != null)
|
|
558
|
-
cond.push(`身高=${heightDisplay || `${formatNumber(heightMeters)}m`}`);
|
|
559
|
-
if (weight != null)
|
|
560
|
-
cond.push(`体重=${weight}kg`);
|
|
561
|
-
const condStr = cond.join(' + ') || '当前条件';
|
|
562
|
-
const items = Array.isArray(results?.items) ? results.items : [];
|
|
563
|
-
if (!items.length)
|
|
564
|
-
return `❌ 未找到符合 ${condStr} 的精灵。`;
|
|
565
|
-
const total = results?.total ?? items.length;
|
|
566
|
-
const lines = [`✅ 符合 ${condStr} 的精灵(共 ${total} 只):`];
|
|
567
|
-
items.slice(0, 10).forEach((item, index) => {
|
|
568
|
-
lines.push(` ${index + 1}. ${this.formatEggSearchTextLine(item)}`);
|
|
569
|
-
});
|
|
570
|
-
if (results?.has_more) {
|
|
571
|
-
lines.push(` ... 还有更多结果,可尝试更精确的尺寸或等待后续分页支持。`);
|
|
572
|
-
}
|
|
573
|
-
return lines.join('\n');
|
|
574
|
-
}
|
|
575
556
|
buildSearchText(pet) {
|
|
576
557
|
const egs = this.getEggGroups(pet);
|
|
577
558
|
const compat = this.getCompatiblePets(pet);
|
|
@@ -585,6 +566,17 @@ class EggService {
|
|
|
585
566
|
lines.push('⚠️ 该精灵属于「未发现」蛋组,无法配种。');
|
|
586
567
|
return lines.join('\n');
|
|
587
568
|
}
|
|
569
|
+
buildSearchTextFromWiki(data) {
|
|
570
|
+
const lines = [
|
|
571
|
+
`🥚 ${data?.pet_name || '未知精灵'} (#${data?.pet_id || '-'})`,
|
|
572
|
+
`属性:${data?.type_label || '未知'}`,
|
|
573
|
+
`蛋组:${data?.egg_groups_label || '暂无蛋组数据'}`,
|
|
574
|
+
`${data?.compatible_total_known === false ? '当前预览精灵数' : '可配种精灵数'}:${data?.total_compatible || 0}`,
|
|
575
|
+
];
|
|
576
|
+
if (data?.is_undiscovered)
|
|
577
|
+
lines.push('⚠️ 该精灵属于「未发现」蛋组,无法配种。');
|
|
578
|
+
return lines.join('\n');
|
|
579
|
+
}
|
|
588
580
|
buildCandidatesText(keyword, candidates) {
|
|
589
581
|
const lines = [`🔍 「${keyword}」匹配到 ${candidates.length} 只精灵,请精确输入:`];
|
|
590
582
|
candidates.slice(0, 10).forEach((p, i) => {
|
|
@@ -683,6 +675,120 @@ class EggService {
|
|
|
683
675
|
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
684
676
|
};
|
|
685
677
|
}
|
|
678
|
+
buildSearchDataFromWiki(pet, compatibleByGroup = {}) {
|
|
679
|
+
const eggGroups = this.wikiEggGroups(pet);
|
|
680
|
+
const eggGroupIds = eggGroups.map(group => group.id);
|
|
681
|
+
const eggGroupLabels = Object.fromEntries(eggGroups.map(group => [group.id, group.name]));
|
|
682
|
+
const seen = new Set();
|
|
683
|
+
let totalCompatible = 0;
|
|
684
|
+
const sections = eggGroups.map(group => {
|
|
685
|
+
const isUndiscovered = group.id === 1 || group.name.includes('未发现');
|
|
686
|
+
if (isUndiscovered) {
|
|
687
|
+
return {
|
|
688
|
+
id: 1,
|
|
689
|
+
label: '未发现',
|
|
690
|
+
desc: '不能和任何精灵生蛋,多用于传说中的精灵',
|
|
691
|
+
count: 0,
|
|
692
|
+
members: [],
|
|
693
|
+
has_more: false,
|
|
694
|
+
total: 0,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
const rawMembers = compatibleByGroup[String(group.id)] || [];
|
|
698
|
+
const members = rawMembers
|
|
699
|
+
.filter(item => String(item?.pet_id ?? item?.id ?? '') !== String(pet?.pet_id ?? pet?.id ?? ''))
|
|
700
|
+
.map(item => {
|
|
701
|
+
const key = String(item?.pet_id ?? item?.id ?? item?.name ?? '');
|
|
702
|
+
if (key && !seen.has(key)) {
|
|
703
|
+
seen.add(key);
|
|
704
|
+
totalCompatible += 1;
|
|
705
|
+
}
|
|
706
|
+
return this.formatWikiMember(item);
|
|
707
|
+
});
|
|
708
|
+
return {
|
|
709
|
+
id: group.id,
|
|
710
|
+
label: group.name,
|
|
711
|
+
desc: '',
|
|
712
|
+
count: members.length,
|
|
713
|
+
members: members.slice(0, 30),
|
|
714
|
+
has_more: members.length > 30,
|
|
715
|
+
total: members.length,
|
|
716
|
+
};
|
|
717
|
+
});
|
|
718
|
+
const petId = pet?.pet_id ?? pet?.id ?? '-';
|
|
719
|
+
const height = pet?.body_size?.height || {};
|
|
720
|
+
const weight = pet?.body_size?.weight || {};
|
|
721
|
+
const attributes = pet?.attributes || {};
|
|
722
|
+
const maleRate = num(pet?.gender_ratio?.male_percent);
|
|
723
|
+
const femaleRate = num(pet?.gender_ratio?.female_percent);
|
|
724
|
+
const totalStats = num(attributes?.sum) ?? ['hp', 'physical_attack', 'magic_attack', 'physical_defense', 'magic_defense', 'speed']
|
|
725
|
+
.reduce((sum, key) => sum + (num(attributes?.[key]) || 0), 0);
|
|
726
|
+
return {
|
|
727
|
+
pet_name: pet?.name || '未知精灵',
|
|
728
|
+
pet_id: petId,
|
|
729
|
+
pet_icon: pet?.icon || pet?.small_icon || petIconUrl(petId),
|
|
730
|
+
pet_image: pet?.small_icon || pet?.icon || petImageUrl(petId),
|
|
731
|
+
type_label: this.wikiTypeLabel(pet),
|
|
732
|
+
egg_groups_label: eggGroups.map(group => group.name).join(' / ') || '暂无蛋组数据',
|
|
733
|
+
egg_groups: eggGroupIds,
|
|
734
|
+
egg_group_labels: eggGroupLabels,
|
|
735
|
+
male_rate: maleRate,
|
|
736
|
+
female_rate: femaleRate,
|
|
737
|
+
hatch_label: '暂无数据',
|
|
738
|
+
weight_label: fmtRange(num(weight?.min_kg), num(weight?.max_kg), 'kg'),
|
|
739
|
+
height_label: fmtRange(num(height?.min_m), num(height?.max_m), 'm'),
|
|
740
|
+
total_compatible: totalCompatible,
|
|
741
|
+
is_undiscovered: eggGroups.some(group => group.id === 1 || group.name.includes('未发现')),
|
|
742
|
+
egg_group_sections: sections,
|
|
743
|
+
total_stats: totalStats,
|
|
744
|
+
egg_details: { has_data: false },
|
|
745
|
+
commandHint: '数据来自新版 Wiki;接口不可用时自动回退本地查蛋',
|
|
746
|
+
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
wikiEggGroups(pet) {
|
|
750
|
+
const groups = Array.isArray(pet?.egg_groups) ? pet.egg_groups : [];
|
|
751
|
+
const normalized = groups
|
|
752
|
+
.map((group) => {
|
|
753
|
+
if (group && typeof group === 'object') {
|
|
754
|
+
const name = String(group?.name || group?.label || '').trim();
|
|
755
|
+
const id = group?.id ?? group?.group_id ?? (name.includes('未发现') ? 1 : name);
|
|
756
|
+
return name ? { id, name } : null;
|
|
757
|
+
}
|
|
758
|
+
const name = String(group || '').trim();
|
|
759
|
+
return name ? { id: name.includes('未发现') ? 1 : name, name } : null;
|
|
760
|
+
})
|
|
761
|
+
.filter(Boolean);
|
|
762
|
+
if (normalized.length)
|
|
763
|
+
return normalized;
|
|
764
|
+
const ids = Array.isArray(pet?.egg_group_ids) ? pet.egg_group_ids : [];
|
|
765
|
+
const names = Array.isArray(pet?.egg_group_names) ? pet.egg_group_names : [];
|
|
766
|
+
return names.map((rawName, index) => {
|
|
767
|
+
const name = String(rawName || '').trim();
|
|
768
|
+
return {
|
|
769
|
+
id: ids[index] ?? (name.includes('未发现') ? 1 : name),
|
|
770
|
+
name,
|
|
771
|
+
};
|
|
772
|
+
}).filter(group => group.name);
|
|
773
|
+
}
|
|
774
|
+
wikiTypeLabel(pet) {
|
|
775
|
+
const typeNames = Array.isArray(pet?.type_names) ? pet.type_names.filter(Boolean) : [];
|
|
776
|
+
if (typeNames.length)
|
|
777
|
+
return typeNames.join(' / ');
|
|
778
|
+
const types = Array.isArray(pet?.types)
|
|
779
|
+
? pet.types.map((type) => type?.name || type).filter(Boolean)
|
|
780
|
+
: [];
|
|
781
|
+
return types.length ? types.join(' / ') : '未知';
|
|
782
|
+
}
|
|
783
|
+
formatWikiMember(item) {
|
|
784
|
+
const groups = this.wikiEggGroups(item);
|
|
785
|
+
return {
|
|
786
|
+
name: item?.name || '未知精灵',
|
|
787
|
+
id: item?.pet_id ?? item?.id ?? '-',
|
|
788
|
+
type_label: this.wikiTypeLabel(item),
|
|
789
|
+
egg_groups_label: groups.map(group => group.name).join(' / ') || '暂无蛋组数据',
|
|
790
|
+
};
|
|
791
|
+
}
|
|
686
792
|
buildPairData(a, b) {
|
|
687
793
|
const ev = this.evaluatePair(a, b);
|
|
688
794
|
const makePetCard = (p) => ({
|
|
@@ -723,6 +829,185 @@ class EggService {
|
|
|
723
829
|
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
724
830
|
};
|
|
725
831
|
}
|
|
832
|
+
// ─── 后端 Egg API 转换(上游 v3.8.0)───
|
|
833
|
+
buildCandidatesFromEggApi(keyword, candidates) {
|
|
834
|
+
return {
|
|
835
|
+
keyword,
|
|
836
|
+
count: candidates.length,
|
|
837
|
+
candidates: candidates.map(item => this.formatEggApiCard(item)),
|
|
838
|
+
commandHint: '请使用更精确的名称重新查询',
|
|
839
|
+
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
buildSearchDataFromEggApi(pet, compatibleByGroup = {}) {
|
|
843
|
+
const groups = this.eggApiGroups(pet);
|
|
844
|
+
const groupIds = [];
|
|
845
|
+
const groupLabels = {};
|
|
846
|
+
for (const group of groups) {
|
|
847
|
+
const id = this.eggGroupId(group);
|
|
848
|
+
if (id === null || groupIds.includes(id))
|
|
849
|
+
continue;
|
|
850
|
+
groupIds.push(id);
|
|
851
|
+
groupLabels[id] = this.eggGroupLabel(group);
|
|
852
|
+
}
|
|
853
|
+
const seen = new Set();
|
|
854
|
+
let totalCompatible = 0;
|
|
855
|
+
const sections = groups.map((group) => {
|
|
856
|
+
const gid = this.eggGroupId(group);
|
|
857
|
+
const label = this.eggGroupLabel(group);
|
|
858
|
+
if (gid === null)
|
|
859
|
+
return null;
|
|
860
|
+
if (this.isUndiscoveredEggGroup(gid, label)) {
|
|
861
|
+
return {
|
|
862
|
+
id: gid,
|
|
863
|
+
label: label || '未发现',
|
|
864
|
+
desc: '不能和任何精灵生蛋,多用于传说中的精灵',
|
|
865
|
+
count: 0,
|
|
866
|
+
members: [],
|
|
867
|
+
has_more: false,
|
|
868
|
+
total: 0,
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
const groupResult = compatibleByGroup[String(gid)] ?? compatibleByGroup[gid] ?? {};
|
|
872
|
+
const rawMembers = Array.isArray(groupResult)
|
|
873
|
+
? groupResult
|
|
874
|
+
: (Array.isArray(groupResult?.items) ? groupResult.items : []);
|
|
875
|
+
const members = [];
|
|
876
|
+
for (const item of rawMembers) {
|
|
877
|
+
const itemId = item?.id ?? item?.pet_id;
|
|
878
|
+
if (String(itemId ?? '') === String(pet?.id ?? pet?.pet_id ?? ''))
|
|
879
|
+
continue;
|
|
880
|
+
const key = String(itemId ?? item?.name ?? '');
|
|
881
|
+
if (key && !seen.has(key)) {
|
|
882
|
+
seen.add(key);
|
|
883
|
+
totalCompatible += 1;
|
|
884
|
+
}
|
|
885
|
+
members.push(this.formatEggApiMember(item));
|
|
886
|
+
}
|
|
887
|
+
const total = num(groupResult?.total) ?? members.length;
|
|
888
|
+
return {
|
|
889
|
+
id: gid,
|
|
890
|
+
label,
|
|
891
|
+
desc: this.eggGroupDesc(group),
|
|
892
|
+
count: total,
|
|
893
|
+
members: members.slice(0, 30),
|
|
894
|
+
has_more: total > 30 || members.length > 30,
|
|
895
|
+
total,
|
|
896
|
+
};
|
|
897
|
+
}).filter(Boolean);
|
|
898
|
+
const [heightMin, heightMax] = this.eggHeightRangeM(pet);
|
|
899
|
+
const [weightMin, weightMax] = this.eggWeightRangeKg(pet);
|
|
900
|
+
const petId = pet?.id ?? pet?.pet_id ?? '-';
|
|
901
|
+
return {
|
|
902
|
+
pet_name: this.eggPetDisplayName(pet),
|
|
903
|
+
pet_id: petId,
|
|
904
|
+
pet_icon: pet?.pet_icon_url || pet?.icon_url || petIconUrl(petId),
|
|
905
|
+
pet_image: pet?.pet_img_url || pet?.image_url || petImageUrl(petId),
|
|
906
|
+
type_label: this.eggApiTypeLabel(pet),
|
|
907
|
+
egg_groups_label: groupIds.map(id => groupLabels[id] || String(id)).join(' / ') || '暂无蛋组数据',
|
|
908
|
+
egg_groups: groupIds,
|
|
909
|
+
egg_group_labels: groupLabels,
|
|
910
|
+
male_rate: null,
|
|
911
|
+
female_rate: null,
|
|
912
|
+
hatch_label: '后端未提供',
|
|
913
|
+
weight_label: fmtRange(weightMin, weightMax, 'kg'),
|
|
914
|
+
height_label: fmtRange(heightMin, heightMax, 'm'),
|
|
915
|
+
total_compatible: num(compatibleByGroup.__all__?.total) ?? totalCompatible,
|
|
916
|
+
compatible_total_known: num(compatibleByGroup.__all__?.total) !== null,
|
|
917
|
+
is_undiscovered: groups.some(group => this.isUndiscoveredEggGroup(this.eggGroupId(group), this.eggGroupLabel(group))),
|
|
918
|
+
egg_group_sections: sections,
|
|
919
|
+
total_stats: '后端未提供',
|
|
920
|
+
egg_details: { has_data: false },
|
|
921
|
+
commandHint: '数据来自后端查蛋;接口不可用时自动回退 Wiki / 本地查蛋' + (num(compatibleByGroup.__all__?.total) === null ? '。总数不可用,当前数量仅为已加载预览' : ''),
|
|
922
|
+
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
formatEggApiCard(item) {
|
|
926
|
+
const petId = item?.id ?? item?.pet_id ?? '-';
|
|
927
|
+
const [heightMin, heightMax] = this.eggHeightRangeM(item);
|
|
928
|
+
const [weightMin, weightMax] = this.eggWeightRangeKg(item);
|
|
929
|
+
return {
|
|
930
|
+
id: petId,
|
|
931
|
+
name: this.eggPetDisplayName(item),
|
|
932
|
+
icon: item?.pet_icon_url || item?.icon_url || petIconUrl(petId),
|
|
933
|
+
image: item?.pet_img_url || item?.image_url || petImageUrl(petId),
|
|
934
|
+
type_label: this.eggApiTypeLabel(item),
|
|
935
|
+
egg_group_ids: [],
|
|
936
|
+
egg_groups_label: this.eggGroupsLabel(item),
|
|
937
|
+
height_min: heightMin,
|
|
938
|
+
height_max: heightMax,
|
|
939
|
+
height_label: fmtRange(heightMin, heightMax, 'm'),
|
|
940
|
+
weight_min: weightMin,
|
|
941
|
+
weight_max: weightMax,
|
|
942
|
+
weight_label: fmtRange(weightMin, weightMax, 'kg'),
|
|
943
|
+
probability: null,
|
|
944
|
+
match_count: null,
|
|
945
|
+
match_info_label: '后端命中',
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
formatEggApiMember(item) {
|
|
949
|
+
return {
|
|
950
|
+
name: this.eggPetDisplayName(item),
|
|
951
|
+
id: item?.id ?? item?.pet_id ?? '-',
|
|
952
|
+
type_label: this.eggApiTypeLabel(item),
|
|
953
|
+
egg_groups_label: this.eggGroupsLabel(item),
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
eggApiGroups(item) {
|
|
957
|
+
const groups = item?.egg_groups ?? item?.egg_group ?? [];
|
|
958
|
+
const list = Array.isArray(groups) ? groups : [groups];
|
|
959
|
+
return list
|
|
960
|
+
.filter(Boolean)
|
|
961
|
+
.map(group => (group && typeof group === 'object' ? group : { name: String(group), official_name: String(group) }));
|
|
962
|
+
}
|
|
963
|
+
eggGroupId(group) {
|
|
964
|
+
return num(group?.group_id ?? group?.id);
|
|
965
|
+
}
|
|
966
|
+
eggGroupLabel(group) {
|
|
967
|
+
return String(group?.official_name || group?.name || group?.display_name || '').trim();
|
|
968
|
+
}
|
|
969
|
+
eggGroupDesc(group) {
|
|
970
|
+
const label = this.eggGroupLabel(group);
|
|
971
|
+
const display = String(group?.display_name || '').trim();
|
|
972
|
+
return display && display !== label ? display : '';
|
|
973
|
+
}
|
|
974
|
+
isUndiscoveredEggGroup(id, label) {
|
|
975
|
+
const text = String(label || '');
|
|
976
|
+
return String(id) === '1' || text.includes('未发现') || text.includes('无法孵蛋');
|
|
977
|
+
}
|
|
978
|
+
eggPetDisplayName(item) {
|
|
979
|
+
const name = String(item?.name || item?.pet_name || '未知精灵').trim() || '未知精灵';
|
|
980
|
+
const form = String(item?.form || '').trim();
|
|
981
|
+
return form && !name.includes(form) ? `${name}(${form})` : name;
|
|
982
|
+
}
|
|
983
|
+
eggApiTypeLabel(item) {
|
|
984
|
+
const types = item?.unit_type;
|
|
985
|
+
if (Array.isArray(types) && types.length)
|
|
986
|
+
return types.filter(Boolean).join(' / ') || '未知';
|
|
987
|
+
return String(item?.type || item?.attribute_name || '未知');
|
|
988
|
+
}
|
|
989
|
+
eggGroupsLabel(item) {
|
|
990
|
+
const labels = this.eggApiGroups(item).map(group => this.eggGroupLabel(group)).filter(Boolean);
|
|
991
|
+
return labels.length ? labels.join(' / ') : '暂无蛋组数据';
|
|
992
|
+
}
|
|
993
|
+
eggHeightRangeM(item) {
|
|
994
|
+
const direct = this.rangePair(item?.height_range_m);
|
|
995
|
+
if (direct[0] != null || direct[1] != null)
|
|
996
|
+
return direct;
|
|
997
|
+
const cm = this.rangePair(item?.height_range_cm);
|
|
998
|
+
if (cm[0] != null || cm[1] != null)
|
|
999
|
+
return [cm[0] == null ? null : cm[0] / 100, cm[1] == null ? null : cm[1] / 100];
|
|
1000
|
+
return [null, null];
|
|
1001
|
+
}
|
|
1002
|
+
eggWeightRangeKg(item) {
|
|
1003
|
+
const direct = this.rangePair(item?.weight_range_kg);
|
|
1004
|
+
if (direct[0] != null || direct[1] != null)
|
|
1005
|
+
return direct;
|
|
1006
|
+
const g = this.rangePair(item?.weight_range_g);
|
|
1007
|
+
if (g[0] != null || g[1] != null)
|
|
1008
|
+
return [g[0] == null ? null : g[0] / 1000, g[1] == null ? null : g[1] / 1000];
|
|
1009
|
+
return [null, null];
|
|
1010
|
+
}
|
|
726
1011
|
buildSizeSearchData(height, weight, results, heightDisplay) {
|
|
727
1012
|
const conditions = [];
|
|
728
1013
|
if (height != null)
|
|
@@ -747,8 +1032,11 @@ class EggService {
|
|
|
747
1032
|
if (weight != null)
|
|
748
1033
|
conditions.push(`体重 ${weight} kg`);
|
|
749
1034
|
const groups = this.sizeApiResultGroups(results);
|
|
750
|
-
const [perfect, ranged] = this.mergeCardsByName(groups.exact.map((item) => this.formatSizeApiCard(item)), groups.candidates.map((item) => this.formatSizeApiCard(item)));
|
|
751
|
-
const
|
|
1035
|
+
const [perfect, ranged] = this.mergeCardsByName(groups.exact.map((item) => this.formatSizeApiCard(item, weight)), groups.candidates.map((item) => this.formatSizeApiCard(item, weight)));
|
|
1036
|
+
const poolName = results?.query?.pool && typeof results.query.pool === 'object'
|
|
1037
|
+
? results.query.pool.name
|
|
1038
|
+
: '';
|
|
1039
|
+
const searchMode = results?.searchMode || poolName || '';
|
|
752
1040
|
const queryLabel = `${conditions.join(' / ') || '尺寸反查'}${searchMode ? ` · 模式 ${searchMode}` : ''}`;
|
|
753
1041
|
return {
|
|
754
1042
|
query_label: queryLabel,
|
|
@@ -760,28 +1048,8 @@ class EggService {
|
|
|
760
1048
|
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
761
1049
|
};
|
|
762
1050
|
}
|
|
763
|
-
buildEggSearchData(heightMeters, weight, results, heightDisplay) {
|
|
764
|
-
const conditions = [];
|
|
765
|
-
if (heightMeters != null)
|
|
766
|
-
conditions.push(`身高 ${heightDisplay || `${formatNumber(heightMeters)} m`}`);
|
|
767
|
-
if (weight != null)
|
|
768
|
-
conditions.push(`体重 ${weight} kg`);
|
|
769
|
-
const cards = (Array.isArray(results?.items) ? results.items : []).map((item) => this.formatEggSearchCard(item));
|
|
770
|
-
const pageNo = num(results?.page_no);
|
|
771
|
-
const totalPages = num(results?.total_pages);
|
|
772
|
-
const pageLabel = pageNo && totalPages ? ` · 第 ${pageNo}/${totalPages} 页` : '';
|
|
773
|
-
return {
|
|
774
|
-
query_label: `${conditions.join(' / ') || '孵蛋反查'}${pageLabel}`,
|
|
775
|
-
perfect_matches: cards,
|
|
776
|
-
range_matches: [],
|
|
777
|
-
total_count: results?.total ?? cards.length,
|
|
778
|
-
has_results: cards.length > 0,
|
|
779
|
-
commandHint: '洛克查蛋 <精灵名> | 洛克查蛋 0.18m 1.5kg | 洛克配种 <父体> <母体>',
|
|
780
|
-
copyright: 'Koishi & WeGame 洛克王国插件',
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
1051
|
buildEggDetails(breeding) {
|
|
784
|
-
if (!breeding)
|
|
1052
|
+
if (!breeding || !Object.keys(breeding).length)
|
|
785
1053
|
return { has_data: false };
|
|
786
1054
|
const baseProb = breeding.egg_base_glass_prob_array;
|
|
787
1055
|
const addProb = breeding.egg_add_glass_prob_array;
|
|
Binary file
|
|
Binary file
|
package/lib/index.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export interface Config {
|
|
|
17
17
|
merchantCheckMode: 'interval' | 'times';
|
|
18
18
|
merchantCheckInterval: number;
|
|
19
19
|
merchantCheckTimes: string[];
|
|
20
|
+
merchantTimezone: string;
|
|
21
|
+
subscriptionGroupAdminEnabled: boolean;
|
|
22
|
+
subscriptionBotAdminEnabled: boolean;
|
|
23
|
+
subscriptionBotAdminAuthority: number;
|
|
20
24
|
homeSubscriptionEnabled: boolean;
|
|
21
25
|
homeSubscriptionIntervalMinutes: number;
|
|
22
26
|
announcementSubscriptionEnabled: boolean;
|
|
@@ -25,6 +29,7 @@ export interface Config {
|
|
|
25
29
|
homeQueryPollIntervalMs: number;
|
|
26
30
|
homeQueryTimeoutMs: number;
|
|
27
31
|
lowBandwidthMode: boolean;
|
|
32
|
+
renderTimeout: number;
|
|
28
33
|
atlasZipUrls: string[];
|
|
29
34
|
atlasGitUrl: string;
|
|
30
35
|
imageCompressionEnabled: boolean;
|