初步增加练度统计

This commit is contained in:
yoimiya-kokomi 2022-08-07 06:36:05 +08:00
parent 8eee550d19
commit 0a175af506
17 changed files with 634 additions and 10 deletions

View File

@ -21,6 +21,9 @@ export { wife, pokeWife, wifeReg } from './character/avatar-wife.js'
// 面板角色列表
export { profileList } from './character/profile-list.js'
// 面板练度统计
export { profileStat } from './character/profile-stat.js'
// 查看当前角色
export async function character (e, { render }) {
let msg = e.original_msg || e.msg

View File

@ -1,7 +1,7 @@
import lodash from 'lodash'
import { autoRefresh, getTargetUid } from './profile-common.js'
import { Common, Profile } from '../../components/index.js'
import { Character } from '../../components/models.js';
import { Character } from '../../components/models.js'
export async function profileList (e, { render }) {
let uid = await getTargetUid(e)
@ -25,6 +25,9 @@ export async function profileList (e, { render }) {
let { id } = ds
let char = Character.get(id)
let tmp = char.getData('id,name,abbr,element,star')
if (tmp.name === '荧' || tmp.name === '空') {
return
}
tmp.source = ds.dataSource
tmp.level = ds.lv || 1
tmp.isNew = newChar[char.name] ? 1 : 0

View File

@ -0,0 +1,70 @@
import lodash from 'lodash'
import { Common } from '../../components/index.js'
import Avatars from '../../components/models/Avatars.js'
export async function profileStat (e, { render }) {
// 缓存时间,单位小时
let cacheCd = 6
let msg = e.msg.replace('#', '').trim()
if (msg === '角色统计' || msg === '武器统计') {
// 暂时避让一下抽卡分析的关键词
return false
}
let MysApi = await e.getMysApi({
auth: 'all',
targetType: 'all',
cookieType: 'all'
})
if (!MysApi) return true
let uid = MysApi.targetUid
/*
let star = 0
if (/(四|4)/.test(msg)) star = 4
if (/(五|5)/.test(msg)) star = 5
*/
let resIndex = await MysApi.getCharacter()
if (!resIndex) {
return true
}
let avatars = new Avatars(uid, resIndex.avatars)
let ids = avatars.getIds()
let talentData = await avatars.getTalentData(ids, MysApi)
// 天赋等级背景
const talentLvMap = '0,1,1,1,2,2,3,3,3,4,5'.split(',')
let avatarRet = []
lodash.forEach(talentData, (avatar) => {
let { talent } = avatar
avatar.aeq = talent?.a?.original + talent?.e?.original + talent?.q?.original || 3
avatarRet.push(avatar)
})
let sortKey = 'level,star,aeq,cons,weapon.level,weapon.star,weapon.affix,fetter'.split(',')
avatarRet = lodash.orderBy(avatarRet, sortKey)
avatarRet = avatarRet.reverse()
let noTalent = false
let talentNotice = `技能列表每${cacheCd}小时更新一次`
if (noTalent) {
talentNotice = '未绑定体力Cookie无法获取天赋列表。请回复 #体力 获取配置教程'
}
return await Common.render('character/profile-stat', {
save_id: uid,
uid,
talentLvMap,
avatars: avatarRet,
isSelf: e.isSelf,
talentNotice,
elem: 'hydro',
}, { e, render, scale: 1.8 })
}

View File

@ -17,7 +17,8 @@ export {
profileHelp,
getOriginalPicture,
uploadCharacterImg,
profileList
profileList,
profileStat
} from './character.js'
export {
@ -53,6 +54,10 @@ let rule = {
reg: '^#(面板角色|角色面板|面板)(列表)?\\s*(\\d{9})?$',
describe: '【#角色】查看当前已获取面板数据的角色列表'
},
profileStat: {
reg: '^#面板练度统计$',
describe: '【#角色】查看当前面板练度统计$'
},
profileHelp: {
reg: '^#角色面板帮助$',
describe: '【#角色】查看当前已获取面板数据的角色列表'

View File

@ -16,6 +16,7 @@ export async function consStat (e, { render }) {
}
let consData = await HutaoApi.getCons()
let overview = await HutaoApi.getOverview()
if (!consData) {
e.reply('角色持有数据获取失败,请稍后重试~')
@ -83,6 +84,7 @@ export async function consStat (e, { render }) {
abbr: Character.getAbbr(),
mode,
conNum,
totalCount: overview?.data?.totalPlayerCount || 0,
lastUpdate: consData.lastUpdate,
pct: function (num) {
return (num * 100).toFixed(2)
@ -108,6 +110,7 @@ export async function abyssPct (e, { render }) {
abyssData = await HutaoApi.getAbyssPct()
modeMulti = 8
}
let overview = await HutaoApi.getOverview()
if (!abyssData) {
e.reply(`深渊${modeName}数据获取失败,请稍后重试~`)
@ -168,6 +171,7 @@ export async function abyssPct (e, { render }) {
chooseFloor,
mode,
modeName,
totalCount: overview?.data?.collectedPlayerCount || 0,
lastUpdate: abyssData.lastUpdate
}, { e, render, scale: 1.5 })
}
@ -408,9 +412,10 @@ export async function uploadData (e, { render }) {
if (!MysApi || !MysApi.isSelfCookie) return false
let ret = {}
let uid = e.selfUser.uid
let resDetail, resAbyss
let resDetail, resAbyss, overview
try {
resAbyss = await MysApi.getSpiralAbyss(1)
overview = await HutaoApi.getOverview()
if (resAbyss.floors.length > 0 && !await Avatars.hasTalentCache(uid)) {
e.reply('正在获取用户信息,请稍候...')
}
@ -477,6 +482,7 @@ export async function uploadData (e, { render }) {
avatars: avatarData,
stat,
save_id: uid,
totalCount: overview?.data?.collectedPlayerCount || 0,
uid
}, { e, render, scale: 1.8 })
} else {

View File

@ -13,10 +13,16 @@ export default class Avatars extends Base {
}
this.uid = uid
let avatars = {}
let abbr = Character.getAbbr()
lodash.forEach(datas, (avatar) => {
let data = Data.getData(avatar, 'id,name,level,star:rarity,cons:actived_constellation_num,fetter')
data.abbr = abbr[data.name] || data.name
data.elem = (avatar.element || '').toLowerCase() || 'hydro'
data.weapon = Data.getData(avatar.weapon, 'name,affix:affix_level,level,star:rarity')
data.weapon.abbr = abbr[data?.weapon?.name || ''] || data?.weapon?.name
if (data.star > 5) {
data.star = 5;
}
let artis = {}
let sets = {}
lodash.forEach(avatar.reliquaries, (arti) => {
@ -53,6 +59,14 @@ export default class Avatars extends Base {
return rets
}
getIds () {
let rets = []
lodash.forEach(this.avatars, (ds) => {
rets.push(ds.id)
})
return rets
}
async getTalentData (ids, MysApi = false) {
let avatarTalent = {}
let talentCache = await redis.get(`genshin:avatar-talent:${this.uid}`)

View File

@ -166,6 +166,10 @@ class Character extends Base {
a.original = 10
}
}
if (this.id * 1 === 10000033) {
let a = ret.a || {}
a.original = a.level - 1
}
return ret
}

View File

@ -49,6 +49,10 @@ let HutaoApi = {
return await HutaoApi.req('/Statistics/TeamCombination')
},
async getOverview () {
return await HutaoApi.req('/Statistics/Overview')
},
async upload (data) {
let body = JSON.stringify(data)
return await HutaoApi.req('/Record/Upload', {

View File

@ -0,0 +1,187 @@
.container {
background-size: cover;
}
.lv {
width: 40px;
text-align: right;
padding-right: 2px;
}
.lv:before {
content: "Lv";
display: inline-block;
font-size: 12px;
}
#profile-stat {
display: table;
border-collapse: collapse;
width: calc(100% + 30px);
margin: 0 -15px -5px;
overflow: hidden;
}
#profile-stat .cont {
font-size: 14px;
background: none;
}
#profile-stat .cons {
height: 22px;
line-height: 22px;
display: inline-block;
width: 19px;
padding: 0 0 0 1px;
text-align: center;
vertical-align: middle;
border-radius: 0 3px 3px 0;
margin-left: -4px;
font-size: 13px;
}
#profile-stat .item-icon.star4,
#profile-stat .cons.star4 {
box-shadow: 0 0 0 1px #dfbeff;
}
#profile-stat .item-icon.star5,
#profile-stat .cons.star5 {
box-shadow: 0 0 0 1px #ffe4b4;
}
#profile-stat .item-name {
text-shadow: 0 0 1px #000, 1px 1px 2px rgba(0, 0, 0, 0.8);
}
#profile-stat .item-name.star4 {
color: #dfbeff;
}
#profile-stat .item-name.star5 {
color: #ffe4b4;
}
#profile-stat .tr > div {
text-align: center;
height: 36px;
vertical-align: middle;
line-height: 36px;
box-shadow: none;
}
#profile-stat .tr > div * {
vertical-align: middle;
}
#profile-stat .tr .index {
color: #333;
width: 30px;
padding-left: 5px;
}
#profile-stat .tr .td-name {
text-align: right;
width: 110px;
padding-right: 8px;
}
#profile-stat .tr .td-name .char-icon {
width: 30px;
height: 30px;
border-radius: 5px;
display: inline-block;
}
#profile-stat .tr .td-name .cons {
margin-left: -5px;
}
#profile-stat .tr .th .name {
justify-content: center;
}
#profile-stat .tr > div.fetter10 {
background: url("./hart.png") center center no-repeat;
background-size: contain;
color: #fff;
}
#profile-stat .tr .cons span {
display: inline-block;
width: 14px;
height: 18px;
line-height: 18px;
vertical-align: middle;
border-radius: 4px;
margin-bottom: 2px;
}
#profile-stat .tr .td-weapon {
text-align: left;
width: 160px;
padding-left: 10px;
}
#profile-stat .tr .td-weapon .weapon-icon {
border-radius: 3px 0 0 3px;
}
#profile-stat .tr .td-weapon .cons {
border-radius: 0 3px 3px 0;
}
#profile-stat .tr .weapon-icon {
width: 25px;
height: 22px;
display: inline-block;
overflow: visible;
margin-left: 3px;
}
#profile-stat .tr .weapon-icon .img {
width: 32px;
height: 32px;
margin: -5px 0 0 -3px;
}
#profile-stat .tr .weapon .avatar-weapon {
display: flex;
align-items: center;
}
#profile-stat .tr .weapon .weapon_lv {
width: 30px;
margin-left: 8px;
}
#profile-stat .tr .weapon .weapon_alv {
width: 14px;
text-align: center;
height: 18px;
line-height: 18px;
padding: 0 3px;
border-radius: 4px;
margin-right: 5px;
}
#profile-stat .tr .lv1 {
background: rgba(60, 63, 65, 0.2);
}
#profile-stat .tr .lv2 {
background: rgba(23, 184, 58, 0.3);
}
#profile-stat .tr .lv3 {
background: rgba(27, 128, 212, 0.3);
}
#profile-stat .tr .lv4 {
background: rgba(146, 90, 255, 0.35);
}
#profile-stat .tr .lv5 {
background: url("../common/item/crown-o.png") center center no-repeat rgba(255, 36, 26, 0.35);
background-size: contain;
}
#profile-stat .td-artis {
width: 120px;
}
#profile-stat .avatar-artis {
margin-left: 3px;
}
#profile-stat .avatar-artis .artis {
position: relative;
width: 30px;
height: 30px;
background: rgba(255, 255, 255, 0.3);
display: inline-block;
}
#profile-stat .avatar-artis.artis2 .img {
position: absolute;
width: 24px;
height: 24px;
}
#profile-stat .avatar-artis.artis2 .img:first-child {
left: -2px;
top: -2px;
}
#profile-stat .avatar-artis.artis2 .img:last-child {
right: -2px;
bottom: -2px;
}
.talent_notice {
color: #888;
text-align: right;
padding: 12px 5px 5px;
}
/*# sourceMappingURL=profile-stat.css.map */

View File

@ -0,0 +1,88 @@
{{extend elemLayout}}
{{block 'css'}}
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/profile-stat.css"/>
{{/block}}
{{block 'main'}}
<div class="head-box">
<div class="title">角色面板练度统计</div>
<div class="label">UID:{{uid}} 共{{avatars.length }}名角色</div>
</div>
<div id="profile-stat">
<div class="cont">
<div class="cont-title">角色详情列表</div>
<div class="cont-table">
<div class="avatar tr thead">
<div class="td-level">Lv</div>
<div class="td-name">角色</div>
<div class="td-fetter">好感</div>
<div class="td-talent">A</div>
<div class="td-talent">E</div>
<div class="td-talent">Q</div>
<div class="td-weapon">武器</div>
<div class="td-artis">圣遗物</div>
</div>
{{each avatars avatar idx}}
{{set talent = avatar.talent}}
{{set weapon = avatar.weapon}}
{{set tk = ['a','e','q'] }}
<div class="avatar tr">
<div class="td-level"> {{avatar.level}}</div>
<div class="td-name star{{avatar.star}}">
<div class="avatar-name">
<span class="item-name star{{avatar.star}}">{{avatar.abbr||avatar.name}}</span>
<div class="item-icon char-icon star{{avatar.star}}">
<span class="img"
style="background-image:url({{_res_path}}/meta/character/{{avatar.name}}/face.png)"></span>
</div>
<span class="cons cons-{{avatar.cons}}">{{avatar.cons}}</span>
</div>
</div>
<div class="td-fetter"> {{avatar.fetter}}</div>
{{each tk talentKey}}
{{set curr = (avatar.talent||{})[talentKey] || {original:1,current:'-'} }}
<div class="td-talent lv{{talentLvMap[curr.original]}} {{curr.current>curr.original?'talent-plus':''}}">
{{curr.current}}
</div>
{{/each}}
<div class="td-weapon weapon_{{avatar.weapon_rarity}}">
{{if weapon?.name}}
<span class="lv">{{weapon.level}}</span>
<div class="item-icon weapon-icon star{{weapon.star}}">
<span class="img"
style="background-image:url({{_res_path}}/meta/weapons/icon/{{weapon.name}}.png)"></span>
</div>
<span class="cons cons-{{weapon.affix+1}} star{{weapon.star}}">{{weapon.affix}}</span>
<span class="item-name star{{weapon.star}}">{{weapon.abbr}}</span>
{{/if}}
</div>
<div class="td-artis">
<div class="item avatar-artis artis{{avatar.sets.length}}">
<div class="artis item-icon">
{{each avatar.sets name}}
<span class="img"
style="background-image:url({{_res_path}}/meta/reliquaries/icon/{{name}}.png)"></span>
{{/each}}
</div>
123.4
</div>
</div>
</div>
{{/each}}
</div>
<p class="talent_notice">{{talentNotice}}</p>
</div>
</div>
{{/block}}

View File

@ -0,0 +1,241 @@
.container {
background-size: cover;
}
.lv {
&:before {
content: "Lv";
display: inline-block;
font-size: 12px;
}
width: 40px;
text-align: right;
padding-right: 2px;
}
#profile-stat {
display: table;
border-collapse: collapse;
width: calc(100% + 30px);
margin: 0 -15px -5px;
overflow: hidden;
.cont {
font-size: 14px;
background: none;
}
.cons {
height: 22px;
line-height: 22px;
display: inline-block;
width: 19px;
padding: 0 0 0 1px;
text-align: center;
vertical-align: middle;
border-radius: 0 3px 3px 0;
margin-left: -4px;
font-size: 13px;
}
.item-icon, .cons {
&.star4 {
box-shadow: 0 0 0 1px rgba(223, 190, 255, 1);
}
&.star5 {
box-shadow: 0 0 0 1px rgba(255, 228, 180, 1);
}
}
.item-name {
text-shadow: 0 0 1px #000, 1px 1px 2px rgba(0, 0, 0, .8);
&.star4 {
color: #dfbeff;
}
&.star5 {
color: #ffe4b4;
}
}
.tr {
& > div {
text-align: center;
height: 36px;
vertical-align: middle;
line-height: 36px;
box-shadow: none;
* {
vertical-align: middle;
}
}
.index {
color: #333;
width: 30px;
padding-left: 5px;
}
.td-name {
text-align: right;
width: 110px;
padding-right: 8px;
.char-icon {
width: 30px;
height: 30px;
border-radius: 5px;
display: inline-block;
}
.cons {
margin-left: -5px;
}
}
.th .name {
justify-content: center;
}
> div.fetter10 {
background: url("./hart.png") center center no-repeat;
background-size: contain;
color: #fff;
}
.cons span {
display: inline-block;
width: 14px;
height: 18px;
line-height: 18px;
vertical-align: middle;
border-radius: 4px;
margin-bottom: 2px;
}
.td-weapon {
text-align: left;
width: 160px;
padding-left: 10px;
.weapon-icon {
border-radius: 3px 0 0 3px;
}
.cons {
border-radius: 0 3px 3px 0;
}
}
.weapon-icon {
width: 25px;
height: 22px;
display: inline-block;
overflow: visible;
margin-left: 3px;
.img {
width: 32px;
height: 32px;
margin: -5px 0 0 -3px;
}
}
.weapon .avatar-weapon {
display: flex;
align-items: center;
}
.weapon .weapon_lv {
width: 30px;
margin-left: 8px;
}
.weapon .weapon_alv {
width: 14px;
text-align: center;
height: 18px;
line-height: 18px;
padding: 0 3px;
border-radius: 4px;
margin-right: 5px;
}
}
.tr {
.lv1 {
background: rgba(60, 63, 65, .2)
}
.lv2 {
background: rgba(23, 184, 58, .3)
}
.lv3 {
background: rgba(27, 128, 212, .3)
}
.lv4 {
background: rgba(146, 90, 255, .35);
}
.lv5 {
background: url("../common/item/crown-o.png") center center no-repeat rgba(255, 36, 26, .35);
background-size: contain;
}
}
.td-artis {
width: 120px;
}
.avatar-artis {
margin-left: 3px;
.artis {
position: relative;
width: 30px;
height: 30px;
background: rgba(255, 255, 255, .3);
display: inline-block;
}
&.artis2 {
.img {
position: absolute;
width: 24px;
height: 24px;
&:first-child {
left: -2px;
top: -2px;
}
&:last-child {
right: -2px;
bottom: -2px;
}
}
}
}
}
.talent_notice {
color: #888;
text-align: right;
padding: 12px 5px 5px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -31,14 +31,12 @@
</div>
</div>
<div class="item avatar-artis artis{{avatar.sets.length}}">
<div class="artis item-icon">
{{each avatar.sets name}}
<span class="img"
style="background-image:url({{_res_path}}/meta/reliquaries/icon/{{name}}.png)"></span>
{{/each}}
</div>
</div>
</div>
{{/if}}

View File

@ -1,6 +1,6 @@
{
"name": "女主",
"abbr": "女主",
"name": "",
"abbr": "",
"id": "",
"title": "",
"allegiance": "——",

View File

@ -26,7 +26,7 @@
<li>可通过 <strong>#深渊{{modeName}}</strong>/<strong>#深渊12层{{modeName}}</strong>来查看整体或指定层的{{modeName}}数据</li>
<li>由于是用户自主上传,数据可能有一定滞后,数据会在深渊开启后一段时间逐步稳定</li>
<li><strong>未经胡桃API开发团队允许不得擅自使用或大范围传播此数据</strong>。详情咨询群910780153</li>
<li>统计数据为本期深渊数据,数据更新时间:{{lastUpdate}}</li>
<li>统计数据为本期深渊数据,{{if totalCount}}本期已提交用户数:<strong>{{totalCount}}</strong>{{/if}}数据更新时间:{{lastUpdate}}</li>
</ul>
</div>
</div>

View File

@ -109,7 +109,8 @@
<ul class="cont-msg">
<li><strong>#上传深渊</strong>会上传你的角色列表及当期深渊挑战数据,<strong>不会上传其他信息</strong>,感谢支持,喵~</li>
<li>统计服务由SG团队<strong>胡桃API</strong>提供,上传的数据将会用于排名以及<strong>#深渊使用率</strong><strong>#角色持有率</strong>等统计</li>
<li>深渊排行为本期深渊排行,更新时间{{abyss.time}}。排名会随时间而更新,数据排名仅供娱乐~</li>
<li>深渊排行为本期深渊排行,{{if totalCount}}本期已提交用户数:<strong>{{totalCount}}</strong>{{/if}}更新时间{{abyss.time}}。</li>
<li>排名会随时间而更新,数据排名仅供娱乐~</li>
</ul>
</div>
</div>

View File

@ -35,7 +35,7 @@ return (num * 100).toFixed(2);
<li>上传命令<strong>仅会上传您的角色列表及当期深渊挑战数据</strong>,不会上传其他额外信息</li>
<li>由于是用户自主上传,数据可能有一定滞后。新角色的持有率会在卡池结束后一段时间逐步稳定</li>
<li><strong>未经胡桃API开发团队允许不得擅自使用或大范围传播此数据</strong>。详情咨询群910780153</li>
<li>数据最后更新时间:{{lastUpdate}}</li>
<li>{{if totalCount}}统计用户数:<strong>{{totalCount}}</strong>{{/if}}数据最后更新时间:{{lastUpdate}}</li>
</ul>
</div>
</div>