diff --git a/apps/gachalog.js b/apps/gachalog.js index 9cc8c1d..9e9c6cd 100644 --- a/apps/gachalog.js +++ b/apps/gachalog.js @@ -1,10 +1,9 @@ import { ZZZPlugin } from '../lib/plugin.js'; import _ from 'lodash'; import render from '../lib/render.js'; -import { ZZZNoteResp } from '../model/note.js'; import { rulePrefix } from '../lib/common.js'; -import { getAuthKey, getStoken } from '../lib/authkey.js'; -import { updateGachaLog } from '../lib/gacha.js'; +import { getAuthKey } from '../lib/authkey.js'; +import { anaylizeGachaLog, updateGachaLog } from '../lib/gacha.js'; export class GachaLog extends ZZZPlugin { constructor() { @@ -26,6 +25,10 @@ export class GachaLog extends ZZZPlugin { reg: `^${rulePrefix}抽卡帮助$`, fnc: 'gachaHelp', }, + { + reg: `^${rulePrefix}抽卡分析$`, + fnc: 'gachaLogAnalysis', + }, ], }); } @@ -87,4 +90,21 @@ export class GachaLog extends ZZZPlugin { await this.reply(msg); return false; } + + async gachaLogAnalysis() { + const uid = await this.getUID(); + if (!uid) { + return false; + } + await this.getPlayerInfo(); + const data = await anaylizeGachaLog(uid); + if (!data) { + await this.reply('未查询到抽卡记录,请先发送抽卡链接'); + return false; + } + const result = { + data, + }; + await render(this.e, 'gachalog/index.html', result); + } } diff --git a/lib/convert/char.js b/lib/convert/char.js index 4358cc3..f7bbb80 100644 --- a/lib/convert/char.js +++ b/lib/convert/char.js @@ -1,12 +1,12 @@ import settings from '../settings.js'; -import PartnerId2SpriteId from '../../resources/map/PartnerId2SpriteId.json'; +import PartnerId2SpriteId from '../../resources/map/PartnerId2SpriteId.json?json'; /** * * @param {string} id * @param {boolean} full 显示全称 * @param {boolean} en 是否为英文 - * @returns string + * @returns string | null */ export const IDToCharName = (id, full = true, en = false) => { const data = PartnerId2SpriteId?.[id]; @@ -19,7 +19,7 @@ export const IDToCharName = (id, full = true, en = false) => { /** * * @param {string} id - * @returns string + * @returns string | null */ export const IDToCharSprite = id => { const data = PartnerId2SpriteId?.[id]; @@ -29,7 +29,7 @@ export const IDToCharSprite = id => { /** * @param {string} name - * @returns string + * @returns string | null */ export const charNameToID = name => { for (const [id, data] of Object.entries(PartnerId2SpriteId)) { @@ -40,7 +40,7 @@ export const charNameToID = name => { /** * @param {string} name - * @returns string + * @returns string | null */ export const charNameToSprite = name => { for (const [_id, data] of Object.entries(PartnerId2SpriteId)) { @@ -51,19 +51,20 @@ export const charNameToSprite = name => { /** * @param {string} atlas - * @returns string + * @returns string | null */ -export const atlasToName = atlas => { +export const atlasToName = _atlas => { const atlas = settings.getConfig('atlas'); for (const [id, data] of Object.entries(atlas)) { - if (data.includes(atlas)) return id; + if (id === _atlas) return id; + if (data.includes(_atlas)) return id; } return null; }; /** * @param {string} atlas - * @returns string + * @returns string | null */ export const atlasToSprite = atlas => { const atlas = settings.getConfig('atlas'); @@ -75,7 +76,7 @@ export const atlasToSprite = atlas => { /** * @param {string} name - * @returns string + * @returns string | null */ export const atlasToID = name => { const atlas = settings.getConfig('atlas'); diff --git a/lib/convert/weapon.js b/lib/convert/weapon.js index 63b068d..22b026c 100644 --- a/lib/convert/weapon.js +++ b/lib/convert/weapon.js @@ -1,4 +1,4 @@ -import WeaponId2Sprite from '../../resources/map/WeaponId2Sprite.json'; +import WeaponId2Sprite from '../../resources/map/WeaponId2Sprite.json?json'; /** * @param {string} id diff --git a/lib/gacha.js b/lib/gacha.js index b70beb8..b2242de 100644 --- a/lib/gacha.js +++ b/lib/gacha.js @@ -127,3 +127,104 @@ export async function updateGachaLog(authKey, uid) { saveGachaLog(uid, previousLog); return previousLog; } + +const RANK_MAP = { + 4: 'S', + 3: 'A', + 2: 'B', +}; +const HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此']; +const NORMAL_LIST = [ + '「11号」', + '猫又', + '莱卡恩', + '丽娜', + '格莉丝', + '珂蕾妲', + '拘缚者', + '燃狱齿轮', + '嵌合编译器', + '钢铁肉垫', + '硫磺石', + '啜泣摇篮', +]; + +export async function anaylizeGachaLog(uid) { + const savedData = getGachaLog(uid); + if (!savedData) { + return null; + } + const result = []; + for (const name in savedData) { + const data = savedData[name].map( + item => + new SingleGachaLog( + item.uid, + item.gacha_id, + item.gacha_type, + item.item_id, + item.count, + item.time, + item.name, + item.lang, + item.item_type, + item.rank_type, + item.id + ) + ); + const earliest = data[data.length - 1]; + const latest = data[0]; + const list = []; + let lastFive = `${data.length}`; + let preIndex = 0; + let luck = 0; + data.forEach((item, i) => { + let isUp = true; + if (item.rank_type === '4') { + if (NORMAL_LIST.includes(item.name)) { + isUp = false; + } + if (lastFive === `${data.length}`) { + lastFive = `${i + 1}`; + } + list.push({ + ...item, + rank_type_label: RANK_MAP[item.rank_type], + isUp: isUp, + }); + if (list.length > 0) { + list[list.length - 1]['totalCount'] = i - preIndex; + } + preIndex = i; + } + if (i === data.length - 1 && list.length > 0) { + list[list.length - 1]['totalCount'] = i - preIndex; + } + }); + const upCount = list.length; + const totalCount = data.length; + const fiveStars = list.length; + logger.mark('fiveStars', fiveStars); + logger.mark('totalCount', totalCount); + let timeRange = '还没有抽卡'; + let avgFive = '-'; + let avgUp = '-'; + if (data.length > 0) { + timeRange = `${latest.time} ~ ${earliest.time}`; + if (fiveStars > 0) avgFive = (totalCount / fiveStars).toFixed(1); + if (upCount > 0) avgUp = (totalCount / upCount).toFixed(1); + } + result.push({ + name, + timeRange, + list, + lastFive, + fiveStars, + upCount, + totalCount, + avgFive, + avgUp, + }); + } + return result; +} diff --git a/model/gacha.js b/model/gacha.js index e5588d4..b9f7532 100644 --- a/model/gacha.js +++ b/model/gacha.js @@ -48,7 +48,7 @@ export class SingleGachaLog { equals(item) { return ( this.uid === item.uid && - this.gacha_id === item.gacha_id && + this.id === item.id && this.gacha_type === this.gacha_type ); } diff --git a/resources/gachalog/images/IconTabUP.png b/resources/gachalog/images/IconTabUP.png new file mode 100644 index 0000000..1b6c231 Binary files /dev/null and b/resources/gachalog/images/IconTabUP.png differ diff --git a/resources/gachalog/images/RANK_A.png b/resources/gachalog/images/RANK_A.png new file mode 100644 index 0000000..b8210ca Binary files /dev/null and b/resources/gachalog/images/RANK_A.png differ diff --git a/resources/gachalog/images/RANK_B.png b/resources/gachalog/images/RANK_B.png new file mode 100644 index 0000000..5e06d32 Binary files /dev/null and b/resources/gachalog/images/RANK_B.png differ diff --git a/resources/gachalog/images/RANK_S.png b/resources/gachalog/images/RANK_S.png new file mode 100644 index 0000000..6963cca Binary files /dev/null and b/resources/gachalog/images/RANK_S.png differ diff --git a/resources/gachalog/images/bg1.png b/resources/gachalog/images/bg1.png new file mode 100644 index 0000000..c796909 Binary files /dev/null and b/resources/gachalog/images/bg1.png differ diff --git a/resources/gachalog/images/bg2.png b/resources/gachalog/images/bg2.png new file mode 100644 index 0000000..f9976a5 Binary files /dev/null and b/resources/gachalog/images/bg2.png differ diff --git a/resources/gachalog/images/bg3.png b/resources/gachalog/images/bg3.png new file mode 100644 index 0000000..b7b6ff9 Binary files /dev/null and b/resources/gachalog/images/bg3.png differ diff --git a/resources/gachalog/images/bg4.png b/resources/gachalog/images/bg4.png new file mode 100644 index 0000000..c796909 Binary files /dev/null and b/resources/gachalog/images/bg4.png differ diff --git a/resources/gachalog/images/emoji/1.png b/resources/gachalog/images/emoji/1.png new file mode 100644 index 0000000..dcc952e Binary files /dev/null and b/resources/gachalog/images/emoji/1.png differ diff --git a/resources/gachalog/images/emoji/10.png b/resources/gachalog/images/emoji/10.png new file mode 100644 index 0000000..b5c42b0 Binary files /dev/null and b/resources/gachalog/images/emoji/10.png differ diff --git a/resources/gachalog/images/emoji/11.png b/resources/gachalog/images/emoji/11.png new file mode 100644 index 0000000..4a93726 Binary files /dev/null and b/resources/gachalog/images/emoji/11.png differ diff --git a/resources/gachalog/images/emoji/12.png b/resources/gachalog/images/emoji/12.png new file mode 100644 index 0000000..7894285 Binary files /dev/null and b/resources/gachalog/images/emoji/12.png differ diff --git a/resources/gachalog/images/emoji/13.png b/resources/gachalog/images/emoji/13.png new file mode 100644 index 0000000..e677d89 Binary files /dev/null and b/resources/gachalog/images/emoji/13.png differ diff --git a/resources/gachalog/images/emoji/14.png b/resources/gachalog/images/emoji/14.png new file mode 100644 index 0000000..1383014 Binary files /dev/null and b/resources/gachalog/images/emoji/14.png differ diff --git a/resources/gachalog/images/emoji/15.png b/resources/gachalog/images/emoji/15.png new file mode 100644 index 0000000..dbaebbc Binary files /dev/null and b/resources/gachalog/images/emoji/15.png differ diff --git a/resources/gachalog/images/emoji/16.png b/resources/gachalog/images/emoji/16.png new file mode 100644 index 0000000..219b8a3 Binary files /dev/null and b/resources/gachalog/images/emoji/16.png differ diff --git a/resources/gachalog/images/emoji/2.png b/resources/gachalog/images/emoji/2.png new file mode 100644 index 0000000..7d037e3 Binary files /dev/null and b/resources/gachalog/images/emoji/2.png differ diff --git a/resources/gachalog/images/emoji/3.png b/resources/gachalog/images/emoji/3.png new file mode 100644 index 0000000..61bf14e Binary files /dev/null and b/resources/gachalog/images/emoji/3.png differ diff --git a/resources/gachalog/images/emoji/4.png b/resources/gachalog/images/emoji/4.png new file mode 100644 index 0000000..9b8fcfc Binary files /dev/null and b/resources/gachalog/images/emoji/4.png differ diff --git a/resources/gachalog/images/emoji/5.png b/resources/gachalog/images/emoji/5.png new file mode 100644 index 0000000..853a7cf Binary files /dev/null and b/resources/gachalog/images/emoji/5.png differ diff --git a/resources/gachalog/images/emoji/6.png b/resources/gachalog/images/emoji/6.png new file mode 100644 index 0000000..868ad09 Binary files /dev/null and b/resources/gachalog/images/emoji/6.png differ diff --git a/resources/gachalog/images/emoji/7.png b/resources/gachalog/images/emoji/7.png new file mode 100644 index 0000000..89987d5 Binary files /dev/null and b/resources/gachalog/images/emoji/7.png differ diff --git a/resources/gachalog/images/emoji/8.png b/resources/gachalog/images/emoji/8.png new file mode 100644 index 0000000..3f634d5 Binary files /dev/null and b/resources/gachalog/images/emoji/8.png differ diff --git a/resources/gachalog/images/emoji/9.png b/resources/gachalog/images/emoji/9.png new file mode 100644 index 0000000..ec77bfd Binary files /dev/null and b/resources/gachalog/images/emoji/9.png differ diff --git a/resources/gachalog/images/role_square_avatar_1011.png b/resources/gachalog/images/role_square_avatar_1011.png new file mode 100644 index 0000000..9a67751 Binary files /dev/null and b/resources/gachalog/images/role_square_avatar_1011.png differ diff --git a/resources/gachalog/index.css b/resources/gachalog/index.css new file mode 100644 index 0000000..6883997 --- /dev/null +++ b/resources/gachalog/index.css @@ -0,0 +1,216 @@ +.card { + margin: 0 1em; +} +.card .user-info { + margin: 0 1em; + margin-bottom: 1.5em; +} +.card .title { + border-image-slice: 37 27 37 131 fill; + border-image-width: 1em 1em 1em 4.5em; + border-image-outset: 0em 0em 0em 0em; + border-image-repeat: stretch stretch; + min-height: 7em; + padding: 0.7em 2.3em 0.7em 3.7em; + display: flex; + align-items: center; +} +.card .title.t1 { + border-image-source: url("./images/bg1.png"); +} +.card .title.t2 { + border-image-source: url("./images/bg2.png"); +} +.card .title.t3 { + border-image-source: url("./images/bg3.png"); +} +.card .title.t4 { + border-image-source: url("./images/bg4.png"); +} +.card .title .info { + flex-grow: 1; + flex-shrink: 1; +} +.card .title .info .type { + display: flex; + align-items: flex-end; + gap: 0.1em; +} +.card .title .info .type .label { + font-size: 1.5em; + text-shadow: 0.05em 0.05em 0.03em rgba(0, 0, 0, 0.4); +} +.card .title .info .type .status { + font-size: 0.8em; + color: #e4e4e4; + margin-bottom: 0.2em; + background: rgba(0, 0, 0, 0.5); + padding: 0em 0.5em; + border-radius: 1em; + backdrop-filter: blur(0.3em); +} +.card .title .info .type .status .value { + color: rgb(128, 237, 84); + margin: 0 0.1em; +} +.card .title .info .time { + font-size: 0.6em; + color: #e4e4e4; +} +.card .title .info .analysis { + width: 15em; + display: flex; + background-color: rgba(0, 0, 0, 0.5); + margin-left: 1em; + margin-top: 0.3em; + margin-bottom: 0.2em; + border-radius: 2em; + padding: 0.1em 0.5em; +} +.card .title .info .analysis .item { + flex-grow: 1; + flex-shrink: 1; + display: flex; + flex-direction: column; + align-items: center; +} +.card .title .info .analysis .item .value { + font-size: 1.3em; +} +.card .title .info .analysis .item .label { + font-size: 0.8em; + color: #e4e4e4; +} +.card .title .comment { + flex-grow: 0; + flex-shrink: 0; +} +.card .title .comment .icon { + width: 4.5em; + aspect-ratio: 1; + background-repeat: no-repeat; + background-position: center; + background-size: contain; + margin-bottom: 0.5em; +} +.card .title .comment.e1 .icon { + background-image: url("./images/emoji/1.png"); +} +.card .title .comment.e2 .icon { + background-image: url("./images/emoji/2.png"); +} +.card .title .comment.e3 .icon { + background-image: url("./images/emoji/3.png"); +} +.card .title .comment.e4 .icon { + background-image: url("./images/emoji/4.png"); +} +.card .title .comment.e5 .icon { + background-image: url("./images/emoji/5.png"); +} +.card .title .comment.e6 .icon { + background-image: url("./images/emoji/6.png"); +} +.card .title .comment.e7 .icon { + background-image: url("./images/emoji/7.png"); +} +.card .title .comment.e8 .icon { + background-image: url("./images/emoji/8.png"); +} +.card .title .comment.e9 .icon { + background-image: url("./images/emoji/9.png"); +} +.card .title .comment.e10 .icon { + background-image: url("./images/emoji/10.png"); +} +.card .title .comment.e11 .icon { + background-image: url("./images/emoji/11.png"); +} +.card .title .comment.e12 .icon { + background-image: url("./images/emoji/12.png"); +} +.card .title .comment.e13 .icon { + background-image: url("./images/emoji/13.png"); +} +.card .title .comment.e14 .icon { + background-image: url("./images/emoji/14.png"); +} +.card .title .comment.e15 .icon { + background-image: url("./images/emoji/15.png"); +} +.card .title .comment.e16 .icon { + background-image: url("./images/emoji/16.png"); +} +.card .title .comment .label { + font-size: 1em; + text-align: center; +} +.card .list { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1em; + padding: 0.5em 1.7em; + margin-bottom: 1.5em; +} +.card .list .item { + width: 100%; + position: relative; + border-radius: 0.5em; + overflow: hidden; + border: 0.2em solid #000000; +} +.card .list .item.up::after { + content: ""; + display: block; + position: absolute; + top: 0.1em; + left: 0.2em; + width: 1.7em; + height: 1.7em; + background: url("./images/IconTabUP.png") no-repeat center center; + background-size: contain; + z-index: 1; +} +.card .list .item .rank { + position: absolute; + top: 0.2em; + right: 0.2em; + width: 1.5em; + aspect-ratio: 1; + background: url("./images/RANK_A.png") no-repeat center center; + background-size: contain; + color: white; + z-index: 2; +} +.card .list .item.rankS .rank { + background-image: url("./images/RANK_S.png"); +} +.card .list .item.rankB .rank { + background-image: url("./images/RANK_B.png"); +} +.card .list .item .image { + width: 100%; + aspect-ratio: 1.5; + background-color: #e2e2e2; +} +.card .list .item .image img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + display: block; +} +.card .list .item .count { + position: absolute; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(0.3em); + border-top-right-radius: 0.5em; + color: white; + text-align: center; + padding: 0 0.3em; + font-size: 0.9em; +} + +/*# sourceMappingURL=index.css.map */ diff --git a/resources/gachalog/index.html b/resources/gachalog/index.html new file mode 100644 index 0000000..1985109 --- /dev/null +++ b/resources/gachalog/index.html @@ -0,0 +1,54 @@ +{{extend defaultLayout}} + +{{block 'css'}} + +{{/block}} + +{{block 'main'}} +
+ {{include sys.playerInfo}} + {{each data item i}} +
+
+
+
{{item.name}}
+
{{item.lastFive}}抽未出S级
+
+
{{item.timeRange}}
+
+
+
{{item.avgFive}}
+
平均出金
+
+
+
{{item.avgUp}}
+
平均UP
+
+
+
{{item.totalCount}}
+
抽卡总数
+
+
+
+ +
+
+ {{each item.list inv j}} +
+
+
+ +
+
{{inv?.totalCount || '-'}}抽
+
+ {{/each}} +
+ + + {{/each}} + +
+{{/block}} diff --git a/resources/gachalog/index.scss b/resources/gachalog/index.scss new file mode 100644 index 0000000..ab8e1c8 --- /dev/null +++ b/resources/gachalog/index.scss @@ -0,0 +1,172 @@ +.card { + margin: 0 1em; + .user-info { + margin: 0 1em; + margin-bottom: 1.5em; + } + .title { + border-image-slice: 37 27 37 131 fill; + border-image-width: 1em 1em 1em 4.5em; + border-image-outset: 0em 0em 0em 0em; + border-image-repeat: stretch stretch; + min-height: 7em; + padding: 0.7em 2.3em 0.7em 3.7em; + display: flex; + align-items: center; + @for $i from 1 through 4 { + &.t#{$i} { + border-image-source: url('./images/bg#{$i}.png'); + } + } + .info { + flex-grow: 1; + flex-shrink: 1; + .type { + display: flex; + align-items: flex-end; + gap: 0.1em; + .label { + font-size: 1.5em; + text-shadow: 0.05em 0.05em 0.03em rgba(0, 0, 0, 0.4); + } + .status { + font-size: 0.8em; + color: #e4e4e4; + margin-bottom: 0.2em; + background: rgba(0, 0, 0, 0.5); + padding: 0em 0.5em; + border-radius: 1em; + backdrop-filter: blur(0.3em); + .value { + color: rgb(128, 237, 84); + margin: 0 0.1em; + } + } + } + .time { + font-size: 0.6em; + color: #e4e4e4; + } + .analysis { + width: 15em; + display: flex; + background-color: rgba(0, 0, 0, 0.5); + margin-left: 1em; + margin-top: 0.3em; + margin-bottom: 0.2em; + border-radius: 2em; + padding: 0.1em 0.5em; + .item { + flex-grow: 1; + flex-shrink: 1; + display: flex; + flex-direction: column; + align-items: center; + .value { + font-size: 1.3em; + } + .label { + font-size: 0.8em; + color: #e4e4e4; + } + } + } + } + .comment { + flex-grow: 0; + flex-shrink: 0; + .icon { + width: 4.5em; + aspect-ratio: 1; + background-repeat: no-repeat; + background-position: center; + background-size: contain; + margin-bottom: 0.5em; + } + @for $i from 1 through 16 { + &.e#{$i} { + .icon { + background-image: url('./images/emoji/#{$i}.png'); + } + } + } + .label { + font-size: 1em; + text-align: center; + } + } + } + .list { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1em; + padding: 0.5em 1.7em; + margin-bottom: 1.5em; + .item { + width: 100%; + position: relative; + border-radius: 0.5em; + overflow: hidden; + border: 0.2em solid #000000; + &.up { + &::after { + content: ''; + display: block; + position: absolute; + top: 0.1em; + left: 0.2em; + width: 1.7em; + height: 1.7em; + background: url('./images/IconTabUP.png') no-repeat center center; + background-size: contain; + z-index: 1; + } + } + .rank { + position: absolute; + top: 0.2em; + right: 0.2em; + width: 1.5em; + aspect-ratio: 1; + background: url('./images/RANK_A.png') no-repeat center center; + background-size: contain; + color: white; + z-index: 2; + } + &.rankS { + .rank { + background-image: url('./images/RANK_S.png'); + } + } + &.rankB { + .rank { + background-image: url('./images/RANK_B.png'); + } + } + .image { + width: 100%; + aspect-ratio: 1.5; + background-color: #e2e2e2; + img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + display: block; + } + } + .count { + position: absolute; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(0.3em); + border-top-right-radius: 0.5em; + color: white; + text-align: center; + padding: 0 0.3em; + font-size: 0.9em; + } + } + } +}