From f4353fc5cadd03370b64692850436b9a99ac9829 Mon Sep 17 00:00:00 2001 From: Kokomi <102026640+yoimiya-kokomi@users.noreply.github.com> Date: Tue, 14 Feb 2023 03:47:22 +0800 Subject: [PATCH] =?UTF-8?q?apps=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E8=B0=83=E6=95=B4=EF=BC=8C=E9=9D=A2=E6=9D=BF=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin.js | 27 +- apps/character.js | 85 +-- apps/character/AvatarCard.js | 183 +++--- apps/character/AvatarWife.js | 268 ++++----- apps/help.js | 95 +--- apps/help/Help.js | 77 +++ apps/poke.js | 9 +- apps/profile.js | 164 +++--- apps/profile/ProfileArtis.js | 21 +- apps/profile/ProfileCommon.js | 112 +--- apps/profile/ProfileDetail.js | 24 +- apps/profile/ProfileList.js | 192 ++++--- apps/profile/ProfileStat.js | 2 + apps/stat.js | 522 +----------------- apps/stat/AbyssStat.js | 164 ++++++ apps/stat/AbyssSummary.js | 137 +++++ apps/stat/AbyssTeam.js | 189 +++++++ apps/{wiki => stat}/HutaoApi.js | 0 apps/wiki.js | 186 +------ apps/wiki/Calendar.js | 15 +- apps/wiki/CharMaterial.js | 18 + apps/wiki/CharTalent.js | 49 ++ apps/wiki/CharWiki.js | 191 ++++--- apps/wiki/CharWikiData.js | 108 ++++ components/App.js | 12 +- models/AvatarArtis.js | 2 +- models/AvatarData.js | 8 +- models/Character.js | 10 +- models/Material.js | 2 +- models/MysApi.js | 43 +- models/Player.js | 50 +- models/ProfileArtis.js | 4 +- models/ProfileData.js | 4 +- models/ProfileDmg.js | 6 +- .../{character-lib => character}/CharCfg.js | 0 models/{character-lib => character}/CharId.js | 0 .../{character-lib => character}/CharImg.js | 0 .../{character-lib => character}/CharMeta.js | 0 .../CharTalent.js | 0 .../MaterialMeta.js | 0 models/{player-lib => player}/EnkaApi.js | 0 models/{player-lib => player}/EnkaData.js | 0 models/{player-lib => player}/MiaoApi.js | 0 models/{player-lib => player}/MiaoData.js | 0 models/{player-lib => player}/MysAvatar.js | 21 +- models/{player-lib => player}/Profile.js | 6 +- models/{player-lib => player}/ProfileMeta.js | 0 models/{profile-lib => profile}/ArtisMark.js | 0 models/{profile-lib => profile}/AttrCalc.js | 0 models/{profile-lib => profile}/CharArtis.js | 0 models/{profile-lib => profile}/DmgAttr.js | 0 models/{profile-lib => profile}/DmgBuffs.js | 0 models/{profile-lib => profile}/DmgCalc.js | 0 .../{profile-lib => profile}/DmgCalcMeta.js | 0 models/{profile-lib => profile}/DmgMastery.js | 0 .../{profile-lib => profile}/ProfileInput.js | 0 resources/character/avatar-list.css | 3 +- resources/character/avatar-list.less | 3 +- resources/common/tpl.css | 28 +- resources/common/tpl/avatar-card.html | 17 +- resources/common/tpl/avatar-card.less | 43 +- resources/meta/info/index.js | 23 + resources/meta/weapon/sword/calc.js | 9 +- resources/stat/abyss-summary.css | 2 +- resources/stat/abyss-summary.less | 2 +- 65 files changed, 1625 insertions(+), 1511 deletions(-) create mode 100644 apps/help/Help.js create mode 100644 apps/stat/AbyssStat.js create mode 100644 apps/stat/AbyssSummary.js create mode 100644 apps/stat/AbyssTeam.js rename apps/{wiki => stat}/HutaoApi.js (100%) create mode 100644 apps/wiki/CharMaterial.js create mode 100644 apps/wiki/CharTalent.js create mode 100644 apps/wiki/CharWikiData.js rename models/{character-lib => character}/CharCfg.js (100%) rename models/{character-lib => character}/CharId.js (100%) rename models/{character-lib => character}/CharImg.js (100%) rename models/{character-lib => character}/CharMeta.js (100%) rename models/{character-lib => character}/CharTalent.js (100%) rename models/{material-lib => material}/MaterialMeta.js (100%) rename models/{player-lib => player}/EnkaApi.js (100%) rename models/{player-lib => player}/EnkaData.js (100%) rename models/{player-lib => player}/MiaoApi.js (100%) rename models/{player-lib => player}/MiaoData.js (100%) rename models/{player-lib => player}/MysAvatar.js (95%) rename models/{player-lib => player}/Profile.js (90%) rename models/{player-lib => player}/ProfileMeta.js (100%) rename models/{profile-lib => profile}/ArtisMark.js (100%) rename models/{profile-lib => profile}/AttrCalc.js (100%) rename models/{profile-lib => profile}/CharArtis.js (100%) rename models/{profile-lib => profile}/DmgAttr.js (100%) rename models/{profile-lib => profile}/DmgBuffs.js (100%) rename models/{profile-lib => profile}/DmgCalc.js (100%) rename models/{profile-lib => profile}/DmgCalcMeta.js (100%) rename models/{profile-lib => profile}/DmgMastery.js (100%) rename models/{profile-lib => profile}/ProfileInput.js (100%) create mode 100644 resources/meta/info/index.js diff --git a/apps/admin.js b/apps/admin.js index c77bac84..c0379406 100644 --- a/apps/admin.js +++ b/apps/admin.js @@ -12,17 +12,22 @@ let app = App.init({ let sysCfgReg = new RegExp(`^#喵喵设置\\s*(${keys.join('|')})?\\s*(.*)$`) -app.reg('update-res', updateRes, { - rule: /^#喵喵(强制)?(更新图像|图像更新)$/, - desc: '【#管理】更新素材' -}) -app.reg('update', updateMiaoPlugin, { - rule: /^#喵喵(强制)?更新/, - desc: '【#管理】喵喵更新' -}) -app.reg('sys-cfg', sysCfg, { - rule: sysCfgReg, - desc: '【#管理】系统设置' +app.reg({ + updateRes: { + rule: /^#喵喵(强制)?(更新图像|图像更新)$/, + fn: updateRes, + desc: '【#管理】更新素材' + }, + update: { + rule: /^#喵喵(强制)?更新/, + fn: updateMiaoPlugin, + desc: '【#管理】喵喵更新' + }, + sysCfg: { + rule: sysCfgReg, + fn: sysCfg, + desc: '【#管理】系统设置' + } }) export default app diff --git a/apps/character.js b/apps/character.js index 89c49a7f..65e87e91 100644 --- a/apps/character.js +++ b/apps/character.js @@ -1,71 +1,36 @@ -import { Common, App } from '../components/index.js' -import { Character } from '../models/index.js' -import { renderAvatar } from './character/AvatarCard.js' +import { App } from '../components/index.js' import { uploadCharacterImg } from './character/ImgUpload.js' -import { wife, wifeReg } from './character/AvatarWife.js' import { getOriginalPicture } from './profile/ProfileUtils.js' +import Avatar from './character/AvatarCard.js' +import Wife from './character/AvatarWife.js' let app = App.init({ id: 'character', name: '角色查询' }) -app.reg('character', character, { - rule: /^#喵喵角色卡片$/, - check: checkCharacter, - name: '角色卡片' -}) - -app.reg('upload-img', uploadCharacterImg, { - rule: /^#*(喵喵)?(上传|添加)(.+)(照片|写真|图片|图像)\s*$/, - name: '上传角色写真' -}) - -app.reg('wife', wife, { - rule: wifeReg, - describe: '#老公 #老婆 查询' -}) - -app.reg('original-pic', getOriginalPicture, { - rule: /^#?(获取|给我|我要|求|发|发下|发个|发一下)?原图(吧|呗)?$/, - describe: '【#原图】 回复角色卡片,可获取原图' +app.reg({ + character: { + rule: /^#喵喵角色卡片$/, + fn: Avatar.render, + check: Avatar.check, + name: '角色卡片' + }, + uploadImg: { + rule: /^#*(喵喵)?(上传|添加)(.+)(照片|写真|图片|图像)\s*$/, + fn: uploadCharacterImg, + name: '上传角色写真' + }, + wife: { + rule: Wife.reg, + fn: Wife.render, + describe: '#老公 #老婆 查询' + }, + originalPic: { + rule: /^#?(获取|给我|我要|求|发|发下|发个|发一下)?原图(吧|呗)?$/, + fn: getOriginalPicture, + describe: '【#原图】 回复角色卡片,可获取原图' + } }) export default app - -// 查看当前角色 -export async function character (e) { - if (!e.char) { - return false - } - return renderAvatar(e, e.char?.name) -} - -function checkCharacter (e) { - let msg = e.original_msg || e.msg - if (!msg || !/^#/.exec(msg)) { - return false - } - if (!Common.cfg('avatarCard')) { - return false - } - let uidRet = /[0-9]{9}/.exec(msg) - if (uidRet) { - e.uid = uidRet[0] - msg = msg.replace(uidRet[0], '') - } - let name = msg.replace(/#|老婆|老公|卡片/g, '').trim() - - // cache gsCfg - Character.gsCfg = Character.gsCfg || e?.runtime?.gsCfg - - let char = Character.get(name.trim()) - - if (!char) { - return false - } - - e.msg = '#喵喵角色卡片' - e.char = char - return true -} diff --git a/apps/character/AvatarCard.js b/apps/character/AvatarCard.js index 44c21670..e34dd87b 100644 --- a/apps/character/AvatarCard.js +++ b/apps/character/AvatarCard.js @@ -4,85 +4,122 @@ import lodash from 'lodash' import { segment } from 'oicq' import moment from 'moment' -export async function renderAvatar (e, avatar, renderType = 'card') { - // 如果传递的是名字,则获取 - if (typeof (avatar) === 'string') { - // 检查角色 - let char = Character.get(avatar) +let Avatar = { + render (e) { + if (!e.char) { + return false + } + return Avatar.renderAvatar(e, e.char?.name) + }, + async renderAvatar (e, avatar, renderType = 'card') { + // 如果传递的是名字,则获取 + if (typeof (avatar) === 'string') { + // 检查角色 + let char = Character.get(avatar) + if (!char) { + return false + } + let mys = await MysApi.init(e) + if (!mys) return true + if (!char.isRelease) { + avatar = { id: char.id, name: char.name, detail: false } + } else { + let player = Player.create(e) + await player.refreshMysDetail() + await player.refreshTalent(char.id) + avatar = player.getAvatar(char.id) + if (!avatar) { + avatar = { id: char.id, name: char.name, detail: false } + } + } + } + return await Avatar.renderCard(e, avatar, renderType) + }, + + async renderCard (e, avatar, renderType = 'card') { + let char = Character.get(avatar.id) if (!char) { return false } - let mys = await MysApi.init(e) - if (!mys) return true - if (!char.isRelease) { - avatar = { id: char.id, name: char.name, detail: false } - } else { - let player = Player.create(e) - await player.refreshMysDetail() - await player.refreshTalent(char.id) - avatar = player.getAvatar(char.id) - if (!avatar) { - avatar = { id: char.id, name: char.name, detail: false } - } + let bg = char.getCardImg(Cfg.get('charPicSe', false)) + if (renderType === 'photo') { + e.reply(segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + bg.img)) + return true + } + let uid = e.uid || (e.targetUser && e.targetUser.uid) + let data = {} + let custom = char.isCustom + let isRelease = char.isRelease + if (isRelease) { + data = avatar.getDetail() + data.imgs = char.imgs + data.source = avatar._source + data.artis = avatar.getArtisDetail() + data.updateTime = moment(new Date(avatar._time)).format('MM-DD HH:mm') + if (data.hasTalent) { + data.talent = avatar.talent + data.talentMap = ['a', 'e', 'q'] + // 计算皇冠个数 + data.crownNum = lodash.filter(lodash.map(data.talent, (d) => d.original), (d) => d >= 10).length + } + } else { + data = char.getData('id,name,sName') } - } - return await renderCard(e, avatar, renderType) -} -// 渲染角色卡片 -async function renderCard (e, avatar, renderType = 'card') { - let char = Character.get(avatar.id) - if (!char) { - return false - } - let bg = char.getCardImg(Cfg.get('charPicSe', false)) - if (renderType === 'photo') { - e.reply(segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + bg.img)) + let width = 600 + let imgCss = '' + let scale = 1.2 + if (bg.mode === 'left') { + const height = 480 + width = height * bg.width / bg.height + imgCss = `img.bg{width:auto;height:${height}px;}` + scale = 1.45 + } + // 渲染图像 + let msgRes = await Common.render('character/character-card', { + saveId: uid, + uid, + bg, + widthStyle: ``, + mode: bg.mode, + custom, + isRelease, + data + }, { e, scale, retMsgId: true }) + if (msgRes && msgRes.message_id) { + // 如果消息发送成功,就将message_id和图片路径存起来,3小时过期 + await redis.set(`miao:original-picture:${msgRes.message_id}`, bg.img, { EX: 3600 * 3 }) + } + return true + }, + check (e) { + let msg = e.original_msg || e.msg + if (!msg || !/^#/.exec(msg)) { + return false + } + if (!Common.cfg('avatarCard')) { + return false + } + let uidRet = /[0-9]{9}/.exec(msg) + if (uidRet) { + e.uid = uidRet[0] + msg = msg.replace(uidRet[0], '') + } + let name = msg.replace(/#|老婆|老公|卡片/g, '').trim() + + // cache gsCfg + Character.gsCfg = Character.gsCfg || e?.runtime?.gsCfg + + let char = Character.get(name.trim()) + + if (!char) { + return false + } + + e.msg = '#喵喵角色卡片' + e.char = char return true } - let uid = e.uid || (e.targetUser && e.targetUser.uid) - let data = {} - let custom = char.isCustom - let isRelease = char.isRelease - if (isRelease) { - data = avatar.getDetail() - data.imgs = char.imgs - data.source = avatar._source - data.artis = avatar.getArtisDetail() - data.updateTime = moment(new Date(avatar._time)).format('MM-DD HH:mm') - if (data.hasTalent) { - data.talent = avatar.talent - data.talentMap = ['a', 'e', 'q'] - // 计算皇冠个数 - data.crownNum = lodash.filter(lodash.map(data.talent, (d) => d.original), (d) => d >= 10).length - } - } else { - data = char.getData('id,name,sName') - } - let width = 600 - let imgCss = '' - let scale = 1.2 - if (bg.mode === 'left') { - const height = 480 - width = height * bg.width / bg.height - imgCss = `img.bg{width:auto;height:${height}px;}` - scale = 1.45 - } - // 渲染图像 - let msgRes = await Common.render('character/character-card', { - saveId: uid, - uid, - bg, - widthStyle: ``, - mode: bg.mode, - custom, - isRelease, - data - }, { e, scale, retMsgId: true }) - if (msgRes && msgRes.message_id) { - // 如果消息发送成功,就将message_id和图片路径存起来,3小时过期 - await redis.set(`miao:original-picture:${msgRes.message_id}`, bg.img, { EX: 3600 * 3 }) - } - return true } +export default Avatar diff --git a/apps/character/AvatarWife.js b/apps/character/AvatarWife.js index 8bbbc691..bab1b961 100644 --- a/apps/character/AvatarWife.js +++ b/apps/character/AvatarWife.js @@ -2,7 +2,7 @@ import lodash from 'lodash' import { Common } from '../../components/index.js' import { Character, MysApi, Player } from '../../models/index.js' -import { renderAvatar } from './AvatarCard.js' +import Avatar from './AvatarCard.js' const relationMap = { wife: { @@ -32,138 +32,9 @@ const relationMap = { } const relation = lodash.flatMap(relationMap, (d) => d.keyword) -export const wifeReg = `^#?\\s*(${relation.join('|')})\\s*(设置|选择|指定|列表|查询|列表|是|是谁|照片|相片|图片|写真|图像)?\\s*([^\\d]*)\\s*(\\d*)$` +const wifeReg = `^#?\\s*(${relation.join('|')})\\s*(设置|选择|指定|列表|查询|列表|是|是谁|照片|相片|图片|写真|图像)?\\s*([^\\d]*)\\s*(\\d*)$` -export async function wife (e) { - let msg = e.msg || '' - if (!msg && !e.isPoke) return false - - if (e.isPoke) { - if (!Common.cfg('avatarPoke')) { - return false - } - } else if (!Common.cfg('avatarWife')) { - return false - } - - let msgRet = (new RegExp(wifeReg)).exec(msg) - if (e.isPoke) { - msgRet = [] - } else if (!msgRet) { - return false - } - let target = msgRet[1] - let action = msgRet[2] || '卡片' - let actionParam = msgRet[3] || '' - - if (!'设置,选择,挑选,指定'.split(',').includes(action) && actionParam) { - return false - } - - let targetCfg = lodash.find(relationMap, (cfg, key) => { - cfg.key = key - return cfg.keyword.includes(target) - }) - if (!targetCfg && !e.isPoke) return true - - let avatarList = [] - let avatar = {} - let wifeList = [] - - let mys = await MysApi.init(e) - if (!mys || !mys.uid) { - return true - } - let player = Player.create(e) - let selfUser = mys.selfUser - let isSelf = true - let renderType = (action === '卡片' ? 'card' : 'photo') - let addRet = [] - switch (action) { - case '卡片': - case '照片': - case '相片': - case '图片': - case '写真': - // 展示老婆卡片 - // 如果选择过,则进行展示 - if (!e.isPoke) { - wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []) - // 存在设置 - if (wifeList && wifeList.length > 0 && isSelf && !e.isPoke) { - if (wifeList[0] === '随机') { - // 如果选择为全部,则从列表中随机选择一个 - avatarList = await getAvatarList(player, targetCfg.type, mys) - let avatar = lodash.sample(avatarList) - return renderAvatar(e, avatar, renderType) - } else { - // 如果指定过,则展示指定角色 - return renderAvatar(e, lodash.sample(wifeList), renderType) - } - } - } - // 如果未指定过,则从列表中排序并随机选择 - avatarList = await getAvatarList(player, e.isPoke ? false : targetCfg.type, mys) - if (avatarList && avatarList.length > 0) { - avatar = lodash.sample(avatarList) - return await renderAvatar(e, avatar, renderType) - } - e.reply('在当前米游社公开展示的角色中未能找到适合展示的角色..') - return true - case '设置': - case '选择': - case '挑选': - case '指定': - if (!isSelf) { - e.reply('只能指定自己的哦~') - return true - } - // 选择老婆 - actionParam = actionParam.replace(/(,|、|;|;)/g, ',') - wifeList = actionParam.split(',') - if (lodash.intersection(['全部', '任意', '随机', '全都要'], wifeList).length > 0) { - addRet = ['随机'] - } else { - wifeList = lodash.map(wifeList, (name) => { - let char = Character.get(name) - if (char && char.checkWifeType(targetCfg.type)) { - return char.name - } - }) - wifeList = lodash.filter(lodash.uniq(wifeList), (d) => !!d) - addRet = wifeList - if (addRet.length === 0) { - e.reply(`在可选的${targetCfg.keyword[0]}列表中未能找到 ${actionParam} ~`) - return true - } - } - await selfUser.setCfg(`wife.${targetCfg.key}`, addRet) - e.reply(`${targetCfg.keyword[0]}已经设置:${addRet.join(',')}`) - return true - case '列表': - case '是': - case '是谁': - // 查看当前选择老婆 - if (!isSelf) { - e.reply('只能查看自己的哦~') - return true - } - wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []) - if (wifeList && wifeList.length > 0) { - e.reply(`你的${targetCfg.keyword[0]}是:${wifeList.join(',')}`) - } else { - e.reply(`尚未设置,回复#${targetCfg.keyword[0]}设置+角色名 来设置,如果设置多位请用逗号间隔`) - } - break - } - return true -} - -export async function pokeWife (e, components) { - return await wife(e, components) -} - -async function getAvatarList (player, type, mys) { +async function getAvatarList (player, type) { await player.refreshMysDetail() let list = [] player.forEachAvatar((avatar) => { @@ -182,3 +53,136 @@ async function getAvatarList (player, type, mys) { list = lodash.orderBy(list, sortKey, lodash.repeat('desc,', sortKey.length).split(',')) return list } + +const Wife = { + reg: wifeReg, + async render (e) { + let msg = e.msg || '' + if (!msg && !e.isPoke) return false + + if (e.isPoke) { + if (!Common.cfg('avatarPoke')) { + return false + } + } else if (!Common.cfg('avatarWife')) { + return false + } + + let msgRet = (new RegExp(wifeReg)).exec(msg) + if (e.isPoke) { + msgRet = [] + } else if (!msgRet) { + return false + } + let target = msgRet[1] + let action = msgRet[2] || '卡片' + let actionParam = msgRet[3] || '' + + if (!'设置,选择,挑选,指定'.split(',').includes(action) && actionParam) { + return false + } + + let targetCfg = lodash.find(relationMap, (cfg, key) => { + cfg.key = key + return cfg.keyword.includes(target) + }) + if (!targetCfg && !e.isPoke) return true + + let avatarList = [] + let avatar = {} + let wifeList = [] + + let mys = await MysApi.init(e) + if (!mys || !mys.uid) { + return true + } + let player = Player.create(e) + let selfUser = mys.selfUser + let isSelf = true + let renderType = (action === '卡片' ? 'card' : 'photo') + let addRet = [] + switch (action) { + case '卡片': + case '照片': + case '相片': + case '图片': + case '写真': + // 展示老婆卡片 + // 如果选择过,则进行展示 + if (!e.isPoke) { + wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []) + // 存在设置 + if (wifeList && wifeList.length > 0 && isSelf && !e.isPoke) { + if (wifeList[0] === '随机') { + // 如果选择为全部,则从列表中随机选择一个 + avatarList = await getAvatarList(player, targetCfg.type, mys) + let avatar = lodash.sample(avatarList) + return Avatar.renderAvatar(e, avatar, renderType) + } else { + // 如果指定过,则展示指定角色 + return Avatar.renderAvatar(e, lodash.sample(wifeList), renderType) + } + } + } + // 如果未指定过,则从列表中排序并随机选择 + avatarList = await getAvatarList(player, e.isPoke ? false : targetCfg.type, mys) + if (avatarList && avatarList.length > 0) { + avatar = lodash.sample(avatarList) + return await Avatar.renderAvatar(e, avatar, renderType) + } + e.reply('在当前米游社公开展示的角色中未能找到适合展示的角色..') + return true + case '设置': + case '选择': + case '挑选': + case '指定': + if (!isSelf) { + e.reply('只能指定自己的哦~') + return true + } + // 选择老婆 + actionParam = actionParam.replace(/(,|、|;|;)/g, ',') + wifeList = actionParam.split(',') + if (lodash.intersection(['全部', '任意', '随机', '全都要'], wifeList).length > 0) { + addRet = ['随机'] + } else { + wifeList = lodash.map(wifeList, (name) => { + let char = Character.get(name) + if (char && char.checkWifeType(targetCfg.type)) { + return char.name + } + }) + wifeList = lodash.filter(lodash.uniq(wifeList), (d) => !!d) + addRet = wifeList + if (addRet.length === 0) { + e.reply(`在可选的${targetCfg.keyword[0]}列表中未能找到 ${actionParam} ~`) + return true + } + } + await selfUser.setCfg(`wife.${targetCfg.key}`, addRet) + e.reply(`${targetCfg.keyword[0]}已经设置:${addRet.join(',')}`) + return true + case '列表': + case '是': + case '是谁': + // 查看当前选择老婆 + if (!isSelf) { + e.reply('只能查看自己的哦~') + return true + } + wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []) + if (wifeList && wifeList.length > 0) { + e.reply(`你的${targetCfg.keyword[0]}是:${wifeList.join(',')}`) + } else { + e.reply(`尚未设置,回复#${targetCfg.keyword[0]}设置+角色名 来设置,如果设置多位请用逗号间隔`) + } + break + } + return true + }, + async poke (e) { + return await Wife.render(e) + } +} + +export default Wife diff --git a/apps/help.js b/apps/help.js index fdece9c8..f0c4bd07 100644 --- a/apps/help.js +++ b/apps/help.js @@ -1,7 +1,5 @@ -import lodash from 'lodash' -import fs from 'fs' -import { Cfg, Version, Common, Data, App } from '../components/index.js' -import HelpTheme from './help/HelpTheme.js' +import { App } from '../components/index.js' +import Help from './help/Help.js' let app = App.init({ id: 'help', @@ -9,84 +7,17 @@ let app = App.init({ desc: '喵喵帮助' }) -app.reg('help', help, { - rule: /^#?(喵喵)?(命令|帮助|菜单|help|说明|功能|指令|使用说明)$/, - desc: '【#帮助】 #喵喵帮助' -}) - -app.reg('version', versionInfo, { - rule: /^#?喵喵版本$/, - desc: '【#帮助】 喵喵版本介绍' +app.reg({ + help: { + rule: /^#?(喵喵)?(命令|帮助|菜单|help|说明|功能|指令|使用说明)$/, + fn: Help.render, + desc: '【#帮助】 #喵喵帮助' + }, + version: { + rule: /^#?喵喵版本$/, + fn: Help.version, + desc: '【#帮助】 喵喵版本介绍' + } }) export default app - -const _path = process.cwd() -const helpPath = `${_path}/plugins/miao-plugin/resources/help` - -async function help (e) { - if (!/喵喵/.test(e.msg) && !Cfg.get('help', false)) { - return false - } - - let custom = {} - let help = {} - if (fs.existsSync(`${helpPath}/help-cfg.js`)) { - console.log('miao-plugin: 检测到存在help-cfg.js配置\n建议将help-cfg.js移为config/help.js或重新复制config/help_default.js进行配置~') - help = await import(`file://${helpPath}/help-cfg.js?version=${new Date().getTime()}`) - } else if (fs.existsSync(`${helpPath}/help-list.js`)) { - console.log('miao-plugin: 检测到存在help-list.js配置,建议将help-list.js移为config/help.js或重新复制config/help_default.js进行配置~') - help = await import(`file://${helpPath}/help-list.js?version=${new Date().getTime()}`) - } - - let { diyCfg, sysCfg } = await Data.importCfg('help') - - // 兼容一下旧字段 - if (lodash.isArray(help.helpCfg)) { - custom = { - helpList: help.helpCfg, - helpCfg: {} - } - } else { - custom = help - } - - let helpConfig = lodash.defaults(diyCfg.helpCfg || {}, custom.helpCfg, sysCfg.helpCfg) - let helpList = diyCfg.helpList || custom.helpList || sysCfg.helpList - - let helpGroup = [] - - lodash.forEach(helpList, (group) => { - if (group.auth && group.auth === 'master' && !e.isMaster) { - return true - } - - lodash.forEach(group.list, (help) => { - let icon = help.icon * 1 - if (!icon) { - help.css = 'display:none' - } else { - let x = (icon - 1) % 10 - let y = (icon - x - 1) / 10 - help.css = `background-position:-${x * 50}px -${y * 50}px` - } - }) - - helpGroup.push(group) - }) - let themeData = await HelpTheme.getThemeData(diyCfg.helpCfg || {}, sysCfg.helpCfg || {}) - return await Common.render('help/index', { - helpCfg: helpConfig, - helpGroup, - ...themeData, - element: 'default' - }, { e, scale: 1.2 }) -} - -async function versionInfo (e) { - return await Common.render('help/version-info', { - currentVersion: Version.version, - changelogs: Version.changelogs, - elem: 'cryo' - }, { e, scale: 1.2 }) -} diff --git a/apps/help/Help.js b/apps/help/Help.js new file mode 100644 index 00000000..581d4416 --- /dev/null +++ b/apps/help/Help.js @@ -0,0 +1,77 @@ +import { Cfg, Common, Data, Version } from '../../components/index.js' +import fs from 'fs' +import lodash from 'lodash' +import HelpTheme from './HelpTheme.js' + +const _path = process.cwd() +const helpPath = `${_path}/plugins/miao-plugin/resources/help` + +const Help = { + async render (e) { + if (!/喵喵/.test(e.msg) && !Cfg.get('help', false)) { + return false + } + + let custom = {} + let help = {} + if (fs.existsSync(`${helpPath}/help-cfg.js`)) { + console.log('miao-plugin: 检测到存在help-cfg.js配置\n建议将help-cfg.js移为config/help.js或重新复制config/help_default.js进行配置~') + help = await import(`file://${helpPath}/help-cfg.js?version=${new Date().getTime()}`) + } else if (fs.existsSync(`${helpPath}/help-list.js`)) { + console.log('miao-plugin: 检测到存在help-list.js配置,建议将help-list.js移为config/help.js或重新复制config/help_default.js进行配置~') + help = await import(`file://${helpPath}/help-list.js?version=${new Date().getTime()}`) + } + + let { diyCfg, sysCfg } = await Data.importCfg('help') + + // 兼容一下旧字段 + if (lodash.isArray(help.helpCfg)) { + custom = { + helpList: help.helpCfg, + helpCfg: {} + } + } else { + custom = help + } + + let helpConfig = lodash.defaults(diyCfg.helpCfg || {}, custom.helpCfg, sysCfg.helpCfg) + let helpList = diyCfg.helpList || custom.helpList || sysCfg.helpList + + let helpGroup = [] + + lodash.forEach(helpList, (group) => { + if (group.auth && group.auth === 'master' && !e.isMaster) { + return true + } + + lodash.forEach(group.list, (help) => { + let icon = help.icon * 1 + if (!icon) { + help.css = 'display:none' + } else { + let x = (icon - 1) % 10 + let y = (icon - x - 1) / 10 + help.css = `background-position:-${x * 50}px -${y * 50}px` + } + }) + + helpGroup.push(group) + }) + let themeData = await HelpTheme.getThemeData(diyCfg.helpCfg || {}, sysCfg.helpCfg || {}) + return await Common.render('help/index', { + helpCfg: helpConfig, + helpGroup, + ...themeData, + element: 'default' + }, { e, scale: 1.2 }) + }, + + async version (e) { + return await Common.render('help/version-info', { + currentVersion: Version.version, + changelogs: Version.changelogs, + elem: 'cryo' + }, { e, scale: 1.2 }) + } +} +export default Help diff --git a/apps/poke.js b/apps/poke.js index a838051c..898d55ec 100644 --- a/apps/poke.js +++ b/apps/poke.js @@ -1,5 +1,5 @@ import { App } from '../components/index.js' -import { pokeWife } from './character/AvatarWife.js' +import Wife from './character/AvatarWife.js' let app = App.init({ id: 'poke', @@ -7,8 +7,11 @@ let app = App.init({ event: 'poke' }) -app.reg('pock-wife', pokeWife, { - describe: '#老公 #老婆 查询' +app.reg({ + pockWife: { + fn: Wife.poke, + describe: '#老公 #老婆 查询' + } }) export default app diff --git a/apps/profile.js b/apps/profile.js index 981d955a..0c270ca1 100644 --- a/apps/profile.js +++ b/apps/profile.js @@ -1,9 +1,9 @@ import { App } from '../components/index.js' -import { getProfile, profileHelp } from './profile/ProfileCommon.js' +import { profileHelp } from './profile/ProfileCommon.js' import { profileArtisList } from './profile/ProfileArtis.js' import { profileDetail } from './profile/ProfileDetail.js' import { profileStat } from './profile/ProfileStat.js' -import { profileList } from './profile/ProfileList.js' +import ProfileList from './profile/ProfileList.js' import { uploadCharacterImg, delProfileImg, profileImgList } from './character/ImgUpload.js' import { enemyLv } from './profile/ProfileUtils.js' import { groupRank, resetRank, refreshRank, manageRank } from './profile/ProfileRank.js' @@ -12,86 +12,110 @@ let app = App.init({ id: 'profile', name: '角色面板' }) -app.reg('profile-detail', profileDetail, { - rule: /^#*([^#]+)\s*(详细|详情|面板|面版|圣遗物|伤害[1-7]?)\s*(\d{9})*(.*[换变改].*)?$/, - name: '角色面板' -}) -app.reg('profile-change', profileDetail, { - rule: /^#.+换.+$/, - name: '角色面板计算' -}) -app.reg('group-profile', groupRank, { - rule: /^#(群|群内)?(排名|排行)?(最强|最高|最高分|最牛|第一)+.+/, - name: '群内最强' -}) +app.reg({ + profileDetail: { + rule: /^#*([^#]+)\s*(详细|详情|面板|面版|圣遗物|伤害[1-7]?)\s*(\d{9})*(.*[换变改].*)?$/, + fn: profileDetail, + name: '角色面板' + }, -app.reg('reset-rank', resetRank, { - rule: /^#(重置|重设)(.*)(排名|排行)$/, - name: '重置排名' -}) + profileChange: { + rule: /^#.+换.+$/, + fn: profileDetail, + name: '角色面板计算' + }, -app.reg('refresh-rank', refreshRank, { - rule: /^#(刷新|更新|重新加载)(群内|群|全部)*(排名|排行)$/, - name: '重置排名' -}) + groupProfile: { + rule: /^#(群|群内)?(排名|排行)?(最强|最高|最高分|最牛|第一)+.+/, + fn: groupRank, + name: '群内最强' + }, -app.reg('manage-rank', manageRank, { - rule: /^#(开启|打开|启用|关闭|禁用)(群内|群|全部)*(排名|排行)$/, - name: '打开关闭' -}) + resetRank: { + rule: /^#(重置|重设)(.*)(排名|排行)$/, + fn: resetRank, + name: '重置排名' + }, -app.reg('rank-list', groupRank, { - rule: /^#(群|群内)?.+(排名|排行)(榜)?$/, - name: '面板排名榜' -}) + refreshRank: { + rule: /^#(刷新|更新|重新加载)(群内|群|全部)*(排名|排行)$/, + fn: refreshRank, + name: '重置排名' + }, -app.reg('artis-list', profileArtisList, { - rule: /^#圣遗物列表\s*(\d{9})?$/, - name: '面板圣遗物列表' -}) + manageRank: { + rule: /^#(开启|打开|启用|关闭|禁用)(群内|群|全部)*(排名|排行)$/, + fn: manageRank, + name: '打开关闭' + }, -app.reg('profile-list', profileList, { - rule: /^#(面板角色|角色面板|面板)(列表)?\s*(\d{9})?$/, - name: '面板角色列表', - desc: '查看当前已获取面板数据的角色列表' -}) + rankList: { + rule: /^#(群|群内)?.+(排名|排行)(榜)?$/, + fn: groupRank, + name: '面板排名榜' + }, -app.reg('profile-stat', profileStat, { - rule: /^#(面板|喵喵|角色|武器|天赋|技能|圣遗物)?练度统计$/, - name: '面板练度统计' -}) -app.reg('avatar-list', profileStat, { - rule: /^(#(角色|查询|查询角色|角色查询|人物)[ |0-9]*$)|(^(#*uid|#*UID)\+*[1|2|5-9][0-9]{8}$)|(^#[\+|+]*[1|2|5-9][0-9]{8})/, - name: '角色查询' -}) + artisList: { + rule: /^#圣遗物列表\s*(\d{9})?$/, + fn: profileArtisList, + name: '面板圣遗物列表' + }, -app.reg('profile-help', profileHelp, { - rule: /^#(角色|换|更换)?面[板版]帮助$/, - name: '角色面板帮助' -}) + profileList: { + rule: /^#(面板角色|角色面板|面板)(列表)?\s*(\d{9})?$/, + fn: ProfileList.render, + name: '面板角色列表', + desc: '查看当前已获取面板数据的角色列表' + }, -app.reg('enemy-lv', enemyLv, { - rule: /^#(敌人|怪物)等级\s*\d{1,3}\s*$/, - describe: '【#角色】 设置伤害计算中目标敌人的等级' -}) + profileStat: { + rule: /^#(面板|喵喵|角色|武器|天赋|技能|圣遗物)?练度统计$/, + fn: profileStat, + name: '面板练度统计' + }, -app.reg('profile-refresh', getProfile, { - rule: /^#(全部面板更新|更新全部面板|获取游戏角色详情|更新面板|面板更新)\s*(\d{9})?$/, - describe: '【#角色】 获取游戏橱窗详情数据' -}) + avatarList: { + rule: /^(#(角色|查询|查询角色|角色查询|人物)[ |0-9]*$)|(^(#*uid|#*UID)\+*[1|2|5-9][0-9]{8}$)|(^#[\+|+]*[1|2|5-9][0-9]{8})/, + fn: profileStat, + name: '角色查询' + }, -app.reg('upload-img', uploadCharacterImg, { - rule: /^#?\s*(?:上传|添加)(.+)(?:面板图)\s*$/, - describe: '【#上传刻晴面板图】 上传角色面板图' -}) -app.reg('del-profile', delProfileImg, { - rule: /^#?\s*(?:移除|清除|删除)(.+)(?:面板图)(\d){1,}\s*$/, - describe: '【#删除刻晴面板图1】 删除指定角色面板图(序号)' -}) -app.reg('profile-img-list', profileImgList, { - rule: /^#?\s*(.+)(?:面板图列表)\s*$/, - describe: '【#刻晴面板图列表】 删除指定角色面板图(序号)' + profileHelp: { + rule: /^#(角色|换|更换)?面[板版]帮助$/, + fn: profileHelp, + name: '角色面板帮助' + }, + + enemyLv: { + rule: /^#(敌人|怪物)等级\s*\d{1,3}\s*$/, + fn: enemyLv, + describe: '【#角色】 设置伤害计算中目标敌人的等级' + }, + + profileRefresh: { + rule: /^#(全部面板更新|更新全部面板|获取游戏角色详情|更新面板|面板更新)\s*(\d{9})?$/, + fn: ProfileList.refresh, + describe: '【#角色】 获取游戏橱窗详情数据' + }, + + uploadImg: { + rule: /^#?\s*(?:上传|添加)(.+)(?:面板图)\s*$/, + fn: uploadCharacterImg, + describe: '【#上传刻晴面板图】 上传角色面板图' + }, + + delProfile: { + rule: /^#?\s*(?:移除|清除|删除)(.+)(?:面板图)(\d){1,}\s*$/, + fn: delProfileImg, + describe: '【#删除刻晴面板图1】 删除指定角色面板图(序号)' + }, + + profileImgList: { + rule: /^#?\s*(.+)(?:面板图列表)\s*$/, + fn: profileImgList, + describe: '【#刻晴面板图列表】 删除指定角色面板图(序号)' + } }) export default app diff --git a/apps/profile/ProfileArtis.js b/apps/profile/ProfileArtis.js index 74891d22..3bb9308c 100644 --- a/apps/profile/ProfileArtis.js +++ b/apps/profile/ProfileArtis.js @@ -4,7 +4,7 @@ * */ import lodash from 'lodash' import { Cfg, Common } from '../../components/index.js' -import { getTargetUid, profileHelp, autoGetProfile } from './ProfileCommon.js' +import { getTargetUid, profileHelp, getProfileRefresh } from './ProfileCommon.js' import { Artifact, Character, ProfileArtis, Player } from '../../models/index.js' /* @@ -12,26 +12,15 @@ import { Artifact, Character, ProfileArtis, Player } from '../../models/index.js * */ export async function profileArtis (e) { let { uid, avatar } = e - - let profile - if (e._profile) { - profile = e._profile - } else { - let autoRet = await autoGetProfile(e, uid, avatar, async () => { - await profileArtis(e) - }) - if (autoRet.err) { - return false - } - profile = autoRet.profile + let profile = e._profile || await getProfileRefresh(e, avatar) + if (!profile) { + return true } - let char = profile.char - if (!profile.hasArtis()) { e.reply('未能获得圣遗物详情,请重新获取面板信息后查看') return true } - + let char = profile.char let charCfg = profile.artis.getCharCfg() let { attrMap } = Artifact.getMeta() diff --git a/apps/profile/ProfileCommon.js b/apps/profile/ProfileCommon.js index 6a80f372..6a17a34e 100644 --- a/apps/profile/ProfileCommon.js +++ b/apps/profile/ProfileCommon.js @@ -1,9 +1,7 @@ /* * 面板公共方法及处理 * */ -import lodash from 'lodash' import { segment } from 'oicq' -import { profileList } from './ProfileList.js' import { Version } from '../../components/index.js' import { Character, MysApi, Player } from '../../models/index.js' @@ -57,7 +55,7 @@ const _getTargetUid = async function (e) { return false } uid = user.uid - if (!uid || !uidReg.test(uid)) { + if ((!uid || !uidReg.test(uid)) && !e._replyNeedUid) { e.reply('请先发送【#绑定+你的UID】来绑定查询目标') return false } @@ -75,114 +73,24 @@ export async function getTargetUid (e) { return uid } -/* -* 自动更新面板数据 -* */ -export async function autoRefresh (e) { - let uid = await getTargetUid(e) - if (!uid || e.isRefreshed) { - return false - } - - let refreshMark = await redis.get(`miao:profile-refresh-cd:${uid}`) - let inCd = await redis.get(`miao:role-all:${uid}`) - - if (refreshMark || inCd) { - return false - } - - await redis.set(`miao:profile-refresh-cd:${uid}`, 'TRUE', { EX: 3600 * 12 }) - e.isRefreshed = true - - // 数据更新 - let player = Player.create(e) - let data = await player.refreshProfile() - if (!data) { - return false - } - - if (!data.chars) { - e.reply('请确认角色已在【游戏内】橱窗展示并开放了查看详情。请在设置完毕5分钟后使用 #面板更新 重新获取') - return false - } else { - let ret = [] - lodash.forEach(data.chars, (ds) => { - let char = Character.get(ds.id) - if (char) { - ret.push(char.name) - } - }) - if (ret.length === 0) { - e.reply('请确认角色已在【游戏内】橱窗展示并开放了查看详情。请在设置完毕5分钟后使用 #面板更新 重新获取') - return false - } else { - // e.reply(`本次获取成功角色: ${ret.join(", ")} `) - return true - } - } -} - -export async function autoGetProfile (e, uid, avatar, callback) { - let refresh = async () => { - let refreshRet = await autoRefresh(e) - if (refreshRet) { - await callback() - } - return refreshRet - } - +export async function getProfileRefresh (e, avatar) { let char = Character.get(avatar) if (!char) { - return { err: true } + return false } let player = Player.create(e) let profile = player.getProfile(char.id) if (!profile || !profile.hasData) { - if (await refresh()) { - return { err: true } - } else { - e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`) - } - return { err: true } + logger.mark(`本地无UID:${player.uid}的${char.name}面板数据,尝试自动请求...`) + await player.refresh({ profile: true }) + profile = player.getProfile(char.id) } - return { profile, char, refresh } -} - -/* -* 面板数据更新 -* */ -export async function getProfile (e) { - let uid = await getTargetUid(e) - if (!uid) { - return true + if (!profile || !profile.hasData) { + e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`) + return false } - - // 数据更新 - let player = Player.create(e) - let ret = await player.refreshProfile() - if (!ret) { - return true - } - - if (!player._update.length === 0) { - e.reply('获取角色面板数据失败,请确认角色已在游戏内橱窗展示,并开放了查看详情。设置完毕后请5分钟后再进行请求~') - } else { - let ret = {} - lodash.forEach(player._update, (id) => { - let char = Character.get(id) - if (char) { - ret[char.name] = true - } - }) - if (ret.length === 0) { - e.reply('获取角色面板数据失败,未能请求到角色数据。请确认角色已在游戏内橱窗展示,并开放了查看详情。设置完毕后请5分钟后再进行请求~') - } else { - e.newChar = ret - return await profileList(e) - } - } - return true + return profile } /* diff --git a/apps/profile/ProfileDetail.js b/apps/profile/ProfileDetail.js index e7fc1c14..20e76be7 100644 --- a/apps/profile/ProfileDetail.js +++ b/apps/profile/ProfileDetail.js @@ -1,5 +1,6 @@ import lodash from 'lodash' -import { autoRefresh, getProfile, getTargetUid } from './ProfileCommon.js' +import { getTargetUid, getProfileRefresh } from './ProfileCommon.js' +import ProfileList from './ProfileList.js' import { Cfg, Common, Format } from '../../components/index.js' import { MysApi, ProfileRank, ProfileArtis, Player, Character } from '../../models/index.js' import ProfileChange from './ProfileChange.js' @@ -93,7 +94,7 @@ export async function profileDetail (e) { if (mode === 'profile' || mode === 'dmg') { return renderProfile(e, char, mode, { dmgIdx }) } else if (mode === 'refresh') { - await getProfile(e) + await ProfileList.refresh(e) return true } else if (mode === 'artis') { return profileArtis(e) @@ -116,23 +117,8 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) { return true } - let player = Player.create(uid) - let profile = e._profile || player.getProfile(char.id) - - let refresh = async () => { - let refreshRet = await autoRefresh(e) - if (refreshRet) { - await renderProfile(e, char, mode, params) - } - return refreshRet - } - - if (!profile || !profile.hasData) { - if (await refresh()) { - return true - } else { - e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`) - } + let profile = e._profile || await getProfileRefresh(e, char.id) + if (!profile) { return true } char = profile.char || char diff --git a/apps/profile/ProfileList.js b/apps/profile/ProfileList.js index cf0ed33e..59298f20 100644 --- a/apps/profile/ProfileList.js +++ b/apps/profile/ProfileList.js @@ -1,93 +1,121 @@ import lodash from 'lodash' -import { autoRefresh, getTargetUid } from './ProfileCommon.js' -import { ProfileRank, Player } from '../../models/index.js' +import { getTargetUid } from './ProfileCommon.js' +import { ProfileRank, Player, Character } from '../../models/index.js' import { Common, Data } from '../../components/index.js' -export async function profileList (e) { - let uid = await getTargetUid(e) - if (!uid) { - return true - } - let isSelfUid = false - if (e.runtime) { - let uids = e.runtime?.user?.ckUids || [] - isSelfUid = uids.join(',').split(',').includes(uid + '') - } - let rank = false - let servName = Player.getProfileServName(uid) - let hasNew = false - let newCount = 0 - - let chars = [] - let msg = '' - let newChar = {} - if (e.newChar) { - msg = '获取角色面板数据成功' - newChar = e.newChar - } - const cfg = await Data.importCfg('cfg') - // 获取面板数据 - let player = Player.create(uid) - let profiles = player.getProfiles() - // 检测标志位 - let qq = (e.at && !e.atBot) ? e.at : e.qq - await ProfileRank.setUidInfo({ uid, profiles, qq, uidType: isSelfUid ? 'ck' : 'bind' }) - - let groupId = e.group_id - if (groupId) { - rank = await ProfileRank.create({ groupId, uid, qq: e.user_id }) - } - const rankCfg = await ProfileRank.getGroupCfg(groupId) - const groupRank = rank && (cfg?.diyCfg?.groupRank || false) && rankCfg.status !== 1 - await player.forEachAvatarAsync(async function (avatar) { - let profile = avatar.getProfile() - if (!profile) { +const ProfileList = { + async refresh (e) { + let uid = await getTargetUid(e) + if (!uid) { return true } - let char = profile.char - let tmp = char.getData('id,face,name,abbr,element,star') - tmp.face = char.getImgs(profile.costume).face - tmp.level = profile.level || 1 - tmp.cons = profile.cons - tmp.isNew = 0 - if (newChar[char.name]) { - tmp.isNew = 1 - newCount++ - } - if (rank) { - tmp.groupRank = await rank.getRank(profile, !!tmp.isNew) - } - chars.push(tmp) - }) - if (chars.length === 0) { - if (await autoRefresh(e)) { - await profileList(e) - return true + // 数据更新 + let player = Player.create(e) + await player.refreshProfile(2) + + if (!player?._update?.length) { + e.reply('获取角色面板数据失败,请确认角色已在游戏内橱窗展示,并开放了查看详情。设置完毕后请5分钟后再进行请求~') } else { - e.reply(`本地暂无uid${uid}的面板数据...`) + let ret = {} + lodash.forEach(player._update, (id) => { + let char = Character.get(id) + if (char) { + ret[char.name] = true + } + }) + if (ret.length === 0) { + e.reply('获取角色面板数据失败,未能请求到角色数据。请确认角色已在游戏内橱窗展示,并开放了查看详情。设置完毕后请5分钟后再进行请求~') + } else { + e.newChar = ret + return await ProfileList.render(e) + } } + return true + }, + async render (e) { + let uid = await getTargetUid(e) + if (!uid) { + return true + } + let isSelfUid = false + if (e.runtime) { + let uids = e.runtime?.user?.ckUids || [] + isSelfUid = uids.join(',').split(',').includes(uid + '') + } + let rank = false + let servName = Player.getProfileServName(uid) + let hasNew = false + let newCount = 0 + + let chars = [] + let msg = '' + let newChar = {} + if (e.newChar) { + msg = '获取角色面板数据成功' + newChar = e.newChar + } + const cfg = await Data.importCfg('cfg') + // 获取面板数据 + let player = Player.create(e) + if (!player.hasProfile) { + await player.refresh({ profile: true }) + } + if (!player.hasProfile) { + e.reply(`本地暂无uid${uid}的面板数据...`) + return true + } + let profiles = player.getProfiles() + + // 检测标志位 + let qq = (e.at && !e.atBot) ? e.at : e.qq + await ProfileRank.setUidInfo({ uid, profiles, qq, uidType: isSelfUid ? 'ck' : 'bind' }) + + let groupId = e.group_id + if (groupId) { + rank = await ProfileRank.create({ groupId, uid, qq: e.user_id }) + } + const rankCfg = await ProfileRank.getGroupCfg(groupId) + const groupRank = rank && (cfg?.diyCfg?.groupRank || false) && rankCfg.status !== 1 + for (let id in profiles) { + let profile = profiles[id] + let char = profile.char + let tmp = char.getData('id,face,name,abbr,element,star') + tmp.face = char.getImgs(profile.costume).face + tmp.level = profile.level || 1 + tmp.cons = profile.cons + tmp.isNew = 0 + if (newChar[char.name]) { + tmp.isNew = 1 + newCount++ + } + if (rank) { + tmp.groupRank = await rank.getRank(profile, !!tmp.isNew) + } + chars.push(tmp) + } + + if (newCount > 0) { + hasNew = newCount <= 8 + } + + chars = lodash.sortBy(chars, ['isNew', 'star', 'level', 'id']) + chars = chars.reverse() + + player.save() + // 渲染图像 + return await Common.render('character/profile-list', { + save_id: uid, + uid, + chars, + servName, + hasNew, + msg, + groupRank, + allowRank: rank && rank.allowRank, + rankCfg + }, { e, scale: 1.6 }) } - - if (newCount > 0) { - hasNew = newCount <= 8 - } - - chars = lodash.sortBy(chars, ['isNew', 'star', 'level', 'id']) - chars = chars.reverse() - - player.save() - // 渲染图像 - return await Common.render('character/profile-list', { - save_id: uid, - uid, - chars, - servName, - hasNew, - msg, - groupRank, - allowRank: rank && rank.allowRank, - rankCfg - }, { e, scale: 1.6 }) } +export default ProfileList diff --git a/apps/profile/ProfileStat.js b/apps/profile/ProfileStat.js index 93611956..e65d039c 100644 --- a/apps/profile/ProfileStat.js +++ b/apps/profile/ProfileStat.js @@ -26,6 +26,8 @@ export async function profileStat (e) { await player.refreshMysInfo() let avatarRet = await player.refreshAndGetAvatarData({ + detail: 1, + talent: 0, rank: true, retType: 'array', sort: true diff --git a/apps/stat.js b/apps/stat.js index cabe7b03..324119f7 100644 --- a/apps/stat.js +++ b/apps/stat.js @@ -2,508 +2,36 @@ * 胡桃数据库的统计 * * */ -import lodash from 'lodash' -import { Cfg, Common, App, Data } from '../components/index.js' -import { Abyss, Character, MysApi, Player } from '../models/index.js' -import HutaoApi from './wiki/HutaoApi.js' +import { App } from '../components/index.js' +import { ConsStat, AbyssPct } from './stat/AbyssStat.js' +import { AbyssTeam } from './stat/AbyssTeam.js' +import { AbyssSummary } from './stat/AbyssSummary.js' let app = App.init({ id: 'stat', name: '深渊统计' }) -app.reg('cons-stat', consStat, { - rule: /^#(喵喵)?角色(持有|持有率|命座|命之座|.命)(分布|统计|持有|持有率)?$/, - desc: '【#统计】 #角色持有率 #角色5命统计' -}) -app.reg('abyss-pct', abyssPct, { - rule: /^#(喵喵)?深渊(第?.{1,2}层)?(角色)?(出场|使用)(率|统计)*$/, - desc: '【#统计】 #深渊出场率 #深渊12层出场率' -}) -app.reg('abyss-team', abyssTeam, { - rule: /#深渊(组队|配队)/, - describe: '【#角色】 #深渊组队' -}) -app.reg('upload-data', uploadData, { - rule: /^#*(喵喵|上传|本期)*(深渊|深境|深境螺旋)[ |0-9]*(数据)?$/, - desc: '上传深渊' +app.reg({ + consStat: { + rule: /^#(喵喵)?角色(持有|持有率|命座|命之座|.命)(分布|统计|持有|持有率)?$/, + fn: ConsStat, + desc: '【#统计】 #角色持有率 #角色5命统计' + }, + abyssPct: { + rule: /^#(喵喵)?深渊(第?.{1,2}层)?(角色)?(出场|使用)(率|统计)*$/, + fn: AbyssPct, + desc: '【#统计】 #深渊出场率 #深渊12层出场率' + }, + abyssTeam: { + rule: /#深渊(组队|配队)/, + fn: AbyssTeam, + describe: '【#角色】 #深渊组队' + }, + abyssSummary: { + rule: /^#*(喵喵|上传|本期)*(深渊|深境|深境螺旋)[ |0-9]*(数据)?$/, + fn: AbyssSummary, + desc: '上传深渊' + } }) export default app - -async function consStat (e) { - let consData = await HutaoApi.getCons() - let overview = await HutaoApi.getOverview() - - 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] || {} - let 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, - abbr: char.abbr, - star: char.star || 3, - side: char.side, - 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, - mode, - conNum, - totalCount: overview?.data?.totalPlayerCount || 0, - lastUpdate: consData.lastUpdate, - pct: function (num) { - return (num * 100).toFixed(2) - } - }, { e, scale: 1.5 }) -} - -async function abyssPct (e) { - let mode = /使用/.test(e.msg) ? 'use' : 'pct' - let modeName - let abyssData - let modeMulti = 1 - - if (mode === 'use') { - modeName = '使用率' - abyssData = await HutaoApi.getAbyssUse() - } else { - modeName = '出场率' - abyssData = await HutaoApi.getAbyssPct() - modeMulti = 8 - } - let overview = await HutaoApi.getOverview() - - if (!abyssData) { - e.reply(`深渊${modeName}数据获取失败,请稍后重试~`) - return true - } - - let ret = [] - let chooseFloor = -1 - let 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') - data = data.reverse() - - lodash.forEach(data, (floorData) => { - 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 * modeMulti, - face: char.face - }) - } - }) - 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, - mode, - modeName, - totalCount: overview?.data?.collectedPlayerCount || 0, - lastUpdate: abyssData.lastUpdate - }, { e, scale: 1.5 }) -} - -async function abyssTeam (e) { - let mys = await MysApi.init(e, 'cookie') - if (!mys || !mys.uid || !mys.isSelfCookie) { - return true - } - let player = Player.create(e) - await player.refreshMysDetail() - await player.refreshTalent() - - let abyssData = await HutaoApi.getAbyssTeam() - if (!abyssData || !abyssData.data) { - e.reply('深渊组队数据获取失败,请稍后重试~') - return true - } - abyssData = abyssData.data - let avatarData = player.getAvatarData() - let avatarRet = {} - let data = {} - let noAvatar = {} - lodash.forEach(avatarData, (avatar) => { - let t = avatar.originalTalent - avatarRet[avatar.id] = Math.min(avatar.level, avatar.weapon?.level || 1) * 100 + Math.max(t?.a, t?.e, t?.q) * 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.floor - if (!data[floor]) { - data[floor] = { - up: {}, - down: {}, - teams: [] - } - } - lodash.forEach(['up', 'down'], (halfKey) => { - lodash.forEach(ds[halfKey], (ds) => { - let teamCfg = getTeamCfg(ds.item) - 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.rate - data[floor][halfKey][teamCfg.key].mark += ds.rate * 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 true - } - lodash.forEach(floorData.teams, (t2) => { - if (t1.mark2 <= 0) { - return true - } - if (t1.half === t2.half || t2.mark2 <= 0) { - return true - } - - let teamKey = t1.half === 'up' ? (t1.team + '+' + t2.team) : (t2.team + '+' + t1.team) - if (ds[teamKey]) { - return true - } - if (hasSame(t1.teamArr, t2.teamArr)) { - return true - } - - 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(avatarData, (ds) => { - let char = Character.get(ds.id) - avatarMap[ds.id] = { - id: ds.id, - name: ds.name, - star: ds.star, - level: ds.level, - cons: ds.cons, - face: char.face - } - }) - - lodash.forEach(noAvatar, (d, id) => { - let char = Character.get(id) - avatarMap[id] = { - id, - name: char.name, - face: char.face, - star: char.star, - level: 0, - cons: 0 - } - }) - - return await Common.render('stat/abyss-team', { - teams: ret, - avatars: avatarMap - }, { e, scale: 1.5 }) -} - -async function uploadData (e) { - let isMatch = /^#(喵喵|上传)深渊(数据)?$/.test(e.original_msg || e.msg || '') - if (!Cfg.get('uploadAbyssData', false) && !isMatch) { - return false - } - let mys = await MysApi.init(e, 'all') - if (!mys || !mys.uid) { - if (isMatch) { - e.reply(`请绑定ck后再使用${e.original_msg || e.msg}`) - } - return false - } - let ret = {} - let uid = mys.uid - let player = Player.create(e) - let resDetail, resAbyss - try { - resAbyss = await mys.getSpiralAbyss(1) - let lvs = Data.getVal(resAbyss, 'floors.0.levels.0') - // 检查是否查询到了深渊信息 - if (!lvs || !lvs.battles) { - e.reply('暂未获得本期深渊挑战数据...') - return true - } else if (lvs && lvs.battles && lvs.battles.length === 0) { - if (!mys.isSelfCookie) { - if (isMatch) { - e.reply(`请绑定ck后再使用${e.original_msg || e.msg}`) - } - return false - } - } - resDetail = await mys.getCharacter() - if (!resDetail || !resAbyss || !resDetail.avatars || resDetail.avatars.length <= 3) { - e.reply('角色信息获取失败') - return true - } - delete resDetail._res - delete resAbyss._res - ret = await HutaoApi.uploadData({ - uid, - resDetail, - resAbyss - }) - } catch (err) { - // console.log(err); - } - // 更新player信息 - player.setMysCharData(resDetail) - - if (ret && ret.retcode === 0) { - let stat = [] - if (ret.data) { - if (resAbyss.floors.length === 0) { - e.reply('暂未获得本期深渊挑战数据...') - return true - } - let abyss = new Abyss(resAbyss) - let abyssData = abyss.getData() - let avatarIds = abyss.getAvatars() - let overview = ret.info || (await HutaoApi.getOverview())?.data || {} - let addMsg = function (title, ds) { - let tmp = {} - if (!ds) { - return false - } - if (!ds.avatarId && !ds.id) { - return false - } - let char = Character.get(ds.avatarId || ds.id) - tmp.title = title - tmp.id = char.id - tmp.value = `${(ds.value / 10000).toFixed(1)} W` - let msg = [] - tmp.msg = msg - let pct = (percent, name) => { - if (percent < 0.2) { - msg.push({ - title: '少于', - value: (Math.max(0.1, 100 - percent * 100)).toFixed(1), - name: name - }) - } else { - msg.push({ - title: '超过', - value: (Math.min(99.9, percent * 100)).toFixed(1), - name: name - }) - } - } - if (ds.percent) { - pct(ds.percent, char.sName) - pct(ds.percentTotal, '总记录') - } else { - msg.push({ - txt: '暂无统计信息' - }) - } - stat.push(tmp) - } - addMsg('最强一击', ret.data?.damage || abyssData?.stat?.dmg || {}) - addMsg('最高承伤', ret.data?.takeDamage || abyssData?.stat.takeDmg || {}) - let abyssStat = abyssData?.stat || {} - lodash.forEach({ defeat: '最多击破', e: '元素战技', q: '元素爆发' }, (title, key) => { - if (abyssStat[key]) { - stat.push({ - title, - id: abyssStat[key]?.id || 0, - value: `${abyssStat[key]?.value}次` - }) - } else { - stat.push({}) - } - }) - await player.refreshTalent(avatarIds) - let avatarData = player.getAvatarData(avatarIds) - return await Common.render('stat/abyss-summary', { - abyss: abyssData, - avatars: avatarData, - stat, - save_id: uid, - totalCount: overview?.collectedPlayerCount || 0, - uid - }, { e, scale: 1.2 }) - } else { - e.reply('暂未获得本期深渊挑战数据...') - return true - } - } else { - e.reply(`${ret.message || '上传失败'},请稍后重试...`) - } - return true -} diff --git a/apps/stat/AbyssStat.js b/apps/stat/AbyssStat.js new file mode 100644 index 00000000..11374c7a --- /dev/null +++ b/apps/stat/AbyssStat.js @@ -0,0 +1,164 @@ +import HutaoApi from './HutaoApi.js' +import lodash from 'lodash' +import { Character } from '../../models/index.js' +import { Common } from '../../components/index.js' + +export async function ConsStat (e) { + let consData = await HutaoApi.getCons() + let overview = await HutaoApi.getOverview() + + 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] || {} + let 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, + abbr: char.abbr, + star: char.star || 3, + side: char.side, + 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, + mode, + conNum, + totalCount: overview?.data?.totalPlayerCount || 0, + lastUpdate: consData.lastUpdate, + pct: function (num) { + return (num * 100).toFixed(2) + } + }, { e, scale: 1.5 }) +} + +export async function AbyssPct (e) { + let mode = /使用/.test(e.msg) ? 'use' : 'pct' + let modeName + let abyssData + let modeMulti = 1 + + if (mode === 'use') { + modeName = '使用率' + abyssData = await HutaoApi.getAbyssUse() + } else { + modeName = '出场率' + abyssData = await HutaoApi.getAbyssPct() + modeMulti = 8 + } + let overview = await HutaoApi.getOverview() + + if (!abyssData) { + e.reply(`深渊${modeName}数据获取失败,请稍后重试~`) + return true + } + + let ret = [] + let chooseFloor = -1 + let 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') + data = data.reverse() + + lodash.forEach(data, (floorData) => { + 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 * modeMulti, + face: char.face + }) + } + }) + 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, + mode, + modeName, + totalCount: overview?.data?.collectedPlayerCount || 0, + lastUpdate: abyssData.lastUpdate + }, { e, scale: 1.5 }) +} diff --git a/apps/stat/AbyssSummary.js b/apps/stat/AbyssSummary.js new file mode 100644 index 00000000..99ff4023 --- /dev/null +++ b/apps/stat/AbyssSummary.js @@ -0,0 +1,137 @@ +import { Cfg, Common, Data } from '../../components/index.js' +import { Abyss, Character, MysApi, Player } from '../../models/index.js' +import HutaoApi from './HutaoApi.js' +import lodash from 'lodash' + +export async function AbyssSummary (e) { + let isMatch = /^#(喵喵|上传)深渊(数据)?$/.test(e.original_msg || e.msg || '') + if (!Cfg.get('uploadAbyssData', false) && !isMatch) { + return false + } + let mys = await MysApi.init(e, 'all') + if (!mys || !mys.uid) { + if (isMatch) { + e.reply(`请绑定ck后再使用${e.original_msg || e.msg}`) + } + return false + } + let ret = {} + let uid = mys.uid + let player = Player.create(e) + let resDetail, resAbyss + try { + resAbyss = await mys.getSpiralAbyss(1) + let lvs = Data.getVal(resAbyss, 'floors.0.levels.0') + // 检查是否查询到了深渊信息 + if (!lvs || !lvs.battles) { + e.reply('暂未获得本期深渊挑战数据...') + return true + } else if (lvs && lvs.battles && lvs.battles.length === 0) { + if (!mys.isSelfCookie) { + if (isMatch) { + e.reply(`请绑定ck后再使用${e.original_msg || e.msg}`) + } + return false + } + } + resDetail = await mys.getCharacter() + if (!resDetail || !resAbyss || !resDetail.avatars || resDetail.avatars.length <= 3) { + e.reply('角色信息获取失败') + return true + } + delete resDetail._res + delete resAbyss._res + ret = await HutaoApi.uploadData({ + uid, + resDetail, + resAbyss + }) + } catch (err) { + // console.log(err); + } + // 更新player信息 + player.setMysCharData(resDetail) + + if (ret && ret.retcode === 0) { + let stat = [] + if (ret.data) { + if (resAbyss.floors.length === 0) { + e.reply('暂未获得本期深渊挑战数据...') + return true + } + let abyss = new Abyss(resAbyss) + let abyssData = abyss.getData() + let avatarIds = abyss.getAvatars() + let overview = ret.info || (await HutaoApi.getOverview())?.data || {} + let addMsg = function (title, ds) { + let tmp = {} + if (!ds) { + return false + } + if (!ds.avatarId && !ds.id) { + return false + } + let char = Character.get(ds.avatarId || ds.id) + tmp.title = title + tmp.id = char.id + tmp.value = `${(ds.value / 10000).toFixed(1)} W` + let msg = [] + tmp.msg = msg + let pct = (percent, name) => { + if (percent < 0.2) { + msg.push({ + title: '少于', + value: (Math.max(0.1, 100 - percent * 100)).toFixed(1), + name: name + }) + } else { + msg.push({ + title: '超过', + value: (Math.min(99.9, percent * 100)).toFixed(1), + name: name + }) + } + } + if (ds.percent) { + pct(ds.percent, char.abbr) + pct(ds.percentTotal, '总记录') + } else { + msg.push({ + txt: '暂无统计信息' + }) + } + stat.push(tmp) + } + addMsg('最强一击', ret.data?.damage || abyssData?.stat?.dmg || {}) + addMsg('最高承伤', ret.data?.takeDamage || abyssData?.stat.takeDmg || {}) + let abyssStat = abyssData?.stat || {} + lodash.forEach({ defeat: '最多击破', e: '元素战技', q: '元素爆发' }, (title, key) => { + if (abyssStat[key]) { + stat.push({ + title, + id: abyssStat[key]?.id || 0, + value: `${abyssStat[key]?.value}次` + }) + } else { + stat.push({}) + } + }) + await player.refreshTalent(avatarIds) + let avatarData = player.getAvatarData(avatarIds) + return await Common.render('stat/abyss-summary', { + abyss: abyssData, + avatars: avatarData, + stat, + save_id: uid, + totalCount: overview?.collectedPlayerCount || 0, + uid + }, { e, scale: 1.2 }) + } else { + e.reply('暂未获得本期深渊挑战数据...') + return true + } + } else { + e.reply(`${ret.message || '上传失败'},请稍后重试...`) + } + return true +} diff --git a/apps/stat/AbyssTeam.js b/apps/stat/AbyssTeam.js new file mode 100644 index 00000000..4586a43d --- /dev/null +++ b/apps/stat/AbyssTeam.js @@ -0,0 +1,189 @@ +import { Character, MysApi, Player } from '../../models/index.js' +import HutaoApi from './HutaoApi.js' +import lodash from 'lodash' +import { Common } from '../../components/index.js' + +export async function AbyssTeam (e) { + let mys = await MysApi.init(e, 'cookie') + if (!mys || !mys.uid || !mys.isSelfCookie) { + return true + } + let player = Player.create(e) + await player.refreshMysDetail() + await player.refreshTalent() + + let abyssData = await HutaoApi.getAbyssTeam() + if (!abyssData || !abyssData.data) { + e.reply('深渊组队数据获取失败,请稍后重试~') + return true + } + abyssData = abyssData.data + let avatarData = player.getAvatarData() + let avatarRet = {} + let data = {} + let noAvatar = {} + lodash.forEach(avatarData, (avatar) => { + let t = avatar.originalTalent + avatarRet[avatar.id] = Math.min(avatar.level, avatar.weapon?.level || 1) * 100 + Math.max(t?.a, t?.e, t?.q) * 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.floor + if (!data[floor]) { + data[floor] = { + up: {}, + down: {}, + teams: [] + } + } + lodash.forEach(['up', 'down'], (halfKey) => { + lodash.forEach(ds[halfKey], (ds) => { + let teamCfg = getTeamCfg(ds.item) + 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.rate + data[floor][halfKey][teamCfg.key].mark += ds.rate * 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 true + } + lodash.forEach(floorData.teams, (t2) => { + if (t1.mark2 <= 0) { + return true + } + if (t1.half === t2.half || t2.mark2 <= 0) { + return true + } + + let teamKey = t1.half === 'up' ? (t1.team + '+' + t2.team) : (t2.team + '+' + t1.team) + if (ds[teamKey]) { + return true + } + if (hasSame(t1.teamArr, t2.teamArr)) { + return true + } + + 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(avatarData, (ds) => { + let char = Character.get(ds.id) + avatarMap[ds.id] = { + id: ds.id, + name: ds.name, + star: ds.star, + level: ds.level, + cons: ds.cons, + face: char.face + } + }) + + lodash.forEach(noAvatar, (d, id) => { + let char = Character.get(id) + avatarMap[id] = { + id, + name: char.name, + face: char.face, + star: char.star, + level: 0, + cons: 0 + } + }) + + return await Common.render('stat/abyss-team', { + teams: ret, + avatars: avatarMap + }, { e, scale: 1.5 }) +} diff --git a/apps/wiki/HutaoApi.js b/apps/stat/HutaoApi.js similarity index 100% rename from apps/wiki/HutaoApi.js rename to apps/stat/HutaoApi.js diff --git a/apps/wiki.js b/apps/wiki.js index f3ae3115..01a2f7aa 100644 --- a/apps/wiki.js +++ b/apps/wiki.js @@ -1,183 +1,23 @@ -import { segment } from 'oicq' -import lodash from 'lodash' +import { App } from '../components/index.js' import Calendar from './wiki/Calendar.js' -import { Format, Cfg, Common, App } from '../components/index.js' -import { Character } from '../models/index.js' import CharWiki from './wiki/CharWiki.js' -let wikiReg = /^(?:#|喵喵)?(.*)(天赋|技能|命座|命之座|资料|图鉴|照片|写真|图片|图像)$/ - let app = App.init({ id: 'wiki', name: '角色资料' }) -app.reg('wiki', wiki, { - rule: '^#喵喵WIKI$', - check: checkCharacter, - desc: '【#资料】 #神里天赋 #夜兰命座' -}) -app.reg('calendar', calendar, { - rule: /^(#|喵喵)+(日历|日历列表)$/, - desc: '【#日历】 活动日历' +app.reg({ + wiki: { + rule: '^#喵喵WIKI$', + check: CharWiki.check, + fn: CharWiki.wiki, + desc: '【#资料】 #神里天赋 #夜兰命座' + }, + calendar: { + rule: /^(#|喵喵)+(日历|日历列表)$/, + fn: Calendar.render, + desc: '【#日历】 活动日历' + } }) export default app - -function checkCharacter (e) { - let msg = e.original_msg || e.msg - if (!e.msg) { - return false - } - let ret = wikiReg.exec(msg) - if (!ret || !ret[1] || !ret[2]) { - return false - } - let mode = 'talent' - if (/命/.test(ret[2])) { - mode = 'cons' - } else if (/(图鉴|资料)/.test(ret[2])) { - mode = 'wiki' - if (!Common.cfg('charWiki')) { - return false - } - } else if (/图|画|写真|照片/.test(ret[2])) { - mode = 'pic' - if (!Common.cfg('charPic')) { - return false - } - } else if (/(材料|养成|成长)/.test(ret[2])) { - mode = 'material' - } - if (['cons', 'talent'].includes(mode) && !Common.cfg('charWikiTalent')) { - return false - } - let char = Character.get(ret[1]) - if (!char) { - return false - } - e.wikiMode = mode - e.msg = '#喵喵WIKI' - e.char = char - return true -} - -async function wiki (e) { - let mode = e.wikiMode - let char = e.char - - if (mode === 'pic') { - let img = char.getCardImg(Cfg.get('charPicSe', false), false) - if (img && img.img) { - e.reply(segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + img.img)) - } else { - e.reply('暂无图片') - } - return true - } - if (char.isCustom) { - if (mode === 'wiki') { - return false - } - e.reply('暂不支持自定义角色') - return true - } - if (!char.isRelease && Cfg.get('notReleasedData') === false) { - e.reply('未实装角色资料已禁用...') - return true - } - let lvs = [] - for (let i = 1; i <= 15; i++) { - lvs.push('Lv' + i) - } - if (mode === 'wiki') { - if (char.source === 'amber') { - e.reply('暂不支持该角色图鉴展示') - return true - } - return await renderWiki({ e, char }) - } else if (mode === 'material') { - return await renderCharMaterial({ e, char }) - } - return await Common.render('wiki/character-talent', { - saveId: `${mode}-${char.id}`, - ...char.getData(), - detail: char.getDetail(), - imgs: char.getImgs(), - mode, - lvs, - line: getLineData(char) - }, { e, scale: 1.1 }) -} - -async function renderWiki ({ e, char }) { - let data = char.getData() - lodash.extend(data, char.getData('weaponTypeName,elemName')) - // 命座持有 - let holding = await CharWiki.getHolding(char.id) - let usage = await CharWiki.getUsage(char.id) - return await Common.render('wiki/character-wiki', { - data, - attr: char.getAttrList(), - detail: char.getDetail(), - imgs: char.getImgs(), - holding, - usage, - materials: char.getMaterials(), - elem: char.elem - }, { e, scale: 1.4 }) -} - -async function renderCharMaterial ({ e, char }) { - let data = char.getData() - return await Common.render('wiki/character-material', { - // saveId: `info-${char.id}`, - data, - attr: char.getAttrList(), - detail: char.getDetail(), - imgs: char.getImgs(), - materials: char.getMaterials(), - elem: char.elem - }, { e, scale: 1.4 }) -} - -const getLineData = function (char) { - let ret = [] - const attrMap = { - atkPct: '大攻击', - hpPct: '大生命', - defPct: '大防御', - cpct: '暴击', - cdmg: '爆伤', - recharge: '充能', - mastery: '精通', - heal: '治疗', - dmg: char.elemName + '伤', - phy: '物伤' - } - lodash.forEach({ hp: '基础生命', atk: '基础攻击', def: '基础防御' }, (label, key) => { - ret.push({ - num: Format.comma(char.baseAttr[key], 1), - label - }) - }) - let ga = char.growAttr - ret.push({ - num: ga.key === 'mastery' ? Format.comma(ga.value, 1) : ga.value, - label: `成长·${attrMap[ga.key]}` - }) - - return ret -} - -async function calendar (e) { - let calData = await Calendar.get() - let mode = 'calendar' - if (/(日历列表|活动)$/.test(e.msg)) { - mode = 'list' - } - - return await Common.render('wiki/calendar', { - ...calData, - displayMode: mode - }, { e, scale: 1.1 }) -} diff --git a/apps/wiki/Calendar.js b/apps/wiki/Calendar.js index 97a39eec..69b34452 100644 --- a/apps/wiki/Calendar.js +++ b/apps/wiki/Calendar.js @@ -1,7 +1,7 @@ import fetch from 'node-fetch' import moment from 'moment' import { Character, Material } from '../../models/index.js' -import { Data } from '../../components/index.js' +import { Common, Data } from '../../components/index.js' import lodash from 'lodash' const ignoreIds = [495, // 有奖问卷调查开启! @@ -397,6 +397,19 @@ let Cal = { nowTime: now.format('YYYY-MM-DD HH:mm'), nowDate: now.date() } + }, + + async render (e) { + let calData = await Cal.get() + let mode = 'calendar' + if (/(日历列表|活动)$/.test(e.msg)) { + mode = 'list' + } + + return await Common.render('wiki/calendar', { + ...calData, + displayMode: mode + }, { e, scale: 1.1 }) } } diff --git a/apps/wiki/CharMaterial.js b/apps/wiki/CharMaterial.js new file mode 100644 index 00000000..1e06ac02 --- /dev/null +++ b/apps/wiki/CharMaterial.js @@ -0,0 +1,18 @@ +import { Common } from '../../components/index.js' + +const CharMaterial = { + async render ({ e, char }) { + let data = char.getData() + return await Common.render('wiki/character-material', { + // saveId: `info-${char.id}`, + data, + attr: char.getAttrList(), + detail: char.getDetail(), + imgs: char.getImgs(), + materials: char.getMaterials(), + elem: char.elem + }, { e, scale: 1.4 }) + } +} + +export default CharMaterial diff --git a/apps/wiki/CharTalent.js b/apps/wiki/CharTalent.js new file mode 100644 index 00000000..4483672c --- /dev/null +++ b/apps/wiki/CharTalent.js @@ -0,0 +1,49 @@ +import { Common, Format } from '../../components/index.js' +import lodash from 'lodash' + +const CharTalent = { + async render (e, mode, char) { + let lvs = [] + for (let i = 1; i <= 15; i++) { + lvs.push('Lv' + i) + } + return await Common.render('wiki/character-talent', { + saveId: `${mode}-${char.id}`, + ...char.getData(), + detail: char.getDetail(), + imgs: char.getImgs(), + mode, + lvs, + line: CharTalent.getLineData(char) + }, { e, scale: 1.1 }) + }, + getLineData (char) { + let ret = [] + const attrMap = { + atkPct: '大攻击', + hpPct: '大生命', + defPct: '大防御', + cpct: '暴击', + cdmg: '爆伤', + recharge: '充能', + mastery: '精通', + heal: '治疗', + dmg: char.elemName + '伤', + phy: '物伤' + } + lodash.forEach({ hp: '基础生命', atk: '基础攻击', def: '基础防御' }, (label, key) => { + ret.push({ + num: Format.comma(char.baseAttr[key], 1), + label + }) + }) + let ga = char.growAttr + ret.push({ + num: ga.key === 'mastery' ? Format.comma(ga.value, 1) : ga.value, + label: `成长·${attrMap[ga.key]}` + }) + return ret + } +} + +export default CharTalent diff --git a/apps/wiki/CharWiki.js b/apps/wiki/CharWiki.js index 94ea4cb8..72563638 100644 --- a/apps/wiki/CharWiki.js +++ b/apps/wiki/CharWiki.js @@ -1,108 +1,107 @@ -import HutaoApi from './HutaoApi.js' +import { Cfg, Common } from '../../components/index.js' +import { Character } from '../../models/index.js' +import { segment } from 'oicq' +import CharTalent from './CharTalent.js' import lodash from 'lodash' -import { Format } from '../../components/index.js' -import { ArtifactSet, Weapon } from '../../models/index.js' +import CharWikiData from './CharWikiData.js' +import CharMaterial from './CharMaterial.js' -let CharWiki = { - /** - * 角色命座持有 - * @param id - * @returns {Promise<{}>} - */ - async getHolding (id) { - let consData = (await HutaoApi.getCons()).data || {} - consData = lodash.find(consData, (ds) => ds.avatar === id) - let holding = {} - if (consData) { - let { holdingRate, rate } = consData - rate = lodash.sortBy(rate, 'id') - holding.num = Format.percent(holdingRate) - holding.cons = [] - lodash.forEach(rate, (ds) => { - holding.cons.push({ - cons: ds.id, - num: Format.percent(ds.value) - }) - }) +const wikiReg = /^(?:#|喵喵)?(.*)(天赋|技能|命座|命之座|资料|图鉴|照片|写真|图片|图像)$/ + +const CharWiki = { + check (e) { + let msg = e.original_msg || e.msg + if (!e.msg) { + return false } - return holding + let ret = wikiReg.exec(msg) + if (!ret || !ret[1] || !ret[2]) { + return false + } + let mode = 'talent' + if (/命/.test(ret[2])) { + mode = 'cons' + } else if (/(图鉴|资料)/.test(ret[2])) { + mode = 'wiki' + if (!Common.cfg('charWiki')) { + return false + } + } else if (/图|画|写真|照片/.test(ret[2])) { + mode = 'pic' + if (!Common.cfg('charPic')) { + return false + } + } else if (/(材料|养成|成长)/.test(ret[2])) { + mode = 'material' + } + if (['cons', 'talent'].includes(mode) && !Common.cfg('charWikiTalent')) { + return false + } + let char = Character.get(ret[1]) + if (!char) { + return false + } + e.wikiMode = mode + e.msg = '#喵喵WIKI' + e.char = char + return true }, - /** - * 角色武器、圣遗物使用 - * @param id - * @returns {Promise<{}|{artis: *[], weapons: *[]}>} - */ - async getUsage (id) { - let ud = (await HutaoApi.getUsage()).data || {} - if (!ud[id]) { - return {} + async wiki (e) { + let mode = e.wikiMode + let char = e.char + + if (mode === 'pic') { + let img = char.getCardImg(Cfg.get('charPicSe', false), false) + if (img && img.img) { + e.reply(segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + img.img)) + } else { + e.reply('暂无图片') + } + return true } - ud = ud[id] - return { - weapons: CharWiki.getWeaponsData(ud.weapons), - artis: CharWiki.getArtisData(ud.artis) + if (char.isCustom) { + if (mode === 'wiki') { + return false + } + e.reply('暂不支持自定义角色') + return true } + if (!char.isRelease && Cfg.get('notReleasedData') === false) { + e.reply('未实装角色资料已禁用...') + return true + } + + if (mode === 'wiki') { + if (char.source === 'amber') { + e.reply('暂不支持该角色图鉴展示') + return true + } + return await CharWiki.render({ e, char }) + } else if (mode === 'material') { + return CharMaterial.render({ e, char }) + } + return await CharTalent.render(e, mode, char) + }, - /** - * 武器使用 - * @param data - * @returns {*[]} - */ - getWeaponsData (data = []) { - let weapons = [] - - lodash.forEach(data, (ds) => { - let weapon = Weapon.get(ds.item) || {} - weapons.push({ - ...weapon.getData('name,abbr,img,star'), - value: ds.rate - }) - }) - - weapons = lodash.sortBy(weapons, 'value') - weapons = weapons.reverse() - lodash.forEach(weapons, (ds) => { - ds.value = Format.percent(ds.value, 1) - }) - return weapons - }, - - /** - * 圣遗物使用 - * @param data - * @returns {*[]} - */ - getArtisData (data = []) { - let artis = [] - - lodash.forEach(data, (ds) => { - let imgs = [] - let abbrs = [] - let ss = ds.item.split(',') - lodash.forEach(ss, (t) => { - t = t.split(':') - let artiSet = ArtifactSet.get(t[0]) - if (artiSet) { - imgs.push(artiSet.img) - abbrs.push(artiSet.abbr + (ss.length === 1 ? t[1] : '')) - } - }) - - artis.push({ - imgs, - title: abbrs.join('+'), - value: ds.rate - }) - }) - - artis = lodash.sortBy(artis, 'value') - artis = artis.reverse() - artis.forEach((ds) => { - ds.value = Format.percent(ds.value) - }) - return artis + async render ({ e, char }) { + let data = char.getData() + lodash.extend(data, char.getData('weaponTypeName,elemName')) + // 命座持有 + let holding = await CharWikiData.getHolding(char.id) + let usage = await CharWikiData.getUsage(char.id) + return await Common.render('wiki/character-wiki', { + data, + attr: char.getAttrList(), + detail: char.getDetail(), + imgs: char.getImgs(), + holding, + usage, + materials: char.getMaterials(), + elem: char.elem + }, { e, scale: 1.4 }) } } + export default CharWiki diff --git a/apps/wiki/CharWikiData.js b/apps/wiki/CharWikiData.js new file mode 100644 index 00000000..a6a35e84 --- /dev/null +++ b/apps/wiki/CharWikiData.js @@ -0,0 +1,108 @@ +import HutaoApi from '../stat/HutaoApi.js' +import lodash from 'lodash' +import { Format } from '../../components/index.js' +import { ArtifactSet, Weapon } from '../../models/index.js' + +let CharWikiData = { + /** + * 角色命座持有 + * @param id + * @returns {Promise<{}>} + */ + async getHolding (id) { + let consData = (await HutaoApi.getCons()).data || {} + consData = lodash.find(consData, (ds) => ds.avatar === id) + let holding = {} + if (consData) { + let { holdingRate, rate } = consData + rate = lodash.sortBy(rate, 'id') + holding.num = Format.percent(holdingRate) + holding.cons = [] + lodash.forEach(rate, (ds) => { + holding.cons.push({ + cons: ds.id, + num: Format.percent(ds.value) + }) + }) + } + return holding + }, + + /** + * 角色武器、圣遗物使用 + * @param id + * @returns {Promise<{}|{artis: *[], weapons: *[]}>} + */ + async getUsage (id) { + let ud = (await HutaoApi.getUsage()).data || {} + if (!ud[id]) { + return {} + } + ud = ud[id] + return { + weapons: CharWikiData.getWeaponsData(ud.weapons), + artis: CharWikiData.getArtisData(ud.artis) + } + }, + + /** + * 武器使用 + * @param data + * @returns {*[]} + */ + getWeaponsData (data = []) { + let weapons = [] + + lodash.forEach(data, (ds) => { + let weapon = Weapon.get(ds.item) || {} + weapons.push({ + ...weapon.getData('name,abbr,img,star'), + value: ds.rate + }) + }) + + weapons = lodash.sortBy(weapons, 'value') + weapons = weapons.reverse() + lodash.forEach(weapons, (ds) => { + ds.value = Format.percent(ds.value, 1) + }) + return weapons + }, + + /** + * 圣遗物使用 + * @param data + * @returns {*[]} + */ + getArtisData (data = []) { + let artis = [] + + lodash.forEach(data, (ds) => { + let imgs = [] + let abbrs = [] + let ss = ds.item.split(',') + lodash.forEach(ss, (t) => { + t = t.split(':') + let artiSet = ArtifactSet.get(t[0]) + if (artiSet) { + imgs.push(artiSet.img) + abbrs.push(artiSet.abbr + (ss.length === 1 ? t[1] : '')) + } + }) + + artis.push({ + imgs, + title: abbrs.join('+'), + value: ds.rate + }) + }) + + artis = lodash.sortBy(artis, 'value') + artis = artis.reverse() + artis.forEach((ds) => { + ds.value = Format.percent(ds.value) + }) + return artis + } +} +export default CharWikiData diff --git a/components/App.js b/components/App.js index c0051361..0d30b3cb 100644 --- a/components/App.js +++ b/components/App.js @@ -9,9 +9,15 @@ class App { } reg (key, fn, cfg = {}) { - this.apps[key] = { - fn, - ...cfg + if (lodash.isPlainObject(key)) { + lodash.forEach(key, (cfg, k) => { + this.reg(k, cfg.fn, cfg) + }) + } else { + this.apps[key] = { + fn, + ...cfg + } } } diff --git a/models/AvatarArtis.js b/models/AvatarArtis.js index 60b3795e..70aaade7 100644 --- a/models/AvatarArtis.js +++ b/models/AvatarArtis.js @@ -5,7 +5,7 @@ import lodash from 'lodash' import Base from './Base.js' import { Artifact, ArtifactSet } from './index.js' import { Format, Data } from '../components/index.js' -import ArtisMark from './profile-lib/ArtisMark.js' +import ArtisMark from './profile/ArtisMark.js' export default class AvatarArtis extends Base { constructor (charid = 0) { diff --git a/models/AvatarData.js b/models/AvatarData.js index 72c12a1f..2eaad25b 100644 --- a/models/AvatarData.js +++ b/models/AvatarData.js @@ -3,8 +3,8 @@ import Base from './Base.js' import moment from 'moment' import { Character, AvatarArtis, ProfileData, Weapon } from './index.js' import { Data } from '../components/index.js' -import AttrCalc from './profile-lib/AttrCalc.js' -import Profile from './player-lib/Profile.js' +import AttrCalc from './profile/AttrCalc.js' +import Profile from './player/Profile.js' const charKey = 'name,abbr,sName,star,imgs,face,side,gacha,weaponTypeName'.split(',') @@ -29,6 +29,10 @@ export default class AvatarData extends Base { return this.char?.name || '' } + get hasData () { + return !!(this.level > 1 || this?.weapon?.name || this?.talent?.a) + } + // 是否是合法面板数据 get isProfile () { return Profile.isProfile(this) diff --git a/models/Character.js b/models/Character.js index a87eafb5..6af8f438 100644 --- a/models/Character.js +++ b/models/Character.js @@ -7,11 +7,11 @@ import lodash from 'lodash' import Base from './Base.js' import { Data, Format } from '../components/index.js' -import CharImg from './character-lib/CharImg.js' -import CharTalent from './character-lib/CharTalent.js' -import CharId from './character-lib/CharId.js' -import CharMeta from './character-lib/CharMeta.js' -import CharCfg from './character-lib/CharCfg.js' +import CharImg from './character/CharImg.js' +import CharTalent from './character/CharTalent.js' +import CharId from './character/CharId.js' +import CharMeta from './character/CharMeta.js' +import CharCfg from './character/CharCfg.js' let { wifeMap, idSort, idMap } = CharId diff --git a/models/Material.js b/models/Material.js index eaad418e..f2d1606f 100644 --- a/models/Material.js +++ b/models/Material.js @@ -4,7 +4,7 @@ import lodash from 'lodash' import Base from './Base.js' import { Data } from '../components/index.js' -import MaterialMeta from './material-lib/MaterialMeta.js' +import MaterialMeta from './material/MaterialMeta.js' let data = Data.readJSON('resources/meta/material/data.json') let abbr = await Data.importDefault('resources/meta/material/abbr.js') diff --git a/models/MysApi.js b/models/MysApi.js index 3b0152ce..e5b6df33 100644 --- a/models/MysApi.js +++ b/models/MysApi.js @@ -12,6 +12,26 @@ export default class MysApi { e.isSelfCookie = this.isSelfCookie } + get isSelfCookie () { + return this.uid * 1 === this.ckUid * 1 || this?.mysInfo?.isSelf + } + + get ckUid () { + return this.ckInfo.uid + } + + get ck () { + return this.ckInfo.ck + } + + get selfUser () { + return new User({ id: this.e.user_id, uid: this.uid }) + } + + get targetUser () { + return new User({ id: this.e.user_id, uid: this.uid }) + } + static async init (e, auth = 'all') { if (!e.runtime) { Version.runtime() @@ -50,31 +70,12 @@ export default class MysApi { if (uid) { return new User({ id: e.user_id, uid }) } else { - e.reply('请先#绑定uid') + e.reply('请先发送【#绑定+你的UID】来绑定查询目标') + e._replyNeedUid = true return false } } - get isSelfCookie () { - return this.uid * 1 === this.ckUid * 1 || this?.mysInfo?.isSelf - } - - get ckUid () { - return this.ckInfo.uid - } - - get ck () { - return this.ckInfo.ck - } - - get selfUser () { - return new User({ id: this.e.user_id, uid: this.uid }) - } - - get targetUser () { - return new User({ id: this.e.user_id, uid: this.uid }) - } - async getMysApi (e, targetType = 'all', option = {}) { if (this.mys) { return this.mys diff --git a/models/Player.js b/models/Player.js index 3c005080..a888e480 100644 --- a/models/Player.js +++ b/models/Player.js @@ -9,8 +9,8 @@ import Base from './Base.js' import { Data } from '../components/index.js' import { AvatarData, ProfileRank, Character } from './index.js' -import MysAvatar from './player-lib/MysAvatar.js' -import Profile from './player-lib/Profile.js' +import MysAvatar from './player/MysAvatar.js' +import Profile from './player/Profile.js' Data.createDir('/data/userData', 'root') @@ -30,6 +30,17 @@ export default class Player extends Base { return this._cache() } + get hasProfile () { + let ret = false + lodash.forEach(this._avatars, (avatar) => { + if (avatar.isProfile) { + ret = true + return false + } + }) + return ret + } + static create (e) { if (e?._mys?.uid || e.uid) { // 传入为e @@ -68,8 +79,8 @@ export default class Player extends Base { save () { let ret = Data.getData(this, 'uid,name,level,word,face,card,sign,info,_info,_mys,_profile') ret.avatars = {} - lodash.forEach(this._avatars, (ds) => { - ret.avatars[ds.id] = ds.toJSON() + this.forEachAvatar((avatar) => { + ret.avatars[avatar.id] = avatar.toJSON() }) // 暂时保留旧数据,防止异常情况 if (this._chars) { @@ -136,9 +147,12 @@ export default class Player extends Base { // 循环Avatar forEachAvatar (fn) { for (let id in this._avatars) { - let ret = fn(this._avatars[id], id) - if (ret === false) { - return false + let avatar = this._avatars[id] + if (avatar && avatar.hasData) { + let ret = fn(this._avatars[id]) + if (ret === false) { + return false + } } } } @@ -195,7 +209,7 @@ export default class Player extends Base { } // 更新面板 - async refreshProfile (force = 1) { + async refreshProfile (force = 2) { return await Profile.refreshProfile(this, force) } @@ -223,14 +237,20 @@ export default class Player extends Base { return await MysAvatar.refreshTalent(this, ids, force) } - async refreshAndGetAvatarData (cfg) { - // 更新角色信息 - await this.refreshMysDetail(cfg.force || 0) - - // 更新天赋信息 - if (cfg.refreshTalent !== false) { - await this.refreshTalent(cfg.ids, cfg.force || 0) + async refresh (cfg) { + if (cfg.detail || cfg.detail === 0) { + await this.refreshMysDetail(cfg.detail) } + if (cfg.talent || cfg.talent === 0) { + await this.refreshTalent(cfg.ids, cfg.talent) + } + if (cfg.profile || cfg.profile === 0) { + await this.refreshProfile(cfg.profile) + } + } + + async refreshAndGetAvatarData (cfg) { + await this.refresh(cfg) let rank = false let e = this.e diff --git a/models/ProfileArtis.js b/models/ProfileArtis.js index 3f2ceb13..90000da9 100644 --- a/models/ProfileArtis.js +++ b/models/ProfileArtis.js @@ -5,9 +5,9 @@ import lodash from 'lodash' import AvatarArtis from './AvatarArtis.js' import { Artifact, ArtifactSet, Character } from './index.js' import { Format } from '../components/index.js' -import ArtisMark from './profile-lib/ArtisMark.js' +import ArtisMark from './profile/ArtisMark.js' import { attrMap } from '../resources/meta/artifact/index.js' -import CharArtis from './profile-lib/CharArtis.js' +import CharArtis from './profile/CharArtis.js' export default class ProfileArtis extends AvatarArtis { constructor (charid = 0, elem = '') { diff --git a/models/ProfileData.js b/models/ProfileData.js index 0799be67..b20ff8af 100644 --- a/models/ProfileData.js +++ b/models/ProfileData.js @@ -2,8 +2,8 @@ import lodash from 'lodash' import AvatarData from './AvatarData.js' import { Data } from '../components/index.js' import { ProfileArtis, ProfileDmg } from './index.js' -import AttrCalc from './profile-lib/AttrCalc.js' -import CharImg from './character-lib/CharImg.js' +import AttrCalc from './profile/AttrCalc.js' +import CharImg from './character/CharImg.js' export default class ProfileData extends AvatarData { constructor (ds = {}, calc = true) { diff --git a/models/ProfileDmg.js b/models/ProfileDmg.js index 16a4bbe1..02e0d410 100644 --- a/models/ProfileDmg.js +++ b/models/ProfileDmg.js @@ -3,9 +3,9 @@ import lodash from 'lodash' import Base from './Base.js' import { Character } from './index.js' import { attrMap } from '../resources/meta/artifact/index.js' -import DmgBuffs from './profile-lib/DmgBuffs.js' -import DmgAttr from './profile-lib/DmgAttr.js' -import DmgCalc from './profile-lib/DmgCalc.js' +import DmgBuffs from './profile/DmgBuffs.js' +import DmgAttr from './profile/DmgAttr.js' +import DmgCalc from './profile/DmgCalc.js' import { Common } from '../components/index.js' export default class ProfileDmg extends Base { diff --git a/models/character-lib/CharCfg.js b/models/character/CharCfg.js similarity index 100% rename from models/character-lib/CharCfg.js rename to models/character/CharCfg.js diff --git a/models/character-lib/CharId.js b/models/character/CharId.js similarity index 100% rename from models/character-lib/CharId.js rename to models/character/CharId.js diff --git a/models/character-lib/CharImg.js b/models/character/CharImg.js similarity index 100% rename from models/character-lib/CharImg.js rename to models/character/CharImg.js diff --git a/models/character-lib/CharMeta.js b/models/character/CharMeta.js similarity index 100% rename from models/character-lib/CharMeta.js rename to models/character/CharMeta.js diff --git a/models/character-lib/CharTalent.js b/models/character/CharTalent.js similarity index 100% rename from models/character-lib/CharTalent.js rename to models/character/CharTalent.js diff --git a/models/material-lib/MaterialMeta.js b/models/material/MaterialMeta.js similarity index 100% rename from models/material-lib/MaterialMeta.js rename to models/material/MaterialMeta.js diff --git a/models/player-lib/EnkaApi.js b/models/player/EnkaApi.js similarity index 100% rename from models/player-lib/EnkaApi.js rename to models/player/EnkaApi.js diff --git a/models/player-lib/EnkaData.js b/models/player/EnkaData.js similarity index 100% rename from models/player-lib/EnkaData.js rename to models/player/EnkaData.js diff --git a/models/player-lib/MiaoApi.js b/models/player/MiaoApi.js similarity index 100% rename from models/player-lib/MiaoApi.js rename to models/player/MiaoApi.js diff --git a/models/player-lib/MiaoData.js b/models/player/MiaoData.js similarity index 100% rename from models/player-lib/MiaoData.js rename to models/player/MiaoData.js diff --git a/models/player-lib/MysAvatar.js b/models/player/MysAvatar.js similarity index 95% rename from models/player-lib/MysAvatar.js rename to models/player/MysAvatar.js index 89706bce..af9f5469 100644 --- a/models/player-lib/MysAvatar.js +++ b/models/player/MysAvatar.js @@ -1,13 +1,17 @@ import lodash from 'lodash' import { Common, Data } from '../../components/index.js' +import { chestInfo } from '../../resources/meta/info/index.js' import moment from 'moment' const MysAvatar = { needRefresh (time, force = 0, forceMap = {}) { - if (!time) { + if (!time || force === 2) { return true } + if (force === true) { + force = 0 + } let duration = new Date() * 1 - time * 1 if (isNaN(duration) || duration < 0) { return true @@ -250,15 +254,16 @@ const MysAvatar = { }, getInfo (player) { + let chestMap = [] + Data.eachStr('common,exquisite,precious,luxurious,magic', (key) => { + chestMap.push({ + key: `${key}Chest`, + ...chestInfo[key] + }) + }) let ret = { ...(player.info || {}), - chestMap: [ - { key: 'commonChest', title: '普通宝箱', max: 2521 }, - { key: 'exquisiteChest', title: '精致宝箱', max: 1585 }, - { key: 'preciousChest', title: '珍贵宝箱', max: 482 }, - { key: 'luxuriousChest', title: '豪华宝箱', max: 184 }, - { key: 'magicChest', title: '奇馈宝箱', max: 140 } - ] + chestMap } let stats = ret.stats || {} if (stats?.activeDay) { diff --git a/models/player-lib/Profile.js b/models/player/Profile.js similarity index 90% rename from models/player-lib/Profile.js rename to models/player/Profile.js index 6d10ee00..9cf4c10d 100644 --- a/models/player-lib/Profile.js +++ b/models/player/Profile.js @@ -1,5 +1,6 @@ import { ProfileReq, ProfileServ } from '../index.js' import { Cfg, Data } from '../../components/index.js' +import MysAvatar from './MysAvatar.js' import enkaCfg from './EnkaApi.js' import MiaoApi from './MiaoApi.js' @@ -34,7 +35,10 @@ const Profile = { * @param force * @returns {Promise} */ - async refreshProfile (player, force = 1) { + async refreshProfile (player, force = 2) { + if (!MysAvatar.needRefresh(player._profile, force, { 0: 24, 1: 2, 2: 0 })) { + return false + } player._update = [] let { uid, e } = player if (uid.toString().length !== 9 || !e) { diff --git a/models/player-lib/ProfileMeta.js b/models/player/ProfileMeta.js similarity index 100% rename from models/player-lib/ProfileMeta.js rename to models/player/ProfileMeta.js diff --git a/models/profile-lib/ArtisMark.js b/models/profile/ArtisMark.js similarity index 100% rename from models/profile-lib/ArtisMark.js rename to models/profile/ArtisMark.js diff --git a/models/profile-lib/AttrCalc.js b/models/profile/AttrCalc.js similarity index 100% rename from models/profile-lib/AttrCalc.js rename to models/profile/AttrCalc.js diff --git a/models/profile-lib/CharArtis.js b/models/profile/CharArtis.js similarity index 100% rename from models/profile-lib/CharArtis.js rename to models/profile/CharArtis.js diff --git a/models/profile-lib/DmgAttr.js b/models/profile/DmgAttr.js similarity index 100% rename from models/profile-lib/DmgAttr.js rename to models/profile/DmgAttr.js diff --git a/models/profile-lib/DmgBuffs.js b/models/profile/DmgBuffs.js similarity index 100% rename from models/profile-lib/DmgBuffs.js rename to models/profile/DmgBuffs.js diff --git a/models/profile-lib/DmgCalc.js b/models/profile/DmgCalc.js similarity index 100% rename from models/profile-lib/DmgCalc.js rename to models/profile/DmgCalc.js diff --git a/models/profile-lib/DmgCalcMeta.js b/models/profile/DmgCalcMeta.js similarity index 100% rename from models/profile-lib/DmgCalcMeta.js rename to models/profile/DmgCalcMeta.js diff --git a/models/profile-lib/DmgMastery.js b/models/profile/DmgMastery.js similarity index 100% rename from models/profile-lib/DmgMastery.js rename to models/profile/DmgMastery.js diff --git a/models/profile-lib/ProfileInput.js b/models/profile/ProfileInput.js similarity index 100% rename from models/profile-lib/ProfileInput.js rename to models/profile/ProfileInput.js diff --git a/resources/character/avatar-list.css b/resources/character/avatar-list.css index 6fed5c15..db82237c 100644 --- a/resources/character/avatar-list.css +++ b/resources/character/avatar-list.css @@ -159,7 +159,7 @@ .chest-list .chest .detail { width: 60px; height: 40px; - font-size: 13px; + font-size: 14px; } .chest-list .chest .info { display: flex; @@ -176,7 +176,6 @@ vertical-align: center; } .chest-list .chest .max { - font-size: 12px; padding-left: 3px; color: #aaa; } diff --git a/resources/character/avatar-list.less b/resources/character/avatar-list.less index e8538af8..3f605a74 100644 --- a/resources/character/avatar-list.less +++ b/resources/character/avatar-list.less @@ -191,7 +191,7 @@ .detail { width: 60px; height: 40px; - font-size: 13px; + font-size: 14px; } .info { @@ -211,7 +211,6 @@ } .max { - font-size: 12px; padding-left: 3px; color: #aaa; } diff --git a/resources/common/tpl.css b/resources/common/tpl.css index 73604baf..045df589 100644 --- a/resources/common/tpl.css +++ b/resources/common/tpl.css @@ -171,6 +171,11 @@ padding: 3px 7.5px 3px 4.5px; border-radius: 0 6px 0 0; color: #fff; + font-size: 16px; + text-shadow: 0 0 1px #000; +} +.avatar-card .avatar-face .avatar-level span { + font-size: 12px; } .avatar-card .cons { border-radius: 0 0 0 7.5px; @@ -178,6 +183,11 @@ position: absolute; right: 0; top: 0; + font-size: 16px; + text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, 0.8); +} +.avatar-card .cons.cons-0 { + display: none; } .avatar-card .avatar-talent { height: 30px; @@ -253,15 +263,23 @@ width: 114px; border-radius: 10.5px 0 0 10.5px; } -.avatar-card.card-wide .img { +.avatar-card.card-wide .avatar-face .img { background-size: 100% auto; background-position: 0 10%; - height: 135px; + height: 202.5px; margin-top: -13.5px; } .avatar-card.card-wide .avatar-info { width: 105px; } +.avatar-card.card-wide .avatar-info strong { + display: block; + height: 45px; + line-height: 45px; +} +.avatar-card.card-wide .avatar-info .lv-info { + height: 30px; +} .avatar-card.card-wide .line { display: block; height: 1px; @@ -307,8 +325,12 @@ .avatar-card .avatar-artis { position: relative; } +.avatar-card .avatar-artis.artis0 .item-icon { + background: url('./item/artifact-icon.webp') rgba(0, 0, 0, 0.3) center no-repeat; + background-size: auto 80%; +} .avatar-card .avatar-artis .artis { - background: rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, 0.4); } .avatar-card .avatar-artis.artis2 .img { position: absolute; diff --git a/resources/common/tpl/avatar-card.html b/resources/common/tpl/avatar-card.html index e5e3c653..a411e965 100644 --- a/resources/common/tpl/avatar-card.html +++ b/resources/common/tpl/avatar-card.html @@ -10,21 +10,24 @@
{{avatar.cons}} -
Lv{{avatar.level}}
+
Lv{{avatar.level}} {{avatar.abbr}}
{{set talent = avatar.talent || {} }} - {{if talent.a && talent.a.level }} +
{{avatar.abbr}} - {{avatar.cons}} - Lv{{avatar.level}} +
+ {{avatar.cons}} + Lv{{avatar.level}} +
+ {{if talent.a && talent.a.level }}
{{each talentMap k}} - {{set t = talent[k] || {} }} {{t.level}} + {{set t = talent[k] || {} }} + {{t.level}} {{/each}}
{{else}} @@ -35,7 +38,7 @@
- {{weapon.affix}} + {{weapon.affix}}
diff --git a/resources/common/tpl/avatar-card.less b/resources/common/tpl/avatar-card.less index 5f992589..ae5bea3d 100644 --- a/resources/common/tpl/avatar-card.less +++ b/resources/common/tpl/avatar-card.less @@ -37,6 +37,12 @@ padding: @px*2 @px*5 @px*2 @px*3; border-radius: 0 @px*4 0 0; color: #fff; + font-size: 16px; + text-shadow: 0 0 1px #000; + + span { + font-size: 12px; + } } } @@ -47,6 +53,12 @@ position: absolute; right: 0; top: 0; + font-size: 16px; + text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, .8); + + &.cons-0 { + display: none; + } } .avatar-talent { @@ -136,17 +148,27 @@ height: @px * 126; width: @px*76; border-radius: @px*7 0 0 @px*7; - } - .img { - background-size: 100% auto; - background-position: 0 10%; - height: 135px; - margin-top: @px*-9; + .img { + background-size: 100% auto; + background-position: 0 10%; + height: @px*135; + margin-top: @px*-9; + } } .avatar-info { width: @px*70; + + strong { + display: block; + height: @px * 30; + line-height: @px*30; + } + + .lv-info { + height: @px*20; + } } .line { @@ -211,8 +233,15 @@ .avatar-artis { position: relative; + &.artis0 { + .item-icon { + background: url('./item/artifact-icon.webp') rgba(0, 0, 0, .3) center no-repeat; + background-size: auto 80%; + } + } + .artis { - background: rgba(0, 0, 0, 0.5) + background: rgba(0, 0, 0, 0.4) } &.artis2 { diff --git a/resources/meta/info/index.js b/resources/meta/info/index.js new file mode 100644 index 00000000..10730715 --- /dev/null +++ b/resources/meta/info/index.js @@ -0,0 +1,23 @@ +// 报箱数统计 +export const chestInfo = { + common: { + title: '普通宝箱', + max: 2542 + }, + exquisite: { + title: '精致宝箱', + max: 1594 + }, + precious: { + title: '珍贵宝箱', + max: 488 + }, + luxurious: { + title: '豪华宝箱', + max: 185 + }, + magic: { + title: '奇馈宝箱', + max: 146 + } +} \ No newline at end of file diff --git a/resources/meta/weapon/sword/calc.js b/resources/meta/weapon/sword/calc.js index 32d5a549..2f69d402 100644 --- a/resources/meta/weapon/sword/calc.js +++ b/resources/meta/weapon/sword/calc.js @@ -148,13 +148,8 @@ export default function (step, staticStep) { } }], - 裁叶萃光: [{ - title: '暴击率提升[cpct]%', - refine: { - cpct: step(4) - } - }, { - title: '暴击率提升4%,普攻与元素战技造成的伤害值提高[aPlus]', + 裁叶萃光: [staticStep('cpct', 4), { + title: '普攻与元素战技造成的伤害值提高[aPlus]', data: { aPlus: ({ attr, calc, refine }) => calc(attr.mastery) * step(120)[refine] / 100, ePlus: ({ attr, calc, refine }) => calc(attr.mastery) * step(120)[refine] / 100 diff --git a/resources/stat/abyss-summary.css b/resources/stat/abyss-summary.css index 7ca2663a..17c2cac0 100644 --- a/resources/stat/abyss-summary.css +++ b/resources/stat/abyss-summary.css @@ -84,7 +84,7 @@ body, color: #d3bc8e; } .avatar-banner { - height: 265px; + height: 300px; width: 175px; background-repeat: no-repeat; background-size: 100% auto; diff --git a/resources/stat/abyss-summary.less b/resources/stat/abyss-summary.less index 67f31342..6338f6c5 100644 --- a/resources/stat/abyss-summary.less +++ b/resources/stat/abyss-summary.less @@ -97,7 +97,7 @@ body, .container { .avatar-banner { - height: 265px; + height: 300px; width: 175px; background-repeat: no-repeat; background-size: 100% auto;