#更新全部面板 一些已知问题修复

* 目前可正确获取圣遗物名称了,`#圣遗物列表`如有错误重新获角色信息后即正常
 * 雷神、莫娜的元素伤害字段数据错误fix
 * 由于服务逻辑与之前数据不一致,部分角色的属性及伤害计算可能会不准确,如有发现请反馈给喵喵
This commit is contained in:
Kokomi 2022-06-03 17:13:12 +08:00
parent 0608ca697b
commit 9b90010daa
11 changed files with 3337 additions and 431 deletions

View File

@ -1,11 +1,14 @@
# 1.6.4
# 1.6.5
* `#更新全部面板` 一些已知问题修复
* 目前可正确获取圣遗物名称了,`#圣遗物列表`如有错误重新获角色信息后即正常
* 雷神、莫娜的元素伤害字段数据错误fix
* 由于服务逻辑与之前数据不一致,部分角色的属性及伤害计算可能会不准确,如有发现请反馈给喵喵
* `#更新全部面板` 功能暂时恢复
* 目前可直接使用无需token
* 部分网络可能无法请求,等待后续服务更新
* 由于服务逻辑与之前数据不一致,部分角色的属性及伤害计算可能会不准确,如有发现请反馈给喵喵
* 目前可直接使用无需token
* 服务来自enka.shinshin.moe部分网络可能无法请求请使用科学处理
# 1.6.1 ~ 1.6.3
# 1.6.1 ~ 1.6.4
* 增加`#喵喵面板设置`命令,可更精细的设置是否允许好友/临时对话/群使用面板功能
* 增加`#录入夜兰面板` 命令,可在更新服务不可用时手工录入角色面板信息

View File

@ -493,9 +493,7 @@ export async function getProfile(e, mode = "refresh") {
return true;
}
let leftMsg = "";
if (!lodash.isUndefined(data.leftCount)) {
leftMsg = `今日剩余${data.leftCount}次请求机会。`
}
if (!data.chars) {
e.reply("获取角色面板数据失败请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~");
} else {
@ -832,6 +830,9 @@ export async function getArtis(e, { render }) {
/* 处理圣遗物 */
if (ds.artis) {
lodash.forEach(ds.artis, (arti) => {
if (!arti.name) {
return;
}
let mark = Reliquaries.getMark(name, arti.attrs);
let maxMark = Reliquaries.getMaxMark(name, arti.main[0] || "");
arti.mark = Format.comma(mark, 1);
@ -846,7 +847,6 @@ export async function getArtis(e, { render }) {
}
});
if (artis.length === 0) {
e.reply("请先获取角色面板数据后再查看圣遗物列表...");
await profileHelp(e);

View File

@ -5,19 +5,18 @@ import Format from "./Format.js";
import Character from "./models/Character.js";
import Reliquaries from "./models/Reliquaries.js";
import Data from "./data/enka.js";
const _path = process.cwd();
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;
}
}
//} catch (e) {
// do nth
//}
const userPath = `${_path}/data/UserData/`;
@ -25,368 +24,11 @@ if (!fs.existsSync(userPath)) {
fs.mkdirSync(userPath);
}
const artifactMap = {
'生命值': {
title: "小生命"
},
'生命值_百分比': {
title: "大生命",
pct: true
},
'暴击率': {
title: "暴击率",
pct: true
},
'暴击伤害': {
title: "暴击伤害",
pct: true
},
'防御力': {
title: "小防御"
},
'防御力_百分比': {
title: "大防御",
pct: true
},
'攻击力': {
title: "小攻击"
},
'攻击力_百分比': {
title: "大攻击",
pct: true
},
'元素精通': {
title: "元素精通"
},
'元素充能效率': {
title: "充能效率",
pct: true
},
'治疗加成': {
title: "治疗加成",
pct: true
}
}
let posIdx = {
"生之花": {
idx: 1
},
"死之羽": {
idx: 2
},
"时之沙": {
idx: 3
},
"空之杯": {
idx: 4
},
"理之冠": {
idx: 5
}
};
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
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) {
let char = Character.get(data["英雄Id"]);
return {
id: data["英雄Id"],
name: char ? char.name : "",
lv: data['等级'],
attr: Data.getAttr(data),
// weapon: Data.getWeapon(data),
artis: Data.getArtifact(data),
//cons: data["命之座数量"] * 1 || 0,
//talent: Data.getTalent(data)
};
},
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);
ret.phyBonus = (data[`属性物理伤害加成`] * 100).toFixed(2);
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 Data2 = {
getData(uid, data) {
let ret = {
uid,
chars: {}
}
lodash.forEach({
name: "nickname",
avatar: "profilePicture.avatarId",
lv: "level"
}, (src, key) => {
ret[key] = lodash.get(data.playerInfo, src, "");
})
lodash.forEach(data.avatarInfoList, (ds) => {
let char = Data2.getAvatar(ds);
ret.chars[char.id] = char;
})
return ret;
},
getAvatar(data) {
let char = Character.get(data.avatarId);
return {
id: data["avatarId"],
name: char ? char.name : "",
dataSource: "shin",
lv: data.propMap['4001'].val * 1,
attr: Data2.getAttr(data.fightPropMap),
// weapon: Data.getWeapon(data),
artis: Data2.getArtifact(data.equipList),
//cons: data["命之座数量"] * 1 || 0,
//talent: Data.getTalent(data)
};
},
getAttr(data) {
let ret = {};
let attrKey = {
// atk: 2001,
atkBase: 4,
def: 2002,
defBase: 7,
hp: 2000,
hpBase: 1,
mastery: 28,
cRate: {
src: 20,
pct: true
},
cDmg: {
src: 22,
pct: true
},
hInc: {
src: 26,
pct: true
},
recharge: {
src: 23,
pct: true
}
};
lodash.forEach(attrKey, (cfg, key) => {
if (!lodash.isObject(cfg)) {
cfg = {src: cfg};
}
let val = data[cfg.src] || 0;
if (cfg.pct) {
val = val * 100
}
ret[key] = val;
});
ret.atk = data['4'] * (1 + data['6']) + data['5'];
let maxDmg = 0;
// 火40 水42 风44 岩45 冰46 雷46
// 41 雷
lodash.forEach("40,41,42,43,44,45,45,46".split(","), (key) => {
maxDmg = Math.max(data[key] * 1, maxDmg);
});
// phy 30
ret.dmgBonus = maxDmg * 100;
ret.phyBonus = data['30'] * 100;
return ret;
},
getArtifact(data) {
let ret = {};
let artiIdx = {
EQUIP_BRACER: 1,
EQUIP_NECKLACE: 2,
EQUIP_SHOES: 3,
EQUIP_RING: 4,
EQUIP_DRESS: 5
}
let attrMap = {
HP: "小生命",
HP_PERCENT: "大生命",
ATTACK: "小攻击",
ATTACK_PERCENT: "大攻击",
DEFENSE: "小防御",
DEFENSE_PERCENT: "大防御",
FIRE_ADD_HURT: "火元素伤害加成",
ICE_ADD_HURT: "冰元素伤害加成",
ROCK_ADD_HURT: "岩元素伤害加成",
ELEC_ADD_HURT: "雷元素伤害加成",
WIND_ADD_HURT: "风元素伤害加成",
WATER_ADD_HURT: "水元素伤害加成",
PHYSICAL_ADD_HURT: "物理伤害加成",
HEAL_ADD: "治疗加成",
ELEMENT_MASTERY: "元素精通",
CRITICAL: "暴击率",
CRITICAL_HURT: "暴击伤害",
CHARGE_EFFICIENCY: "充能效率",
}
let get = function (d) {
if (!d) {
return [];
}
let id = d.appendPropId || d.mainPropId || "";
id = id.replace("FIGHT_PROP_", "");
if (!attrMap[id]) {
return [];
}
return [attrMap[id], d.statValue];
}
lodash.forEach(data, (ds) => {
let flat = ds.flat || {}, sub = flat.reliquarySubstats || [];
let idx = artiIdx[flat.equipType];
if (!idx) {
return;
}
ret[`arti${idx}`] = {
name: "",
id: ds.itemId,
main: get(flat.reliquaryMainstat),
attrs: [
get(sub[0]),
get(sub[1]),
get(sub[2]),
get(sub[3])
]
}
})
return ret;
},
};
let Profile = {
async request(uid, e) {
let api = `https://enka.shinshin.moe/u/${uid}/__data.json`;
@ -395,7 +37,7 @@ let Profile = {
e.reply("请求过快,请稍后重试..");
return false;
} else if (inCd === 'pending') {
e.reply("距上次请求刷新成功间隔小于2分钟,请稍后重试..");
e.reply("距上次请求刷新成功间隔小于5分钟,请稍后重试..");
return false;
}
await redis.set(`miao:role-all:${uid}`, 'loading', {EX: 20});
@ -414,78 +56,29 @@ let Profile = {
e.reply(`请打开角色展柜的显示详情`);
return false;
}
// enka服务测冷却时间5分钟
await redis.set(`miao:role-all:${uid}`, 'pending', {EX: 300});
let userData = {};
userData = Profile.save(uid, data, 'enka')
return userData;
} catch (err) {
console.log(err);
e.reply(`请求失败`);
return false;
}
// 请求成功Bot侧对该uid冷却10分钟
// 请勿将时间改短10分钟之内若发起请求会命中服务侧的uid缓存返回之前的数据并导致服务侧重新计时
await redis.set(`miao:role-all:${uid}`, 'pending', {EX: 120});
let userData = {};
userData = Profile.save(uid, data, true)
userData.leftCount = 99;
return userData;
},
async request_bak(uid, e) {
let cfg = config.miaoApi || {};
if (!cfg.api) {
e.reply("该功能为小范围非公开功能需具备Token才可使用~");
return false;
}
if (!cfg.qq || !cfg.token || cfg.token.length !== 32) {
e.reply("Token错误无法请求数据~");
return false;
}
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});
e.reply("开始获取数据,可能会需要一定时间~");
await sleep(1000);
let api = `${cfg.api}?uid=${uid}&qq=${cfg.qq}&token=${cfg.token}`;
if (e.avatar) {
api += `&avatar=${e.avatar}`;
}
let req = await fetch(api);
let data = await req.json();
if (data.status !== 0 || !data.data) {
e.reply(`请求失败:${data.msg || "未知错误"}`);
return false;
}
// 请求成功Bot侧对该uid冷却10分钟
// 请勿将时间改短10分钟之内若发起请求会命中服务侧的uid缓存返回之前的数据并导致服务侧重新计时
await redis.set(`miao:role-all:${uid}`, 'pending', {EX: 600});
let leftCount = data.leftCount;
data = data.data;
let userData = {};
if (data && data["角色名称"]) {
userData = Profile.save(uid, data)
}
userData.leftCount = leftCount;
return userData;
},
save(uid, ds, isNewFormat = false) {
save(uid, ds, dataSource = 'enka') {
let userData = {};
const userFile = `${userPath}/${uid}.json`;
if (fs.existsSync(userFile)) {
userData = JSON.parse(fs.readFileSync(userFile, "utf8")) || {};
}
let data;
if (isNewFormat) {
data = Data2.getData(uid, ds);
} else {
data = Data.getData(uid, ds);
}
data = Data.getData(uid, ds);
lodash.assignIn(userData, lodash.pick(data, "uid,name,lv,avatar".split(",")));
userData.chars = userData.chars || {};

242
components/data/enka.js Normal file
View File

@ -0,0 +1,242 @@
import lodash from "lodash";
import Character from "../models/Character.js";
import meta from "./enka_meta.js";
import cmeta from "./enka_char.js";
import _Data from "../Data.js";
let _path = process.cwd();
let relis = _Data.readJSON(`${_path}/plugins/miao-plugin/resources/meta/reliquaries/`, "data.json") || {};
let relisMap = {}
lodash.forEach(relis, (ds) => {
relisMap[ds.name] = ds;
})
const artiIdx = {
EQUIP_BRACER: 1,
EQUIP_NECKLACE: 2,
EQUIP_SHOES: 3,
EQUIP_RING: 4,
EQUIP_DRESS: 5
};
const attrMap = {
HP: "小生命",
HP_PERCENT: "大生命",
ATTACK: "小攻击",
ATTACK_PERCENT: "大攻击",
DEFENSE: "小防御",
DEFENSE_PERCENT: "大防御",
FIRE_ADD_HURT: "火元素伤害加成",
ICE_ADD_HURT: "冰元素伤害加成",
ROCK_ADD_HURT: "岩元素伤害加成",
ELEC_ADD_HURT: "雷元素伤害加成",
WIND_ADD_HURT: "风元素伤害加成",
WATER_ADD_HURT: "水元素伤害加成",
PHYSICAL_ADD_HURT: "物理伤害加成",
HEAL_ADD: "治疗加成",
ELEMENT_MASTERY: "元素精通",
CRITICAL: "暴击率",
CRITICAL_HURT: "暴击伤害",
CHARGE_EFFICIENCY: "充能效率",
};
let Data = {
getData(uid, data) {
let ret = {
uid,
chars: {}
}
lodash.forEach({
name: "nickname",
avatar: "profilePicture.avatarId",
lv: "level"
}, (src, key) => {
ret[key] = lodash.get(data.playerInfo, src, "");
})
lodash.forEach(data.avatarInfoList, (ds) => {
let char = Data.getAvatar(ds);
ret.chars[char.id] = char;
})
return ret;
},
getAvatar(data) {
let char = Character.get(data.avatarId);
let ret = {
id: data["avatarId"],
name: char ? char.name : "",
dataSource: "enka",
lv: data.propMap['4001'].val * 1,
fetter: data.fetterInfo.expLevel,
attr: Data.getAttr(data.fightPropMap),
weapon: Data.getWeapon(data.equipList),
artis: Data.getArtifact(data.equipList),
cons: data.talentIdList ? data.talentIdList.length : 0,
talent: Data.getTalent(char.id, data.skillLevelMap, data.proudSkillExtraLevelMap || {})
};
return Data.dataFix(ret);
},
getAttr(data) {
let ret = {};
let attrKey = {
// atk: 2001,
atkBase: 4,
def: 2002,
defBase: 7,
hp: 2000,
hpBase: 1,
mastery: 28,
cRate: {
src: 20,
pct: true
},
cDmg: {
src: 22,
pct: true
},
hInc: {
src: 26,
pct: true
},
recharge: {
src: 23,
pct: true
}
};
lodash.forEach(attrKey, (cfg, key) => {
if (!lodash.isObject(cfg)) {
cfg = {src: cfg};
}
let val = data[cfg.src] || 0;
if (cfg.pct) {
val = val * 100
}
ret[key] = val;
});
ret.atk = data['4'] * (1 + data['6']) + data['5'];
let maxDmg = 0;
// 火40 水42 风44 岩45 冰46 雷46
// 41 雷
lodash.forEach("40,41,42,43,44,45,45,46".split(","), (key) => {
maxDmg = Math.max(data[key] * 1, maxDmg);
});
// phy 30
ret.dmgBonus = maxDmg * 100;
ret.phyBonus = data['30'] * 100;
return ret;
},
getArtifact(data) {
let ret = {};
let get = function (d) {
if (!d) {
return [];
}
let id = d.appendPropId || d.mainPropId || "";
id = id.replace("FIGHT_PROP_", "");
if (!attrMap[id]) {
return [];
}
return [attrMap[id], d.statValue];
}
lodash.forEach(data, (ds) => {
let flat = ds.flat || {}, sub = flat.reliquarySubstats || [];
let idx = artiIdx[flat.equipType];
if (!idx) {
return;
}
let setName = meta[flat.setNameTextMapHash] || "";
let setCfg = relisMap[setName] || {name: "", sets: {}},
artiCfg = setCfg.sets[`arti${idx}`] || {name: ""};
ret[`arti${idx}`] = {
name: artiCfg.name,
set: setCfg.name,
level: Math.min(20, ((ds.reliquary && ds.reliquary.level) || 1) - 1),
main: get(flat.reliquaryMainstat),
attrs: [
get(sub[0]),
get(sub[1]),
get(sub[2]),
get(sub[3])
]
}
})
return ret;
},
getWeapon(data) {
let ds = {}
lodash.forEach(data, (temp) => {
if (temp.flat && temp.flat.itemType === "ITEM_WEAPON") {
ds = temp;
return false;
}
})
let {weapon, flat} = ds;
return {
name: meta[flat.nameTextMapHash],
star: flat.rankLevel,
leve: weapon.level,
promote: weapon.promoteLevel,
affix: lodash.values(weapon.affixMap)[0]
}
},
getTalent(charid, ds = {}, ext = {}) {
let cm = cmeta[charid] || {};
let cn = cm.Skills || {}, ce = cm.ProudMap;
let idx = 1;
let idxMap = {1: 'a', 2: 'e', 3: 'q', 'a': 'a', 's': 'e', 'e': 'q'};
lodash.forEach(cn, (n, id) => {
let nRet = /skill_(\w)/.exec(n.toLowerCase());
idxMap[id] = nRet && nRet[1] ? idxMap[nRet[1]] : idxMap[idx];
idx++;
});
lodash.forEach(ce, (n, id) => {
idxMap[n] = idxMap[id];
})
let ret = {};
lodash.forEach(ds, (lv, id) => {
let key = idxMap[id];
ret[key] = {
level_original: lv,
level_current: lv
}
})
lodash.forEach(ext, (lv, id) => {
let key = idxMap[id];
if (ret[key]) {
ret[key].level_current = ret[key].level_current + lv;
}
})
return ret;
},
dataFix(ret) {
let {attr, id} = ret;
id = id * 1;
switch (id) {
case 10000052:
// 雷神被动加成fix
attr.dmgBonus = Math.min(0, attr.dmgBonus - (attr.recharge - 100) * 0.4)
break;
case 10000041:
// 莫娜被动fix
attr.dmgBonus = Math.min(0, attr.dmgBonus - attr.recharge * 0.2)
break;
}
return ret;
}
};
export default Data;

1098
components/data/enka_char.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,387 @@
export default {
20848859: "黑岩斩刀",
33330467: "元素熟练",
37147251: "匣里日月",
43015699: "待定",
54857595: "止水息雷",
83115355: "被怜爱的少女",
85795635: "专注",
88505754: "枫原万叶",
135182203: "止水息雷",
147298547: "流浪大地的乐团",
156294403: "沉沦之心",
160493219: "暗铁剑",
168956722: "七七",
197755235: "贯虹之槊",
212557731: "祭雷之人",
240385755: "破浪",
246984427: "踏火息雷",
262428003: "祭冰之人",
270124867: "护国的无垢之心",
287454963: "祭风之人",
288666635: "无垢之心",
302691299: "琥珀玥",
303155515: "离簇不归",
310247243: "神乐之真意",
334242634: "申鹤",
339931171: "乘胜追击",
342097547: "辰砂之纺锤",
346510395: "衔珠海皇",
368014203: "斩裂晴空的龙脊",
391273955: "斫断黑翼的利齿",
411685275: "钢轮弓",
479076483: "冷刃",
481755219: "黑岩刺枪",
486287579: "余热",
500612819: "「旗杆」",
500987603: "(test)穿模测试",
506630267: "顺风而行",
514784907: "踏火止水",
521221323: "护国的无垢之心",
540938627: "掠食者",
566772267: "御伽大王御伽话",
577103787: "能量沐浴",
578575283: "流月针",
597991835: "白夜皓月",
613846163: "降世",
618786571: "钺矛",
623494555: "摧坚",
623534363: "西风秘典",
630452219: "樱之斋宫",
646100491: "千岩诀·同心",
650049651: "风花之颂",
655825874: "云堇",
656120259: "神射手之誓",
680510411: "白影剑",
688991243: "息灾",
693354267: "尘世之锁",
697277554: "烟绯",
716252627: "千岩长枪",
729851187: "冰之川与雪之砂",
735056795: "西风大剑",
807607555: "天空之卷",
824949859: "嘟嘟!大冒险",
828711395: "阿莫斯之弓",
836208539: "炊金",
850802171: "白铁大剑",
855894507: "战狂",
862591315: "苍白之火",
877751435: "宗室大剑",
902264035: "风鹰剑",
902282051: "收割",
909145139: "护国的无垢之心",
930640955: "钟剑",
933076627: "冰风迷途的勇士",
942758755: "专注",
944332883: "斫峰之刃",
949506483: "海洋的胜利",
968378595: "西风之鹰的抗争",
968893378: "班尼特",
991968139: "非时之梦·常世灶食",
1006042610: "神里绫华",
1021898539: "弹弓",
1021947690: "魈",
1028735635: "抗争的践行之歌",
1053433018: "砂糖",
1072884907: "万国诸海图谱",
1075647299: "松籁响起之时",
1082448331: "微光的海渊民",
1089950259: "天空之傲",
1097898243: "沉重",
1103732675: "幸运儿",
1113306282: "莫娜",
1114777131: "和弦",
1119368259: "旅程",
1130996346: "香菱",
1133599347: "矢志不忘",
1148024603: "「渔获」",
1154009435: "试作星镰",
1163263227: "流浪乐章",
1163616891: "霜葬",
1182966603: "佣兵重剑",
1186209435: "赌徒",
1212345779: "角斗士的终幕礼",
1217552947: "白刃流转",
1240067179: "西风猎弓",
1319974859: "激励",
1321135667: "匣里龙吟",
1337666507: "千岩牢固",
1344953075: "顺风而行",
1345343763: "磐岩结绿",
1383639611: "奇迹",
1388004931: "飞天御剑",
1390797107: "白缨枪",
1404688115: "别离的思念之歌",
1406746947: "异世界行记",
1414366819: "金璋皇极",
1437658243: "螭骨剑",
1438974835: "逆飞的流星",
1455107995: "四风原典",
1468367538: "迪奥娜",
1479961579: "铁影阔剑",
1483922610: "九条裟罗",
1485303435: "注能之刺",
1492752155: "气定神闲",
1499235563: "乘胜追击",
1499817443: "苍翠之风",
1516554699: "石英大剑",
1522029867: "踏火息雷",
1524173875: "炽烈的炎之魔女",
1533656818: "旅行者",
1541919827: "染血的骑士道",
1545992315: "「正义」",
1558036915: "辰砂往生录",
1562601179: "翠绿之影",
1588620330: "神里绫人",
1595734083: "(test)穿模测试",
1600275315: "波乱月白经津",
1608953539: "黎明神剑",
1610242915: "传承",
1628928163: "风花之愿",
1632377563: "渡过烈火的贤人",
1651985379: "极昼的先兆者",
1660598451: "岩藏之胤",
1675686363: "祭礼大剑",
1706534267: "有话直说",
1722706579: "止水融冰",
1745286795: "名士振舞",
1745712907: "驭浪的海祇民",
1751039235: "昔日宗室之仪",
1756609915: "海染砗磲",
1771603299: "金璋皇极",
1773425155: "降临之剑",
1789612403: "回响",
1820235315: "无矢之歌",
1836628747: "叛逆的守护者",
1860795787: "曚云之月",
1864015138: "刻晴",
1873342283: "平息鸣雷的尊者",
1890163363: "不灭月华",
1901973075: "冬极白星",
1921418842: "诺艾尔",
1932742643: "灭却之戒法",
1934830979: "无尽的渴慕",
1940821986: "久岐忍",
1940919994: "胡桃",
1966438658: "安柏",
1982136171: "专注",
1990641987: "祭礼剑",
1990820123: "天目影打刀",
1991707099: "试作斩岩",
1997709467: "和璞鸢",
2006422931: "千岩古剑",
2009975571: "(test)穿模测试",
2017033267: "气定神闲",
2025598051: "顺风而行",
2040573235: "悠古的磐岩",
2060049099: "祭火之人",
2108574027: "碎石",
2109571443: "专注",
2125206395: "银剑",
2149411851: "金璋皇极",
2172529947: "乘胜追击",
2176134843: "专注",
2190368347: "决",
2191797987: "冒险家",
2195665683: "祭礼残章",
2242027395: "黑檀弓",
2276480763: "绝缘之旗印",
2279290283: "魔导绪论",
2297485451: "速射弓斗",
2312640651: "气定神闲",
2317820211: "注能之针",
2322648115: "粉碎",
2324146259: "白辰之环",
2340970067: "历练的猎弓",
2359799475: "恶王丸",
2364208851: "行者之心",
2365025043: "街巷游侠",
2375993851: "宗室长剑",
2383998915: "驭浪的海祇民",
2384519283: "弹弓",
2388785242: "早柚",
2400012995: "祭礼弓",
2410593283: "无锋剑",
2417717595: "暗巷猎手",
2425414923: "落霞",
2433755451: "揭旗的叛逆之歌",
2440850563: "回响长天的诗歌",
2466140362: "温迪",
2469300579: "乘胜追击",
2470306939: "飞雷御执",
2474354867: "西风剑",
2476346187: "踏火止水",
2491797315: "喜多院十文字",
2504399314: "宵宫",
2512309395: "如雷的盛怒",
2521338131: "试作金珀",
2534304035: "雾切御腰物",
2539208459: "证誓之明瞳",
2546254811: "华馆梦醒形骸记",
2556914683: "绝弦",
2587614459: "忍冬之果",
2614170427: "飞天大御剑",
2646367730: "北斗",
2664629131: "匣里灭辰",
2666951267: "训练大剑",
2673337443: "注能之矢",
2679781122: "甘雨",
2684365579: "登场乐",
2705029563: "口袋魔导书",
2713453234: "八重神子",
2719832059: "(test)穿模测试",
2743659331: "激流",
2749508387: "金璋皇极",
2749853923: "腐殖之剑",
2753539619: "雪葬的星银",
2764598579: "流放者",
2792766467: "无工之剑",
2796697027: "新手长枪",
2832648187: "宗室长弓",
2834803571: "金璋皇极",
2848374378: "夜兰",
2853296811: "穿刺高天的利齿",
2871793795: "锐利",
2876340530: "重云",
2890909531: "武人",
2893964243: "飞矢传书",
2915865819: "渊中霞彩",
2918525947: "飞雷之弦振",
2935286715: "宗室猎枪",
2947140987: "暗巷闪光",
2949448555: "苍古自由之誓",
2963220587: "翡玉法球",
3001782875: "气定神闲",
3018479371: "船歌",
3024507506: "雷电将军",
3063488107: "强力攻击",
3068316954: "荒泷一斗",
3070169307: "铁尖枪",
3079462611: "驭浪的海祇民",
3090373787: "暗巷的酒与诗",
3097441915: "以理服人",
3112448011: "决心",
3112679155: "终末嗟叹之诗",
3156385731: "昭心",
3169209451: "弓藏",
3192689683: "霜葬",
3221566250: "琴",
3235324891: "护摩之杖",
3252085691: "顺风而行",
3258658763: "嗜魔",
3265161211: "注能之锋",
3273999011: "黑岩绯玉",
3277782506: "菲谢尔",
3302787771: "霜葬",
3305772819: "奔袭战术",
3314157803: "克柔",
3337185491: "浅濑之弭",
3337249451: "过载",
3339083250: "可莉",
3344622722: "丽莎",
3364338659: "无边际的眷顾",
3371922315: "神樱神游神乐舞",
3378007475: "黑岩长剑",
3400133546: "五郎",
3406113971: "顺风而行",
3421967235: "吃虎鱼刀",
3439749859: "苍翠猎弓",
3443142923: "龙脊长枪",
3447737235: "黑岩战弓",
3456986819: "嘟嘟可故事集",
3465493459: "精准",
3500935003: "讨龙英杰谭",
3535784755: "勇士之心",
3541083923: "角斗士",
3555115602: "托马",
3584825427: "学徒笔记",
3587062891: "千岩诀·同心",
3587621259: "笛剑",
3600623979: "猎弓",
3608180322: "迪卢克",
3618167299: "学士",
3625393819: "试作澹月",
3626268211: "来歆余响",
3673792067: "旅行剑",
3684723963: "雨裁",
3689108098: "埃洛伊",
3717667418: "优菈",
3717849275: "薙草之稻光",
3719372715: "甲级宝珏",
3722933411: "试作古华",
3755004051: "西风长枪",
3762437019: "(test)穿模测试",
3775299170: "芭芭拉",
3782508715: "游医",
3796702635: "变化万端",
3796905611: "黑剑",
3816664530: "旅行者",
3827789435: "宗室秘法录",
3832443723: "不屈",
3836188467: "无羁的朱赤之蝶",
3847143266: "达达利亚",
3862787418: "钟离",
3890292467: "教官",
3898539027: "浮游四方的灵云",
3914045794: "珊瑚宫心海",
3914951691: "赤角石溃杵",
3933622347: "天空之翼",
3949653579: "幽夜华尔兹",
3966753539: "洗濯诸类之形",
3975746731: "鸦羽弓",
3995710363: "狼的末路",
3996017211: "收割",
3999792907: "祭水之人",
4000770243: "街巷伏击",
4022012131: "乘胜追击",
4049410651: "决斗之枪",
4055003299: "天空之刃",
4060235987: "日月辉",
4080317355: "勇气",
4082302819: "守护之心",
4090429643: "沐浴龙血的剑",
4103022435: "铁蜂刺",
4103766499: "黑缨枪",
4108620722: "阿贝多",
4113638323: "昭理的鸢之枪",
4119663210: "凯亚",
4122509083: "断浪长鳍",
4124851547: "雾切之回光",
4127888970: "凝光",
4137694339: "(test)竿测试",
4139294531: "信使",
4144069251: "追忆之注连",
4158505619: "天空之脊",
4160147242: "雷泽",
4162981171: "(test)穿模测试",
4186179883: "破魔之弓",
4193089947: "桂木斩长正",
4197635682: "行秋",
4226083179: "名士振舞",
4230231107: "若水",
4245213187: "注能之卷",
4258047555: "极夜二重奏",
4260733330: "罗莎莉亚",
4267718859: "反曲弓",
4273845410: "辛焱",
4275754179: "如狼般狩猎者",
FIGHT_PROP_ATTACK: "攻击力",
FIGHT_PROP_CHARGE_EFFICIENCY: "元素充能效率",
FIGHT_PROP_CRITICAL: "暴击率",
FIGHT_PROP_CRITICAL_HURT: "暴击伤害",
FIGHT_PROP_DEFENSE: "防御力",
FIGHT_PROP_ELEC_ADD_HURT: "雷元素伤害加成",
FIGHT_PROP_ELEMENT_MASTERY: "元素精通",
FIGHT_PROP_FIRE_ADD_HURT: "火元素伤害加成",
FIGHT_PROP_GRASS_ADD_HURT: "草元素伤害加成",
FIGHT_PROP_HEALED_ADD: "受治疗加成",
FIGHT_PROP_HEAL_ADD: "治疗加成",
FIGHT_PROP_ICE_ADD_HURT: "冰元素伤害加成",
FIGHT_PROP_MAX_HP: "生命值上限",
FIGHT_PROP_PHYSICAL_ADD_HURT: "物理伤害加成",
FIGHT_PROP_ROCK_ADD_HURT: "岩元素伤害加成",
FIGHT_PROP_SHIELD_COST_MINUS_RATIO: "护盾强效",
FIGHT_PROP_WATER_ADD_HURT: "水元素伤害加成",
FIGHT_PROP_WIND_ADD_HURT: "风元素伤害加成",
level: "等级",
}

205
components/data/miao.js Normal file
View File

@ -0,0 +1,205 @@
import lodash from "lodash";
import Character from "../models/Character.js";
const artifactMap = {
'生命值': {
title: "小生命"
},
'生命值_百分比': {
title: "大生命",
pct: true
},
'暴击率': {
title: "暴击率",
pct: true
},
'暴击伤害': {
title: "暴击伤害",
pct: true
},
'防御力': {
title: "小防御"
},
'防御力_百分比': {
title: "大防御",
pct: true
},
'攻击力': {
title: "小攻击"
},
'攻击力_百分比': {
title: "大攻击",
pct: true
},
'元素精通': {
title: "元素精通"
},
'元素充能效率': {
title: "充能效率",
pct: true
},
'治疗加成': {
title: "治疗加成",
pct: true
}
}
let posIdx = {
"生之花": {
idx: 1
},
"死之羽": {
idx: 2
},
"时之沙": {
idx: 3
},
"空之杯": {
idx: 4
},
"理之冠": {
idx: 5
}
};
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) {
let char = Character.get(data["英雄Id"]);
return {
id: data["英雄Id"],
name: char ? char.name : "",
lv: data['等级'],
attr: Data.getAttr(data),
// weapon: Data.getWeapon(data),
artis: Data.getArtifact(data),
//cons: data["命之座数量"] * 1 || 0,
//talent: Data.getTalent(data)
};
},
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);
ret.phyBonus = (data[`属性物理伤害加成`] * 100).toFixed(2);
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;
}
}

View File

@ -1,6 +1,9 @@
import {attrMark, maxMark, attrMap, usefulAttr} from "../../resources/meta/reliquaries/reliquaries-mark.js";
import lodash from "lodash";
//let meta = Data.readJSON("../../resources/meta/reliquaries", "data.json");
let Reliquaries = {
getUseful(char) {
let attrKey = usefulAttr[char] || "";
@ -78,6 +81,14 @@ let Reliquaries = {
return scoreMap[idx][0];
}
}
},
getSet(name) {
for (let idx in meta) {
if (meta[idx].name === name) {
return meta[idx];
}
}
}
}

File diff suppressed because it is too large Load Diff

141
tools/reli-data-sprider.js Normal file
View File

@ -0,0 +1,141 @@
import fs from "fs";
import cheerio from "cheerio";
import lodash from "lodash";
import fetch from "node-fetch";
const _path = process.cwd();
const artiIdx = {
Flower: 1,
Plume: 2,
Sands: 3,
Goblet: 4,
Circlet: 5
}
let getCharData = async function (url, key, name = '') {
url = "https://genshin.honeyhunterworld.com/" + url;
console.log('req' + key, url)
let req = await fetch(url);
let txt = await req.text();
const $ = cheerio.load(txt);
let ret = getBasic($, name);
name = ret.name;
return ret;
}
async function saveCharData(url, key, name) {
let data = await getCharData(url, key, name);
name = name || data.name;
if (!name) {
console.log("角色名不存在" + url);
return;
}
let charPath = `${_path}/plugins/miao-plugin/resources/meta/character/${data.name}/`
if (!fs.existsSync(charPath)) {
fs.mkdirSync(charPath);
}
fs.writeFileSync(`${charPath}data.json`, JSON.stringify(data, "", "\t"));
console.log(data.name + "下载完成");
}
function getEffect(txt) {
let regRet = /(\d) Piece:/.exec(txt);
if (regRet) {
return [regRet[1], txt.replace(regRet[0], "").trim().replace(/。$/, "")]
}
return [];
}
async function getSets(id) {
const url = `https://genshin.honeyhunterworld.com/db/art/family/a_${id}/?lang=CHS`;
let req = await fetch(url);
let txt = await req.text();
fs.writeFileSync("./test.html", txt);
let $ = cheerio.load(txt);
let sets = $(".wrappercont .add_stat_table:eq(0)");
let ret = {}
sets.find("td").each(function () {
let line = $(this);
let nRet = /\s*(.*?)\s+\(([^\s]+)/.exec(line.find("span").text());
let idRet = /a_(\d+)/.exec(line.find("a").attr("href"));
if (nRet && idRet) {
ret[`arti${artiIdx[nRet[2]]}`] = {
id: idRet[1],
name: nRet[1]
}
}
})
return ret;
}
async function down() {
const url = "https://genshin.honeyhunterworld.com/db/artifact/?lang=CHS";
let req = await fetch(url);
let txt = await req.text();
let $ = cheerio.load(txt);
let char = $(".art_stat_table_new");
let ret = {}
let tmp = {};
char.each(function () {
let trs = $(this).find("tr:gt(0)");
trs.each(function (idx) {
let self = $(this);
if (idx % 2 === 0) {
let na = self.find("td:eq(2) a")
let idRet = /a_(\d+)/.exec(na.attr("href"))
if (idRet) {
tmp = {
id: idRet[1],
name: na.text(),
sets: {},
effect: {}
}
} else {
tmp = {effect: {}}
}
}
let et = getEffect(self.find(`td:eq(${idx % 2 === 0 ? 4 : 1})`).text());
if (et && et[0]) {
tmp.effect[et[0]] = et[1];
}
if (idx % 2 === 1 && tmp.id) {
ret[tmp.id] = tmp;
}
})
});
for (let idx in ret) {
let ds = ret[idx];
ds.sets = await getSets(ds.id);
console.log(`arti ${ds.id}:${ds.name} Done`);
}
let filePath = `${_path}/plugins/miao-plugin/resources/meta/reliquaries/`
fs.writeFileSync(`${filePath}data.json`, JSON.stringify(ret, "", "\t"));
}
//await saveCharData("https://genshin.honeyhunterworld.com/db/char/ayaka/?lang=CHS", "ayaka");
await down();

View File

@ -0,0 +1,89 @@
import {Data} from "../components/index.js";
import lodash from "lodash";
import fs from "fs";
import request from "request";
const _path = process.cwd() + "/plugins/miao-plugin/resources/meta/reliquaries/";
let relis = Data.readJSON(_path, 'data.json');
for(let idx in relis){
}
lodash.forEach(readDir, (c) => {
console.log(c);
if (!fs.existsSync(`${_cRoot}/${c}/data.json`)) {
return;
}
let char = Data.readJSON(`${_cRoot}/${c}/`, 'data.json');
if (char.name) {
// 正面
// 角色条
img(char, char.imgs.profile, "profile.png");
// 名片
img(char, char.imgs.party, "party.png");
// img(char, char.imgs.char, "char.png");
// 立绘-竖版
img(char, char.imgs.gacha_card, "gacha_card.png");
// 立绘
img(char, char.imgs.gacha_splash, "gacha_splash.png");
// 正面像
img(char, char.imgs.face, "face.png");
img(char, char.imgs.side, "face.png");
// 天赋
if (char.talent) {
img(char, char.talent.a.icon, "talent_a.png");
img(char, char.talent.e.icon, "talent_e.png");
img(char, char.talent.q.icon, "talent_q.png");
}
// 被动天赋
lodash.forEach(char.passive, (p, idx) => {
img(char, p.icon, `passive_${idx}.png`);
});
// 命座
lodash.forEach(char.cons, (con, idx) => {
img(char, con.icon, `cons_${idx}.png`)
});
}
})
let cacheFile = async function () {
let cacheFn = async function (file) {
if (fs.existsSync(`${_cRoot}/${file.file}`)) {
console.log(`已存在,跳过 ${file.file}`);
return true;
}
try {
let stream = fs.createWriteStream(`${_cRoot}/${file.file}`);
await request("https://genshin.honeyhunterworld.com/" + file.url).pipe(stream);
return new Promise((resolve) => {
stream.on('finish', resolve)
});
} catch (e) {
return false;
}
console.log(`下载成功: ${file.file}`);
return true;
};
console.log('开始下载');
await Data.asyncPool(5, imgs, cacheFn);
}
await cacheFile();
console.log('下载成功');