koishi-plugin-rocom 1.0.6 → 1.0.7
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/client.d.ts +4 -0
- package/lib/client.js +56 -1
- package/lib/commands/merchant.js +1 -1
- package/lib/commands/query.js +649 -13
- 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 +50 -518
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;
|
|
@@ -793,7 +1344,7 @@ function register(deps) {
|
|
|
793
1344
|
client.getBattleList(ctx, fwToken, 1, '', userIdentifier),
|
|
794
1345
|
]);
|
|
795
1346
|
if (!roleRes?.role)
|
|
796
|
-
return '
|
|
1347
|
+
return `获取角色档案失败:${client.getLastErrorBrief('请重新登录后重试')}`;
|
|
797
1348
|
const role = roleRes.role;
|
|
798
1349
|
const ev = evalRes || {};
|
|
799
1350
|
const sm = sumRes || {};
|
|
@@ -965,7 +1516,7 @@ function register(deps) {
|
|
|
965
1516
|
client.getBattleList(ctx, fwToken, 4, '', userIdentifier),
|
|
966
1517
|
]);
|
|
967
1518
|
if (!roleRes?.role)
|
|
968
|
-
return '
|
|
1519
|
+
return `获取战绩数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
969
1520
|
const role = roleRes.role;
|
|
970
1521
|
const bo = boRes || {};
|
|
971
1522
|
const battles = (blRes?.battles || []).map((battle) => {
|
|
@@ -1025,8 +1576,10 @@ function register(deps) {
|
|
|
1025
1576
|
client.getRole(ctx, fwToken, undefined, userIdentifier),
|
|
1026
1577
|
client.getPets(ctx, fwToken, petSubset, pageNo, 10, userIdentifier),
|
|
1027
1578
|
]);
|
|
1028
|
-
if (!roleRes?.role
|
|
1029
|
-
return '
|
|
1579
|
+
if (!roleRes?.role)
|
|
1580
|
+
return `获取背包数据失败:${client.getLastErrorBrief('请重新登录后重试')}`;
|
|
1581
|
+
if (!petRes?.pets)
|
|
1582
|
+
return `获取背包数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1030
1583
|
const role = roleRes.role;
|
|
1031
1584
|
const data = {
|
|
1032
1585
|
userName: role.name || '洛克',
|
|
@@ -1079,7 +1632,7 @@ function register(deps) {
|
|
|
1079
1632
|
}
|
|
1080
1633
|
const res = await client.getLineupList(ctx, fwToken, pageNo, category, userIdentifier);
|
|
1081
1634
|
if (!res?.lineups)
|
|
1082
|
-
return '
|
|
1635
|
+
return `获取阵容数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1083
1636
|
const data = {
|
|
1084
1637
|
category: category || '热门推荐',
|
|
1085
1638
|
lineups: (res.lineups || []).map((lineup) => ({
|
|
@@ -1087,6 +1640,7 @@ function register(deps) {
|
|
|
1087
1640
|
tags: lineup.tags || [],
|
|
1088
1641
|
likes: lineup.likes || 0,
|
|
1089
1642
|
author_name: lineup.author_name || '?',
|
|
1643
|
+
author_avatar: lineup.author_avatar || '',
|
|
1090
1644
|
lineup_code: String(lineup.id || ''),
|
|
1091
1645
|
pets: (lineup.lineup?.pets || []).map((pet) => ({
|
|
1092
1646
|
pet_name: pet.pet_name || '',
|
|
@@ -1112,7 +1666,7 @@ function register(deps) {
|
|
|
1112
1666
|
const userIdentifier = session.userId;
|
|
1113
1667
|
const firstPageRes = await client.getLineupList(ctx, fwToken, 1, '', userIdentifier);
|
|
1114
1668
|
if (!firstPageRes?.lineups)
|
|
1115
|
-
return '
|
|
1669
|
+
return `获取阵容数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1116
1670
|
let targetLineup = (firstPageRes.lineups || []).find((lineup) => isTargetLineup(lineup, normalizedLineupId));
|
|
1117
1671
|
if (!targetLineup) {
|
|
1118
1672
|
const totalPages = Math.max(1, Number(firstPageRes.total_pages) || 1);
|
|
@@ -1159,7 +1713,7 @@ function register(deps) {
|
|
|
1159
1713
|
const userIdentifier = session.userId;
|
|
1160
1714
|
const res = await client.getExchangePosters(ctx, fwToken, page, userIdentifier);
|
|
1161
1715
|
if (!res?.posters)
|
|
1162
|
-
return '
|
|
1716
|
+
return `获取交换大厅数据失败:${client.getLastErrorBrief('请稍后重试')}`;
|
|
1163
1717
|
const data = {
|
|
1164
1718
|
filterLabel: '全部',
|
|
1165
1719
|
posts: (res.posters || []).map((poster) => {
|
|
@@ -1198,7 +1752,7 @@ function register(deps) {
|
|
|
1198
1752
|
return '请提供玩家 UID。用法:洛克.玩家 <UID>';
|
|
1199
1753
|
const res = await client.ingamePlayerSearch(ctx, uid);
|
|
1200
1754
|
if (!res)
|
|
1201
|
-
return `玩家搜索失败:${client.
|
|
1755
|
+
return `玩家搜索失败:${client.getLastErrorBrief()}`;
|
|
1202
1756
|
await sendImage(deps, session, 'player-search', buildPlayerSearchRenderData(res, uid), `【洛克玩家】UID ${uid}`);
|
|
1203
1757
|
});
|
|
1204
1758
|
ctx.command('洛克').subcommand('.家园 [uid:string]', '通过 UID 查询家园菜园、守卫和室内精灵')
|
|
@@ -1213,9 +1767,91 @@ function register(deps) {
|
|
|
1213
1767
|
return '请提供玩家 UID,或先完成绑定后使用 洛克.家园。';
|
|
1214
1768
|
const res = await client.ingameHomeInfo(ctx, targetUid);
|
|
1215
1769
|
if (!res)
|
|
1216
|
-
return `家园查询失败:${client.
|
|
1770
|
+
return `家园查询失败:${client.getLastErrorBrief()}`;
|
|
1217
1771
|
await sendImage(deps, session, 'home', buildHomeRenderData(deps, res, targetUid), `【洛克家园】UID ${targetUid}`);
|
|
1218
1772
|
});
|
|
1773
|
+
ctx.command('洛克').subcommand('.刷新面板 [uid:string]', '刷新指定 UID 的精灵面板缓存')
|
|
1774
|
+
.alias('洛克刷新面板')
|
|
1775
|
+
.alias('刷新面板')
|
|
1776
|
+
.alias('刷新精灵')
|
|
1777
|
+
.alias('强制刷新')
|
|
1778
|
+
.action(async ({ session }, uid = '') => {
|
|
1779
|
+
const targetUid = panelUidFromBinding(deps, session, uid);
|
|
1780
|
+
if (!targetUid)
|
|
1781
|
+
return '请提供 UID,或先绑定账号后再使用 洛克.刷新面板。';
|
|
1782
|
+
if (!/^\d+$/.test(targetUid))
|
|
1783
|
+
return '请输入正确的 UID 格式。';
|
|
1784
|
+
await session?.send?.(`正在刷新 UID ${targetUid} 的精灵面板,请稍候...`);
|
|
1785
|
+
const record = await refreshPanelPets(deps, targetUid, session?.userId || '');
|
|
1786
|
+
if (!record)
|
|
1787
|
+
return panelRefreshErrorHint(client);
|
|
1788
|
+
const updatedAtText = formatPanelUpdatedAt(record.updatedAt);
|
|
1789
|
+
const data = panelPetListData(targetUid, record.pets, updatedAtText);
|
|
1790
|
+
const fallback = panelPetListFallback(targetUid, record.pets, updatedAtText);
|
|
1791
|
+
await sendImage(deps, session, 'pet-panel', data, fallback);
|
|
1792
|
+
});
|
|
1793
|
+
ctx.command('洛克').subcommand('.精灵面板 [query:text] [uid:string]', '查询精灵面板详情(按名称或GID),不填参数则显示精灵列表')
|
|
1794
|
+
.alias('洛克精灵面板')
|
|
1795
|
+
.alias('精灵面板')
|
|
1796
|
+
.alias('面板')
|
|
1797
|
+
.alias('查询精灵面板')
|
|
1798
|
+
.alias('查看面板')
|
|
1799
|
+
.action(async ({ session }, query, uid = '') => {
|
|
1800
|
+
const queryText = String(query || '').trim();
|
|
1801
|
+
const targetUid = panelUidFromBinding(deps, session, uid);
|
|
1802
|
+
if (!targetUid)
|
|
1803
|
+
return '请提供 UID,或先绑定账号后再使用 洛克.精灵面板。';
|
|
1804
|
+
if (!/^\d+$/.test(targetUid))
|
|
1805
|
+
return '请输入正确的 UID 格式。';
|
|
1806
|
+
const { record } = await resolvePanelPets(deps, targetUid, session?.userId || '', false);
|
|
1807
|
+
if (!record)
|
|
1808
|
+
return panelRefreshErrorHint(client);
|
|
1809
|
+
if (!record.pets.length)
|
|
1810
|
+
return `UID ${targetUid} 当前没有可用精灵数据,请先执行 洛克.刷新面板。`;
|
|
1811
|
+
if (!queryText) {
|
|
1812
|
+
const updatedAtText = formatPanelUpdatedAt(record.updatedAt);
|
|
1813
|
+
const data = panelPetListData(targetUid, record.pets, updatedAtText);
|
|
1814
|
+
const fallback = panelPetListFallback(targetUid, record.pets, updatedAtText);
|
|
1815
|
+
await sendImage(deps, session, 'pet-panel', data, fallback);
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
const matched = findPanelPets(record.pets, queryText);
|
|
1819
|
+
if (!matched.length) {
|
|
1820
|
+
return [
|
|
1821
|
+
`未在 UID ${targetUid} 的面板缓存中找到「${queryText}」。`,
|
|
1822
|
+
'可先执行:洛克.刷新面板 [UID]',
|
|
1823
|
+
'或尝试更精确名称 / GID。',
|
|
1824
|
+
].join('\n');
|
|
1825
|
+
}
|
|
1826
|
+
let selected = null;
|
|
1827
|
+
if (matched.length === 1) {
|
|
1828
|
+
selected = matched[0];
|
|
1829
|
+
}
|
|
1830
|
+
else {
|
|
1831
|
+
const optionLines = matched.slice(0, 20).map((pet, index) => `${index + 1}. ${pet.name} (GID:${pet.gid}) Lv.${pet.level} ${pet.mutationLabel}`);
|
|
1832
|
+
await session?.send?.([
|
|
1833
|
+
`检测到 ${matched.length} 个候选,请在 60 秒内回复序号选择:`,
|
|
1834
|
+
...optionLines,
|
|
1835
|
+
'超时将默认选择第 1 个。',
|
|
1836
|
+
].join('\n'));
|
|
1837
|
+
try {
|
|
1838
|
+
const input = await session?.prompt?.(60 * 1000);
|
|
1839
|
+
const choice = Number(String(input || '').trim());
|
|
1840
|
+
if (Number.isFinite(choice) && choice >= 1 && choice <= Math.min(matched.length, 20)) {
|
|
1841
|
+
selected = matched[choice - 1];
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
catch {
|
|
1845
|
+
// Ignore prompt failures and fall back to default choice.
|
|
1846
|
+
}
|
|
1847
|
+
if (!selected)
|
|
1848
|
+
selected = matched[0];
|
|
1849
|
+
}
|
|
1850
|
+
const updatedAtText = formatPanelUpdatedAt(record.updatedAt);
|
|
1851
|
+
const data = panelPetDetailData(targetUid, selected, updatedAtText);
|
|
1852
|
+
const fallback = panelPetDetailFallback(targetUid, selected, updatedAtText);
|
|
1853
|
+
await sendImage(deps, session, 'pet-panel-detail', data, fallback);
|
|
1854
|
+
});
|
|
1219
1855
|
ctx.command('洛克').subcommand('.商店 <shopId:string>', '通过 ingame 接口查询商店信息')
|
|
1220
1856
|
.alias('洛克商店')
|
|
1221
1857
|
.action(async ({ session }, shopId) => {
|
|
@@ -1223,7 +1859,7 @@ function register(deps) {
|
|
|
1223
1859
|
return '请提供商店 ID。用法:洛克.商店 <shop_id>';
|
|
1224
1860
|
const res = await client.ingameMerchantInfo(ctx, shopId);
|
|
1225
1861
|
if (!res)
|
|
1226
|
-
return `商店查询失败:${client.
|
|
1862
|
+
return `商店查询失败:${client.getLastErrorBrief()}`;
|
|
1227
1863
|
await sendImage(deps, session, 'ingame-shop', buildShopRenderData(res, shopId), `【洛克商店】shop_id=${shopId}`);
|
|
1228
1864
|
});
|
|
1229
1865
|
ctx.command('洛克').subcommand('.好友关系 <userIds:string>', '查询好友关系')
|
|
@@ -1236,7 +1872,7 @@ function register(deps) {
|
|
|
1236
1872
|
return (0, account_1.notLoggedInHint)();
|
|
1237
1873
|
const res = await client.getFriendship(ctx, fwToken, userIds, session.userId);
|
|
1238
1874
|
if (!res)
|
|
1239
|
-
return `好友关系查询失败:${client.
|
|
1875
|
+
return `好友关系查询失败:${client.getLastErrorBrief()}`;
|
|
1240
1876
|
await sendImage(deps, session, 'friendship', buildFriendshipRenderData(res, userIds), `【好友关系】${userIds}`);
|
|
1241
1877
|
});
|
|
1242
1878
|
ctx.command('洛克').subcommand('.学生 [area:number] [accountType:number]', '查询学生认证状态与学生活动福利')
|
|
@@ -1251,9 +1887,9 @@ function register(deps) {
|
|
|
1251
1887
|
client.getStudentPerks(ctx, fwToken, area, accountType, userIdentifier),
|
|
1252
1888
|
]);
|
|
1253
1889
|
if (!stateRes)
|
|
1254
|
-
return `学生认证状态查询失败:${client.
|
|
1890
|
+
return `学生认证状态查询失败:${client.getLastErrorBrief()}`;
|
|
1255
1891
|
if (!perksRes)
|
|
1256
|
-
return `学生活动福利查询失败:${client.
|
|
1892
|
+
return `学生活动福利查询失败:${client.getLastErrorBrief()}`;
|
|
1257
1893
|
await sendImage(deps, session, 'student', buildStudentRenderData(stateRes, perksRes, area, accountType), '【洛克学生】认证与福利信息');
|
|
1258
1894
|
});
|
|
1259
1895
|
ctx.command('订阅家园菜园 [uid:string]', '订阅指定 UID 的家园菜园成熟提醒')
|