2022-04-14 20:53:22 +00:00
|
|
|
|
import fs from "fs";
|
|
|
|
|
import fetch from "node-fetch";
|
|
|
|
|
import lodash from "lodash";
|
2022-04-15 14:33:51 +00:00
|
|
|
|
import Format from "./Format.js";
|
2022-04-15 22:24:25 +00:00
|
|
|
|
import Character from "./models/Character.js";
|
2022-04-17 11:03:44 +00:00
|
|
|
|
import Reliquaries from "./models/Reliquaries.js";
|
2022-04-14 20:53:22 +00:00
|
|
|
|
|
|
|
|
|
const _path = process.cwd();
|
2022-04-16 09:40:30 +00:00
|
|
|
|
const cfgPath = `${_path}/plugins/miao-plugin/config.js`;
|
|
|
|
|
let config = {};
|
|
|
|
|
//try {
|
|
|
|
|
if (fs.existsSync(cfgPath)) {
|
|
|
|
|
let fileData = await import (`file://${cfgPath}`);
|
|
|
|
|
if (fileData && fileData.config) {
|
|
|
|
|
config = fileData.config;
|
2022-04-14 20:53:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-16 09:40:30 +00:00
|
|
|
|
//} catch (e) {
|
|
|
|
|
// do nth
|
|
|
|
|
//}
|
2022-04-14 20:53:22 +00:00
|
|
|
|
|
|
|
|
|
const userPath = `${_path}/data/UserData/`;
|
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(userPath)) {
|
|
|
|
|
fs.mkdirSync(userPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const artifactMap = {
|
|
|
|
|
'生命值': {
|
|
|
|
|
title: "小生命"
|
|
|
|
|
},
|
|
|
|
|
'生命值_百分比': {
|
|
|
|
|
title: "大生命",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
'暴击率': {
|
|
|
|
|
title: "暴击率",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
'暴击伤害': {
|
2022-04-15 22:05:31 +00:00
|
|
|
|
title: "暴击伤害",
|
2022-04-14 20:53:22 +00:00
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
'防御力': {
|
|
|
|
|
title: "小防御"
|
|
|
|
|
},
|
|
|
|
|
'防御力_百分比': {
|
|
|
|
|
title: "大防御",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
'攻击力': {
|
|
|
|
|
title: "小攻击"
|
|
|
|
|
},
|
|
|
|
|
'攻击力_百分比': {
|
|
|
|
|
title: "大攻击",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
'元素精通': {
|
2022-04-15 22:05:31 +00:00
|
|
|
|
title: "元素精通"
|
2022-04-14 20:53:22 +00:00
|
|
|
|
},
|
|
|
|
|
'元素充能效率': {
|
2022-04-15 22:05:31 +00:00
|
|
|
|
title: "充能效率",
|
2022-04-14 20:53:22 +00:00
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
'治疗加成': {
|
2022-04-15 22:05:31 +00:00
|
|
|
|
title: "治疗加成",
|
2022-04-14 20:53:22 +00:00
|
|
|
|
pct: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-17 11:03:44 +00:00
|
|
|
|
|
2022-04-23 21:16:37 +00:00
|
|
|
|
let posIdx = {
|
|
|
|
|
"生之花": {
|
|
|
|
|
idx: 1
|
|
|
|
|
},
|
|
|
|
|
"死之羽": {
|
|
|
|
|
idx: 2
|
|
|
|
|
},
|
|
|
|
|
"时之沙": {
|
|
|
|
|
idx: 3
|
|
|
|
|
},
|
|
|
|
|
"空之杯": {
|
|
|
|
|
idx: 4
|
|
|
|
|
},
|
|
|
|
|
"理之冠": {
|
|
|
|
|
idx: 5
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-04-14 20:53:22 +00:00
|
|
|
|
let Data = {
|
|
|
|
|
getData(uid, data) {
|
|
|
|
|
let ret = {
|
|
|
|
|
uid,
|
|
|
|
|
chars: {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
lodash.forEach({
|
|
|
|
|
name: "角色名称",
|
|
|
|
|
avatar: "头像ID",
|
|
|
|
|
lv: "冒险等阶"
|
|
|
|
|
}, (title, key) => {
|
|
|
|
|
ret[key] = data[title] || "";
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
lodash.forEach(data.items, (ds) => {
|
|
|
|
|
let char = Data.getAvatar(ds);
|
|
|
|
|
ret.chars[char.id] = char;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
getAvatar(data) {
|
2022-04-15 22:24:25 +00:00
|
|
|
|
let char = Character.get(data["英雄Id"]);
|
2022-04-14 20:53:22 +00:00
|
|
|
|
return {
|
|
|
|
|
id: data["英雄Id"],
|
2022-04-15 22:24:25 +00:00
|
|
|
|
name: char ? char.name : "",
|
2022-04-14 20:53:22 +00:00
|
|
|
|
lv: data['等级'],
|
|
|
|
|
attr: Data.getAttr(data),
|
2022-04-16 20:23:19 +00:00
|
|
|
|
// weapon: Data.getWeapon(data),
|
2022-04-14 20:53:22 +00:00
|
|
|
|
artis: Data.getArtifact(data),
|
2022-04-16 20:23:19 +00:00
|
|
|
|
//cons: data["命之座数量"] * 1 || 0,
|
|
|
|
|
//talent: Data.getTalent(data)
|
2022-04-14 20:53:22 +00:00
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
getAttr(data) {
|
|
|
|
|
let ret = {};
|
|
|
|
|
let attrKey = {
|
|
|
|
|
atk: "攻击力_总",
|
|
|
|
|
atkBase: "属性攻击力",
|
|
|
|
|
def: "防御力_总",
|
|
|
|
|
defBase: "属性防御力",
|
|
|
|
|
hp: "生命值上限_总",
|
|
|
|
|
hpBase: "属性生命值上限",
|
|
|
|
|
mastery: "属性元素精通",
|
|
|
|
|
cRate: {
|
|
|
|
|
title: "属性暴击率",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
cDmg: {
|
|
|
|
|
title: "属性暴击伤害",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
hInc: {
|
|
|
|
|
title: "属性治疗加成",
|
|
|
|
|
pct: true
|
|
|
|
|
},
|
|
|
|
|
recharge: {
|
|
|
|
|
title: "属性元素充能效率",
|
|
|
|
|
pct: true
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
lodash.forEach(attrKey, (cfg, key) => {
|
|
|
|
|
if (typeof (cfg) === "string") {
|
|
|
|
|
cfg = { title: cfg };
|
|
|
|
|
}
|
|
|
|
|
let val = data[cfg.title] || "";
|
|
|
|
|
if (cfg.pct) {
|
|
|
|
|
val = (val * 100).toFixed(2)
|
|
|
|
|
}
|
|
|
|
|
ret[key] = val;
|
|
|
|
|
});
|
|
|
|
|
let maxDmg = 0;
|
|
|
|
|
lodash.forEach("火水草雷风冰岩".split(""), (key) => {
|
|
|
|
|
maxDmg = Math.max(data[`属性${key}元素伤害加成`] * 1, maxDmg);
|
|
|
|
|
});
|
|
|
|
|
ret.dmgBonus = (maxDmg * 100).toFixed(2);
|
2022-04-15 22:24:25 +00:00
|
|
|
|
ret.phyBonus = (data[`属性物理伤害加成`] * 100).toFixed(2);
|
2022-04-14 20:53:22 +00:00
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
},
|
|
|
|
|
getWeapon(data) {
|
|
|
|
|
return {
|
|
|
|
|
name: data['武器名称'],
|
|
|
|
|
lv: data['武器等级'],
|
|
|
|
|
refine: data["武器精炼"]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
getArtifact(data) {
|
|
|
|
|
let ret = {};
|
|
|
|
|
let get = function (idx, key) {
|
|
|
|
|
let v = data[`圣遗物${idx}${key}`];
|
|
|
|
|
let ret = /^([^\d]*)([\d\.\-]*)$/.exec(v);
|
|
|
|
|
if (ret && ret[1]) {
|
|
|
|
|
let title = ret[1], val = ret[2];
|
|
|
|
|
if (artifactMap[title]) {
|
|
|
|
|
if (artifactMap[title].pct) {
|
|
|
|
|
val = (val * 100).toFixed(2);
|
|
|
|
|
}
|
|
|
|
|
title = artifactMap[title].title;
|
|
|
|
|
}
|
|
|
|
|
return [title, val];
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (let idx = 1; idx <= 5; idx++) {
|
|
|
|
|
ret[`arti${idx}`] = {
|
|
|
|
|
name: data[`圣遗物${idx}名称`],
|
|
|
|
|
type: data[`圣遗物${idx}类型`],
|
|
|
|
|
main: get(idx, "主词条"),
|
|
|
|
|
attrs: [
|
|
|
|
|
get(idx, "副词条1"),
|
|
|
|
|
get(idx, "副词条2"),
|
|
|
|
|
get(idx, "副词条3"),
|
|
|
|
|
get(idx, "副词条4"),
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
},
|
|
|
|
|
getTalent(data) {
|
|
|
|
|
let ret = {};
|
|
|
|
|
lodash.forEach({
|
|
|
|
|
a: 1,
|
|
|
|
|
e: 2,
|
|
|
|
|
q: 3
|
|
|
|
|
}, (idx, key) => {
|
|
|
|
|
let val = data[`天赋主动名称${idx}`]
|
|
|
|
|
let regRet = /等级(\d*)$/.exec(val);
|
|
|
|
|
if (regRet && regRet[1]) {
|
|
|
|
|
ret[key] = regRet[1] * 1 || 1
|
|
|
|
|
} else {
|
|
|
|
|
ret[key] = 1;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let Profile = {
|
2022-04-15 22:05:31 +00:00
|
|
|
|
async request(uid, e) {
|
2022-04-16 09:40:30 +00:00
|
|
|
|
let cfg = config.miaoApi || {};
|
2022-04-14 20:53:22 +00:00
|
|
|
|
if (!cfg.api) {
|
2022-04-20 20:28:54 +00:00
|
|
|
|
e.reply("该功能为小范围非公开功能,需具备Token才可使用~");
|
2022-04-15 22:05:31 +00:00
|
|
|
|
return false;
|
2022-04-14 20:53:22 +00:00
|
|
|
|
}
|
2022-04-16 09:40:30 +00:00
|
|
|
|
if (!cfg.qq || !cfg.token || cfg.token.length !== 32) {
|
|
|
|
|
e.reply("Token错误,无法请求数据~");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-04-20 20:28:54 +00:00
|
|
|
|
let inCd = await redis.get(`miao:role-all:${uid}`);
|
|
|
|
|
if (inCd === 'loading') {
|
|
|
|
|
e.reply("请求过快,请稍后重试..");
|
|
|
|
|
return false;
|
|
|
|
|
} else if (inCd === 'pending') {
|
|
|
|
|
e.reply("距上次请求刷新成功间隔小于10分钟,请稍后重试..");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
await redis.set(`miao:role-all:${uid}`, 'loading', { EX: 20 });
|
2022-04-28 19:38:43 +00:00
|
|
|
|
e.reply("开始获取数据,可能会需要一定时间~");
|
|
|
|
|
let api = `${cfg.api}?uid=${uid}&qq=${cfg.qq}&token=${cfg.token}`;
|
|
|
|
|
if (e.avatar) {
|
|
|
|
|
api += `&avatar=${e.avatar}`;
|
|
|
|
|
}
|
2022-04-14 20:53:22 +00:00
|
|
|
|
let req = await fetch(api);
|
2022-04-16 09:40:30 +00:00
|
|
|
|
let data = await req.json();
|
|
|
|
|
if (data.status !== 0 || !data.data) {
|
2022-04-20 20:28:54 +00:00
|
|
|
|
e.reply(`请求失败:${data.msg || "未知错误"}`);
|
2022-04-16 09:40:30 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-04-23 21:16:37 +00:00
|
|
|
|
// 请求成功Bot侧对该uid冷却10分钟
|
|
|
|
|
// 请勿将时间改短,10分钟之内若发起请求会命中服务侧的uid缓存,返回之前的数据,并导致服务侧重新计时
|
2022-04-20 20:28:54 +00:00
|
|
|
|
await redis.set(`miao:role-all:${uid}`, 'pending', { EX: 600 });
|
2022-04-25 20:44:12 +00:00
|
|
|
|
let leftCount = data.leftCount;
|
2022-04-16 09:40:30 +00:00
|
|
|
|
data = data.data;
|
2022-04-14 20:53:22 +00:00
|
|
|
|
let userData = {};
|
|
|
|
|
if (data && data["角色名称"]) {
|
|
|
|
|
userData = Profile.save(uid, data)
|
|
|
|
|
}
|
2022-04-25 20:44:12 +00:00
|
|
|
|
userData.leftCount = leftCount;
|
2022-04-14 20:53:22 +00:00
|
|
|
|
return userData;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
save(uid, ds) {
|
|
|
|
|
let userData = {};
|
|
|
|
|
const userFile = `${userPath}/${uid}.json`;
|
|
|
|
|
if (fs.existsSync(userFile)) {
|
|
|
|
|
userData = JSON.parse(fs.readFileSync(userFile, "utf8")) || {};
|
|
|
|
|
}
|
|
|
|
|
let data = Data.getData(uid, ds);
|
|
|
|
|
lodash.assignIn(userData, lodash.pick(data, "uid,name,lv,avatar".split(",")));
|
|
|
|
|
userData.chars = userData.chars || {};
|
|
|
|
|
lodash.forEach(data.chars, (char, charId) => {
|
|
|
|
|
userData.chars[charId] = char;
|
|
|
|
|
});
|
|
|
|
|
fs.writeFileSync(userFile, JSON.stringify(userData), "", " ");
|
2022-04-15 22:05:31 +00:00
|
|
|
|
return data;
|
2022-04-14 20:53:22 +00:00
|
|
|
|
},
|
2022-04-23 21:16:37 +00:00
|
|
|
|
|
2022-04-14 20:53:22 +00:00
|
|
|
|
get(uid, charId) {
|
|
|
|
|
const userFile = `${userPath}/${uid}.json`;
|
|
|
|
|
let userData = {};
|
|
|
|
|
if (fs.existsSync(userFile)) {
|
|
|
|
|
userData = JSON.parse(fs.readFileSync(userFile, "utf8")) || {};
|
|
|
|
|
}
|
|
|
|
|
if (userData && userData.chars && userData.chars[charId]) {
|
|
|
|
|
return userData.chars[charId];
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2022-04-15 14:33:51 +00:00
|
|
|
|
},
|
2022-04-23 21:16:37 +00:00
|
|
|
|
|
|
|
|
|
getAll(uid) {
|
|
|
|
|
const userFile = `${userPath}/${uid}.json`;
|
|
|
|
|
let userData = {};
|
|
|
|
|
if (fs.existsSync(userFile)) {
|
|
|
|
|
userData = JSON.parse(fs.readFileSync(userFile, "utf8")) || {};
|
|
|
|
|
}
|
|
|
|
|
if (userData && userData.chars) {
|
|
|
|
|
return userData.chars;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
|
2022-04-15 14:33:51 +00:00
|
|
|
|
formatArti(ds) {
|
|
|
|
|
if (lodash.isArray(ds[0])) {
|
|
|
|
|
let ret = [];
|
|
|
|
|
lodash.forEach(ds, (d) => {
|
|
|
|
|
ret.push(Profile.formatArti(d));
|
|
|
|
|
})
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
let title = ds[0], val = ds[1];
|
2022-04-16 19:39:45 +00:00
|
|
|
|
if (!title || title === "undefined") {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2022-04-15 22:05:31 +00:00
|
|
|
|
if (/伤害加成/.test(title) && val < 1) {
|
|
|
|
|
val = Format.pct(val * 100);
|
|
|
|
|
} else if (/伤害加成|大|暴|充能|治疗/.test(title)) {
|
2022-04-15 14:33:51 +00:00
|
|
|
|
val = Format.pct(val);
|
|
|
|
|
} else {
|
2022-04-15 22:05:31 +00:00
|
|
|
|
val = Format.comma(val, 1);
|
2022-04-15 14:33:51 +00:00
|
|
|
|
}
|
2022-04-15 22:05:31 +00:00
|
|
|
|
|
|
|
|
|
|
2022-04-15 14:33:51 +00:00
|
|
|
|
if (/元素伤害加成/.test(title)) {
|
|
|
|
|
title = title.replace("元素伤害", "伤");
|
2022-04-15 22:30:19 +00:00
|
|
|
|
} else if (title === "物理伤害加成") {
|
|
|
|
|
title = "物伤加成";
|
2022-04-15 14:33:51 +00:00
|
|
|
|
}
|
|
|
|
|
return [title, val];
|
2022-04-15 22:05:31 +00:00
|
|
|
|
},
|
2022-04-23 21:16:37 +00:00
|
|
|
|
|
2022-04-15 22:05:31 +00:00
|
|
|
|
getArtiMark(data, ds) {
|
2022-04-17 11:03:44 +00:00
|
|
|
|
Reliquaries.getMark(data)
|
2022-04-15 22:05:31 +00:00
|
|
|
|
let total = 0;
|
|
|
|
|
lodash.forEach(data, (ret) => {
|
|
|
|
|
if (ret[0] && ret[1]) {
|
|
|
|
|
total += mark[ret[0]] * ret[1];
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
if (ds && /暴/.test(ds[0])) {
|
|
|
|
|
total += 20;
|
|
|
|
|
}
|
|
|
|
|
return total;
|
2022-04-23 21:16:37 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getArtiDetail(profile, avatar) {
|
|
|
|
|
|
|
|
|
|
let reliquaries = [],
|
|
|
|
|
totalMark = 0,
|
|
|
|
|
totalMaxMark = 0;
|
|
|
|
|
|
|
|
|
|
lodash.forEach(avatar.reliquaries, (ds) => {
|
|
|
|
|
let pos = ds.pos_name;
|
|
|
|
|
let arti = profile.artis[`arti${posIdx[pos].idx}`];
|
|
|
|
|
if (arti) {
|
|
|
|
|
let mark = Reliquaries.getMark(avatar.name, arti.attrs);
|
|
|
|
|
let maxMark = Reliquaries.getMaxMark(avatar.name, arti.main[0] || "");
|
|
|
|
|
totalMark += mark;
|
|
|
|
|
totalMaxMark += maxMark;
|
|
|
|
|
ds.mark = Format.comma(mark, 1);
|
|
|
|
|
ds.markType = Reliquaries.getMarkScore(mark, maxMark);
|
|
|
|
|
ds.main = Profile.formatArti(arti.main);
|
|
|
|
|
ds.attrs = Profile.formatArti(arti.attrs);
|
|
|
|
|
}
|
|
|
|
|
posIdx[pos].data = ds;
|
|
|
|
|
})
|
|
|
|
|
lodash.forEach(posIdx, (ds) => {
|
|
|
|
|
if (ds && ds.data) {
|
|
|
|
|
reliquaries.push(ds.data);
|
|
|
|
|
} else {
|
|
|
|
|
reliquaries.push({});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
reliquaries,
|
|
|
|
|
totalMark,
|
|
|
|
|
totalMaxMark,
|
|
|
|
|
markScore: Reliquaries.getMarkScore(totalMark * 1.05, totalMaxMark)
|
|
|
|
|
}
|
2022-04-14 20:53:22 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
export default Profile;
|