mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-15 12:47:48 +00:00
298 lines
9.1 KiB
JavaScript
298 lines
9.1 KiB
JavaScript
import { ZZZPlugin } from '../lib/plugin.js';
|
||
import { parsePlayerInfo, refreshPanelFromEnka } from '../model/Enka/enkaApi.js';
|
||
import { getCk } from '../lib/common.js';
|
||
import {
|
||
mergePanel,
|
||
getPanelList,
|
||
refreshPanel as refreshPanelFunction,
|
||
getPanelOrigin,
|
||
updatePanelData,
|
||
formatPanelData,
|
||
getPanelListOrigin,
|
||
} from '../lib/avatar.js';
|
||
import settings from '../lib/settings.js';
|
||
import _ from 'lodash';
|
||
import { rulePrefix } from '../lib/common.js';
|
||
|
||
export class Panel extends ZZZPlugin {
|
||
constructor() {
|
||
super({
|
||
name: '[ZZZ-Plugin]Panel',
|
||
dsc: 'zzzpanel',
|
||
event: 'message',
|
||
priority: _.get(settings.getConfig('priority'), 'panel', 70),
|
||
rule: [
|
||
{
|
||
reg: `${rulePrefix}(.*)面板(展柜)?(刷新|更新|列表)?$`,
|
||
fnc: 'handleRule',
|
||
},
|
||
{
|
||
reg: `${rulePrefix}练度(统计)?$`,
|
||
fnc: 'proficiency',
|
||
},
|
||
{
|
||
reg: `${rulePrefix}原图$`,
|
||
fnc: 'getCharOriImage',
|
||
},
|
||
],
|
||
handler: [
|
||
{ key: 'zzz.tool.panel', fn: 'getCharPanelTool' },
|
||
{ key: 'zzz.tool.panelList', fn: 'getCharPanelListTool' },
|
||
],
|
||
});
|
||
}
|
||
async handleRule() {
|
||
if (!this.e.msg) return;
|
||
const reg = new RegExp(`${rulePrefix}(.*?)(?:展柜)?面板(?:展柜)?(刷新|更新|列表)?$`);
|
||
const pre = this.e.msg.match(reg)[4]?.trim();
|
||
const suf = this.e.msg.match(reg)[5]?.trim();
|
||
if (['刷新', '更新'].includes(pre) || ['刷新', '更新'].includes(suf))
|
||
return await this.refreshPanel();
|
||
if (!pre || suf === '列表') return await this.getCharPanelList();
|
||
const queryPanelReg = new RegExp(`${rulePrefix}(.*)面板$`);
|
||
if (queryPanelReg.test(this.e.msg)) return await this.getCharPanel();
|
||
return false;
|
||
}
|
||
|
||
async refreshPanel() {
|
||
const uid = await this.getUID();
|
||
const lastQueryTime = await redis.get(`ZZZ:PANEL:${uid}:LASTTIME`);
|
||
const panelSettings = settings.getConfig('panel');
|
||
const coldTime = _.get(panelSettings, 'interval', 300);
|
||
if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) {
|
||
return this.reply(`${coldTime}秒内只能更新一次,请稍后再试`);
|
||
}
|
||
const isEnka = this.e.msg.includes('展柜') || !(await getCk(this.e))
|
||
let result
|
||
if (isEnka) {
|
||
const data = await refreshPanelFromEnka(uid)
|
||
.catch(err => err)
|
||
if (data instanceof Error) {
|
||
logger.warn(`Enka服务调用失败:`, data)
|
||
return this.reply(`Enka服务调用失败:${data.message}`)
|
||
}
|
||
if (typeof data === 'object') {
|
||
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now())
|
||
const { playerInfo, panelList } = data
|
||
if (!panelList.length) {
|
||
return this.reply('面板列表为空,请确保已于游戏中展示角色')
|
||
}
|
||
result = await mergePanel(uid, panelList)
|
||
await this.getPlayerInfo(playerInfo)
|
||
} else if (typeof data === 'number') {
|
||
return this.reply(`Enka服务调用失败,状态码:${data}`)
|
||
}
|
||
} else {
|
||
const oriReply = this.reply.bind(this);
|
||
let errorMsg = '';
|
||
this.reply = (msg) => errorMsg += '\n' + msg;
|
||
try {
|
||
const { api, deviceFp } = await this.getAPI();
|
||
await oriReply('正在更新面板列表,请稍候...');
|
||
await this.getPlayerInfo();
|
||
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
|
||
result = await refreshPanelFunction(api, deviceFp);
|
||
} catch (err) {
|
||
logger.error('面板列表更新失败:', err);
|
||
errorMsg = (err.message || '') + errorMsg;
|
||
}
|
||
this.reply = oriReply;
|
||
if (errorMsg && !result) {
|
||
return this.reply(`面板列表更新失败,请稍后再试或尝试%更新展柜面板:\n${errorMsg.trim()}`);
|
||
}
|
||
}
|
||
if (!result) return false;
|
||
const newChar = result.filter(item => item.isNew);
|
||
const finalData = {
|
||
newChar: newChar.length,
|
||
list: result,
|
||
};
|
||
await this.render('panel/refresh.html', finalData);
|
||
}
|
||
|
||
async getCharPanelList() {
|
||
const uid = await this.getUID();
|
||
const result = getPanelList(uid);
|
||
if (!result.length) {
|
||
return this.reply(`UID:${uid}无本地面板数据,请先%更新面板 或 %更新展柜面板`);
|
||
}
|
||
const hasCk = !!(await getCk(this.e));
|
||
await this.getPlayerInfo(hasCk ? undefined : parsePlayerInfo({ uid }));
|
||
const timer = setTimeout(() => {
|
||
if (this?.reply) {
|
||
this.reply('查询成功,正在下载图片资源,请稍候。');
|
||
}
|
||
}, 5000);
|
||
for (const item of result) {
|
||
await item.get_basic_assets();
|
||
}
|
||
clearTimeout(timer);
|
||
const finalData = {
|
||
count: result?.length || 0,
|
||
list: result,
|
||
};
|
||
await this.render('panel/list.html', finalData);
|
||
}
|
||
|
||
async getCharPanelListTool(uid, origin = false) {
|
||
if (!uid) {
|
||
return false;
|
||
}
|
||
if (origin) {
|
||
const result = getPanelListOrigin(uid);
|
||
return result;
|
||
}
|
||
const result = getPanelList(uid);
|
||
return result;
|
||
}
|
||
|
||
async getCharPanel() {
|
||
const uid = await this.getUID();
|
||
const reg = new RegExp(`${rulePrefix}(.+)面板$`);
|
||
const match = this.e.msg.match(reg);
|
||
if (!match) return false;
|
||
const name = match[4];
|
||
const data = getPanelOrigin(uid, name);
|
||
if (data === false) {
|
||
return this.reply(`角色${name}不存在,请确保角色名称/别称存在`);
|
||
} else if (data === null) {
|
||
return this.reply(`暂无角色${name}面板数据,请先%更新面板`);
|
||
}
|
||
let handler = this.e.runtime.handler || {};
|
||
|
||
if (handler.has('zzz.tool.panel')) {
|
||
await handler.call('zzz.tool.panel', this.e, {
|
||
uid,
|
||
data: data,
|
||
needSave: false,
|
||
});
|
||
}
|
||
}
|
||
|
||
async getCharPanelTool(e, _data = {}) {
|
||
if (e) this.e = e;
|
||
if (e?.reply) this.reply = e.reply;
|
||
|
||
const {
|
||
uid = undefined,
|
||
data = undefined,
|
||
needSave = true,
|
||
reply = true,
|
||
needImg = true
|
||
} = _data;
|
||
if (!uid) {
|
||
return this.reply('UID为空');
|
||
}
|
||
if (!data) {
|
||
return this.reply('数据为空');
|
||
}
|
||
if (needSave) {
|
||
updatePanelData(uid, [data]);
|
||
}
|
||
const timer = setTimeout(() => {
|
||
const msg = '查询成功,正在下载图片资源,请稍候。'
|
||
if (this?.reply && needImg) {
|
||
this.reply(msg);
|
||
} else {
|
||
logger.mark(msg)
|
||
}
|
||
}, 5000);
|
||
const parsedData = formatPanelData(data);
|
||
await parsedData.get_detail_assets();
|
||
clearTimeout(timer);
|
||
const finalData = {
|
||
uid,
|
||
charData: parsedData,
|
||
};
|
||
const image = needImg ? await this.render('panel/card.html', finalData, {
|
||
retType: 'base64',
|
||
}) : needImg;
|
||
|
||
if (reply) {
|
||
const res = await this.reply(image);
|
||
if (res?.message_id && parsedData.role_icon)
|
||
await redis.set(
|
||
`ZZZ:PANEL:IMAGE:${res.message_id}`,
|
||
parsedData.role_icon,
|
||
{
|
||
EX: 3600 * 3,
|
||
}
|
||
);
|
||
return {
|
||
message: res,
|
||
image,
|
||
};
|
||
}
|
||
|
||
return image;
|
||
}
|
||
async proficiency() {
|
||
const uid = await this.getUID();
|
||
const result = getPanelList(uid);
|
||
if (!result) {
|
||
return this.reply('未找到面板数据,请先%更新面板 或 %更新展柜面板');
|
||
}
|
||
await this.getPlayerInfo();
|
||
result.sort((a, b) => {
|
||
return b.proficiency_score - a.proficiency_score;
|
||
});
|
||
const WeaponCount = result.filter(item => item?.weapon).length,
|
||
SWeaponCount = result.filter(
|
||
item => item?.weapon && item.weapon.rarity === 'S'
|
||
).length;
|
||
const general = {
|
||
total: result.length,
|
||
SCount: result.filter(item => item.rarity === 'S').length,
|
||
SWeaponRate: (SWeaponCount / WeaponCount) * 100,
|
||
SSSCount: result.reduce((acc, item) => {
|
||
if (item.equip) {
|
||
acc += item.equip.filter(
|
||
equip => ['SSS', 'ACE', 'MAX'].includes(equip.comment)
|
||
).length;
|
||
}
|
||
return acc;
|
||
}, 0),
|
||
highRank: result.filter(item => item.rank > 4).length,
|
||
};
|
||
const timer = setTimeout(() => {
|
||
if (this?.reply) {
|
||
this.reply('查询成功,正在下载图片资源,请稍候。');
|
||
}
|
||
}, 5000);
|
||
for (const item of result) {
|
||
await item.get_small_basic_assets();
|
||
}
|
||
clearTimeout(timer);
|
||
const finalData = {
|
||
general,
|
||
list: result,
|
||
};
|
||
await this.render('proficiency/index.html', finalData);
|
||
}
|
||
async getCharOriImage() {
|
||
let source;
|
||
if (this.e.getReply) {
|
||
source = await this.e.getReply();
|
||
} else if (this.e.source) {
|
||
if (this.e.group?.getChatHistory) {
|
||
// 支持at图片添加,以及支持后发送
|
||
source = (
|
||
await this.e.group.getChatHistory(this.e.source?.seq, 1)
|
||
).pop();
|
||
} else if (this.e.friend?.getChatHistory) {
|
||
source = (
|
||
await this.e.friend.getChatHistory(this.e.source?.time + 1, 1)
|
||
).pop();
|
||
}
|
||
}
|
||
const id = source?.message_id;
|
||
if (!id) {
|
||
return this.reply('未找到消息源,请引用要查看的图片');
|
||
}
|
||
const image = await redis.get(`ZZZ:PANEL:IMAGE:${id}`);
|
||
if (!image) {
|
||
return this.reply('未找到原图');
|
||
}
|
||
await this.reply(segment.image(image));
|
||
}
|
||
}
|