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 @@ + + + + + + + + + + +
+
+
+ {{if chooseFloor == -1}} +
深渊出场率统计
+
全角色出场率统计(出场记录/总记录)
+ {{else}} +
深渊第{{floorName[chooseFloor]}}出场率
+
全角色出场率统计(出场记录/总记录)
+ {{/if}} + +
+ + {{each abyss ds}} + {{if chooseFloor == -1 || chooseFloor == ds.floor}} +
+
+ + 第{{floorName[ds.floor]}} + +
+
+ {{each ds.avatars char}} +
+ + +
{{pct(char.value)}}%
+
+ {{/each}} + +
+
+ + {{/if}} + {{/each}} +

数据来源:DGP-Studio-胡桃API . 最后更新时间:{{lastUpdate}}

+ +
+
+ + + \ No newline at end of file diff --git a/resources/stat/bg1.png b/resources/stat/bg1.png new file mode 100644 index 00000000..cbf068c2 Binary files /dev/null and b/resources/stat/bg1.png differ diff --git a/resources/stat/character.css b/resources/stat/character.css new file mode 100644 index 00000000..30cbcb64 --- /dev/null +++ b/resources/stat/character.css @@ -0,0 +1,282 @@ + +.weapon_mode .for_talent { + display: none !important; +} + +.talent_mode .for_weapon { + display: none !important; +} + + +.data-box { + border-radius: 15px; + margin-top: 20px; + margin-bottom: 20px; + padding: 0px 15px 5px 15px; + overflow: hidden; + background: #f5f5f5; + box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%); + position: relative; +} + +.tab_lable { + position: absolute; + top: -10px; + left: -8px; + background: #a98242; + color: #fff; + font-size: 14px; + padding: 3px 10px; + border-radius: 15px 0px 15px 15px; + z-index: 20; +} + +.data_line { + display: flex; + justify-content: space-around; + margin-bottom: 14px; +} + +.data_line_item { + width: 100px; + text-align: center; + /*margin: 0 20px;*/ +} + +.num { + font-family: tttgbnumber; + font-size: 24px; +} + +.num .unit { + font-size: 12px; +} + + + +.data-box { + +} + +.char-list { + display: table; + border-collapse: collapse; + width: calc(100% + 30px); + margin: 0 -15px -5px; + font-size: 12px; + /* border-radius: 0 0 15px 15px; */ + overflow: hidden; +} + +.char-list .avatar { + display: table-row; + font-family: tttgbnumber; + overflow: visible; +} + +.char-list .avatar { + display: table-row; +} + +.char-list .avatar > div { + box-shadow: 0 0 1px 0 #555 inset; +} + +.char-list .avatar:nth-child(odd) { + background: #e0e0e0; +} + +.char-list .avatar:nth-child(even) { + +} + +.char-list .avatar:nth-child(1) { + background: #ccc; + +} + +.char-list .avatar > div { + display: table-cell; + text-align: center; + height: 30px; + vertical-align: middle; + line-height: 30px; +} + +.char-list .avatar .index { + color: #333; + width: 30px; + padding-left: 5px; + +} + +.char-list .avatar .name_cont { + width: 80px; +} + + +.char-list .avatar .star4 { + background: rgba(137, 189, 233, .6); +} + +.char-list .avatar .star5 { + background: rgba(239, 214, 137, .6); +} + +.char-list .avatar .name_cont { + width: 80px; +} + +.char-list .avatar .name { + text-align: left; + display: flex; + width: 80px; +} + +.char-list .th, +.char-list .th div{ + font-weight: bold; + height:40px; + line-height:40px; + overflow: hidden; +} + +.char-list .th .name { + justify-content: center; +} + +.char-list .avatar .name .avatar_img { + width: 26px; + height: 26px; + position: relative; + margin-right: 3px; +} + +.char-list .avatar .name img { + width: 100%; + height: 100%; + position: absolute; + top: -3px; + margin-left: 3px; +} + +.char-list .avatar .name .avatar_name { + white-space: nowrap; + overflow: hidden; + width: 48px; +} + +.char-list .avatar .res { + font-size: 12px; + width: 90px; +} + +.char-list .avatar .res img { + width: 20px; + height: 20px; + vertical-align: middle; +} + +.char-list .avatar > div.lvl90 { +} + +.char-list .avatar > div.fetter10 { + background: url("./hart.png") center center no-repeat; + background-size: contain; + color: #fff; +} + +.char-list .cons { + width: 400px; + position: relative; + z-index: 98; + overflow: visible; +} + +.char-list .cons-pct, +.char-list .cons-bg { + display: flex; +} + +.char-list .th .cons-pct { + margin: 0; + color: #fff; + font-weight: bold; +} + +.char-list .th .cons-pct > div:first-child { + padding-left: 10px; +} + +.char-list .th .cons-pct > div:last-child { + padding-right: 10px; +} + + +.char-list .cons-pct { + margin: 0 10px; + z-index: 100; + position: relative; + color: #fff; +} + +.char-list .cons-pct > div { + flex: 1; + font-size: 12px; + mix-blend-mode: difference; + font-weight: normal; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.7); +} + +.char-list .cons-bg { + position: absolute; + left: 5px; + right: 5px; + bottom: 5px; + height: 20px; + z-index: 99; + border-radius: 3px; + overflow: hidden; + background: #888; +} + +.char-list .life_bg { + background: #888; +} + +.char-list .cons-bg > div:last-child { + border-radius: 0 3px 3px 0; +} + +.char-list .cons-bg > div { + height: 20px; +} + +.char-list .life0 { + background: #5b8185 +} + +.char-list .life1 { + background: #ce881f; +} + +.char-list .life2 { + background: #7ba824; +} + +.char-list .life3 { + background: #3e8853; +} + +.char-list .life4 { + background: #1a426e; +} + +.char-list .life5 { + background: #632e62; +} + +.char-list .life6 { + background: #9b2d1f; +} + diff --git a/resources/stat/character.html b/resources/stat/character.html new file mode 100644 index 00000000..74a91a60 --- /dev/null +++ b/resources/stat/character.html @@ -0,0 +1,88 @@ + + + + + + + + + + +
+
+
+ {{if mode === "char"}} +
角色持有率统计
+
全角色综合持有率统计
+ {{else}} +
角色{{conNum == -1 ? "命座" : "零一二三四五满"[conNum]+"命"}}分布统计
+
统计所有角色命座,无论角色是否出场
+ {{/if}} + + +
+ +
+
+
+
#
+
+ 角色 +
+
持有率
+
+
+
0命
+
1命
+
2命
+
3命
+
4命
+
5命
+
6命
+ {{if mode === "char"}} +
+ {{/if}} +
+ +
+
+ {{each chars char idx}} +
+
{{idx+1}}
+
+
+
+ +
+
+ {{abbr[char.name] || char.name}} +
+
+
+
{{pct(char.hold)}}
+
+
+ {{each char.cons con idx}} +
{{pct(con.value)}}
+ {{/each}} + {{if mode ==="char"}} +
{{pct(1-char.hold)}}
+ {{/if}} +
+
+ {{each char.cons con idx}} +
+ {{/each}} +
+
+
+ {{/each}} +
+

数据来源:DGP-Studio-胡桃API . 最后更新时间:{{lastUpdate}}

+
+ +
+
+ + + \ No newline at end of file diff --git a/resources/stat/common.css b/resources/stat/common.css new file mode 100644 index 00000000..a14699cb --- /dev/null +++ b/resources/stat/common.css @@ -0,0 +1,103 @@ +@font-face { + font-family: "tttgbnumber"; + src: url("../../font/tttgbnumber.ttf"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: HYWenHei-55W; + src: url('../../font/HYWenHei-55W.ttf'); +} + +@font-face { + font-family: "NZBZ"; + src: url("../font/NZBZ.ttf"); + font-weight: normal; + font-style: normal; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + user-select: none; +} + +body { + font-size: 18px; + color: #1e1f20; + font-family: PingFangSC-Medium, PingFang SC, sans-serif; + transform: scale(1.4); + transform-origin: 0 0; + background: url("./bg1.png") top left no-repeat #2a3860; + background-size: contain; + width: 600px; +} + +.container { + width: 600px; + padding: 20px 15px 10px 15px; + background: url("./footer.png") left bottom no-repeat; + background-size: contain; +} + + +.head-box { + border-radius: 15px; + padding: 10px 20px; + position: relative; + color: #fff; + margin-top:30px; + +} + +.head-box .title { + font-size: 36px; + font-family: NZBZ; +} + + +.head-box .genshin_logo { + position: absolute; + top: 1px; + right: 15px; + width: 97px; +} +.head-box .label{ + font-size:16px; +} + + + + + + + + + + + +.notice { + color: #888; + font-size: 12px; + text-align: right; + padding: 12px 5px 5px; +} +.notice-center{ + color:#fff; + text-align: center; + margin-bottom:10px; + text-shadow:1px 1px 1px #333; +} +.copyright { + font-size: 16px; + font-family: "tttgbnumber"; + text-align: center; + color: #fff; + position: relative; + text-align: center; + padding-left: 10px; + text-shadow:1px 1px 1px #000; + margin-bottom:10px; +} diff --git a/resources/stat/footer.png b/resources/stat/footer.png new file mode 100644 index 00000000..69a851f5 Binary files /dev/null and b/resources/stat/footer.png differ diff --git a/说明.txt b/说明.txt index 7dd95273..46ea5aed 100644 --- a/说明.txt +++ b/说明.txt @@ -10,13 +10,20 @@ git地址:https://gitee.com/yoimiya-kokomi/miao-plugin Miao-Plugin需要最新版本的Yunzai-Bot 将整个miao-plugin文件夹放置在Yunzai-Bot/plugins/目录下,重启Yunzai-Bot即可使用。 -【命令说明】 +【#老婆 命令说明】 #心海 #神里 #宵宫 : 查询角色卡片,与Yunzai命令一致,会以新版卡片样式进行展示 #老婆 #老公 #女朋友:展示老婆、老公卡片,默认会在角色池符合条件的5个内随机挑选 #老婆设置心海,神里,宵宫 :设置老婆,如需设置多个老婆可逗号分割。未持有或不符合的角色会自动忽略 #老婆设置随机 : 设置为随机,角色池中所有符合条件的角色都会作为备选随机展示 #老婆写真 #老婆照片: 会返回不包含任何信息的纯图片 +【#角色/#深渊 命令说明】 +#角色持有率:查看当前所有玩家角色持有率统计 +#角色命座:查看当前所有玩家角色命座分布统计。如需查看自己的角色命座请使用 #命座 命令 +#深渊出场率:查看当期9-12层深渊出场前14的角色 +#深渊12层出场率:查看当期12层深渊所有角色的出场率 +> 此版块的数据源来自 DGP-Studio-胡桃API + 【自定义角色背景图】 如需添加自定义的图片,可放置在 miao-plugin/resources/characterImg 的对应角色名的目录下 支持 jpg/jpeg/png/webp 格式的图片