miao-plugin/apps/stat.js
yoimiya-kokomi b9eb8695ed * #刻晴 角色卡片功能升级
* `#老婆设置刻晴,心海`不再检查是否具有角色或展示在米游社展柜
    * `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据
    * 在未能获取到角色数据时也会展示角色卡片
    * 角色卡片后续会进行样式升级,按需展示更多内容
* `#深渊出场率`增加楼层排序,以防止服务侧数据乱序导致顺序错乱
* B服面板的天赋与皇冠信息现在可以正确的展示了
2022-06-30 04:40:06 +08:00

388 lines
8.9 KiB
JavaScript

/*
* 胡桃数据库的统计
*
* */
import { HutaoApi, Character } from "../components/models.js";
import { Cfg } from "../components/index.js";
import lodash from "lodash";
import { segment } from "oicq";
import fs from "fs";
import Common from "../components/Common.js";
export async function consStat(e, { render }) {
if (Cfg.isDisable(e, "wiki.abyss")) {
return;
}
let consData = await HutaoApi.getCons();
if (!consData) {
e.reply("角色持有数据获取失败,请稍后重试~");
return true;
}
let msg = e.msg;
let mode = /持有/.test(msg) ? "char" : "cons";
let conNum = -1;
if (mode === "cons") {
lodash.forEach([/0|零/, /1|一/, /2|二/, /3|三/, /4|四/, /5|五/, /6|六|满/], (reg, idx) => {
if (reg.test(msg)) {
conNum = idx;
return false;
}
})
}
if (!consData && !consData.data) {
return true;
}
let data = consData.data;
let Lumine = lodash.filter(data, (ds) => ds.avatar === 10000007)[0] || {},
Aether = lodash.filter(data, (ds) => ds.avatar === 10000005)[0] || {};
Lumine.holdingRate = (1 - Aether.holdingRate) || Lumine.holdingRate;
let ret = [];
lodash.forEach(data, (ds) => {
let char = Character.get(ds.avatar);
let data = {
name: char.name || ds.avatar,
star: char.star || 3,
hold: ds.holdingRate
};
if (mode === "char") {
data.cons = lodash.map(ds.rate, (c) => {
c.value = c.value * ds.holdingRate;
return c;
});
} else {
data.cons = ds.rate
}
data.cons = lodash.sortBy(data.cons, ['id'])
ret.push(data);
});
if (conNum > -1) {
ret = lodash.sortBy(ret, [`cons[${conNum}].value`]);
ret.reverse();
} else {
ret = lodash.sortBy(ret, ['hold']);
}
//渲染图像
return await Common.render("stat/character", {
chars: ret,
abbr: Character.getAbbr(),
mode: mode,
conNum,
lastUpdate: consData.lastUpdate,
pct: function (num) {
return (num * 100).toFixed(2);
},
}, { e, render, scale: 1.5 });
}
export async function abyssPct(e, { render }) {
if (Cfg.isDisable(e, "wiki.abyss")) {
return;
}
let abyssData = await HutaoApi.getAbyssPct();
if (!abyssData) {
e.reply("深渊出场数据获取失败,请稍后重试~");
return true;
}
let ret = [], chooseFloor = -1, msg = e.msg;
const floorName = {
"12": "十二层",
"11": "十一层",
"10": "十层",
"9": "九层"
};
// 匹配深渊楼层信息
lodash.forEach(floorName, (cn, num) => {
let reg = new RegExp(`${cn}|${num}`);
if (reg.test(msg)) {
chooseFloor = num;
return false;
}
});
let data = abyssData.data;
data = lodash.sortBy(data, "floor");
lodash.forEach(data, (floorData) => {
let floor = {
floor: floorData.floor,
};
let avatars = [];
lodash.forEach(floorData.avatarUsage, (ds) => {
let char = Character.get(ds.id);
if (char) {
avatars.push({
name: char.name,
star: char.star,
value: ds.value * 8
})
}
})
avatars = lodash.sortBy(avatars, "value", ["asc"]);
avatars.reverse();
if (chooseFloor === -1) {
avatars = avatars.slice(0, 14);
}
ret.push({
floor: floorData.floor,
avatars
});
})
return await Common.render("stat/abyss-pct", {
abyss: ret,
floorName,
chooseFloor,
lastUpdate: abyssData.lastUpdate,
pct: function (num) {
return (num * 100).toFixed(2);
},
}, { e, render, scale: 1.5 });
}
async function getTalentData(e, isUpdate = false) {
// 技能查询缓存
let cachePath = `./data/cache/`;
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath);
}
cachePath += "talentList/";
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath);
}
let avatarRet = [];
let uid = e.selfUser.uid;
let hasCache = await redis.get(`cache:uid-talent-new:${uid}`);
if (hasCache) {
// 有缓存优先使用缓存
let jsonRet = fs.readFileSync(cachePath + `${uid}.json`, "utf8");
avatarRet = JSON.parse(jsonRet);
return avatarRet;
} else if (!isUpdate) {
e.noReplyTalentList = true;
await YunzaiApps.mysInfo.talentList(e);
return await getTalentData(e, true);
}
return false;
}
export async function abyssTeam(e, { render }) {
let MysApi = await e.getMysApi({
auth: "cookie", // 所有用户均可查询
targetType: "self", // 被查询用户可以是任意用户
cookieType: "self" // cookie可以是任意可用cookie
});
if (!MysApi || !MysApi.selfUser || !MysApi.selfUser.uid) {
return true;
}
let abyssData = await HutaoApi.getAbyssTeam();
if (!abyssData || !abyssData.data) {
e.reply("深渊组队数据获取失败,请稍后重试~");
return true;
}
abyssData = abyssData.data;
let talentData = await getTalentData(e);
if (!talentData || talentData.length === 0) {
e.reply("暂时未能获得角色的练度信息,请使用【#练度统计】命令尝试手工获取...");
return true;
}
let avatarRet = {};
let data = {};
let noAvatar = {};
lodash.forEach(talentData, (avatar) => {
avatarRet[avatar.id] = Math.min(avatar.level, avatar.weapon_level) * 100 + Math.max(avatar.a_original, avatar.e_original, avatar.q_original) * 1000
});
let getTeamCfg = (str) => {
let teams = str.split(",");
teams.sort();
let teamMark = 0;
lodash.forEach(teams, (a) => {
if (!avatarRet[a]) {
teamMark = -1;
noAvatar[a] = true;
}
if (teamMark !== -1) {
teamMark += avatarRet[a] * 1;
}
})
if (teamMark === -1) {
teamMark = 1;
}
return {
key: teams.join(","),
mark: teamMark
};
}
let hasSame = function (team1, team2) {
for (let idx = 0; idx < team1.length; idx++) {
if (team2.includes(team1[idx])) {
return true;
}
}
return false;
}
lodash.forEach(abyssData, (ds) => {
let floor = ds.level.floor;
if (!data[floor]) {
data[floor] = {
up: {},
down: {},
teams: []
};
}
lodash.forEach(ds.teams, (ds) => {
lodash.forEach(['up', 'down'], (halfKey) => {
let teamCfg = getTeamCfg(ds.id[`${halfKey}Half`]);
if (teamCfg) {
if (!data[floor][halfKey][teamCfg.key]) {
data[floor][halfKey][teamCfg.key] = {
count: 0,
mark: 0,
hasTeam: teamCfg.mark > 1
};
}
data[floor][halfKey][teamCfg.key].count += ds.value;
data[floor][halfKey][teamCfg.key].mark += ds.value * teamCfg.mark;
}
})
});
let temp = [];
lodash.forEach(['up', 'down'], (halfKey) => {
lodash.forEach(data[floor][halfKey], (ds, team) => {
temp.push({
team,
teamArr: team.split(","),
half: halfKey,
count: ds.count,
mark: ds.mark,
mark2: 1,
hasTeam: ds.hasTeam
})
})
temp = lodash.sortBy(temp, "mark")
data[floor].teams = temp.reverse();
});
});
let ret = {};
lodash.forEach(data, (floorData, floor) => {
ret[floor] = {}
let ds = ret[floor];
lodash.forEach(floorData.teams, (t1) => {
if (t1.mark2 <= 0) {
return;
}
lodash.forEach(floorData.teams, (t2) => {
if (t1.mark2 <= 0) {
return false;
}
if (t1.half === t2.half || t2.mark2 <= 0) {
return;
}
let teamKey = t1.half === "up" ? (t1.team + "+" + t2.team) : (t2.team + "+" + t1.team);
if (ds[teamKey]) {
return;
}
if (hasSame(t1.teamArr, t2.teamArr)) {
return;
}
ds[teamKey] = {
up: t1.half === "up" ? t1 : t2,
down: t1.half === "up" ? t2 : t1,
count: Math.min(t1.count, t2.count),
mark: t1.hasTeam && t2.hasTeam ? t1.mark + t2.mark : t1.count + t2.count // 如果不存在组队则进行评分惩罚
}
t1.mark2--;
t2.mark2--;
return false;
});
if (lodash.keys(ds).length >= 20) {
return false;
}
})
});
lodash.forEach(ret, (ds, floor) => {
ds = lodash.sortBy(lodash.values(ds), 'mark');
ds = ds.reverse();
ds = ds.slice(0, 4);
lodash.forEach(ds, (team) => {
team.up.teamArr = Character.sortIds(team.up.teamArr);
team.down.teamArr = Character.sortIds(team.down.teamArr);
})
ret[floor] = ds;
})
let avatarMap = {};
lodash.forEach(talentData, (ds) => {
avatarMap[ds.id] = {
id: ds.id,
name: ds.name,
star: ds.rarity,
level: ds.level,
cons: ds.cons
}
})
lodash.forEach(noAvatar, (d, id) => {
let char = Character.get(id);
avatarMap[id] = {
id,
name: char.name,
star: char.star,
level: 0,
cons: 0,
}
})
return await Common.render("stat/abyss-team", {
teams: ret,
avatars: avatarMap,
}, { e, render, scale: 1.5 });
}