mirror of
https://github.com/yoimiya-kokomi/miao-plugin.git
synced 2024-11-21 14:38:30 +00:00
apps目录结构调整,面板自动更新逻辑更新
This commit is contained in:
parent
a54b9c6bd4
commit
f4353fc5ca
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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: `<style>html,body,#container{width:${width}px} ${imgCss}</style>`,
|
||||
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: `<style>html,body,#container{width:${width}px} ${imgCss}</style>`,
|
||||
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
|
||||
|
@ -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
|
||||
|
95
apps/help.js
95
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 })
|
||||
}
|
||||
|
77
apps/help/Help.js
Normal file
77
apps/help/Help.js
Normal file
@ -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
|
@ -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
|
||||
|
164
apps/profile.js
164
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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
522
apps/stat.js
522
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
|
||||
}
|
||||
|
164
apps/stat/AbyssStat.js
Normal file
164
apps/stat/AbyssStat.js
Normal file
@ -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 })
|
||||
}
|
137
apps/stat/AbyssSummary.js
Normal file
137
apps/stat/AbyssSummary.js
Normal file
@ -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
|
||||
}
|
189
apps/stat/AbyssTeam.js
Normal file
189
apps/stat/AbyssTeam.js
Normal file
@ -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 })
|
||||
}
|
186
apps/wiki.js
186
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 })
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
18
apps/wiki/CharMaterial.js
Normal file
18
apps/wiki/CharMaterial.js
Normal file
@ -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
|
49
apps/wiki/CharTalent.js
Normal file
49
apps/wiki/CharTalent.js
Normal file
@ -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
|
@ -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
|
||||
|
108
apps/wiki/CharWikiData.js
Normal file
108
apps/wiki/CharWikiData.js
Normal file
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = '') {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
@ -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<boolean|number>}
|
||||
*/
|
||||
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) {
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -10,21 +10,24 @@
|
||||
<div class="img mini" style="background-image:url({{_res_path}}{{avatar.face}})"></div>
|
||||
<div class="img wide avatar-{{avatar.name}}" style="background-image:url({{_res_path}}{{avatar.gacha}})"></div>
|
||||
<span class="cons cons-{{avatar.cons}} mini">{{avatar.cons}}</span>
|
||||
<div class="avatar-level">Lv{{avatar.level}}</div>
|
||||
<div class="avatar-level"><span>Lv</span>{{avatar.level}} {{avatar.abbr}}</div>
|
||||
</div>
|
||||
<div class="avatar-info">
|
||||
{{set talent = avatar.talent || {} }}
|
||||
{{if talent.a && talent.a.level }}
|
||||
|
||||
<div class="avatar-name wide">
|
||||
<strong>{{avatar.abbr}}</strong>
|
||||
<span class="cons cons-{{avatar.cons}}">{{avatar.cons}}</span>
|
||||
<span class="avatar-level">Lv{{avatar.level}}</span>
|
||||
<div class="lv-info">
|
||||
<span class="cons cons-{{avatar.cons}}">{{avatar.cons}}</span>
|
||||
<span class="avatar-level">Lv{{avatar.level}}</span>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
{{if talent.a && talent.a.level }}
|
||||
<div class="avatar-talent">
|
||||
{{each talentMap k}}
|
||||
{{set t = talent[k] || {} }} <span
|
||||
class="talent-item talent-{{k}} talent-{{t.original===10?'crown':'none'}} talent-{{t.level>t.original?'plus':'none'}}">{{t.level}}</span>
|
||||
{{set t = talent[k] || {} }}
|
||||
<span class="talent-item talent-{{k}} talent-{{t.original===10?'crown':'none'}} talent-{{t.level>t.original?'plus':'none'}}">{{t.level}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
@ -35,7 +38,7 @@
|
||||
<div class="item avatar-weapon">
|
||||
<div class="item-icon star{{weapon.star}}">
|
||||
<span class="img" style="background-image:url({{_res_path}}{{weapon.img}})"></span>
|
||||
<span class="cons cons-{{weapon.affix}}">{{weapon.affix}}</span>
|
||||
<span class="cons cons-{{weapon.affix > 4 ? weapon.affix + 1 : weapon.affix}}">{{weapon.affix}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item avatar-artis artis{{avatar.artisSet?.names?.length}}">
|
||||
|
@ -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 {
|
||||
|
23
resources/meta/info/index.js
Normal file
23
resources/meta/info/index.js
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -84,7 +84,7 @@ body,
|
||||
color: #d3bc8e;
|
||||
}
|
||||
.avatar-banner {
|
||||
height: 265px;
|
||||
height: 300px;
|
||||
width: 175px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% auto;
|
||||
|
@ -97,7 +97,7 @@ body, .container {
|
||||
|
||||
|
||||
.avatar-banner {
|
||||
height: 265px;
|
||||
height: 300px;
|
||||
width: 175px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% auto;
|
||||
|
Loading…
Reference in New Issue
Block a user