diff --git a/apps/character.js b/apps/character.js index 8488fd54..70f3dd0d 100644 --- a/apps/character.js +++ b/apps/character.js @@ -79,6 +79,8 @@ export async function wife(e, { render, User }) { action = msgRet[2] || "卡片", actionParam = msgRet[3] || ""; + actionParam = actionParam.toString(); + let targetCfg = lodash.find(relationMap, (cfg, key) => { cfg.key = key; return cfg.keyword.includes(target); diff --git a/apps/stat.js b/apps/stat.js new file mode 100644 index 00000000..b8e0dc6f --- /dev/null +++ b/apps/stat.js @@ -0,0 +1,153 @@ +/* +* 胡桃数据库的统计 +* +* */ +import { HutaoApi, Character } from "../components/models.js"; +import lodash from "lodash"; +import { segment } from "oicq"; + +export async function consStat(e, { render }) { + + 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 ret = []; + + lodash.forEach(consData.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 + } + + + ret.push(data); + }); + + if (conNum > -1) { + ret = lodash.sortBy(ret, [`cons[${conNum}].value`]); + ret.reverse(); + } else { + ret = lodash.sortBy(ret, ['hold']); + } + + let base64 = await render("stat", "character", { + chars: ret, + abbr: Character.getAbbr(), + mode: mode, + conNum, + lastUpdate: consData.lastUpdate, + pct: function (num) { + return (num * 100).toFixed(2); + } + }, "png"); + if (base64) { + e.reply(segment.image(`base64://${base64}`)); + } + return true; +} + +export async function abyssPct(e, { render }) { + 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; + } + }); + + console.log('floor', chooseFloor); + + + lodash.forEach(abyssData.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 + }) + } + }) + avatars = lodash.sortBy(avatars, "value", ["asc"]); + avatars.reverse(); + if (chooseFloor === -1) { + avatars = avatars.slice(0, 14); + } + + ret.push({ + floor: floorData.floor, + avatars + }); + }) + + + let base64 = await render("stat", "abyss-pct", { + abyss: ret, + floorName, + chooseFloor, + lastUpdate: abyssData.lastUpdate, + pct: function (num) { + return (num * 100).toFixed(2); + } + }, "png"); + if (base64) { + e.reply(segment.image(`base64://${base64}`)); + } + return true; + +} \ No newline at end of file diff --git a/components/models.js b/components/models.js index f720f741..0a8d601f 100644 --- a/components/models.js +++ b/components/models.js @@ -1,3 +1,4 @@ import Character from "./models/Character.js"; +import HutaoApi from "./models/HutaoApi.js"; -export { Character }; +export { Character, HutaoApi }; diff --git a/components/models/Character.js b/components/models/Character.js index 7476969a..a8eaa9cb 100644 --- a/components/models/Character.js +++ b/components/models/Character.js @@ -22,6 +22,8 @@ const elemName = { geo: "岩" }; +let genshin = await import(`file://${_path}/config/genshin/roleId.js`); + class Character extends Base { constructor(name) { @@ -103,6 +105,8 @@ let getMetaData = function (name) { // 处理基础信息 let ret = Data.getData(meta, "Name,Title,desc:Description,astro:AstrolabeName", metaCfg); + ret.star = /4star/.test(meta.Star) ? 4 : 5; + // 处理图像信息 ret.img = Data.getData(meta, "Weapon,Element,City,Profile,GachaCard,GachaSplash,Source", metaCfg); @@ -185,7 +189,12 @@ const getTalentData = function (data) { Character.get = function (val) { - let roleid = YunzaiApps.mysInfo['roleIdToName'](val); + let roleid; + if (typeof (val) === "number") { + roleid = val; + } else { + roleid = YunzaiApps.mysInfo['roleIdToName'](val); + } let name = YunzaiApps.mysInfo['roleIdToName'](roleid, true); if (!name) { return false; @@ -197,4 +206,8 @@ Character.get = function (val) { return characterMap[name]; }; +Character.getAbbr = function () { + return genshin.abbr; +} + export default Character; diff --git a/components/models/HutaoApi.js b/components/models/HutaoApi.js new file mode 100644 index 00000000..a57ec94c --- /dev/null +++ b/components/models/HutaoApi.js @@ -0,0 +1,66 @@ +/* +* 胡桃API Miao-Plugin 封装 +* https://github.com/DGP-Studio/DGP.Genshin.HutaoAPI +* +* +* */ + +import Base from "./Base.js"; +import fetch from "node-fetch"; + +const host = "https://hutao-api.snapgenshin.com"; + +let hutaoApi = null; + +function getApi(api) { + return `${host}${api}`; +} + +let HutaoApi = { + _auth: false, + async login() { + let response = await fetch(getApi('/Auth/Login'), { + method: 'POST', + headers: { + 'Content-Type': 'text/json; charset=utf-8', + }, + body: JSON.stringify({ + "Appid": "appid", + "Secret": "secret" + }) + }); + let res = await response.json(); + }, + async req(url, data) { + let cacheData = await redis.get(`hutao:${url}`); + if (cacheData) { + return JSON.parse(cacheData) + } + + let response = await fetch(getApi(`${url}?Authorization=demo`), { + method: "GET", + headers: { + Authorization: `Bearer{token}` + } + }); + let retData = await response.json(); + let d = new Date(); + retData.lastUpdate = `${d.toLocaleDateString()} ${d.toTimeString().substr(0, 5)}`; + await redis.set(`hutao:${url}`, JSON.stringify(retData), { EX: 3600 }); + return retData; + + }, + + // 角色持有及命座分布 + async getCons() { + return await HutaoApi.req("/Statistics/Constellation"); + }, + + async getAbyssPct() { + return await HutaoApi.req("/Statistics/AvatarParticipation"); + } + +}; + + +export default HutaoApi; \ No newline at end of file diff --git a/index.js b/index.js index 5368461e..cfaa6afb 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,8 @@ import { character, wife, wifeReg } from "./apps/character.js"; +import { consStat, abyssPct } from "./apps/stat.js"; //import { wiki } from "./apps/wiki.js"; -export { character, wife }; +export { character, wife, consStat, abyssPct }; export const rule = { @@ -17,6 +18,16 @@ export const rule = { priority: 203, describe: "【#老婆,#老公,#女儿】角色详情", }, + consStat: { + hrehash: true, + reg: "^#角色(持有|持有率|命座|命之座|.命)(分布|统计)?$", + priority: 200 + }, + abyssPct: { + prehash: true, + reg: "^#深渊(第?.{1,2}层)?(角色)?出场(率|统计)*$", + priority: 200 + } /* wiki: { reg: "^#*.*(缓存)$", diff --git a/resources/common/bg3.png b/resources/common/bg3.png new file mode 100644 index 00000000..44aa8c36 Binary files /dev/null and b/resources/common/bg3.png differ diff --git a/resources/common/bg4.png b/resources/common/bg4.png new file mode 100644 index 00000000..80795471 Binary files /dev/null and b/resources/common/bg4.png differ diff --git a/resources/common/bg5.png b/resources/common/bg5.png new file mode 100644 index 00000000..2630db5b Binary files /dev/null and b/resources/common/bg5.png differ diff --git a/resources/stat/abyss-pct.css b/resources/stat/abyss-pct.css new file mode 100644 index 00000000..7eb0c139 --- /dev/null +++ b/resources/stat/abyss-pct.css @@ -0,0 +1,92 @@ + +.card-list { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.card-list .item { + margin: 0px 0 10px 10px; + border-radius: 7px; + box-shadow: 0 2px 6px 0 rgb(132 93 90 / 30%); + height: 88px; + position: relative; + overflow: hidden; + background: #e7e5d9; +} + +.card-list .item img { + width: 70px; + height: 70px; + border-radius: 7px 7px 20px 0; +} + +.card-list .item.star5 img { + background-image: url(../common/bg5.png); + width: 100%; + height: 70px; + /*filter: brightness(1.1);*/ + background-size: 100%; + background-repeat: no-repeat; +} + +.card-list .item.star4 img { + width: 100%; + height: 70px; + background-image: url(../common/bg4.png); + background-size: 100%; + background-repeat: no-repeat; +} + +.card-list .item .num { + position: absolute; + top: 0px; + right: 0px; + z-index: 9; + font-size: 18px; + text-align: center; + color: #fff; + border-radius: 3px; + padding: 1px 5px; + border-radius: 3px; + background: rgb(0 0 0 / 50%); + font-family: "tttgbnumber"; +} + +.card-list .item .name, +.card-list .item .num_name { + position: absolute; + top: 70px; + left: 0px; + z-index: 9; + font-size: 12px; + text-align: center; + width: 100%; + height: 16px; + line-height: 18px; +} + +.card-list .item .num_name { + font-family: "tttgbnumber"; + font-size: 18px; +} + +.line_box { + height: 32px; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 16px; + color: #fff; + padding-bottom: 5px; + margin-top: 15px; + margin-bottom: 5px; +} + +.line_box .line { + height: 1px; + flex-grow: 1; + background-color: #ebebeb; + margin: 0px 10px; +} diff --git a/resources/stat/abyss-pct.html b/resources/stat/abyss-pct.html new file mode 100644 index 00000000..5fc0689c --- /dev/null +++ b/resources/stat/abyss-pct.html @@ -0,0 +1,54 @@ + + +
+ + + + + + + +数据来源:DGP-Studio-胡桃API . 最后更新时间:{{lastUpdate}}
+数据来源:DGP-Studio-胡桃API . 最后更新时间:{{lastUpdate}}
+