koishi-plugin-rocom 1.0.6 → 1.0.8
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/LICENSE +661 -0
- package/lib/client.d.ts +4 -0
- package/lib/client.js +56 -1
- package/lib/commands/account.d.ts +1 -1
- package/lib/commands/account.js +42 -7
- package/lib/commands/merchant.js +1 -1
- package/lib/commands/query.js +648 -14
- package/lib/render-templates/pet-panel/index.html +61 -0
- package/lib/render-templates/pet-panel/style.css +183 -0
- package/lib/render-templates/pet-panel-detail/index.html +123 -0
- package/lib/render-templates/pet-panel-detail/style.css +258 -0
- package/lib/render.d.ts +3 -3
- package/lib/render.js +33 -14
- package/package.json +1 -1
- package/readme.md +51 -519
package/lib/commands/query.js
CHANGED
|
@@ -297,6 +297,557 @@ function homePetIcon(petId, iconUrl = '') {
|
|
|
297
297
|
assetId += 3000;
|
|
298
298
|
return `https://game.gtimg.cn/images/rocom/rocodata/jingling/${assetId}/icon.png`;
|
|
299
299
|
}
|
|
300
|
+
function homePetImage(petId, imageUrl = '') {
|
|
301
|
+
if (imageUrl)
|
|
302
|
+
return imageUrl;
|
|
303
|
+
let assetId = Number(String(petId || '0'));
|
|
304
|
+
if (!Number.isFinite(assetId) || assetId <= 0)
|
|
305
|
+
return '';
|
|
306
|
+
if (assetId < 3000)
|
|
307
|
+
assetId += 3000;
|
|
308
|
+
return `https://game.gtimg.cn/images/rocom/rocodata/jingling/${assetId}/image.png`;
|
|
309
|
+
}
|
|
310
|
+
const PANEL_PET_STAT_DEFS = [
|
|
311
|
+
{ key: 'hp', label: 'HP', addiType: 1, aliases: ['hp'] },
|
|
312
|
+
{ key: 'attack', label: '物攻', addiType: 2, aliases: ['attack', 'atk'] },
|
|
313
|
+
{ key: 'special_attack', label: '魔攻', addiType: 3, aliases: ['special_attack', 'magic_attack', 'sp_attack'] },
|
|
314
|
+
{ key: 'defense', label: '物防', addiType: 4, aliases: ['defense', 'def'] },
|
|
315
|
+
{ key: 'special_defense', label: '魔防', addiType: 5, aliases: ['special_defense', 'magic_defense', 'sp_defense'] },
|
|
316
|
+
{ key: 'speed', label: '速度', addiType: 6, aliases: ['speed', 'spd'] },
|
|
317
|
+
];
|
|
318
|
+
const PANEL_PET_CACHE_PATH = node_path_1.default.join('data', 'rocom', 'rocom_pet_panel_cache.json');
|
|
319
|
+
const PANEL_EMPTY_FEATURE = { id: '', name: '暂无特性', desc: '当前接口未返回特性描述。' };
|
|
320
|
+
const PANEL_EMPTY_SKILL_HINT = '当前接口未返回技能详情。';
|
|
321
|
+
let panelPetCacheMem = null;
|
|
322
|
+
function panelPetCacheFilePath(deps) {
|
|
323
|
+
return node_path_1.default.join(deps.ctx.baseDir, PANEL_PET_CACHE_PATH);
|
|
324
|
+
}
|
|
325
|
+
function ensureBloodlineLabel(raw, mutationLabel) {
|
|
326
|
+
if (raw && raw !== '未知血脉')
|
|
327
|
+
return raw;
|
|
328
|
+
return mutationLabel === '普通' ? '普通' : `${mutationLabel}系`;
|
|
329
|
+
}
|
|
330
|
+
function loadPanelPetCache(deps) {
|
|
331
|
+
if (panelPetCacheMem)
|
|
332
|
+
return panelPetCacheMem;
|
|
333
|
+
const filePath = panelPetCacheFilePath(deps);
|
|
334
|
+
try {
|
|
335
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
336
|
+
panelPetCacheMem = {};
|
|
337
|
+
return panelPetCacheMem;
|
|
338
|
+
}
|
|
339
|
+
const text = node_fs_1.default.readFileSync(filePath, 'utf-8');
|
|
340
|
+
const parsed = JSON.parse(text);
|
|
341
|
+
panelPetCacheMem = parsed && typeof parsed === 'object' ? parsed : {};
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
logger.warn(`加载精灵面板缓存失败: ${err}`);
|
|
345
|
+
panelPetCacheMem = {};
|
|
346
|
+
}
|
|
347
|
+
return panelPetCacheMem;
|
|
348
|
+
}
|
|
349
|
+
function savePanelPetCache(deps, data) {
|
|
350
|
+
const filePath = panelPetCacheFilePath(deps);
|
|
351
|
+
const dir = node_path_1.default.dirname(filePath);
|
|
352
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
353
|
+
const tempPath = `${filePath}.tmp`;
|
|
354
|
+
node_fs_1.default.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
355
|
+
node_fs_1.default.renameSync(tempPath, filePath);
|
|
356
|
+
panelPetCacheMem = data;
|
|
357
|
+
}
|
|
358
|
+
function readPanelPetCache(deps, uid) {
|
|
359
|
+
const cache = loadPanelPetCache(deps);
|
|
360
|
+
return cache[String(uid)] || null;
|
|
361
|
+
}
|
|
362
|
+
function writePanelPetCache(deps, uid, pets) {
|
|
363
|
+
const cache = loadPanelPetCache(deps);
|
|
364
|
+
cache[String(uid)] = {
|
|
365
|
+
uid: String(uid),
|
|
366
|
+
pets,
|
|
367
|
+
updatedAt: Math.floor(Date.now() / 1000),
|
|
368
|
+
};
|
|
369
|
+
savePanelPetCache(deps, cache);
|
|
370
|
+
return cache[String(uid)];
|
|
371
|
+
}
|
|
372
|
+
function toNumber(value, fallback = 0) {
|
|
373
|
+
const num = Number(value);
|
|
374
|
+
return Number.isFinite(num) ? num : fallback;
|
|
375
|
+
}
|
|
376
|
+
function normalizeMutationLabel(mutationType) {
|
|
377
|
+
if (mutationType === 9)
|
|
378
|
+
return '了不起';
|
|
379
|
+
if (mutationType === 8)
|
|
380
|
+
return '炫彩';
|
|
381
|
+
if (mutationType === 1)
|
|
382
|
+
return '异色';
|
|
383
|
+
return '普通';
|
|
384
|
+
}
|
|
385
|
+
function normalizeGender(value) {
|
|
386
|
+
const text = String(value ?? '').trim();
|
|
387
|
+
if (!text)
|
|
388
|
+
return '未知';
|
|
389
|
+
if (['1', 'male', 'man', 'boy', 'm', '公'].includes(text.toLowerCase()))
|
|
390
|
+
return '公';
|
|
391
|
+
if (['2', 'female', 'woman', 'girl', 'f', '母'].includes(text.toLowerCase()))
|
|
392
|
+
return '母';
|
|
393
|
+
if (text === '0')
|
|
394
|
+
return '无';
|
|
395
|
+
return text;
|
|
396
|
+
}
|
|
397
|
+
function pickFirstNonEmpty(...values) {
|
|
398
|
+
for (const value of values) {
|
|
399
|
+
const text = String(value ?? '').trim();
|
|
400
|
+
if (text)
|
|
401
|
+
return text;
|
|
402
|
+
}
|
|
403
|
+
return '';
|
|
404
|
+
}
|
|
405
|
+
function uniqueStrings(items) {
|
|
406
|
+
const out = [];
|
|
407
|
+
const seen = new Set();
|
|
408
|
+
for (const item of items) {
|
|
409
|
+
const text = String(item || '').trim();
|
|
410
|
+
if (!text || seen.has(text))
|
|
411
|
+
continue;
|
|
412
|
+
seen.add(text);
|
|
413
|
+
out.push(text);
|
|
414
|
+
}
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
function normalizeSkillItem(raw) {
|
|
418
|
+
const id = pickFirstNonEmpty(raw?.id, raw?.skill_id, raw?.skillId);
|
|
419
|
+
return {
|
|
420
|
+
id,
|
|
421
|
+
name: pickFirstNonEmpty(raw?.name, raw?.skill_name, raw?.skillName) || (id ? `技能#${id}` : '未知技能'),
|
|
422
|
+
isEquipped: Boolean(raw?.is_equipped ?? raw?.equipped ?? raw?.isEquipped),
|
|
423
|
+
cost: pickFirstNonEmpty(raw?.cost, raw?.energy_cost, raw?.consume, raw?.spend) || '-',
|
|
424
|
+
power: pickFirstNonEmpty(raw?.power, raw?.damage, raw?.power_value, raw?.damage_value) || '-',
|
|
425
|
+
typeLabel: pickFirstNonEmpty(raw?.element, raw?.type_name, raw?.typeName, raw?.attribute_name, raw?.attributeName) || '未知',
|
|
426
|
+
iconUrl: pickFirstNonEmpty(raw?.skill_img_url, raw?.icon, raw?.icon_url, raw?.img_url, raw?.image_url),
|
|
427
|
+
description: pickFirstNonEmpty(raw?.desc, raw?.description, raw?.detail),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function parsePanelSkills(raw) {
|
|
431
|
+
const items = [];
|
|
432
|
+
const pushAll = (input) => {
|
|
433
|
+
if (!input)
|
|
434
|
+
return;
|
|
435
|
+
if (Array.isArray(input)) {
|
|
436
|
+
for (const item of input)
|
|
437
|
+
items.push(item);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (typeof input === 'object') {
|
|
441
|
+
items.push(input);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
pushAll(raw?.display_info?.skill?.skill_data);
|
|
445
|
+
pushAll(raw?.display_info?.skill_data);
|
|
446
|
+
pushAll(raw?.display_info?.skills);
|
|
447
|
+
pushAll(raw?.display_info?.skills_info);
|
|
448
|
+
pushAll(raw?.skill?.skill_data);
|
|
449
|
+
pushAll(raw?.skill_data);
|
|
450
|
+
pushAll(raw?.skills);
|
|
451
|
+
pushAll(raw?.skills_info);
|
|
452
|
+
const featureCandidates = [];
|
|
453
|
+
const skills = [];
|
|
454
|
+
const equipSkills = [];
|
|
455
|
+
const seenSkill = new Set();
|
|
456
|
+
const appendSkill = (skill) => {
|
|
457
|
+
const key = `${skill.name}#${skill.id}`;
|
|
458
|
+
if (seenSkill.has(key))
|
|
459
|
+
return;
|
|
460
|
+
seenSkill.add(key);
|
|
461
|
+
skills.push(skill);
|
|
462
|
+
if (skill.isEquipped)
|
|
463
|
+
equipSkills.push(skill);
|
|
464
|
+
};
|
|
465
|
+
for (const item of items) {
|
|
466
|
+
if (!item || typeof item !== 'object')
|
|
467
|
+
continue;
|
|
468
|
+
const type = toNumber(item?.type, 1);
|
|
469
|
+
if (type === 2) {
|
|
470
|
+
featureCandidates.push({
|
|
471
|
+
id: pickFirstNonEmpty(item?.id, item?.feature_id, item?.featureId),
|
|
472
|
+
name: pickFirstNonEmpty(item?.name, item?.feature_name, item?.featureName) || '未知特性',
|
|
473
|
+
desc: pickFirstNonEmpty(item?.desc, item?.description, item?.feature_desc, item?.featureDesc),
|
|
474
|
+
});
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
appendSkill(normalizeSkillItem(item));
|
|
478
|
+
}
|
|
479
|
+
if (!skills.length) {
|
|
480
|
+
const fallbackSkills = Array.isArray(raw?.display_info?.equip_skills) ? raw.display_info.equip_skills : [];
|
|
481
|
+
for (const skill of fallbackSkills) {
|
|
482
|
+
appendSkill({ ...normalizeSkillItem(skill), isEquipped: true });
|
|
483
|
+
}
|
|
484
|
+
const normalSkills = Array.isArray(raw?.display_info?.skills) ? raw.display_info.skills : [];
|
|
485
|
+
for (const skill of normalSkills)
|
|
486
|
+
appendSkill(normalizeSkillItem(skill));
|
|
487
|
+
}
|
|
488
|
+
const featureRaw = raw?.display_info?.feature || raw?.feature || raw?.feature_info || raw?.featureInfo;
|
|
489
|
+
if (featureRaw && typeof featureRaw === 'object') {
|
|
490
|
+
featureCandidates.unshift({
|
|
491
|
+
id: pickFirstNonEmpty(featureRaw?.id, featureRaw?.feature_id, featureRaw?.featureId),
|
|
492
|
+
name: pickFirstNonEmpty(featureRaw?.name, featureRaw?.feature_name, featureRaw?.featureName) || '未知特性',
|
|
493
|
+
desc: pickFirstNonEmpty(featureRaw?.desc, featureRaw?.description, featureRaw?.feature_desc, featureRaw?.featureDesc),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
const feature = featureCandidates.find(item => item.name && item.name !== '未知特性') || featureCandidates[0] || PANEL_EMPTY_FEATURE;
|
|
497
|
+
if (!equipSkills.length && skills.length) {
|
|
498
|
+
equipSkills.push(...skills.slice(0, Math.min(4, skills.length)).map(item => ({ ...item, isEquipped: true })));
|
|
499
|
+
}
|
|
500
|
+
return { feature, equipSkills, skills };
|
|
501
|
+
}
|
|
502
|
+
function extractAttributeValue(source, addiMap, def) {
|
|
503
|
+
let value = addiMap[def.addiType];
|
|
504
|
+
let talent = 0;
|
|
505
|
+
let effortAdd = 0;
|
|
506
|
+
for (const key of def.aliases) {
|
|
507
|
+
const attr = source?.[key];
|
|
508
|
+
if (!attr || typeof attr !== 'object')
|
|
509
|
+
continue;
|
|
510
|
+
if (!value)
|
|
511
|
+
value = toNumber(attr.value, value);
|
|
512
|
+
talent = toNumber(attr.talent, talent);
|
|
513
|
+
effortAdd = toNumber(attr.effort_add, effortAdd);
|
|
514
|
+
}
|
|
515
|
+
return { value, talent, effortAdd };
|
|
516
|
+
}
|
|
517
|
+
function parsePanelAttributes(raw) {
|
|
518
|
+
const display = raw?.display_info || {};
|
|
519
|
+
const attrSource = display?.attribute_info || raw?.attribute_info || {};
|
|
520
|
+
const addiData = Array.isArray(display?.attribute_new_info?.addi_attr_data)
|
|
521
|
+
? display.attribute_new_info.addi_attr_data
|
|
522
|
+
: Array.isArray(raw?.attribute_new_info?.addi_attr_data)
|
|
523
|
+
? raw.attribute_new_info.addi_attr_data
|
|
524
|
+
: [];
|
|
525
|
+
const addiMap = {};
|
|
526
|
+
for (const item of addiData) {
|
|
527
|
+
const type = toNumber(item?.type);
|
|
528
|
+
if (!type)
|
|
529
|
+
continue;
|
|
530
|
+
addiMap[type] = toNumber(item?.addi_attr);
|
|
531
|
+
}
|
|
532
|
+
return PANEL_PET_STAT_DEFS.map(def => {
|
|
533
|
+
const { value, talent, effortAdd } = extractAttributeValue(attrSource, addiMap, def);
|
|
534
|
+
return {
|
|
535
|
+
key: def.key,
|
|
536
|
+
label: def.label,
|
|
537
|
+
value,
|
|
538
|
+
talent,
|
|
539
|
+
effortAdd,
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
function parsePanelPetItem(raw, index) {
|
|
544
|
+
if (!raw || typeof raw !== 'object')
|
|
545
|
+
return null;
|
|
546
|
+
const homePet = raw?.home_pet_info && typeof raw.home_pet_info === 'object' ? raw.home_pet_info : raw;
|
|
547
|
+
const display = raw?.display_info && typeof raw.display_info === 'object' ? raw.display_info : {};
|
|
548
|
+
const petId = pickFirstNonEmpty(display?.base_conf_id, homePet?.pet_cfg_id, homePet?.pet_id, raw?.pet_cfg_id, raw?.pet_id, raw?.pet_base_id);
|
|
549
|
+
if (!petId || petId === '0')
|
|
550
|
+
return null;
|
|
551
|
+
const gid = pickFirstNonEmpty(homePet?.pet_gid, raw?.pet_gid, raw?.gid, raw?.id, index + 1);
|
|
552
|
+
const name = pickFirstNonEmpty(display?.name, homePet?.name, homePet?.pet_name, raw?.name, raw?.pet_name) || `精灵 ${petId}`;
|
|
553
|
+
const level = toNumber(display?.level ?? raw?.level ?? homePet?.level, 1);
|
|
554
|
+
const mutationType = toNumber(display?.mutation_type ?? raw?.mutation_type ?? homePet?.mutation_type, 0);
|
|
555
|
+
const bloodId = pickFirstNonEmpty(display?.blood_id, raw?.blood_id, homePet?.blood_id, homePet?.bloodline_id);
|
|
556
|
+
const petTypesInfo = Array.isArray(display?.pet_types_info)
|
|
557
|
+
? display.pet_types_info
|
|
558
|
+
: Array.isArray(raw?.pet_types_info)
|
|
559
|
+
? raw.pet_types_info
|
|
560
|
+
: [];
|
|
561
|
+
const elementLabels = uniqueStrings(petTypesInfo.map((item) => pickFirstNonEmpty(item?.name, item?.type_name, item?.label)));
|
|
562
|
+
const elementIcons = uniqueStrings(petTypesInfo.map((item) => pickFirstNonEmpty(item?.icon, item?.icon_url, item?.img_url)));
|
|
563
|
+
const bloodlineLabelRaw = pickFirstNonEmpty(display?.bloodline_info?.name, display?.bloodline_info?.title, raw?.bloodline_info?.name, raw?.bloodline_info?.title, bloodId ? `血脉 ${bloodId}` : '') || '未知血脉';
|
|
564
|
+
const { feature, equipSkills, skills } = parsePanelSkills(raw);
|
|
565
|
+
const attributes = parsePanelAttributes(raw);
|
|
566
|
+
const iconUrl = homePetIcon(petId, pickFirstNonEmpty(display?.pet_icon_url, raw?.pet_icon_url, raw?.icon_url, raw?.pet_img_url));
|
|
567
|
+
const imageUrl = homePetImage(petId, pickFirstNonEmpty(display?.pet_img_url, raw?.pet_img_url, raw?.image_url, raw?.icon_url));
|
|
568
|
+
const mutationLabel = normalizeMutationLabel(mutationType);
|
|
569
|
+
return {
|
|
570
|
+
gid,
|
|
571
|
+
petId,
|
|
572
|
+
name,
|
|
573
|
+
level,
|
|
574
|
+
gender: normalizeGender(display?.gender ?? raw?.gender ?? homePet?.gender),
|
|
575
|
+
energy: toNumber(display?.energy ?? raw?.energy ?? homePet?.energy, 0),
|
|
576
|
+
mutationType,
|
|
577
|
+
mutationLabel,
|
|
578
|
+
bloodId,
|
|
579
|
+
bloodlineLabel: ensureBloodlineLabel(bloodlineLabelRaw, mutationLabel),
|
|
580
|
+
elementLabels,
|
|
581
|
+
elementIcons,
|
|
582
|
+
iconUrl,
|
|
583
|
+
imageUrl,
|
|
584
|
+
feature,
|
|
585
|
+
equipSkills,
|
|
586
|
+
skills,
|
|
587
|
+
attributes,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function normalizeMutationBySubset(subset) {
|
|
591
|
+
if (subset === 1)
|
|
592
|
+
return 9;
|
|
593
|
+
if (subset === 2)
|
|
594
|
+
return 1;
|
|
595
|
+
if (subset === 3)
|
|
596
|
+
return 8;
|
|
597
|
+
return 0;
|
|
598
|
+
}
|
|
599
|
+
function parseMutationTypeFromPet(pet, subset) {
|
|
600
|
+
const direct = toNumber(pet?.pet_mutation ?? pet?.mutation_type ?? pet?.mutation ?? pet?.petMutation, NaN);
|
|
601
|
+
if (Number.isFinite(direct) && direct >= 0)
|
|
602
|
+
return direct;
|
|
603
|
+
return normalizeMutationBySubset(subset);
|
|
604
|
+
}
|
|
605
|
+
function panelPetItemFromBagPet(raw, subset, index) {
|
|
606
|
+
if (!raw || typeof raw !== 'object')
|
|
607
|
+
return null;
|
|
608
|
+
const petId = pickFirstNonEmpty(raw?.pet_base_id, raw?.pet_id, raw?.id);
|
|
609
|
+
if (!petId || petId === '0')
|
|
610
|
+
return null;
|
|
611
|
+
const mutationType = parseMutationTypeFromPet(raw, subset);
|
|
612
|
+
const mutationLabel = normalizeMutationLabel(mutationType);
|
|
613
|
+
const petNameRaw = pickFirstNonEmpty(raw?.pet_name, raw?.name) || `精灵 ${petId}`;
|
|
614
|
+
const [baseName] = petNameRaw.split('&');
|
|
615
|
+
const level = toNumber(raw?.pet_level ?? raw?.level, 1);
|
|
616
|
+
const types = Array.isArray(raw?.pet_types_info) ? raw.pet_types_info : [];
|
|
617
|
+
const elementLabels = uniqueStrings(types.map((item) => pickFirstNonEmpty(item?.name, item?.type_name)));
|
|
618
|
+
const elementIcons = uniqueStrings(types.map((item) => pickFirstNonEmpty(item?.icon, item?.icon_url)));
|
|
619
|
+
const iconFromImage = String(raw?.pet_img_url || '').replace('/image.png', '/icon.png');
|
|
620
|
+
const iconUrl = homePetIcon(petId, iconFromImage);
|
|
621
|
+
const imageUrl = homePetImage(petId, pickFirstNonEmpty(raw?.pet_img_url, raw?.image_url));
|
|
622
|
+
const gid = pickFirstNonEmpty(raw?.pet_gid, raw?.gid, raw?.id, `${subset}-${index + 1}`);
|
|
623
|
+
return {
|
|
624
|
+
gid,
|
|
625
|
+
petId,
|
|
626
|
+
name: baseName || petNameRaw,
|
|
627
|
+
level,
|
|
628
|
+
gender: '未知',
|
|
629
|
+
energy: 0,
|
|
630
|
+
mutationType,
|
|
631
|
+
mutationLabel,
|
|
632
|
+
bloodId: '',
|
|
633
|
+
bloodlineLabel: ensureBloodlineLabel('', mutationLabel),
|
|
634
|
+
elementLabels,
|
|
635
|
+
elementIcons,
|
|
636
|
+
iconUrl,
|
|
637
|
+
imageUrl,
|
|
638
|
+
feature: PANEL_EMPTY_FEATURE,
|
|
639
|
+
equipSkills: [],
|
|
640
|
+
skills: [],
|
|
641
|
+
attributes: PANEL_PET_STAT_DEFS.map((def) => ({
|
|
642
|
+
key: def.key,
|
|
643
|
+
label: def.label,
|
|
644
|
+
value: 0,
|
|
645
|
+
talent: 0,
|
|
646
|
+
effortAdd: 0,
|
|
647
|
+
})),
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function panelRefreshErrorHint(client) {
|
|
651
|
+
const detail = client.getLastErrorBrief('请稍后重试');
|
|
652
|
+
if (/api key|access token|anonymous token|认证凭证|未授权|401|403/i.test(String(detail))) {
|
|
653
|
+
return '刷新失败:当前环境缺少 API 凭证,且本账号未登录,无法拉取面板数据。请先执行 洛克.QQ登录 或 洛克.微信登录。';
|
|
654
|
+
}
|
|
655
|
+
return `刷新失败:${detail}`;
|
|
656
|
+
}
|
|
657
|
+
async function fetchPanelPetsFromBag(deps, userId) {
|
|
658
|
+
const fwToken = await (0, account_1.getPrimaryToken)(deps, userId);
|
|
659
|
+
if (!fwToken)
|
|
660
|
+
return null;
|
|
661
|
+
const userIdentifier = userId;
|
|
662
|
+
const listTasks = [];
|
|
663
|
+
for (const subset of [0, 1, 2, 3]) {
|
|
664
|
+
listTasks.push(deps.client.getPets(deps.ctx, fwToken, subset, 1, 200, userIdentifier));
|
|
665
|
+
}
|
|
666
|
+
const listRes = await Promise.all(listTasks);
|
|
667
|
+
const pets = [];
|
|
668
|
+
const dedup = new Set();
|
|
669
|
+
listRes.forEach((res, idx) => {
|
|
670
|
+
const subset = idx;
|
|
671
|
+
const rows = Array.isArray(res?.pets) ? res.pets : [];
|
|
672
|
+
rows.forEach((raw, rowIndex) => {
|
|
673
|
+
const pet = panelPetItemFromBagPet(raw, subset, rowIndex);
|
|
674
|
+
if (!pet)
|
|
675
|
+
return;
|
|
676
|
+
const key = `${pet.petId}#${pet.name}#${pet.mutationType}`;
|
|
677
|
+
if (dedup.has(key))
|
|
678
|
+
return;
|
|
679
|
+
dedup.add(key);
|
|
680
|
+
pets.push(pet);
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
return pets;
|
|
684
|
+
}
|
|
685
|
+
function extractPanelPetSources(homeInfo) {
|
|
686
|
+
const cell = homeCellInfo(homeInfo);
|
|
687
|
+
const sources = [];
|
|
688
|
+
const pushAll = (value) => {
|
|
689
|
+
if (!Array.isArray(value))
|
|
690
|
+
return;
|
|
691
|
+
for (const item of value)
|
|
692
|
+
sources.push(item);
|
|
693
|
+
};
|
|
694
|
+
pushAll(homeInfo?.home_pets);
|
|
695
|
+
pushAll(cell?.home_pets);
|
|
696
|
+
pushAll(cell?.home_pet_info?.home_pet_list);
|
|
697
|
+
pushAll(homeInfo?.friend_cell_home_brief_info?.home_pets);
|
|
698
|
+
return sources;
|
|
699
|
+
}
|
|
700
|
+
function buildPanelPetList(homeInfo) {
|
|
701
|
+
const sources = extractPanelPetSources(homeInfo);
|
|
702
|
+
const pets = [];
|
|
703
|
+
const dedup = new Set();
|
|
704
|
+
for (let i = 0; i < sources.length; i++) {
|
|
705
|
+
const pet = parsePanelPetItem(sources[i], i);
|
|
706
|
+
if (!pet)
|
|
707
|
+
continue;
|
|
708
|
+
const key = `${pet.gid}#${pet.petId}`;
|
|
709
|
+
if (dedup.has(key))
|
|
710
|
+
continue;
|
|
711
|
+
dedup.add(key);
|
|
712
|
+
pets.push(pet);
|
|
713
|
+
}
|
|
714
|
+
return pets;
|
|
715
|
+
}
|
|
716
|
+
function panelUidFromBinding(deps, session, uid = '') {
|
|
717
|
+
const inputUid = String(uid || '').trim();
|
|
718
|
+
if (inputUid)
|
|
719
|
+
return inputUid;
|
|
720
|
+
return String(deps.userMgr.getPrimaryBinding(session?.userId || '')?.role_id || '');
|
|
721
|
+
}
|
|
722
|
+
function parsePanelPetQuery(rawQuery) {
|
|
723
|
+
const query = String(rawQuery || '').trim();
|
|
724
|
+
const gid = (query.match(/\d+/)?.[0] || '').trim();
|
|
725
|
+
const name = query.replace(/\d+/g, '').trim();
|
|
726
|
+
return { query, gid, name };
|
|
727
|
+
}
|
|
728
|
+
function findPanelPets(pets, queryRaw) {
|
|
729
|
+
const parsed = parsePanelPetQuery(queryRaw);
|
|
730
|
+
if (!parsed.query)
|
|
731
|
+
return [];
|
|
732
|
+
let candidates = pets;
|
|
733
|
+
if (parsed.gid) {
|
|
734
|
+
candidates = candidates.filter(pet => String(pet.gid) === parsed.gid);
|
|
735
|
+
}
|
|
736
|
+
if (parsed.name) {
|
|
737
|
+
const exact = candidates.filter(pet => pet.name === parsed.name);
|
|
738
|
+
if (exact.length)
|
|
739
|
+
return exact;
|
|
740
|
+
const normalized = parsed.name.toLowerCase();
|
|
741
|
+
candidates = candidates.filter((pet) => String(pet.name || '').toLowerCase().includes(normalized));
|
|
742
|
+
}
|
|
743
|
+
if (!parsed.gid && !parsed.name) {
|
|
744
|
+
const normalized = parsed.query.toLowerCase();
|
|
745
|
+
candidates = candidates.filter((pet) => String(pet.gid).includes(parsed.query)
|
|
746
|
+
|| String(pet.petId).includes(parsed.query)
|
|
747
|
+
|| String(pet.name || '').toLowerCase().includes(normalized));
|
|
748
|
+
}
|
|
749
|
+
return candidates;
|
|
750
|
+
}
|
|
751
|
+
function panelPetListFallback(uid, pets, updatedAtText) {
|
|
752
|
+
const lines = [
|
|
753
|
+
`【UID ${uid} 精灵面板已刷新】`,
|
|
754
|
+
`刷新时间:${updatedAtText}`,
|
|
755
|
+
`共 ${pets.length} 只精灵`,
|
|
756
|
+
];
|
|
757
|
+
for (const pet of pets.slice(0, 40)) {
|
|
758
|
+
const tags = [...pet.elementLabels, pet.bloodlineLabel].filter(Boolean).join(' / ');
|
|
759
|
+
lines.push(`- ${pet.name} (GID:${pet.gid}) Lv.${pet.level} ${pet.mutationLabel}${tags ? ` | ${tags}` : ''}`);
|
|
760
|
+
}
|
|
761
|
+
if (pets.length > 40)
|
|
762
|
+
lines.push(`... 其余 ${pets.length - 40} 只请使用图片面板查看`);
|
|
763
|
+
lines.push('查询详情:洛克.精灵面板 <名称或GID> [UID]');
|
|
764
|
+
return lines.join('\n');
|
|
765
|
+
}
|
|
766
|
+
function panelPetDetailFallback(uid, pet, cacheUpdatedAtText) {
|
|
767
|
+
const lines = [
|
|
768
|
+
`【精灵面板】${pet.name} (GID:${pet.gid})`,
|
|
769
|
+
`UID: ${uid} | 刷新时间: ${cacheUpdatedAtText}`,
|
|
770
|
+
`等级: Lv.${pet.level} | 稀有: ${pet.mutationLabel} | 血脉: ${pet.bloodlineLabel}`,
|
|
771
|
+
`属性: ${pet.elementLabels.join(' / ') || '未知'}`,
|
|
772
|
+
];
|
|
773
|
+
if (pet.feature?.name) {
|
|
774
|
+
lines.push(`特性: ${pet.feature.name}`);
|
|
775
|
+
if (pet.feature.desc)
|
|
776
|
+
lines.push(`说明: ${pet.feature.desc}`);
|
|
777
|
+
}
|
|
778
|
+
if (pet.equipSkills.length) {
|
|
779
|
+
lines.push('已装备技能:');
|
|
780
|
+
for (const skill of pet.equipSkills.slice(0, 6)) {
|
|
781
|
+
lines.push(`- ${skill.name} [${skill.typeLabel}] 能耗:${skill.cost} 威力:${skill.power}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return lines.join('\n');
|
|
785
|
+
}
|
|
786
|
+
function panelPetListData(uid, pets, updatedAtText) {
|
|
787
|
+
return {
|
|
788
|
+
title: '精灵面板',
|
|
789
|
+
subtitle: `UID ${uid} · 共 ${pets.length} 只精灵`,
|
|
790
|
+
updatedAtText,
|
|
791
|
+
cards: pets.map((pet) => ({
|
|
792
|
+
gid: pet.gid,
|
|
793
|
+
petId: pet.petId,
|
|
794
|
+
name: pet.name,
|
|
795
|
+
level: pet.level,
|
|
796
|
+
mutationLabel: pet.mutationLabel,
|
|
797
|
+
bloodlineLabel: pet.bloodlineLabel,
|
|
798
|
+
iconUrl: pet.iconUrl,
|
|
799
|
+
elementLabels: pet.elementLabels,
|
|
800
|
+
elementIcons: pet.elementIcons,
|
|
801
|
+
})),
|
|
802
|
+
commandHint: '洛克.精灵面板 <名称或GID> [UID] | 洛克.刷新面板 [UID]',
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function panelPetDetailData(uid, pet, updatedAtText) {
|
|
806
|
+
return {
|
|
807
|
+
title: '精灵详情面板',
|
|
808
|
+
subtitle: `UID ${uid} · GID ${pet.gid}`,
|
|
809
|
+
updatedAtText,
|
|
810
|
+
pet,
|
|
811
|
+
hasEquipSkills: pet.equipSkills.length > 0,
|
|
812
|
+
hasSkills: pet.skills.length > 0,
|
|
813
|
+
emptySkillHint: PANEL_EMPTY_SKILL_HINT,
|
|
814
|
+
commandHint: '再次刷新:洛克.刷新面板 [UID] | 查询其他精灵:洛克.精灵面板 <名称或GID> [UID]',
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
async function refreshPanelPets(deps, uid, userId = '') {
|
|
818
|
+
const res = await deps.client.ingameHomeInfo(deps.ctx, uid, 20000);
|
|
819
|
+
if (res) {
|
|
820
|
+
const homeInfo = homeInfoPayload(res);
|
|
821
|
+
const pets = buildPanelPetList(homeInfo);
|
|
822
|
+
return writePanelPetCache(deps, uid, pets);
|
|
823
|
+
}
|
|
824
|
+
// 无 ingame 凭证时,自动降级到已登录账号的背包接口
|
|
825
|
+
const fallbackUserId = String(userId || '').trim();
|
|
826
|
+
const fallbackPets = fallbackUserId ? await fetchPanelPetsFromBag(deps, fallbackUserId) : null;
|
|
827
|
+
if (fallbackPets && fallbackPets.length) {
|
|
828
|
+
return writePanelPetCache(deps, uid, fallbackPets);
|
|
829
|
+
}
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
async function resolvePanelPets(deps, uid, userId = '', forceRefresh = false) {
|
|
833
|
+
if (forceRefresh) {
|
|
834
|
+
const refreshed = await refreshPanelPets(deps, uid, userId);
|
|
835
|
+
if (refreshed)
|
|
836
|
+
return { record: refreshed, refreshed: true };
|
|
837
|
+
return { record: null, refreshed: true };
|
|
838
|
+
}
|
|
839
|
+
const cache = readPanelPetCache(deps, uid);
|
|
840
|
+
if (cache?.pets?.length)
|
|
841
|
+
return { record: cache, refreshed: false };
|
|
842
|
+
const refreshed = await refreshPanelPets(deps, uid, userId);
|
|
843
|
+
return { record: refreshed, refreshed: true };
|
|
844
|
+
}
|
|
845
|
+
function formatPanelUpdatedAt(ts) {
|
|
846
|
+
const value = Number(ts);
|
|
847
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
848
|
+
return new Date().toLocaleString('zh-CN');
|
|
849
|
+
return new Date(value * 1000).toLocaleString('zh-CN');
|
|
850
|
+
}
|
|
300
851
|
function extractHomePet(raw, index, guard = false) {
|
|
301
852
|
if (!raw || typeof raw !== 'object')
|
|
302
853
|
return null;
|
|
@@ -783,7 +1334,6 @@ function register(deps) {
|
|
|
783
1334
|
if (!fwToken)
|
|
784
1335
|
return (0, account_1.notLoggedInHint)();
|
|
785
1336
|
const userIdentifier = session.userId;
|
|
786
|
-
await session.send('正在获取洛克王国数据...');
|
|
787
1337
|
const [roleRes, evalRes, sumRes, collRes, boRes, blRes] = await Promise.all([
|
|
788
1338
|
client.getRole(ctx, fwToken, undefined, userIdentifier),
|
|
789
1339
|
client.getEvaluation(ctx, fwToken, userIdentifier),
|
|
@@ -793,7 +1343,7 @@ function register(deps) {
|
|
|
793
1343
|
client.getBattleList(ctx, fwToken, 1, '', userIdentifier),
|
|
794
1344
|
]);
|
|
795
1345
|
if (!roleRes?.role)
|
|
796
|
-
return '
|
|
1346
|
+
return `获取角色档案失败:${client.getLastErrorBrief('请重新登录后重试')}`;
|
|
797
1347
|
const role = roleRes.role;
|
|
798
1348
|
const ev = evalRes || {};
|
|
799
1349
|
const sm = sumRes || {};
|
|
@@ -965,7 +1515,7 @@ function register(deps) {
|
|
|
965
1515
|
client.getBattleList(ctx, fwToken, 4, '', userIdentifier),
|
|
966
1516
|
]);
|
|
967
1517
|
if (!roleRes?.role)
|
|
968
|
-
return '
|
|
1518
|
+
return `获取战绩数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
969
1519
|
const role = roleRes.role;
|
|
970
1520
|
const bo = boRes || {};
|
|
971
1521
|
const battles = (blRes?.battles || []).map((battle) => {
|
|
@@ -1025,8 +1575,10 @@ function register(deps) {
|
|
|
1025
1575
|
client.getRole(ctx, fwToken, undefined, userIdentifier),
|
|
1026
1576
|
client.getPets(ctx, fwToken, petSubset, pageNo, 10, userIdentifier),
|
|
1027
1577
|
]);
|
|
1028
|
-
if (!roleRes?.role
|
|
1029
|
-
return '
|
|
1578
|
+
if (!roleRes?.role)
|
|
1579
|
+
return `获取背包数据失败:${client.getLastErrorBrief('请重新登录后重试')}`;
|
|
1580
|
+
if (!petRes?.pets)
|
|
1581
|
+
return `获取背包数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1030
1582
|
const role = roleRes.role;
|
|
1031
1583
|
const data = {
|
|
1032
1584
|
userName: role.name || '洛克',
|
|
@@ -1079,7 +1631,7 @@ function register(deps) {
|
|
|
1079
1631
|
}
|
|
1080
1632
|
const res = await client.getLineupList(ctx, fwToken, pageNo, category, userIdentifier);
|
|
1081
1633
|
if (!res?.lineups)
|
|
1082
|
-
return '
|
|
1634
|
+
return `获取阵容数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1083
1635
|
const data = {
|
|
1084
1636
|
category: category || '热门推荐',
|
|
1085
1637
|
lineups: (res.lineups || []).map((lineup) => ({
|
|
@@ -1087,6 +1639,7 @@ function register(deps) {
|
|
|
1087
1639
|
tags: lineup.tags || [],
|
|
1088
1640
|
likes: lineup.likes || 0,
|
|
1089
1641
|
author_name: lineup.author_name || '?',
|
|
1642
|
+
author_avatar: lineup.author_avatar || '',
|
|
1090
1643
|
lineup_code: String(lineup.id || ''),
|
|
1091
1644
|
pets: (lineup.lineup?.pets || []).map((pet) => ({
|
|
1092
1645
|
pet_name: pet.pet_name || '',
|
|
@@ -1112,7 +1665,7 @@ function register(deps) {
|
|
|
1112
1665
|
const userIdentifier = session.userId;
|
|
1113
1666
|
const firstPageRes = await client.getLineupList(ctx, fwToken, 1, '', userIdentifier);
|
|
1114
1667
|
if (!firstPageRes?.lineups)
|
|
1115
|
-
return '
|
|
1668
|
+
return `获取阵容数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1116
1669
|
let targetLineup = (firstPageRes.lineups || []).find((lineup) => isTargetLineup(lineup, normalizedLineupId));
|
|
1117
1670
|
if (!targetLineup) {
|
|
1118
1671
|
const totalPages = Math.max(1, Number(firstPageRes.total_pages) || 1);
|
|
@@ -1159,7 +1712,7 @@ function register(deps) {
|
|
|
1159
1712
|
const userIdentifier = session.userId;
|
|
1160
1713
|
const res = await client.getExchangePosters(ctx, fwToken, page, userIdentifier);
|
|
1161
1714
|
if (!res?.posters)
|
|
1162
|
-
return '
|
|
1715
|
+
return `获取交换大厅数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1163
1716
|
const data = {
|
|
1164
1717
|
filterLabel: '全部',
|
|
1165
1718
|
posts: (res.posters || []).map((poster) => {
|
|
@@ -1198,7 +1751,7 @@ function register(deps) {
|
|
|
1198
1751
|
return '请提供玩家 UID。用法:洛克.玩家 <UID>';
|
|
1199
1752
|
const res = await client.ingamePlayerSearch(ctx, uid);
|
|
1200
1753
|
if (!res)
|
|
1201
|
-
return `玩家搜索失败:${client.
|
|
1754
|
+
return `玩家搜索失败:${client.getLastErrorBrief()}`;
|
|
1202
1755
|
await sendImage(deps, session, 'player-search', buildPlayerSearchRenderData(res, uid), `【洛克玩家】UID ${uid}`);
|
|
1203
1756
|
});
|
|
1204
1757
|
ctx.command('洛克').subcommand('.家园 [uid:string]', '通过 UID 查询家园菜园、守卫和室内精灵')
|
|
@@ -1213,9 +1766,90 @@ function register(deps) {
|
|
|
1213
1766
|
return '请提供玩家 UID,或先完成绑定后使用 洛克.家园。';
|
|
1214
1767
|
const res = await client.ingameHomeInfo(ctx, targetUid);
|
|
1215
1768
|
if (!res)
|
|
1216
|
-
return `家园查询失败:${client.
|
|
1769
|
+
return `家园查询失败:${client.getLastErrorBrief()}`;
|
|
1217
1770
|
await sendImage(deps, session, 'home', buildHomeRenderData(deps, res, targetUid), `【洛克家园】UID ${targetUid}`);
|
|
1218
1771
|
});
|
|
1772
|
+
ctx.command('洛克').subcommand('.刷新面板 [uid:string]', '刷新指定 UID 的精灵面板缓存')
|
|
1773
|
+
.alias('洛克刷新面板')
|
|
1774
|
+
.alias('刷新面板')
|
|
1775
|
+
.alias('刷新精灵')
|
|
1776
|
+
.alias('强制刷新')
|
|
1777
|
+
.action(async ({ session }, uid = '') => {
|
|
1778
|
+
const targetUid = panelUidFromBinding(deps, session, uid);
|
|
1779
|
+
if (!targetUid)
|
|
1780
|
+
return '请提供 UID,或先绑定账号后再使用 洛克.刷新面板。';
|
|
1781
|
+
if (!/^\d+$/.test(targetUid))
|
|
1782
|
+
return '请输入正确的 UID 格式。';
|
|
1783
|
+
const record = await refreshPanelPets(deps, targetUid, session?.userId || '');
|
|
1784
|
+
if (!record)
|
|
1785
|
+
return panelRefreshErrorHint(client);
|
|
1786
|
+
const updatedAtText = formatPanelUpdatedAt(record.updatedAt);
|
|
1787
|
+
const data = panelPetListData(targetUid, record.pets, updatedAtText);
|
|
1788
|
+
const fallback = panelPetListFallback(targetUid, record.pets, updatedAtText);
|
|
1789
|
+
await sendImage(deps, session, 'pet-panel', data, fallback);
|
|
1790
|
+
});
|
|
1791
|
+
ctx.command('洛克').subcommand('.精灵面板 [query:text] [uid:string]', '查询精灵面板详情(按名称或GID),不填参数则显示精灵列表')
|
|
1792
|
+
.alias('洛克精灵面板')
|
|
1793
|
+
.alias('精灵面板')
|
|
1794
|
+
.alias('面板')
|
|
1795
|
+
.alias('查询精灵面板')
|
|
1796
|
+
.alias('查看面板')
|
|
1797
|
+
.action(async ({ session }, query, uid = '') => {
|
|
1798
|
+
const queryText = String(query || '').trim();
|
|
1799
|
+
const targetUid = panelUidFromBinding(deps, session, uid);
|
|
1800
|
+
if (!targetUid)
|
|
1801
|
+
return '请提供 UID,或先绑定账号后再使用 洛克.精灵面板。';
|
|
1802
|
+
if (!/^\d+$/.test(targetUid))
|
|
1803
|
+
return '请输入正确的 UID 格式。';
|
|
1804
|
+
const { record } = await resolvePanelPets(deps, targetUid, session?.userId || '', false);
|
|
1805
|
+
if (!record)
|
|
1806
|
+
return panelRefreshErrorHint(client);
|
|
1807
|
+
if (!record.pets.length)
|
|
1808
|
+
return `UID ${targetUid} 当前没有可用精灵数据,请先执行 洛克.刷新面板。`;
|
|
1809
|
+
if (!queryText) {
|
|
1810
|
+
const updatedAtText = formatPanelUpdatedAt(record.updatedAt);
|
|
1811
|
+
const data = panelPetListData(targetUid, record.pets, updatedAtText);
|
|
1812
|
+
const fallback = panelPetListFallback(targetUid, record.pets, updatedAtText);
|
|
1813
|
+
await sendImage(deps, session, 'pet-panel', data, fallback);
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
const matched = findPanelPets(record.pets, queryText);
|
|
1817
|
+
if (!matched.length) {
|
|
1818
|
+
return [
|
|
1819
|
+
`未在 UID ${targetUid} 的面板缓存中找到「${queryText}」。`,
|
|
1820
|
+
'可先执行:洛克.刷新面板 [UID]',
|
|
1821
|
+
'或尝试更精确名称 / GID。',
|
|
1822
|
+
].join('\n');
|
|
1823
|
+
}
|
|
1824
|
+
let selected = null;
|
|
1825
|
+
if (matched.length === 1) {
|
|
1826
|
+
selected = matched[0];
|
|
1827
|
+
}
|
|
1828
|
+
else {
|
|
1829
|
+
const optionLines = matched.slice(0, 20).map((pet, index) => `${index + 1}. ${pet.name} (GID:${pet.gid}) Lv.${pet.level} ${pet.mutationLabel}`);
|
|
1830
|
+
await session?.send?.([
|
|
1831
|
+
`检测到 ${matched.length} 个候选,请在 60 秒内回复序号选择:`,
|
|
1832
|
+
...optionLines,
|
|
1833
|
+
'超时将默认选择第 1 个。',
|
|
1834
|
+
].join('\n'));
|
|
1835
|
+
try {
|
|
1836
|
+
const input = await session?.prompt?.(60 * 1000);
|
|
1837
|
+
const choice = Number(String(input || '').trim());
|
|
1838
|
+
if (Number.isFinite(choice) && choice >= 1 && choice <= Math.min(matched.length, 20)) {
|
|
1839
|
+
selected = matched[choice - 1];
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
catch {
|
|
1843
|
+
// Ignore prompt failures and fall back to default choice.
|
|
1844
|
+
}
|
|
1845
|
+
if (!selected)
|
|
1846
|
+
selected = matched[0];
|
|
1847
|
+
}
|
|
1848
|
+
const updatedAtText = formatPanelUpdatedAt(record.updatedAt);
|
|
1849
|
+
const data = panelPetDetailData(targetUid, selected, updatedAtText);
|
|
1850
|
+
const fallback = panelPetDetailFallback(targetUid, selected, updatedAtText);
|
|
1851
|
+
await sendImage(deps, session, 'pet-panel-detail', data, fallback);
|
|
1852
|
+
});
|
|
1219
1853
|
ctx.command('洛克').subcommand('.商店 <shopId:string>', '通过 ingame 接口查询商店信息')
|
|
1220
1854
|
.alias('洛克商店')
|
|
1221
1855
|
.action(async ({ session }, shopId) => {
|
|
@@ -1223,7 +1857,7 @@ function register(deps) {
|
|
|
1223
1857
|
return '请提供商店 ID。用法:洛克.商店 <shop_id>';
|
|
1224
1858
|
const res = await client.ingameMerchantInfo(ctx, shopId);
|
|
1225
1859
|
if (!res)
|
|
1226
|
-
return `商店查询失败:${client.
|
|
1860
|
+
return `商店查询失败:${client.getLastErrorBrief()}`;
|
|
1227
1861
|
await sendImage(deps, session, 'ingame-shop', buildShopRenderData(res, shopId), `【洛克商店】shop_id=${shopId}`);
|
|
1228
1862
|
});
|
|
1229
1863
|
ctx.command('洛克').subcommand('.好友关系 <userIds:string>', '查询好友关系')
|
|
@@ -1236,7 +1870,7 @@ function register(deps) {
|
|
|
1236
1870
|
return (0, account_1.notLoggedInHint)();
|
|
1237
1871
|
const res = await client.getFriendship(ctx, fwToken, userIds, session.userId);
|
|
1238
1872
|
if (!res)
|
|
1239
|
-
return `好友关系查询失败:${client.
|
|
1873
|
+
return `好友关系查询失败:${client.getLastErrorBrief()}`;
|
|
1240
1874
|
await sendImage(deps, session, 'friendship', buildFriendshipRenderData(res, userIds), `【好友关系】${userIds}`);
|
|
1241
1875
|
});
|
|
1242
1876
|
ctx.command('洛克').subcommand('.学生 [area:number] [accountType:number]', '查询学生认证状态与学生活动福利')
|
|
@@ -1251,9 +1885,9 @@ function register(deps) {
|
|
|
1251
1885
|
client.getStudentPerks(ctx, fwToken, area, accountType, userIdentifier),
|
|
1252
1886
|
]);
|
|
1253
1887
|
if (!stateRes)
|
|
1254
|
-
return `学生认证状态查询失败:${client.
|
|
1888
|
+
return `学生认证状态查询失败:${client.getLastErrorBrief()}`;
|
|
1255
1889
|
if (!perksRes)
|
|
1256
|
-
return `学生活动福利查询失败:${client.
|
|
1890
|
+
return `学生活动福利查询失败:${client.getLastErrorBrief()}`;
|
|
1257
1891
|
await sendImage(deps, session, 'student', buildStudentRenderData(stateRes, perksRes, area, accountType), '【洛克学生】认证与福利信息');
|
|
1258
1892
|
});
|
|
1259
1893
|
ctx.command('订阅家园菜园 [uid:string]', '订阅指定 UID 的家园菜园成熟提醒')
|