mirror of
https://github.com/yoimiya-kokomi/miao-plugin.git
synced 2024-11-21 22:48:13 +00:00
commit
b8d1db30cc
37
CHANGELOG.md
37
CHANGELOG.md
@ -1,18 +1,27 @@
|
||||
# 2.2.2
|
||||
# 2.3.0
|
||||
|
||||
* 重写底层面板、角色数据获取与保存逻辑
|
||||
* 底层完全兼容面板及Mys数据,对于miao-plugin的大部分场景可做到数据通用
|
||||
* 角色数据及天赋增加缓存逻辑,有缓存数据情况下可在ck失效/验证码等情况下正常使用大部分功能
|
||||
* 全量使用通过圣遗物属性计算得到的面板数据
|
||||
* 面板底层数据结构及存储逻辑优化,兼容老版本数据
|
||||
* Enka服务下使用statsIds存储圣遗物数据,能够更精确的计算角色属性
|
||||
* 增加`#角色`功能,查询并展示Mys角色信息
|
||||
* Yunzai需要跟随游戏版本升级的功能会逐步在miao-plugin中提供,以保障基础功能相对长期可用
|
||||
* 大部分功能目前默认关闭,可在`#喵喵设置`中设置并开启
|
||||
* 为`#喵喵设置`增加更多配置项
|
||||
* 允许禁用面板替换功能
|
||||
* 允许禁用非实装角色资料,关闭可禁用非实装角色资料及面板替换
|
||||
* 允许禁用面板替换功能
|
||||
* 允许禁用获取角色或面板原图功能
|
||||
* 可选择面板服务,可选喵喵Api优先(需具备Token)或Enka优先
|
||||
* 更新迪希雅、米卡的最新天赋与命座数据
|
||||
* 全量使用通过属性计算得到的面板数据,移除相关配置项
|
||||
|
||||
# 2.2.1
|
||||
|
||||
* 增加瑶瑶伤害计算
|
||||
* 增加丽莎的皮肤数据,需要重新更新面板数据以获得信息
|
||||
* 为群排名人数、圣遗物列表展示数增加`#喵喵设置`的配置 **@SmallK111407**
|
||||
* 可通过`#喵喵设置`配置`#面板练度统计`替换Yunzai`#练度统计`功能 **@SmallK111407**
|
||||
* 可设置群排名人数、圣遗物列表展示数 **@SmallK111407**
|
||||
* 角色信息及伤害计算更新
|
||||
* 更新迪希雅、米卡的最新天赋与命座数据
|
||||
* 增加瑶瑶伤害计算
|
||||
* 其他功能及界面优化,部分已知问题调整
|
||||
* `#上传深渊` 界面与样式调整
|
||||
* `#刷新排名`、`#禁用排名`、`#启用排名`可由群管理员进行管理
|
||||
* 增加`#删除面板`命令,目前绑定CK用户使用,Bot主人可删除任意UID数据
|
||||
|
||||
# 2.2.0
|
||||
|
||||
@ -154,11 +163,11 @@
|
||||
* 部分依赖MysApi查询的功能在V3下暂时只支持查自己
|
||||
* 增加提纳里、柯莱、多莉的资料及角色图像
|
||||
* 可通过 `#柯莱天赋`、`#柯莱命座`查看资料
|
||||
* 增加 `#深渊使用率`命令,数据源自SG团队胡桃API
|
||||
* 增加 `#深渊使用率`命令,数据源自DGP-Studio胡桃API
|
||||
* 新增 `#上传深渊数据`命令
|
||||
* 上传自己角色的深渊挑战数据及角色列表,并展示在本期深渊中伤害与承伤排名
|
||||
* 上传数据用于 `#角色持有率 #深渊出场率`等统计,可使统计更加及时准确
|
||||
* 数据统计及服务来自SG团队胡桃API
|
||||
* 数据统计及服务来自DGP-Studio胡桃API
|
||||
* 增加 `#添加刻晴图像`命令,感谢 **@叶**
|
||||
* 可通过命令上传添加指定角色图片,上传至 **resources/character-img/刻晴/upload**
|
||||
* 请将图像与命令一同发送,后续会支持at图像及命令后发送图像
|
||||
@ -267,7 +276,7 @@
|
||||
|
||||
* 增加 `#深渊配队` 功能
|
||||
* 根据当前账号的角色练度及本期深渊出场数据,推荐较匹配的配队方案
|
||||
* 深渊出场数据来自胡桃API,为Snap Genshin用户自主上传的深渊挑战记录,感谢SG团队
|
||||
* 深渊出场数据来自DGP-Studio胡桃API
|
||||
* 配队方案仅供参考
|
||||
* `#角色面板` 伤害计算新增部分角色
|
||||
* 目前支持:雷神、胡桃、魈、神子、甘雨、宵宫、公子、绫人、绫华、心海、钟离
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,124 +1,125 @@
|
||||
import { Character, Avatar, MysApi } from '../../models/index.js'
|
||||
import { Cfg, Common, Profile } from '../../components/index.js'
|
||||
import { Character, MysApi, Player } from '../../models/index.js'
|
||||
import { Cfg, Common } from '../../components/index.js'
|
||||
import lodash from 'lodash'
|
||||
import { segment } from 'oicq'
|
||||
import moment from 'moment'
|
||||
|
||||
export async function renderAvatar (e, avatar, renderType = 'card') {
|
||||
// 如果传递的是名字,则获取
|
||||
let uid = e.uid
|
||||
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
|
||||
uid = mys.uid
|
||||
if (!char.isRelease) {
|
||||
avatar = { id: char.id, name: char.name, detail: false }
|
||||
} else {
|
||||
let profile = Profile.get(uid, char.id, true)
|
||||
if (profile && profile.hasData) {
|
||||
// 优先使用Profile数据
|
||||
avatar = profile
|
||||
} else {
|
||||
// 使用Mys数据兜底
|
||||
let charData = await mys.getCharacter()
|
||||
if (!charData) return true
|
||||
|
||||
let avatars = charData.avatars
|
||||
if (char.isTraveler) {
|
||||
char = await char.checkAvatars(avatars, uid)
|
||||
}
|
||||
avatars = lodash.keyBy(avatars, 'id')
|
||||
avatar = avatars[char.id] || { 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, ds, renderType = 'card') {
|
||||
let char = Character.get(ds)
|
||||
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}`, JSON.stringify({ type: 'character', img: 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) {
|
||||
let mys = await MysApi.init(e)
|
||||
let avatar = new Avatar(ds, uid, mys.isSelfCookie)
|
||||
data = avatar.getData('id,name,sName,level,fetter,cons,weapon,elem,artis,artisSet,imgs,dataSourceName,updateTime')
|
||||
data.talent = await avatar.getTalent(mys)
|
||||
if (data.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 async function getAvatarList (e, type, mys) {
|
||||
let data = await mys.getCharacter()
|
||||
if (!data) return false
|
||||
|
||||
let avatars = data.avatars
|
||||
|
||||
if (!avatars || avatars.length <= 0) {
|
||||
return false
|
||||
}
|
||||
let list = []
|
||||
for (let val of avatars) {
|
||||
if (type !== false) {
|
||||
if (!Character.checkWifeType(val.id, type)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (val.rarity > 5) {
|
||||
val.rarity = 5
|
||||
}
|
||||
list.push(val)
|
||||
}
|
||||
|
||||
if (list.length <= 0) {
|
||||
return false
|
||||
}
|
||||
let sortKey = 'level,fetter,weapon_level,rarity,weapon_rarity,cons,weapon_affix_level'
|
||||
list = lodash.orderBy(list, sortKey, lodash.repeat('desc,', sortKey.length).split(','))
|
||||
return list
|
||||
|
||||
}
|
||||
export default Avatar
|
||||
|
@ -1,8 +1,8 @@
|
||||
// #老婆
|
||||
import lodash from 'lodash'
|
||||
import { Common } from '../../components/index.js'
|
||||
import { Character, MysApi, AvatarList } from '../../models/index.js'
|
||||
import { getAvatarList, renderAvatar } from './AvatarCard.js'
|
||||
import { Character, MysApi, Player } from '../../models/index.js'
|
||||
import Avatar from './AvatarCard.js'
|
||||
|
||||
const relationMap = {
|
||||
wife: {
|
||||
@ -32,140 +32,157 @@ 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
|
||||
async function getAvatarList (player, type) {
|
||||
await player.refreshMysDetail()
|
||||
let list = []
|
||||
player.forEachAvatar((avatar) => {
|
||||
if (type !== false) {
|
||||
if (!avatar.char.checkWifeType(type)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
list.push(avatar)
|
||||
})
|
||||
|
||||
if (e.isPoke) {
|
||||
if (!Common.cfg('avatarPoke')) {
|
||||
if (list.length <= 0) {
|
||||
return false
|
||||
}
|
||||
let sortKey = 'level,fetter,weapon_level,rarity,weapon_rarity,cons,weapon_affix_level'
|
||||
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('avatarCard')) {
|
||||
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] || ''
|
||||
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
|
||||
}
|
||||
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 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 avatarList = []
|
||||
let avatar = {}
|
||||
let wifeList = []
|
||||
|
||||
let mys = await MysApi.init(e)
|
||||
if (!mys || !mys.uid) {
|
||||
return true
|
||||
}
|
||||
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(e, targetCfg.type, mys)
|
||||
let avatar = lodash.sample(avatarList)
|
||||
return renderAvatar(e, avatar, renderType)
|
||||
} else {
|
||||
// 如果指定过,则展示指定角色
|
||||
return renderAvatar(e, lodash.sample(wifeList), renderType)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果未指定过,则从列表中排序并随机选择前5个
|
||||
if (e.isPoke) {
|
||||
avatarList = await getAvatarList(e, false, mys)
|
||||
// 如果未指定过,则从列表中排序并随机选择
|
||||
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)
|
||||
return await Avatar.renderAvatar(e, avatar, renderType)
|
||||
}
|
||||
} else {
|
||||
avatarList = await getAvatarList(e, targetCfg.type, mys)
|
||||
if (avatarList && avatarList.length > 0) {
|
||||
avatar = lodash.sample(avatarList.slice(0, 5))
|
||||
return await renderAvatar(e, avatar, renderType)
|
||||
}
|
||||
}
|
||||
e.reply('在当前米游社公开展示的角色中未能找到适合展示的角色..')
|
||||
return true
|
||||
case '设置':
|
||||
case '选择':
|
||||
case '挑选':
|
||||
case '指定':
|
||||
if (!isSelf) {
|
||||
e.reply('只能指定自己的哦~')
|
||||
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} ~`)
|
||||
case '设置':
|
||||
case '选择':
|
||||
case '挑选':
|
||||
case '指定':
|
||||
if (!isSelf) {
|
||||
e.reply('只能指定自己的哦~')
|
||||
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('只能查看自己的哦~')
|
||||
// 选择老婆
|
||||
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
|
||||
}
|
||||
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
|
||||
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)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export async function pokeWife (e, components) {
|
||||
return await wife(e, components)
|
||||
}
|
||||
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
|
||||
|
300
apps/profile.js
300
apps/profile.js
@ -1,209 +1,137 @@
|
||||
import { Common, App, Data, Cfg } from '../components/index.js'
|
||||
import { Character } from '../models/index.js'
|
||||
import { getTargetUid, getProfile, profileHelp } from './profile/ProfileCommon.js'
|
||||
import { profileArtis, profileArtisList } from './profile/ProfileArtis.js'
|
||||
import { renderProfile } from './profile/ProfileDetail.js'
|
||||
import { profileStat } from './profile/ProfileStat.js'
|
||||
import { profileList } from './profile/ProfileList.js'
|
||||
import { App, Cfg } from '../components/index.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 { uploadCharacterImg, delProfileImg, profileImgList } from './character/ImgUpload.js'
|
||||
import { enemyLv } from './profile/ProfileUtils.js'
|
||||
import ProfileChange from './profile/ProfileChange.js'
|
||||
import { groupRank, resetRank, refreshRank, manageRank } from './profile/ProfileRank.js'
|
||||
|
||||
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: {
|
||||
name: '角色面板',
|
||||
fn: profileDetail,
|
||||
rule: /^#*([^#]+)\s*(详细|详情|面板|面版|圣遗物|伤害[1-7]?)\s*(\d{9})*(.*[换变改].*)?$/
|
||||
},
|
||||
|
||||
app.reg('reset-rank', resetRank, {
|
||||
rule: /^#(重置|重设)(.*)(排名|排行)$/,
|
||||
name: '重置排名'
|
||||
})
|
||||
profileChange: {
|
||||
name: '角色面板计算',
|
||||
fn: profileDetail,
|
||||
rule: /^#.+换.+$/
|
||||
},
|
||||
|
||||
app.reg('refresh-rank', refreshRank, {
|
||||
rule: /^#(刷新|更新|重新加载)(群内|群|全部)*(排名|排行)$/,
|
||||
name: '重置排名'
|
||||
})
|
||||
groupProfile: {
|
||||
name: '群内最强',
|
||||
fn: groupRank,
|
||||
rule: /^#(群|群内)?(排名|排行)?(最强|最高|最高分|最牛|第一)+.+/
|
||||
},
|
||||
|
||||
app.reg('manage-rank', manageRank, {
|
||||
rule: /^#(开启|打开|启用|关闭|禁用)(群内|群|全部)*(排名|排行)$/,
|
||||
name: '打开关闭'
|
||||
})
|
||||
resetRank: {
|
||||
name: '重置排名',
|
||||
fn: resetRank,
|
||||
rule: /^#(重置|重设)(.*)(排名|排行)$/
|
||||
},
|
||||
|
||||
app.reg('rank-list', groupRank, {
|
||||
rule: /^#(群|群内)?.+(排名|排行)(榜)?$/,
|
||||
name: '面板排名榜'
|
||||
})
|
||||
refreshRank: {
|
||||
name: '重置排名',
|
||||
fn: refreshRank,
|
||||
rule: /^#(刷新|更新|重新加载)(群内|群|全部)*(排名|排行)$/
|
||||
},
|
||||
|
||||
app.reg('artis-list', profileArtisList, {
|
||||
rule: /^#圣遗物列表\s*(\d{9})?$/,
|
||||
name: '面板圣遗物列表'
|
||||
})
|
||||
manageRank: {
|
||||
name: '打开关闭',
|
||||
fn: manageRank,
|
||||
rule: /^#(开启|打开|启用|关闭|禁用)(群内|群|全部)*(排名|排行)$/
|
||||
},
|
||||
|
||||
app.reg('profile-list', profileList, {
|
||||
rule: /^#(面板角色|角色面板|面板)(列表)?\s*(\d{9})?$/,
|
||||
name: '面板角色列表',
|
||||
desc: '查看当前已获取面板数据的角色列表'
|
||||
})
|
||||
rankList: {
|
||||
name: '面板排名榜',
|
||||
fn: groupRank,
|
||||
rule: /^#(群|群内)?.+(排名|排行)(榜)?$/
|
||||
},
|
||||
|
||||
app.reg('profile-stat', profileStat, {
|
||||
rule: /^#(面板|喵喵|角色|武器|天赋|技能|圣遗物)?练度统计$/,
|
||||
name: '面板练度统计$'
|
||||
})
|
||||
artisList: {
|
||||
name: '面板圣遗物列表',
|
||||
fn: profileArtisList,
|
||||
rule: /^#圣遗物列表\s*(\d{9})?$/
|
||||
},
|
||||
|
||||
app.reg('profile-help', profileHelp, {
|
||||
rule: /^#(角色|换|更换)?面[板版]帮助$/,
|
||||
name: '角色面板帮助'
|
||||
})
|
||||
profileList: {
|
||||
name: '面板角色列表',
|
||||
desc: '查看当前已获取面板数据的角色列表',
|
||||
fn: ProfileList.render,
|
||||
rule: /^#(面板角色|角色面板|面板)(列表)?\s*(\d{9})?$/
|
||||
},
|
||||
|
||||
app.reg('enemy-lv', enemyLv, {
|
||||
rule: /^#(敌人|怪物)等级\s*\d{1,3}\s*$/,
|
||||
describe: '【#角色】 设置伤害计算中目标敌人的等级'
|
||||
})
|
||||
profileStat: {
|
||||
name: '面板练度统计',
|
||||
fn: ProfileStat.stat,
|
||||
rule: /^#(面板|喵喵)练度统计$/,
|
||||
yzRule: /^#*(我的)*(技能|天赋|武器|角色|练度|五|四|5|4|星)+(汇总|统计|列表)(force|五|四|5|4|星)*[ |0-9]*$/,
|
||||
yzCheck: () => Cfg.get('profileStat', false)
|
||||
},
|
||||
|
||||
app.reg('profile-refresh', getProfile, {
|
||||
rule: /^#(全部面板更新|更新全部面板|获取游戏角色详情|更新面板|面板更新)\s*(\d{9})?$/,
|
||||
describe: '【#角色】 获取游戏橱窗详情数据'
|
||||
})
|
||||
avatarList: {
|
||||
name: '角色查询',
|
||||
fn: ProfileStat.avatarList,
|
||||
rule: /^#喵喵(角色|查询)[ |0-9]*$/,
|
||||
yzRule: /^(#(角色|查询|查询角色|角色查询|人物)[ |0-9]*$)|(^(#*uid|#*UID)\+*[1|2|5-9][0-9]{8}$)|(^#[\+|+]*[1|2|5-9][0-9]{8})/,
|
||||
yzCheck: () => Cfg.get('avatarList', false)
|
||||
},
|
||||
|
||||
app.reg('upload-img', uploadCharacterImg, {
|
||||
rule: /^#?\s*(?:上传|添加)(.+)(?:面板图)\s*$/,
|
||||
describe: '【#上传刻晴面板图】 上传角色面板图'
|
||||
profileHelp: {
|
||||
name: '角色面板帮助',
|
||||
fn: profileHelp,
|
||||
rule: /^#(角色|换|更换)?面[板版]帮助$/
|
||||
},
|
||||
|
||||
enemyLv: {
|
||||
name: '敌人等级',
|
||||
fn: enemyLv,
|
||||
describe: '【#角色】 设置伤害计算中目标敌人的等级',
|
||||
rule: /^#(敌人|怪物)等级\s*\d{1,3}\s*$/
|
||||
},
|
||||
|
||||
profileRefresh: {
|
||||
name: '面板更新',
|
||||
describe: '【#角色】 获取游戏橱窗详情数据',
|
||||
fn: ProfileList.refresh,
|
||||
rule: /^#(全部面板更新|更新全部面板|获取游戏角色详情|更新面板|面板更新)\s*(\d{9})?$/
|
||||
},
|
||||
|
||||
uploadImg: {
|
||||
name: '上传面板图',
|
||||
describe: '【#上传刻晴面板图】 上传角色面板图',
|
||||
fn: uploadCharacterImg,
|
||||
rule: /^#?\s*(?:上传|添加)(.+)(?:面板图)\s*$/
|
||||
},
|
||||
|
||||
delProfile: {
|
||||
name: '删除面板图',
|
||||
describe: '【#删除刻晴面板图1】 删除指定角色面板图(序号)',
|
||||
fn: delProfileImg,
|
||||
rule: /^#?\s*(?:移除|清除|删除)(.+)(?:面板图)(\d){1,}\s*$/
|
||||
},
|
||||
|
||||
profileImgList: {
|
||||
name: '面板图列表',
|
||||
describe: '【#刻晴面板图列表】 删除指定角色面板图(序号)',
|
||||
fn: profileImgList,
|
||||
rule: /^#?\s*(.+)(?:面板图列表)\s*$/
|
||||
},
|
||||
|
||||
profileDel: {
|
||||
name: '删除面板',
|
||||
describe: '【#角色】 删除游戏橱窗详情数据',
|
||||
fn: ProfileList.del,
|
||||
rule: /^#(删除全部面板|删除面板|删除面板数据)\s*(\d{9})?$/
|
||||
}
|
||||
})
|
||||
app.reg('del-profile', delProfileImg, {
|
||||
rule: /^#?\s*(?:移除|清除|删除)(.+)(?:面板图)(\d){1,}\s*$/,
|
||||
describe: '【#删除刻晴面板图1】 删除指定角色面板图(序号)'
|
||||
})
|
||||
app.reg('profile-img-list', profileImgList, {
|
||||
rule: /^#?\s*(.+)(?:面板图列表)\s*$/,
|
||||
describe: '【#刻晴面板图列表】 删除指定角色面板图(序号)'
|
||||
})
|
||||
/**
|
||||
app.reg('del-uidflie', delProfile, {
|
||||
rule: /^#?\s*(?:移除|清除|删除)面板数据$/,
|
||||
describe: '【#删除面板数据】 删除面板数据'
|
||||
})
|
||||
*/
|
||||
|
||||
export default app
|
||||
|
||||
export async function delProfile (e) {
|
||||
let uid = await getTargetUid(e)
|
||||
if (!uid) {
|
||||
return true
|
||||
}
|
||||
if (Data.delfile(`data/UserData/${uid}.json`)) {
|
||||
e.reply(`uid:${uid}缓存面板数据已删除~`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 查看当前角色
|
||||
export async function profileDetail (e) {
|
||||
let msg = e.original_msg || e.msg
|
||||
if (!msg) {
|
||||
return false
|
||||
}
|
||||
if (!/详细|详情|面板|面版|圣遗物|伤害|换/.test(msg)) {
|
||||
return false
|
||||
}
|
||||
let mode = 'profile'
|
||||
let profileChange = false
|
||||
let changeMsg = msg
|
||||
let pc = ProfileChange.matchMsg(msg)
|
||||
if (pc && pc.char && pc.change) {
|
||||
if (!Cfg.get('profileChange')) {
|
||||
e.reply('面板替换功能已禁用...')
|
||||
return true
|
||||
}
|
||||
e.uid = pc.uid || e.runtime.uid
|
||||
profileChange = ProfileChange.getProfile(e.uid, pc.char, pc.change)
|
||||
if (profileChange && profileChange.char) {
|
||||
msg = `#${profileChange.char?.name}${pc.mode || '面板'}`
|
||||
e._profile = profileChange
|
||||
e._profileMsg = changeMsg
|
||||
}
|
||||
}
|
||||
let uidRet = /[0-9]{9}/.exec(msg)
|
||||
if (uidRet) {
|
||||
e.uid = uidRet[0]
|
||||
msg = msg.replace(uidRet[0], '')
|
||||
}
|
||||
|
||||
let name = msg.replace(/#|老婆|老公/g, '').trim()
|
||||
msg = msg.replace('面版', '面板')
|
||||
let dmgRet = /伤害(\d?)$/.exec(name)
|
||||
let dmgIdx = 0
|
||||
if (/(最强|最高|最高分|最牛|第一)/.test(msg)) {
|
||||
mode = /(分|圣遗物|评分|ACE)/.test(msg) ? 'rank-mark' : 'rank-dmg'
|
||||
name = name.replace(/(最强|最高分|第一|最高|最牛|圣遗物|评分|群)/g, '')
|
||||
}
|
||||
if (/(详情|详细|面板|面版)\s*$/.test(msg) && !/更新|录入|输入/.test(msg)) {
|
||||
mode = 'profile'
|
||||
name = name.replace(/(详情|详细|面板)/, '').trim()
|
||||
} else if (dmgRet) {
|
||||
mode = 'dmg'
|
||||
name = name.replace(/伤害[0-5]?/, '').trim()
|
||||
if (dmgRet[1]) {
|
||||
dmgIdx = dmgRet[1] * 1
|
||||
}
|
||||
} else if (/(详情|详细|面板)更新$/.test(msg) || (/更新/.test(msg) && /(详情|详细|面板)$/.test(msg))) {
|
||||
mode = 'refresh'
|
||||
name = name.replace(/详情|详细|面板|更新/g, '').trim()
|
||||
} else if (/圣遗物/.test(msg)) {
|
||||
mode = 'artis'
|
||||
name = name.replace('圣遗物', '').trim()
|
||||
}
|
||||
if (!Common.cfg('avatarProfile')) {
|
||||
// 面板开关关闭
|
||||
return false
|
||||
}
|
||||
let char = Character.get(name.trim())
|
||||
if (!char) {
|
||||
return false
|
||||
}
|
||||
|
||||
let uid = e.uid || await getTargetUid(e)
|
||||
if (!uid) {
|
||||
return true
|
||||
}
|
||||
e.uid = uid
|
||||
e.avatar = char.id
|
||||
|
||||
if (char.isCustom) {
|
||||
e.reply('自定义角色暂不支持此功能')
|
||||
return true
|
||||
}
|
||||
if (!char.isRelease) {
|
||||
if (!profileChange) {
|
||||
e.reply('角色尚未实装')
|
||||
return true
|
||||
} else if (Cfg.get('notReleasedData') === false) {
|
||||
e.reply('未实装角色面板已禁用...')
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'profile' || mode === 'dmg') {
|
||||
return renderProfile(e, char, mode, { dmgIdx })
|
||||
} else if (mode === 'refresh') {
|
||||
await getProfile(e)
|
||||
return true
|
||||
} else if (mode === 'artis') {
|
||||
return profileArtis(e)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -3,35 +3,24 @@
|
||||
*
|
||||
* */
|
||||
import lodash from 'lodash'
|
||||
import { Cfg, Profile, Common } from '../../components/index.js'
|
||||
import { getTargetUid, profileHelp, autoGetProfile } from './ProfileCommon.js'
|
||||
import { Artifact, Character, ProfileArtis } from '../../models/index.js'
|
||||
import { Cfg, Common } from '../../components/index.js'
|
||||
import { getTargetUid, profileHelp, getProfileRefresh } from './ProfileCommon.js'
|
||||
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()
|
||||
@ -43,7 +32,7 @@ export async function profileArtis (e) {
|
||||
return await Common.render('character/artis-mark', {
|
||||
uid,
|
||||
elem: char.elem,
|
||||
splash: char.getImgs(profile.costume).splash0,
|
||||
splash: profile.costumeSplash,
|
||||
data: profile,
|
||||
costume: profile.costume ? '2' : '',
|
||||
artisDetail,
|
||||
@ -64,14 +53,12 @@ export async function profileArtisList (e) {
|
||||
}
|
||||
|
||||
let artis = []
|
||||
let profiles = Profile.getAll(uid) || {}
|
||||
|
||||
if (!profiles || profiles.length === 0) {
|
||||
e.reply('暂无角色圣遗物详情')
|
||||
return true
|
||||
}
|
||||
|
||||
lodash.forEach(profiles || [], (profile) => {
|
||||
let player = Player.create(uid)
|
||||
player.forEachAvatar((avatar) => {
|
||||
let profile = avatar.getProfile()
|
||||
if (!profile) {
|
||||
return true
|
||||
}
|
||||
let name = profile.name
|
||||
let char = Character.get(name)
|
||||
if (!profile.hasData || !profile.hasArtis()) {
|
||||
|
@ -2,8 +2,8 @@
|
||||
* 面板数据替换相关逻辑
|
||||
*/
|
||||
import lodash from 'lodash'
|
||||
import { Profile, Data } from '../../components/index.js'
|
||||
import { Character, ProfileData, Weapon } from '../../models/index.js'
|
||||
import { Data } from '../../components/index.js'
|
||||
import { Character, ProfileData, Weapon, Player } from '../../models/index.js'
|
||||
|
||||
const keyMap = {
|
||||
artis: '圣遗物',
|
||||
@ -153,8 +153,9 @@ const ProfileChange = {
|
||||
if (!charid) {
|
||||
return false
|
||||
}
|
||||
let player = Player.create(uid)
|
||||
|
||||
let source = Profile.get(uid, charid)
|
||||
let source = player.getProfile(charid)
|
||||
let dc = ds.char || {}
|
||||
if (!source || !source.hasData) {
|
||||
source = {}
|
||||
@ -168,7 +169,7 @@ const ProfileChange = {
|
||||
|
||||
let profiles = {}
|
||||
if (source && source.id) {
|
||||
profiles[`${source.uid}:${source.id}`] = source
|
||||
profiles[`${player.uid}:${source.id}`] = source
|
||||
}
|
||||
// 获取source
|
||||
let getSource = function (cfg) {
|
||||
@ -179,7 +180,8 @@ const ProfileChange = {
|
||||
let id = cfg.char || source.id
|
||||
let key = cuid + ':' + id
|
||||
if (!profiles[key]) {
|
||||
profiles[key] = Profile.get(cuid, id) || {}
|
||||
let cPlayer = Player.create(cuid)
|
||||
profiles[key] = cPlayer.getProfile(id) || {}
|
||||
}
|
||||
return profiles[key]?.id ? profiles[key] : source
|
||||
}
|
||||
@ -193,7 +195,7 @@ const ProfileChange = {
|
||||
elem: char.elem,
|
||||
dataSource: 'change',
|
||||
promote
|
||||
}, uid, false)
|
||||
}, false)
|
||||
|
||||
// 设置武器
|
||||
let wCfg = ds.weapon || {}
|
||||
|
@ -1,16 +1,14 @@
|
||||
/*
|
||||
* 面板公共方法及处理
|
||||
* */
|
||||
import lodash from 'lodash'
|
||||
import { segment } from 'oicq'
|
||||
import { profileList } from './ProfileList.js'
|
||||
import { Profile, Version } from '../../components/index.js'
|
||||
import { Character, MysApi } from '../../models/index.js'
|
||||
import { Version } from '../../components/index.js'
|
||||
import { Character, MysApi, Player } from '../../models/index.js'
|
||||
|
||||
/*
|
||||
* 获取面板查询的 目标uid
|
||||
* */
|
||||
export async function getTargetUid (e) {
|
||||
const _getTargetUid = async function (e) {
|
||||
let uidReg = /[1-9][0-9]{8}/
|
||||
|
||||
if (e.uid && uidReg.test(e.uid)) {
|
||||
@ -57,7 +55,7 @@ export async function getTargetUid (e) {
|
||||
return false
|
||||
}
|
||||
uid = user.uid
|
||||
if (!uid || !uidReg.test(uid)) {
|
||||
if ((!uid || !uidReg.test(uid)) && !e._replyNeedUid) {
|
||||
e.reply('请先发送【#绑定+你的UID】来绑定查询目标')
|
||||
return false
|
||||
}
|
||||
@ -67,152 +65,34 @@ export async function getTargetUid (e) {
|
||||
return uid || false
|
||||
}
|
||||
|
||||
/*
|
||||
* 自动更新面板数据
|
||||
* */
|
||||
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 data = await Profile.request(uid, e)
|
||||
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 getTargetUid (e) {
|
||||
let uid = await _getTargetUid(e)
|
||||
if (uid) {
|
||||
e.uid = uid
|
||||
}
|
||||
return uid
|
||||
}
|
||||
|
||||
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 profile = Profile.get(uid, char.id)
|
||||
let player = Player.create(e)
|
||||
let profile = player.getProfile(char.id)
|
||||
if (!profile || !profile.hasData) {
|
||||
if (await refresh()) {
|
||||
return { err: true }
|
||||
} else {
|
||||
logger.mark(`本地无UID:${player.uid}的${char.name}面板数据,尝试自动请求...`)
|
||||
await player.refresh({ profile: true })
|
||||
profile = player.getProfile(char.id)
|
||||
}
|
||||
if (!profile || !profile.hasData) {
|
||||
if (!e._isReplyed) {
|
||||
e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`)
|
||||
}
|
||||
return { err: true }
|
||||
} else if (!['enka', 'miao'].includes(profile.dataSource)) {
|
||||
if (!await refresh()) {
|
||||
e.reply('缓存数据错误,请重新获取面板信息后查看')
|
||||
}
|
||||
return { err: true }
|
||||
return false
|
||||
}
|
||||
|
||||
return { profile, char, refresh }
|
||||
}
|
||||
|
||||
/*
|
||||
* 面板数据更新
|
||||
* */
|
||||
export async function getProfile (e) {
|
||||
let uid = await getTargetUid(e)
|
||||
if (!uid) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 数据更新
|
||||
let data = await Profile.request(uid, e)
|
||||
if (!data) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!data.chars) {
|
||||
e.reply('获取角色面板数据失败,请确认角色已在游戏内橱窗展示,并开放了查看详情。设置完毕后请5分钟后再进行请求~')
|
||||
} else {
|
||||
let ret = {}
|
||||
lodash.forEach(data.chars, (ds) => {
|
||||
let char = Character.get(ds.id)
|
||||
if (char) {
|
||||
ret[char.name] = true
|
||||
}
|
||||
})
|
||||
if (ret.length === 0) {
|
||||
e.reply('获取角色面板数据失败,未能请求到角色数据。请确认角色已在游戏内橱窗展示,并开放了查看详情。设置完毕后请5分钟后再进行请求~')
|
||||
} else {
|
||||
e.newChar = ret
|
||||
return await profileList(e)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取面板列表
|
||||
* */
|
||||
export async function getProfileAll (e) {
|
||||
let uid = await getTargetUid(e)
|
||||
if (!uid) {
|
||||
return true
|
||||
}
|
||||
|
||||
let profiles = Profile.getAll(uid) || {}
|
||||
|
||||
let chars = []
|
||||
lodash.forEach(profiles || [], (ds) => {
|
||||
if (!['enka', 'miao'].includes(ds.dataSource)) {
|
||||
return true
|
||||
}
|
||||
ds.name && chars.push(ds.name)
|
||||
})
|
||||
|
||||
if (chars.length === 0) {
|
||||
if (await autoRefresh(e)) {
|
||||
await getProfileAll(e)
|
||||
return true
|
||||
} else {
|
||||
e.reply('尚未获取任何角色数据')
|
||||
await profileHelp(e)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
e.reply(`uid${uid} 已获取面板角色: ` + chars.join(', '))
|
||||
|
||||
return true
|
||||
return profile
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1,7 +1,106 @@
|
||||
import lodash from 'lodash'
|
||||
import { autoRefresh } from './ProfileCommon.js'
|
||||
import { Common, Format, Profile } from '../../components/index.js'
|
||||
import { MysApi, Avatar, ProfileRank, ProfileArtis } from '../../models/index.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'
|
||||
import { profileArtis } from './ProfileArtis.js'
|
||||
|
||||
// 查看当前角色
|
||||
export async function profileDetail (e) {
|
||||
let msg = e.original_msg || e.msg
|
||||
if (!msg) {
|
||||
return false
|
||||
}
|
||||
if (!/详细|详情|面板|面版|圣遗物|伤害|换/.test(msg)) {
|
||||
return false
|
||||
}
|
||||
let mode = 'profile'
|
||||
let profileChange = false
|
||||
let changeMsg = msg
|
||||
let pc = ProfileChange.matchMsg(msg)
|
||||
if (pc && pc.char && pc.change) {
|
||||
if (!Cfg.get('profileChange')) {
|
||||
e.reply('面板替换功能已禁用...')
|
||||
return true
|
||||
}
|
||||
e.uid = pc.uid || e.runtime.uid
|
||||
profileChange = ProfileChange.getProfile(e.uid, pc.char, pc.change)
|
||||
if (profileChange && profileChange.char) {
|
||||
msg = `#${profileChange.char?.name}${pc.mode || '面板'}`
|
||||
e._profile = profileChange
|
||||
e._profileMsg = changeMsg
|
||||
}
|
||||
}
|
||||
let uidRet = /[0-9]{9}/.exec(msg)
|
||||
if (uidRet) {
|
||||
e.uid = uidRet[0]
|
||||
msg = msg.replace(uidRet[0], '')
|
||||
}
|
||||
|
||||
let name = msg.replace(/#|老婆|老公/g, '').trim()
|
||||
msg = msg.replace('面版', '面板')
|
||||
let dmgRet = /伤害(\d?)$/.exec(name)
|
||||
let dmgIdx = 0
|
||||
if (/(最强|最高|最高分|最牛|第一)/.test(msg)) {
|
||||
mode = /(分|圣遗物|评分|ACE)/.test(msg) ? 'rank-mark' : 'rank-dmg'
|
||||
name = name.replace(/(最强|最高分|第一|最高|最牛|圣遗物|评分|群)/g, '')
|
||||
}
|
||||
if (/(详情|详细|面板|面版)\s*$/.test(msg) && !/更新|录入|输入/.test(msg)) {
|
||||
mode = 'profile'
|
||||
name = name.replace(/(详情|详细|面板)/, '').trim()
|
||||
} else if (dmgRet) {
|
||||
mode = 'dmg'
|
||||
name = name.replace(/伤害[0-5]?/, '').trim()
|
||||
if (dmgRet[1]) {
|
||||
dmgIdx = dmgRet[1] * 1
|
||||
}
|
||||
} else if (/(详情|详细|面板)更新$/.test(msg) || (/更新/.test(msg) && /(详情|详细|面板)$/.test(msg))) {
|
||||
mode = 'refresh'
|
||||
name = name.replace(/详情|详细|面板|更新/g, '').trim()
|
||||
} else if (/圣遗物/.test(msg)) {
|
||||
mode = 'artis'
|
||||
name = name.replace('圣遗物', '').trim()
|
||||
}
|
||||
if (!Common.cfg('avatarProfile')) {
|
||||
return false // 面板开关关闭
|
||||
}
|
||||
let char = Character.get(name.trim())
|
||||
if (!char) {
|
||||
return false
|
||||
}
|
||||
|
||||
let uid = e.uid || await getTargetUid(e)
|
||||
if (!uid) {
|
||||
return true
|
||||
}
|
||||
e.uid = uid
|
||||
e.avatar = char.id
|
||||
|
||||
if (char.isCustom) {
|
||||
e.reply('自定义角色暂不支持此功能')
|
||||
return true
|
||||
}
|
||||
if (!char.isRelease) {
|
||||
if (!profileChange) {
|
||||
e.reply('角色尚未实装')
|
||||
return true
|
||||
} else if (Cfg.get('notReleasedData') === false) {
|
||||
e.reply('未实装角色面板已禁用...')
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'profile' || mode === 'dmg') {
|
||||
return renderProfile(e, char, mode, { dmgIdx })
|
||||
} else if (mode === 'refresh') {
|
||||
await ProfileList.refresh(e)
|
||||
return true
|
||||
} else if (mode === 'artis') {
|
||||
return profileArtis(e)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export async function renderProfile (e, char, mode = 'profile', params = {}) {
|
||||
let selfUser = await MysApi.initUser(e)
|
||||
@ -18,25 +117,10 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
|
||||
return true
|
||||
}
|
||||
|
||||
let profile = e._profile || Profile.get(uid, 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
|
||||
}
|
||||
let avatar = new Avatar(profile)
|
||||
char = profile.char || char
|
||||
let a = profile.attr
|
||||
let c = Format.comma
|
||||
@ -96,11 +180,12 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
|
||||
let artisDetail = profile.getArtisMark()
|
||||
let artisKeyTitle = ProfileArtis.getArtisKeyTitle()
|
||||
let imgs = char.getImgs(profile.costume)
|
||||
let costumeSplash = profile.costumeSplash
|
||||
// 渲染图像
|
||||
let msgRes = await Common.render('character/profile-detail', {
|
||||
save_id: uid,
|
||||
uid,
|
||||
data: avatar.getData('name,abbr,cons,level,weapon,talent,dataSource,updateTime'),
|
||||
data: profile.getData('name,abbr,cons,level,weapon,talent,dataSource,updateTime'),
|
||||
attr,
|
||||
elem: char.elem,
|
||||
dmgData,
|
||||
@ -111,15 +196,16 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
|
||||
artisKeyTitle,
|
||||
enemyLv,
|
||||
imgs,
|
||||
costumeSplash,
|
||||
enemyName: dmgCalc.enemyName || '小宝',
|
||||
talentMap: { a: '普攻', e: '战技', q: '爆发' },
|
||||
bodyClass: `char-${char.name}`,
|
||||
mode,
|
||||
changeProfile: e._profileMsg
|
||||
}, { e, scale: 1.6,retMsgId: true })
|
||||
}, { e, scale: 1.6, retMsgId: true })
|
||||
if (msgRes && msgRes.message_id) {
|
||||
// 如果消息发送成功,就将message_id和图片路径存起来,3小时过期
|
||||
await redis.set(`miao:original-picture:${msgRes.message_id}`, imgs.splash0, { EX: 3600 * 3 })
|
||||
await redis.set(`miao:original-picture:${msgRes.message_id}`, JSON.stringify({ type: 'profile', img: costumeSplash }), { EX: 3600 * 3 })
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1,91 +1,168 @@
|
||||
import lodash from 'lodash'
|
||||
import { autoRefresh, getTargetUid } from './ProfileCommon.js'
|
||||
import { ProfileRank } from '../../models/index.js'
|
||||
import { Common, Profile, Data } from '../../components/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 = Profile.getServName(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 profiles = Profile.getAll(uid)
|
||||
// 检测标志位
|
||||
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 Profile.forEach(uid, async function (profile) {
|
||||
if (!profile.hasData) {
|
||||
const ProfileList = {
|
||||
/**
|
||||
* 刷新面板
|
||||
* @param e
|
||||
* @returns {Promise<boolean|*>}
|
||||
*/
|
||||
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.source = profile.dataSource
|
||||
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
|
||||
},
|
||||
|
||||
/**
|
||||
* 渲染面板
|
||||
* @param e
|
||||
* @returns {Promise<boolean|*>}
|
||||
*/
|
||||
|
||||
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,
|
||||
updateTime: player.getUpdateTime(),
|
||||
allowRank: rank && rank.allowRank,
|
||||
rankCfg
|
||||
}, { e, scale: 1.6 })
|
||||
},
|
||||
/**
|
||||
* 删除面板数据
|
||||
* @param e
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async del (e) {
|
||||
let ret = /^#(删除全部面板|删除面板|删除面板数据)\s*(\d{9})?$/.exec(e.msg)
|
||||
let uid = await getTargetUid(e)
|
||||
if (!uid) {
|
||||
return true
|
||||
}
|
||||
let targetUid = ret[2]
|
||||
|
||||
let user = e?.runtime?.user || {}
|
||||
if (!user.hasCk && !e.isMaster) {
|
||||
e.reply('为确保数据安全,目前仅允许绑定CK用户删除自己UID的面板数据,请联系Bot主人删除...')
|
||||
return true
|
||||
}
|
||||
|
||||
if (!targetUid) {
|
||||
e.reply(`你确认要删除面板数据吗? 请回复 #删除面板${uid} 以删除面板数据`)
|
||||
return true
|
||||
}
|
||||
|
||||
let ckUids = (user?.ckUids || []).join(',').split(',')
|
||||
if (!ckUids.includes(targetUid) && !e.isMaster) {
|
||||
e.reply(`仅允许删除自己的UID数据[${ckUids.join(',')}]`)
|
||||
return true
|
||||
}
|
||||
|
||||
Player.delByUid(targetUid)
|
||||
e.reply(`UID${targetUid}的本地数据已删除,排名数据已清除...`)
|
||||
return true
|
||||
}
|
||||
|
||||
if (newCount > 0) {
|
||||
hasNew = newCount <= 8
|
||||
}
|
||||
|
||||
chars = lodash.sortBy(chars, ['isNew', 'star', 'level', 'id'])
|
||||
chars = chars.reverse()
|
||||
|
||||
// 渲染图像
|
||||
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
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Character, ProfileRank, ProfileDmg, Avatar } from '../../models/index.js'
|
||||
import { Character, ProfileRank, ProfileDmg, Player } from '../../models/index.js'
|
||||
import { renderProfile } from './ProfileDetail.js'
|
||||
import { Data, Profile, Common, Format } from '../../components/index.js'
|
||||
import { Data, Common, Format } from '../../components/index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
export async function groupRank (e) {
|
||||
@ -30,11 +30,11 @@ export async function groupRank (e) {
|
||||
}
|
||||
let groupCfg = await ProfileRank.getGroupCfg(groupId)
|
||||
if (!groupRank) {
|
||||
e.reply('群面板排名功能已禁用,主人可通过【#喵喵设置】启用...')
|
||||
e.reply('群面板排名功能已禁用,Bot主人可通过【#喵喵设置】启用...')
|
||||
return true
|
||||
}
|
||||
if (groupCfg.status === 1) {
|
||||
e.reply('本群已关闭群排名,主人可通过【#启用排名】启用...')
|
||||
e.reply('本群已关闭群排名,群管理员或Bot主人可通过【#启用排名】启用...')
|
||||
return true
|
||||
}
|
||||
if (type === 'detail') {
|
||||
@ -105,8 +105,8 @@ export async function refreshRank (e) {
|
||||
if (!groupId) {
|
||||
return true
|
||||
}
|
||||
if (!e.isMaster) {
|
||||
e.reply('只有管理员可刷新排名...')
|
||||
if (!e.isMaster && !this.e.member?.is_admin) {
|
||||
e.reply('只有主人及群管理员可刷新排名...')
|
||||
return true
|
||||
}
|
||||
e.reply('面板数据刷新中,等待时间可能较长,请耐心等待...')
|
||||
@ -115,7 +115,8 @@ export async function refreshRank (e) {
|
||||
let count = 0
|
||||
for (let qq in groupUids) {
|
||||
for (let { uid, type } of groupUids[qq]) {
|
||||
let profiles = Profile.getAll(uid)
|
||||
let player = new Player(uid)
|
||||
let profiles = player.getProfiles()
|
||||
// 刷新rankLimit
|
||||
await ProfileRank.setUidInfo({ uid, profiles, qq, uidType: type })
|
||||
let rank = await ProfileRank.create({ groupId, uid, qq })
|
||||
@ -140,8 +141,8 @@ export async function manageRank (e) {
|
||||
return true
|
||||
}
|
||||
let isClose = /(关闭|禁用)/.test(e.msg)
|
||||
if (!e.isMaster) {
|
||||
e.reply(`只有管理员可${isClose ? '禁用' : '启用'}排名...`)
|
||||
if (!e.isMaster && !this.e.member?.is_admin) {
|
||||
e.reply(`只有主人及群管理员可${isClose ? '禁用' : '启用'}排名...`)
|
||||
return true
|
||||
}
|
||||
await ProfileRank.setGroupStatus(groupId, isClose ? 1 : 0)
|
||||
@ -156,13 +157,17 @@ async function renderCharRankList ({ e, uids, char, mode, groupId }) {
|
||||
let list = []
|
||||
for (let ds of uids) {
|
||||
let uid = ds.uid || ds.value
|
||||
let profile = Profile.get(uid, ds.charId || char.id)
|
||||
let player = Player.create(uid)
|
||||
let avatar = player.getAvatar(ds.charId || char.id)
|
||||
if (!avatar) {
|
||||
continue
|
||||
}
|
||||
let profile = avatar.getProfile()
|
||||
|
||||
if (profile) {
|
||||
let profileRank = await ProfileRank.create({ groupId, uid })
|
||||
let data = await profileRank.getRank(profile, true)
|
||||
let mark = data?.mark?.data
|
||||
let avatar = new Avatar(profile, uid)
|
||||
let tmp = {
|
||||
uid,
|
||||
isMax: !char,
|
||||
|
@ -1,62 +1,77 @@
|
||||
import lodash from 'lodash'
|
||||
import {Cfg, Common, Data } from '../../components/index.js'
|
||||
import { AvatarList, ProfileRank } from '../../models/index.js'
|
||||
import { Common } from '../../components/index.js'
|
||||
import { MysApi, Player, Character } from '../../models/index.js'
|
||||
|
||||
export async function profileStat (e) {
|
||||
let isMatch = /^#(面板|喵喵|角色|武器|天赋|技能|圣遗物)练度统计?$/.test(e.original_msg || e.msg || '')
|
||||
if (!Cfg.get('profileStat', false) && !isMatch) {
|
||||
return false
|
||||
}
|
||||
// 缓存时间,单位小时
|
||||
const ProfileStat = {
|
||||
async stat (e) {
|
||||
return ProfileStat.render(e, false)
|
||||
},
|
||||
|
||||
let msg = e.msg.replace('#', '').trim()
|
||||
if (msg === '角色统计' || msg === '武器统计') {
|
||||
// 暂时避让一下抽卡分析的关键词
|
||||
return false
|
||||
}
|
||||
|
||||
let avatars = await AvatarList.getAll(e)
|
||||
if (!avatars) {
|
||||
return true
|
||||
}
|
||||
let uid = avatars.uid
|
||||
let rank = false
|
||||
if (e.group_id) {
|
||||
rank = await ProfileRank.create({ group: e.group_id, uid, qq: e.user_id })
|
||||
}
|
||||
let talentData = await avatars.getTalentData()
|
||||
// 天赋等级背景
|
||||
let avatarRet = []
|
||||
lodash.forEach(talentData, (avatar) => {
|
||||
let { talent, id } = avatar
|
||||
avatar.aeq = talent?.a?.original + talent?.e?.original + talent?.q?.original || 3
|
||||
avatarRet.push(avatar)
|
||||
let profile = avatars.getProfile(id)
|
||||
if (profile) {
|
||||
if (profile.hasData) {
|
||||
let mark = profile.getArtisMark(false)
|
||||
avatar.artisMark = Data.getData(mark, 'mark,markClass,names')
|
||||
if (rank) {
|
||||
rank.getRank(profile)
|
||||
}
|
||||
}
|
||||
async avatarList (e) {
|
||||
return ProfileStat.render(e, true)
|
||||
},
|
||||
async render (e, isAvatarList = false) {
|
||||
// 缓存时间,单位小时
|
||||
let msg = e.msg.replace('#', '').trim()
|
||||
if (msg === '角色统计' || msg === '武器统计') {
|
||||
// 暂时避让一下抽卡分析的关键词
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
let sortKey = 'level,star,aeq,cons,weapon.level,weapon.star,weapon.affix,fetter'.split(',')
|
||||
avatarRet = lodash.orderBy(avatarRet, sortKey)
|
||||
avatarRet = avatarRet.reverse()
|
||||
let talentNotice = ''
|
||||
if (!avatars.isSelfCookie || avatarRet.length <= 8) {
|
||||
talentNotice = '未绑定CK,信息可能展示不完全。回复<span>#体力帮助</span>获取CK配置帮助'
|
||||
let mys = await MysApi.init(e)
|
||||
if (!mys || !mys.uid) return false
|
||||
|
||||
const uid = mys.uid
|
||||
|
||||
let player = Player.create(e)
|
||||
|
||||
let avatarRet = await player.refreshAndGetAvatarData({
|
||||
detail: 1,
|
||||
talent: isAvatarList ? 0 : 1,
|
||||
rank: true,
|
||||
retType: 'array',
|
||||
sort: true
|
||||
})
|
||||
|
||||
if (avatarRet.length === 0) {
|
||||
e.reply(`暂未获得#${uid}角色数据,请绑定CK或 #更新面板`)
|
||||
return true
|
||||
}
|
||||
|
||||
let talentNotice = []
|
||||
|
||||
if (!mys.isSelfCookie) {
|
||||
talentNotice.push('未绑定CK,信息可能展示不完全')
|
||||
}
|
||||
|
||||
let faceChar = Character.get(player.face || avatarRet[0]?.id)
|
||||
let face = {
|
||||
banner: faceChar.imgs?.banner,
|
||||
face: faceChar.imgs?.face,
|
||||
name: player.name || `#${uid}`,
|
||||
sign: player.sign,
|
||||
level: player.level
|
||||
}
|
||||
|
||||
let info = player.getInfo()
|
||||
info.stats = info.stats || {}
|
||||
info.statMap = {
|
||||
achievement: '成就',
|
||||
wayPoint: '锚点',
|
||||
avatar: '角色',
|
||||
avatar5: '五星角色',
|
||||
goldCount: '金卡总数'
|
||||
}
|
||||
|
||||
return await Common.render(isAvatarList ? 'character/avatar-list' : 'character/profile-stat', {
|
||||
save_id: uid,
|
||||
uid,
|
||||
info,
|
||||
updateTime: player.getUpdateTime(),
|
||||
isSelfCookie: e.isSelfCookie,
|
||||
face,
|
||||
avatars: avatarRet,
|
||||
talentNotice
|
||||
}, { e, scale: 1.4 })
|
||||
}
|
||||
|
||||
return await Common.render('character/profile-stat', {
|
||||
save_id: uid,
|
||||
uid,
|
||||
talentLvMap: '0,1,1,1,2,2,3,3,3,4,5'.split(','),
|
||||
avatars: avatarRet,
|
||||
isSelf: e.isSelf,
|
||||
talentNotice
|
||||
}, { e, scale: 1.8 })
|
||||
}
|
||||
export default ProfileStat
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { segment } from 'oicq'
|
||||
import { MysApi } from '../../models/index.js'
|
||||
import { Cfg } from '../../components/index.js'
|
||||
|
||||
/** 获取角色卡片的原图 */
|
||||
export async function getOriginalPicture (e) {
|
||||
@ -14,6 +15,7 @@ export async function getOriginalPicture (e) {
|
||||
if (!/^\[图片]$/.test(e.source.message)) {
|
||||
return true
|
||||
}
|
||||
let originalPic = Cfg.get('originalPic') * 1
|
||||
// 获取原消息
|
||||
let source
|
||||
if (e.isGroup) {
|
||||
@ -24,7 +26,25 @@ export async function getOriginalPicture (e) {
|
||||
if (source) {
|
||||
let imgPath = await redis.get(`miao:original-picture:${source.message_id}`)
|
||||
if (imgPath) {
|
||||
e.reply([segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + decodeURIComponent(imgPath))], false, { recallMsg: 30 })
|
||||
try {
|
||||
if (imgPath[0] === '{') {
|
||||
imgPath = JSON.parse(imgPath)
|
||||
} else {
|
||||
imgPath = { img: imgPath, type: '' }
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
if (imgPath.type === 'character' && [2, 0].includes(originalPic)) {
|
||||
e.reply('已禁止获取角色原图...')
|
||||
return true
|
||||
}
|
||||
if (imgPath.type === 'profile' && [1, 0].includes(originalPic)) {
|
||||
e.reply('已禁止获取面板原图...')
|
||||
return true
|
||||
}
|
||||
if (imgPath && imgPath.img) {
|
||||
e.reply([segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + decodeURIComponent(imgPath.img))], false, { recallMsg: 30 })
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (source.time) {
|
||||
|
517
apps/stat.js
517
apps/stat.js
@ -2,503 +2,36 @@
|
||||
* 胡桃数据库的统计
|
||||
*
|
||||
* */
|
||||
import lodash from 'lodash'
|
||||
import { Cfg, Common, App, Data } from '../components/index.js'
|
||||
import { Abyss, AvatarList, Character, MysApi } 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 abyssData = await HutaoApi.getAbyssTeam()
|
||||
if (!abyssData || !abyssData.data) {
|
||||
e.reply('深渊组队数据获取失败,请稍后重试~')
|
||||
return true
|
||||
}
|
||||
abyssData = abyssData.data
|
||||
let avatars
|
||||
try {
|
||||
avatars = await AvatarList.getAll(e, mys)
|
||||
// resDetail = await mys.getCharacter()
|
||||
if (!avatars) {
|
||||
e.reply('角色信息获取失败')
|
||||
return true
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log(err);
|
||||
}
|
||||
let avatarData = await avatars.getTalentData()
|
||||
let avatarRet = {}
|
||||
let data = {}
|
||||
let noAvatar = {}
|
||||
lodash.forEach(avatarData, (avatar) => {
|
||||
let t = avatar.talent
|
||||
avatarRet[avatar.id] = Math.min(avatar.level, avatar.weapon?.level || 1) * 100 + Math.max(t.a?.original, t.e?.original, t.q?.original) * 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 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
|
||||
}
|
||||
}
|
||||
if (resAbyss.floors.length > 0 && !await AvatarList.hasTalentCache(uid)) {
|
||||
e.reply('正在获取用户信息,请稍候...')
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 avatars = new AvatarList(uid, resDetail.avatars)
|
||||
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.name)
|
||||
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 avatarData = await avatars.getTalentData(avatarIds, mys)
|
||||
return await Common.render('stat/abyss-summary', {
|
||||
abyss: abyssData,
|
||||
avatars: avatarData,
|
||||
stat,
|
||||
save_id: uid,
|
||||
totalCount: overview?.collectedPlayerCount || 0,
|
||||
uid
|
||||
}, { e, scale: 1.8 })
|
||||
} 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, Cfg } from '../../components/index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const ignoreIds = [495, // 有奖问卷调查开启!
|
||||
@ -227,7 +227,7 @@ let Cal = {
|
||||
data.weekly = char.getMaterials('weekly')?.icon
|
||||
charTalent[t].chars.push(data)
|
||||
}
|
||||
}, 'official')
|
||||
}, Cfg.get('notReleasedData') ? 'official' : 'release')
|
||||
let charNum = 0
|
||||
lodash.forEach(charBirth, (charList) => {
|
||||
charNum = Math.max(charNum, charList.length)
|
||||
@ -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
|
@ -1,5 +1,5 @@
|
||||
import lodash from 'lodash'
|
||||
import plugin from './common-lib/plugin.js'
|
||||
import Plugin from './common/Plugin.js'
|
||||
|
||||
class App {
|
||||
constructor (cfg) {
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +27,7 @@ class App {
|
||||
let rules = []
|
||||
let check = []
|
||||
let event = cfg.event
|
||||
let cls = class extends plugin {
|
||||
let cls = class extends Plugin {
|
||||
constructor () {
|
||||
super({
|
||||
name: `喵喵:${cfg.name || cfg.id}`,
|
||||
@ -63,6 +69,8 @@ class App {
|
||||
fnc: key
|
||||
})
|
||||
|
||||
console.log('rule', rule)
|
||||
|
||||
if (app.check) {
|
||||
check.push(app.check)
|
||||
}
|
||||
@ -86,6 +94,24 @@ class App {
|
||||
e.original_msg = e.original_msg || e.msg
|
||||
return await app.fn.call(this, e)
|
||||
}
|
||||
|
||||
if (app.yzRule && app.yzCheck) {
|
||||
let yzKey = `Yz${key}`
|
||||
let yzRule = lodash.trim(app.yzRule.toString(), '/')
|
||||
|
||||
rules.push({
|
||||
reg: yzRule,
|
||||
fnc: yzKey
|
||||
})
|
||||
cls.prototype[yzKey] = async function () {
|
||||
if (!app.yzCheck()) {
|
||||
return false
|
||||
}
|
||||
let e = this.e
|
||||
e.original_msg = e.original_msg || e.msg
|
||||
return await app.fn.call(this, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cls
|
||||
}
|
||||
@ -133,6 +159,14 @@ class App {
|
||||
if (ret === true) {
|
||||
return true
|
||||
}
|
||||
} else if (app.yzRule && app.yzCheck()) {
|
||||
rule = new RegExp(app.yzRule)
|
||||
if (rule.test(msg)) {
|
||||
let ret = await app.fn(e, {})
|
||||
if (ret === true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event === 'poke' && msg === '#poke#') {
|
||||
let ret = await app.fn(e, {})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import fs from 'fs'
|
||||
import lodash from 'lodash'
|
||||
import cfgData from './cfg-lib/cfg-data.js'
|
||||
import cfgData from './cfg/CfgData.js'
|
||||
|
||||
const _path = process.cwd()
|
||||
const _cfgPath = `${_path}/plugins/miao-plugin/components/`
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Cfg from './Cfg.js'
|
||||
import render from './common-lib/render.js'
|
||||
import render from './common/Render.js'
|
||||
import { Version } from './index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
|
@ -58,9 +58,12 @@ let Data = {
|
||||
delete data._res
|
||||
return fs.writeFileSync(`${root}/${file}`, JSON.stringify(data, null, space))
|
||||
},
|
||||
delfile (file) {
|
||||
delFile (file, root = '') {
|
||||
root = getRoot(root)
|
||||
try {
|
||||
fs.unlinkSync(`${_path}/${file}`)
|
||||
if (fs.existsSync(`${root}/${file}`)) {
|
||||
fs.unlinkSync(`${root}/${file}`)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error(`文件删除失败:${error}`)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import lodash from 'lodash'
|
||||
import Elem from './common-lib/elem.js'
|
||||
import Elem from './common/Elem.js'
|
||||
import { Cfg } from '../components/index.js'
|
||||
|
||||
let Format = {
|
||||
|
@ -1,129 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import lodash from 'lodash'
|
||||
import { Character, ProfileReq, ProfileData } from '../models/index.js'
|
||||
import Miao from './profile-data/miao.js'
|
||||
import Enka from './profile-data/enka.js'
|
||||
|
||||
const _path = process.cwd()
|
||||
const userPath = `${_path}/data/UserData/`
|
||||
if (!fs.existsSync(userPath)) {
|
||||
fs.mkdirSync(userPath)
|
||||
}
|
||||
|
||||
ProfileReq.regServ({ Miao, Enka })
|
||||
|
||||
let Profile = {
|
||||
async request (uid, e) {
|
||||
if (uid.toString().length !== 9) {
|
||||
return false
|
||||
}
|
||||
let req = new ProfileReq({ e, uid })
|
||||
let data
|
||||
try {
|
||||
data = await req.request()
|
||||
if (!data) {
|
||||
return false
|
||||
}
|
||||
return Profile.save(uid, data)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
e.reply('请求失败')
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
save (uid, data) {
|
||||
let userData = {}
|
||||
const userFile = `${userPath}/${uid}.json`
|
||||
if (fs.existsSync(userFile)) {
|
||||
try {
|
||||
userData = JSON.parse(fs.readFileSync(userFile, 'utf8')) || {}
|
||||
} catch (e) {
|
||||
userData = {}
|
||||
}
|
||||
}
|
||||
lodash.assignIn(userData, lodash.pick(data, 'uid,name,lv,avatar'.split(',')))
|
||||
userData.chars = userData.chars || {}
|
||||
lodash.forEach(data.chars, (char, charId) => {
|
||||
userData.chars[charId] = char
|
||||
})
|
||||
fs.writeFileSync(userFile, JSON.stringify(userData), '', ' ')
|
||||
return data
|
||||
},
|
||||
|
||||
_get (uid, charId) {
|
||||
const userFile = `${userPath}/${uid}.json`
|
||||
let userData = {}
|
||||
if (fs.existsSync(userFile)) {
|
||||
try {
|
||||
userData = JSON.parse(fs.readFileSync(userFile, 'utf8')) || {}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (userData && userData.chars) {
|
||||
let char = Character.get(charId)
|
||||
if (char.isTraveler) {
|
||||
let charData = userData.chars['10000005'] || userData.chars['10000007'] || false
|
||||
if (charData) {
|
||||
char.checkAvatars(charData, uid)
|
||||
}
|
||||
return charData
|
||||
} else {
|
||||
return userData.chars[charId]
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
get (uid, charId, onlyHasData = false) {
|
||||
let data = Profile._get(uid, charId)
|
||||
if (data) {
|
||||
let profile = new ProfileData(data, uid)
|
||||
if (onlyHasData && !profile.hasData) {
|
||||
return false
|
||||
}
|
||||
return profile
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
getAll (uid) {
|
||||
const userFile = `${userPath}/${uid}.json`
|
||||
let userData = {}
|
||||
if (fs.existsSync(userFile)) {
|
||||
try {
|
||||
userData = JSON.parse(fs.readFileSync(userFile, 'utf8')) || {}
|
||||
} catch (e) {
|
||||
userData = {}
|
||||
}
|
||||
}
|
||||
if (userData && userData.chars) {
|
||||
let ret = {}
|
||||
lodash.forEach(userData.chars, (ds, id) => {
|
||||
let profile = new ProfileData(ds, uid)
|
||||
if (profile.hasData) {
|
||||
ret[id] = profile
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
async forEach (uid, fn) {
|
||||
let profiles = Profile.getAll(uid)
|
||||
for (let id in profiles) {
|
||||
let ret = await fn(profiles[id], id)
|
||||
if (ret === false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getServName (uid) {
|
||||
let Serv = ProfileReq.getServ(uid)
|
||||
return Serv.name
|
||||
}
|
||||
}
|
||||
export default Profile
|
@ -3,7 +3,6 @@ import Format from './Format.js'
|
||||
import Common from './Common.js'
|
||||
import Cfg from './Cfg.js'
|
||||
import Version from './Version.js'
|
||||
import Profile from './Profile.js'
|
||||
import App from './App.js'
|
||||
|
||||
export { Data, Cfg, Format, Common, Version, Profile, App }
|
||||
export { Data, Cfg, Format, Common, Version, App }
|
||||
|
@ -1,227 +0,0 @@
|
||||
import lodash from 'lodash'
|
||||
import enkaMeta from './enka-meta.js'
|
||||
import { Character, ArtifactSet, ProfileData } from '../../models/index.js'
|
||||
|
||||
const artiIdx = {
|
||||
EQUIP_BRACER: 1,
|
||||
EQUIP_NECKLACE: 2,
|
||||
EQUIP_SHOES: 3,
|
||||
EQUIP_RING: 4,
|
||||
EQUIP_DRESS: 5
|
||||
}
|
||||
|
||||
const attrMap = {
|
||||
HP: '小生命',
|
||||
HP_PERCENT: '大生命',
|
||||
ATTACK: '小攻击',
|
||||
ATTACK_PERCENT: '大攻击',
|
||||
DEFENSE: '小防御',
|
||||
DEFENSE_PERCENT: '大防御',
|
||||
FIRE_ADD_HURT: '火元素伤害加成',
|
||||
ICE_ADD_HURT: '冰元素伤害加成',
|
||||
ROCK_ADD_HURT: '岩元素伤害加成',
|
||||
ELEC_ADD_HURT: '雷元素伤害加成',
|
||||
WIND_ADD_HURT: '风元素伤害加成',
|
||||
WATER_ADD_HURT: '水元素伤害加成',
|
||||
PHYSICAL_ADD_HURT: '物理伤害加成',
|
||||
GRASS_ADD_HURT: '草元素伤害加成',
|
||||
HEAL_ADD: '治疗加成',
|
||||
ELEMENT_MASTERY: '元素精通',
|
||||
CRITICAL: '暴击率',
|
||||
CRITICAL_HURT: '暴击伤害',
|
||||
CHARGE_EFFICIENCY: '充能效率'
|
||||
}
|
||||
|
||||
let EnkaData = {
|
||||
getProfile (data) {
|
||||
let char = Character.get(data.avatarId)
|
||||
let profile = new ProfileData({ id: char.id })
|
||||
profile.setBasic({
|
||||
level: data.propMap['4001'].val * 1,
|
||||
promote: data.propMap['1002'].val * 1,
|
||||
cons: data.talentIdList ? data.talentIdList.length : 0,
|
||||
fetter: data.fetterInfo.expLevel,
|
||||
costume: char.checkCostume(data.costumeId) ? data.costumeId : 0,
|
||||
dataSource: 'enka'
|
||||
})
|
||||
profile.setAttr(EnkaData.getAttr(data.fightPropMap))
|
||||
profile.setWeapon(EnkaData.getWeapon(data.equipList))
|
||||
profile.setArtis(EnkaData.getArtifact(data.equipList))
|
||||
let talentRet = EnkaData.getTalent(char.id, data.skillLevelMap)
|
||||
profile.setTalent(talentRet.talent, 'original')
|
||||
// 为旅行者增加elem
|
||||
if (talentRet.elem) {
|
||||
profile.elem = talentRet.elem
|
||||
}
|
||||
return EnkaData.dataFix(profile)
|
||||
},
|
||||
getAttr (data) {
|
||||
let ret = {}
|
||||
let attrKey = {
|
||||
// atk: 2001,
|
||||
atkBase: 4,
|
||||
def: 2002,
|
||||
defBase: 7,
|
||||
hp: 2000,
|
||||
hpBase: 1,
|
||||
mastery: 28,
|
||||
cpct: {
|
||||
src: 20,
|
||||
pct: true
|
||||
},
|
||||
cdmg: {
|
||||
src: 22,
|
||||
pct: true
|
||||
},
|
||||
heal: {
|
||||
src: 26,
|
||||
pct: true
|
||||
},
|
||||
recharge: {
|
||||
src: 23,
|
||||
pct: true
|
||||
}
|
||||
}
|
||||
lodash.forEach(attrKey, (cfg, key) => {
|
||||
if (!lodash.isObject(cfg)) {
|
||||
cfg = { src: cfg }
|
||||
}
|
||||
let val = data[cfg.src] || 0
|
||||
if (cfg.pct) {
|
||||
val = val * 100
|
||||
}
|
||||
ret[key] = val
|
||||
})
|
||||
ret.atk = data['4'] * (1 + (data['6'] || 0)) + (data['5'] || 0)
|
||||
let maxDmg = 0
|
||||
// 火40 水42 风44 岩45 冰46 雷46
|
||||
// 41 雷
|
||||
lodash.forEach('40,41,42,43,44,45,45,46'.split(','), (key) => {
|
||||
maxDmg = Math.max(data[key] * 1, maxDmg)
|
||||
})
|
||||
// phy 30
|
||||
ret.dmg = maxDmg * 100
|
||||
ret.phy = data['30'] * 100
|
||||
|
||||
return ret
|
||||
},
|
||||
|
||||
getArtifact (data) {
|
||||
let ret = {}
|
||||
|
||||
let get = function (d) {
|
||||
if (!d) {
|
||||
return []
|
||||
}
|
||||
let id = d.appendPropId || d.mainPropId || ''
|
||||
id = id.replace('FIGHT_PROP_', '')
|
||||
if (!attrMap[id]) {
|
||||
return []
|
||||
}
|
||||
return [attrMap[id], d.statValue]
|
||||
}
|
||||
lodash.forEach(data, (ds) => {
|
||||
let flat = ds.flat || {}
|
||||
let sub = flat.reliquarySubstats || []
|
||||
let idx = artiIdx[flat.equipType]
|
||||
if (!idx) {
|
||||
return
|
||||
}
|
||||
let setName = enkaMeta[flat.setNameTextMapHash] || ''
|
||||
ret[idx] = {
|
||||
name: ArtifactSet.getArtiNameBySet(setName, idx),
|
||||
set: setName,
|
||||
level: Math.min(20, ((ds.reliquary && ds.reliquary.level) || 1) - 1),
|
||||
main: get(flat.reliquaryMainstat),
|
||||
attrs: [
|
||||
get(sub[0]),
|
||||
get(sub[1]),
|
||||
get(sub[2]),
|
||||
get(sub[3])
|
||||
]
|
||||
}
|
||||
})
|
||||
return ret
|
||||
},
|
||||
getWeapon (data) {
|
||||
let ds = {}
|
||||
lodash.forEach(data, (temp) => {
|
||||
if (temp.flat && temp.flat.itemType === 'ITEM_WEAPON') {
|
||||
ds = temp
|
||||
return false
|
||||
}
|
||||
})
|
||||
let { weapon, flat } = ds
|
||||
return {
|
||||
name: enkaMeta[flat.nameTextMapHash],
|
||||
star: flat.rankLevel,
|
||||
level: weapon.level,
|
||||
promote: weapon.promoteLevel,
|
||||
affix: (lodash.values(weapon.affixMap)[0] || 0) + 1
|
||||
}
|
||||
},
|
||||
getTalent (charid, ds = {}) {
|
||||
let char = Character.get(charid)
|
||||
let { talentId = {}, talentElem = {}, talentKey = {} } = char.meta
|
||||
let elem = ''
|
||||
let idx = 0
|
||||
let ret = {}
|
||||
lodash.forEach(ds, (lv, id) => {
|
||||
let key
|
||||
if (talentId[id]) {
|
||||
let tid = talentId[id]
|
||||
key = talentKey[tid]
|
||||
elem = elem || talentElem[tid]
|
||||
ret[key] = lv
|
||||
} else {
|
||||
key = ['a', 'e', 'q'][idx++]
|
||||
ret[key] = ret[key] || lv
|
||||
}
|
||||
})
|
||||
return {
|
||||
elem: elem,
|
||||
talent: ret
|
||||
}
|
||||
},
|
||||
dataFix (ret) {
|
||||
if (ret._fix) {
|
||||
return ret
|
||||
}
|
||||
let { attr, id, weapon } = ret
|
||||
let count = 0
|
||||
id = id * 1
|
||||
switch (id) {
|
||||
case 10000052:
|
||||
// 雷神被动加成fix
|
||||
attr.dmg = Math.max(0, attr.dmg - (attr.recharge - 100) * 0.4)
|
||||
break
|
||||
case 10000041:
|
||||
// 莫娜被动fix
|
||||
attr.dmg = Math.max(0, attr.dmg - attr.recharge * 0.2)
|
||||
break
|
||||
case 10000070:
|
||||
// 妮露满命效果fix
|
||||
if (ret.cons === 6) {
|
||||
count = Math.floor(attr.hp / 1000)
|
||||
attr.cpct = Math.max(5, attr.cpct - Math.min(30, count * 0.6))
|
||||
attr.cdmg = Math.max(50, attr.cdmg - Math.min(60, count * 1.2))
|
||||
}
|
||||
break
|
||||
}
|
||||
let wDmg = {
|
||||
息灾: 12,
|
||||
波乱月白经津: 12,
|
||||
雾切之回光: 12,
|
||||
猎人之径: 12
|
||||
}
|
||||
let { name, affix } = weapon
|
||||
// 修正武器的加伤
|
||||
if (wDmg[name]) {
|
||||
attr.dmg = Math.max(0, attr.dmg - wDmg[name] - wDmg[name] * (affix - 1) / 4)
|
||||
}
|
||||
ret._fix = true
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
export default EnkaData
|
@ -1,172 +0,0 @@
|
||||
import { Character, ProfileData } from '../../models/index.js'
|
||||
import lodash from 'lodash'
|
||||
import { artiIdx, artiSetMap, attrMap } from './miao-meta.js'
|
||||
|
||||
let MiaoData = {
|
||||
key: 'miao',
|
||||
name: '喵喵Api',
|
||||
|
||||
getData (uid, data) {
|
||||
let ret = {
|
||||
uid,
|
||||
chars: {}
|
||||
}
|
||||
if (data.cacheExpireAt) {
|
||||
let exp = Math.max(0, Math.round(data.cacheExpireAt - (new Date() / 1000)))
|
||||
ret.ttl = Math.max(60, exp)
|
||||
}
|
||||
return ret
|
||||
},
|
||||
|
||||
getAvatar (ds) {
|
||||
let char = Character.get(ds.id)
|
||||
return {
|
||||
id: ds.id,
|
||||
name: char ? char.name : '',
|
||||
dataSource: 'miao',
|
||||
level: ds.level
|
||||
}
|
||||
},
|
||||
getProfile (ds) {
|
||||
let char = Character.get(ds.id)
|
||||
let profile = new ProfileData({ id: char.id })
|
||||
profile.setBasic({
|
||||
level: ds.level,
|
||||
cons: ds.constellationNum || 0,
|
||||
fetter: ds.fetterLevel,
|
||||
costume: char.checkCostume(ds.costumeID) ? ds.costumeID : 0,
|
||||
dataSource: 'miao'
|
||||
})
|
||||
profile.setAttr(MiaoData.getAttr(ds.combatValue))
|
||||
profile.setWeapon(MiaoData.getWeapon(ds.weapon))
|
||||
profile.setArtis(MiaoData.getArtifact(ds.reliquary))
|
||||
let talentRet = MiaoData.getTalent(char.id, ds.skill)
|
||||
profile.setTalent(talentRet.talent, 'level')
|
||||
if (talentRet.elem) {
|
||||
profile.elem = talentRet.elem
|
||||
}
|
||||
return profile
|
||||
},
|
||||
getAttr (data) {
|
||||
let ret = {}
|
||||
lodash.forEach({
|
||||
atk: 'attack',
|
||||
atkBase: 'baseATK',
|
||||
hp: 'health',
|
||||
hpBase: 'baseHP',
|
||||
def: 'defense',
|
||||
defBase: 'baseDEF',
|
||||
mastery: 'elementMastery',
|
||||
cpct: {
|
||||
src: 'critRate',
|
||||
pct: true
|
||||
},
|
||||
cdmg: {
|
||||
src: 'critDamage',
|
||||
pct: true
|
||||
},
|
||||
heal: {
|
||||
src: 'heal',
|
||||
pct: true
|
||||
},
|
||||
recharge: {
|
||||
src: 'recharge',
|
||||
pct: true
|
||||
}
|
||||
}, (cfg, key) => {
|
||||
if (!lodash.isObject(cfg)) {
|
||||
cfg = { src: cfg }
|
||||
}
|
||||
let val = data[cfg.src] || 0
|
||||
if (cfg.pct) {
|
||||
val = val * 100
|
||||
}
|
||||
ret[key] = val
|
||||
})
|
||||
let maxDmg = 0
|
||||
let hurt = data.addHurt || {}
|
||||
lodash.forEach('fire,elec,water,grass,wind,rock,ice'.split(','), (key) => {
|
||||
maxDmg = Math.max(hurt[key] * 100, maxDmg)
|
||||
})
|
||||
ret.dmg = maxDmg
|
||||
ret.phy = hurt.physical * 100
|
||||
return ret
|
||||
},
|
||||
getWeapon (weapon) {
|
||||
return {
|
||||
name: weapon.name,
|
||||
star: weapon.rank,
|
||||
level: weapon.level,
|
||||
promote: weapon.promoteLevel,
|
||||
affix: (weapon.affixLevel || 0) + 1
|
||||
}
|
||||
},
|
||||
getArtifact (data) {
|
||||
let ret = {}
|
||||
let get = function (d) {
|
||||
if (!d) {
|
||||
return []
|
||||
}
|
||||
let name = d.name
|
||||
name = name.replace('FIGHT_PROP_', '')
|
||||
if (!attrMap[name]) {
|
||||
return []
|
||||
}
|
||||
let value = d.value
|
||||
if (value && value < 1) {
|
||||
value = value * 100
|
||||
}
|
||||
return [attrMap[name], value]
|
||||
}
|
||||
|
||||
lodash.forEach(data, (ds) => {
|
||||
let sub = ds.appendAffix || []
|
||||
let idx = artiIdx[ds.type]
|
||||
if (!idx) {
|
||||
return
|
||||
}
|
||||
ret[`arti${idx}`] = {
|
||||
name: ds.name,
|
||||
set: artiSetMap[ds.name] || '',
|
||||
level: ds.level,
|
||||
main: get(ds.mainAffix),
|
||||
attrs: [
|
||||
get(sub[0]),
|
||||
get(sub[1]),
|
||||
get(sub[2]),
|
||||
get(sub[3])
|
||||
]
|
||||
}
|
||||
})
|
||||
return ret
|
||||
},
|
||||
getTalent (charid, data = {}) {
|
||||
let char = Character.get(charid)
|
||||
let { talentId = {}, talentElem = {}, talentKey = {} } = char.meta
|
||||
let elem = ''
|
||||
let idx = 0
|
||||
let ret = {}
|
||||
lodash.forEach(data, (ds) => {
|
||||
let key
|
||||
if (talentId[ds.id]) {
|
||||
let tid = talentId[ds.id]
|
||||
key = talentKey[tid]
|
||||
elem = elem || talentElem[tid]
|
||||
ret[key] = {
|
||||
level: ds.level
|
||||
}
|
||||
} else {
|
||||
key = ['a', 'e', 'q'][idx++]
|
||||
ret[key] = ret[key] || {
|
||||
level: ds.level
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
talent: ret,
|
||||
elem
|
||||
}
|
||||
}
|
||||
}
|
||||
export default MiaoData
|
@ -1,48 +0,0 @@
|
||||
import { Data } from '../index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const _path = process.cwd()
|
||||
|
||||
export const artiIdx = {
|
||||
生之花: 1,
|
||||
死之羽: 2,
|
||||
时之沙: 3,
|
||||
空之杯: 4,
|
||||
理之冠: 5
|
||||
}
|
||||
|
||||
let relis = Data.readJSON('resources/meta/artifact/data.json')
|
||||
let setMap = {}
|
||||
|
||||
lodash.forEach(relis, (ds) => {
|
||||
if (ds.sets) {
|
||||
lodash.forEach(ds.sets, (tmp) => {
|
||||
if (tmp.name) {
|
||||
setMap[tmp.name] = ds.name
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
export const artiSetMap = setMap
|
||||
|
||||
export const attrMap = {
|
||||
HP: '小生命',
|
||||
HP_PERCENT: '大生命',
|
||||
ATTACK: '小攻击',
|
||||
ATTACK_PERCENT: '大攻击',
|
||||
DEFENSE: '小防御',
|
||||
DEFENSE_PERCENT: '大防御',
|
||||
FIRE_ADD_HURT: '火元素伤害加成',
|
||||
ICE_ADD_HURT: '冰元素伤害加成',
|
||||
ROCK_ADD_HURT: '岩元素伤害加成',
|
||||
ELEC_ADD_HURT: '雷元素伤害加成',
|
||||
WIND_ADD_HURT: '风元素伤害加成',
|
||||
WATER_ADD_HURT: '水元素伤害加成',
|
||||
PHYSICAL_ADD_HURT: '物理伤害加成',
|
||||
GRASS_ADD_HURT: '草元素伤害加成',
|
||||
HEAL_ADD: '治疗加成',
|
||||
ELEMENT_MASTERY: '元素精通',
|
||||
CRITICAL: '暴击率',
|
||||
CRITICAL_HURT: '暴击伤害',
|
||||
CHARGE_EFFICIENCY: '充能效率'
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
import lodash from 'lodash'
|
||||
import Character from '../models/Character.js'
|
||||
|
||||
const artifactMap = {
|
||||
生命值: {
|
||||
title: '小生命'
|
||||
},
|
||||
生命值_百分比: {
|
||||
title: '大生命',
|
||||
pct: true
|
||||
},
|
||||
暴击率: {
|
||||
title: '暴击率',
|
||||
pct: true
|
||||
},
|
||||
暴击伤害: {
|
||||
title: '暴击伤害',
|
||||
pct: true
|
||||
},
|
||||
防御力: {
|
||||
title: '小防御'
|
||||
},
|
||||
防御力_百分比: {
|
||||
title: '大防御',
|
||||
pct: true
|
||||
},
|
||||
攻击力: {
|
||||
title: '小攻击'
|
||||
},
|
||||
攻击力_百分比: {
|
||||
title: '大攻击',
|
||||
pct: true
|
||||
},
|
||||
元素精通: {
|
||||
title: '元素精通'
|
||||
},
|
||||
元素充能效率: {
|
||||
title: '充能效率',
|
||||
pct: true
|
||||
},
|
||||
治疗加成: {
|
||||
title: '治疗加成',
|
||||
pct: true
|
||||
}
|
||||
}
|
||||
|
||||
let posIdx = {
|
||||
生之花: {
|
||||
idx: 1
|
||||
},
|
||||
死之羽: {
|
||||
idx: 2
|
||||
},
|
||||
时之沙: {
|
||||
idx: 3
|
||||
},
|
||||
空之杯: {
|
||||
idx: 4
|
||||
},
|
||||
理之冠: {
|
||||
idx: 5
|
||||
}
|
||||
}
|
||||
|
||||
let Data = {
|
||||
getData (uid, data) {
|
||||
let ret = {
|
||||
uid,
|
||||
chars: {}
|
||||
}
|
||||
|
||||
lodash.forEach({
|
||||
name: '角色名称',
|
||||
avatar: '头像ID',
|
||||
level: '冒险等阶'
|
||||
}, (title, key) => {
|
||||
ret[key] = data[title] || ''
|
||||
})
|
||||
|
||||
lodash.forEach(data.items, (ds) => {
|
||||
let char = Data.getAvatar(ds)
|
||||
ret.chars[char.id] = char
|
||||
})
|
||||
|
||||
return ret
|
||||
},
|
||||
getAvatar (data) {
|
||||
let char = Character.get(data['英雄Id'])
|
||||
return {
|
||||
id: data['英雄Id'],
|
||||
name: char ? char.name : '',
|
||||
level: data['等级'],
|
||||
attr: Data.getAttr(data),
|
||||
// weapon: Data.getWeapon(data),
|
||||
artis: Data.getArtifact(data)
|
||||
// cons: data["命之座数量"] * 1 || 0,
|
||||
// talent: Data.getTalent(data)
|
||||
}
|
||||
},
|
||||
getAttr (data) {
|
||||
let ret = {}
|
||||
let attrKey = {
|
||||
atk: '攻击力_总',
|
||||
atkBase: '属性攻击力',
|
||||
def: '防御力_总',
|
||||
defBase: '属性防御力',
|
||||
hp: '生命值上限_总',
|
||||
hpBase: '属性生命值上限',
|
||||
mastery: '属性元素精通',
|
||||
cRate: {
|
||||
title: '属性暴击率',
|
||||
pct: true
|
||||
},
|
||||
cDmg: {
|
||||
title: '属性暴击伤害',
|
||||
pct: true
|
||||
},
|
||||
hInc: {
|
||||
title: '属性治疗加成',
|
||||
pct: true
|
||||
},
|
||||
recharge: {
|
||||
title: '属性元素充能效率',
|
||||
pct: true
|
||||
}
|
||||
}
|
||||
lodash.forEach(attrKey, (cfg, key) => {
|
||||
if (typeof (cfg) === 'string') {
|
||||
cfg = { title: cfg }
|
||||
}
|
||||
let val = data[cfg.title] || ''
|
||||
if (cfg.pct) {
|
||||
val = (val * 100).toFixed(2)
|
||||
}
|
||||
ret[key] = val
|
||||
})
|
||||
let maxDmg = 0
|
||||
lodash.forEach('火水草雷风冰岩'.split(''), (key) => {
|
||||
maxDmg = Math.max(data[`属性${key}元素伤害加成`] * 1, maxDmg)
|
||||
})
|
||||
ret.dmgBonus = (maxDmg * 100).toFixed(2)
|
||||
ret.phyBonus = (data['属性物理伤害加成'] * 100).toFixed(2)
|
||||
|
||||
return ret
|
||||
},
|
||||
getWeapon (data) {
|
||||
return {
|
||||
name: data['武器名称'],
|
||||
level: data['武器等级'],
|
||||
refine: data['武器精炼']
|
||||
}
|
||||
},
|
||||
getArtifact (data) {
|
||||
let ret = {}
|
||||
let get = function (idx, key) {
|
||||
let v = data[`圣遗物${idx}${key}`]
|
||||
let ret = /^([^\d]*)([\d\.\-]*)$/.exec(v)
|
||||
if (ret && ret[1]) {
|
||||
let title = ret[1]; let val = ret[2]
|
||||
if (artifactMap[title]) {
|
||||
if (artifactMap[title].pct) {
|
||||
val = (val * 100).toFixed(2)
|
||||
}
|
||||
title = artifactMap[title].title
|
||||
}
|
||||
return [title, val]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
for (let idx = 1; idx <= 5; idx++) {
|
||||
ret[`arti${idx}`] = {
|
||||
name: data[`圣遗物${idx}名称`],
|
||||
type: data[`圣遗物${idx}类型`],
|
||||
main: get(idx, '主词条'),
|
||||
attrs: [
|
||||
get(idx, '副词条1'),
|
||||
get(idx, '副词条2'),
|
||||
get(idx, '副词条3'),
|
||||
get(idx, '副词条4')
|
||||
]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
},
|
||||
getTalent (data) {
|
||||
let ret = {}
|
||||
lodash.forEach({
|
||||
a: 1,
|
||||
e: 2,
|
||||
q: 3
|
||||
}, (idx, key) => {
|
||||
let val = data[`天赋主动名称${idx}`]
|
||||
let regRet = /等级(\d*)$/.exec(val)
|
||||
if (regRet && regRet[1]) {
|
||||
ret[key] = regRet[1] * 1 || 1
|
||||
} else {
|
||||
ret[key] = 1
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
}
|
@ -57,6 +57,10 @@ export const helpList = [{
|
||||
icon: 65,
|
||||
title: '#圣遗物列表 #雷神圣遗物',
|
||||
desc: '查看圣遗物列表 / 评分详情'
|
||||
}, {
|
||||
icon: 79,
|
||||
title: '#面板帮助',
|
||||
desc: '面板替换及其他帮助信息'
|
||||
}, {
|
||||
icon: 64,
|
||||
title: '#深渊 #深渊12层',
|
||||
|
@ -1,4 +1,39 @@
|
||||
export const cfgSchema = {
|
||||
apps: {
|
||||
title: 'Yunzai功能(开启使用喵喵版功能)',
|
||||
cfg: {
|
||||
avatarList: {
|
||||
title: '#角色 #UID',
|
||||
key: '角色列表',
|
||||
def: false
|
||||
},
|
||||
avatarCard: {
|
||||
title: '#刻晴 #老婆',
|
||||
key: '角色卡片',
|
||||
def: true
|
||||
},
|
||||
uploadAbyssData: {
|
||||
title: '#深渊',
|
||||
key: '深渊',
|
||||
def: false
|
||||
},
|
||||
profileStat: {
|
||||
title: '#练度统计',
|
||||
key: '练度统计',
|
||||
def: false
|
||||
},
|
||||
help: {
|
||||
title: '#帮助 #菜单',
|
||||
key: '帮助',
|
||||
def: false
|
||||
},
|
||||
avatarPoke: {
|
||||
title: '戳一戳展示角色卡片',
|
||||
key: '戳一戳',
|
||||
def: true
|
||||
}
|
||||
}
|
||||
},
|
||||
profile: {
|
||||
title: '角色面板相关设置',
|
||||
cfg: {
|
||||
@ -12,12 +47,6 @@ export const cfgSchema = {
|
||||
key: '面板替换',
|
||||
def: true
|
||||
},
|
||||
profileStat: {
|
||||
title: '面板练度统计',
|
||||
key: '练度统计',
|
||||
def: false,
|
||||
desc: '使用【#面板练度统计】功能取代【#练度统计】功能,默认关闭'
|
||||
},
|
||||
groupRank: {
|
||||
title: '群面板排名',
|
||||
key: '排名',
|
||||
@ -63,34 +92,6 @@ export const cfgSchema = {
|
||||
}
|
||||
}
|
||||
},
|
||||
char: {
|
||||
title: '玩家&老婆卡片展示',
|
||||
cfg: {
|
||||
avatarCard: {
|
||||
title: '角色查询',
|
||||
key: '角色',
|
||||
def: true,
|
||||
desc: '使用喵喵版角色卡片作为默认角色卡片功能',
|
||||
showDesc: false
|
||||
},
|
||||
uploadAbyssData: {
|
||||
title: '上传深渊',
|
||||
key: '深渊',
|
||||
def: false,
|
||||
desc: '使用【#上传深渊】功能取代【#深渊】功能,默认关闭'
|
||||
},
|
||||
avatarWife: {
|
||||
title: '老婆查询',
|
||||
key: '老婆',
|
||||
def: true
|
||||
},
|
||||
avatarPoke: {
|
||||
title: '戳一戳卡片',
|
||||
key: '戳一戳',
|
||||
def: true
|
||||
}
|
||||
}
|
||||
},
|
||||
wiki: {
|
||||
title: '角色资料与信息查询',
|
||||
cfg: {
|
||||
@ -137,11 +138,13 @@ export const cfgSchema = {
|
||||
input: (n) => Math.min(200, Math.max(50, (n * 1 || 100))),
|
||||
desc: '可选值50~200,建议100。设置高精度会提高图片的精细度,但因图片较大可能会影响渲染与发送速度'
|
||||
},
|
||||
help: {
|
||||
title: '喵喵作为默认帮助',
|
||||
key: '帮助',
|
||||
def: false,
|
||||
desc: '开启后将使用喵喵版帮助作为Yunzai的默认帮助,默认关闭'
|
||||
originalPic: {
|
||||
title: '原图',
|
||||
key: '原图',
|
||||
type: 'num',
|
||||
def: 3,
|
||||
input: (n) => Math.min(3, Math.max(n * 1 || 0, 0)),
|
||||
desc: '允许获取原图,0:不允许, 1:仅允许角色图, 2:仅允许面板图, 3:开启'
|
||||
},
|
||||
commaGroup: {
|
||||
title: '数字逗号分组',
|
||||
|
@ -41,6 +41,10 @@ export const helpList = [{
|
||||
icon: 65,
|
||||
title: '#圣遗物列表 #雷神圣遗物',
|
||||
desc: '查看圣遗物列表 / 评分详情'
|
||||
}, {
|
||||
icon: 79,
|
||||
title: '#面板帮助',
|
||||
desc: '面板替换及其他帮助信息'
|
||||
}, {
|
||||
icon: 64,
|
||||
title: '#深渊 #深渊12层',
|
||||
|
@ -2,10 +2,14 @@
|
||||
* 圣遗物
|
||||
* */
|
||||
import Base from './Base.js'
|
||||
import { Format } from '../components/index.js'
|
||||
import { ArtifactSet } from './index.js'
|
||||
import { artiMap, attrMap } from '../resources/meta/artifact/index.js'
|
||||
import { artiMap, attrMap, mainIdMap, attrIdMap } from '../resources/meta/artifact/index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
class Artifact extends Base {
|
||||
static getAttrs
|
||||
|
||||
constructor (name) {
|
||||
super()
|
||||
let cache = this._getCache(`arti:${name}`)
|
||||
@ -29,6 +33,10 @@ class Artifact extends Base {
|
||||
return this.set
|
||||
}
|
||||
|
||||
get img () {
|
||||
return `meta/artifact/imgs/${this.setName}/${this.idx}.webp`
|
||||
}
|
||||
|
||||
static get (name) {
|
||||
if (artiMap[name]) {
|
||||
return new Artifact(name)
|
||||
@ -36,10 +44,6 @@ class Artifact extends Base {
|
||||
return false
|
||||
}
|
||||
|
||||
get img () {
|
||||
return `meta/artifact/imgs/${this.setName}/${this.idx}.webp`
|
||||
}
|
||||
|
||||
static getSetNameByArti (name) {
|
||||
let arti = Artifact.get(name)
|
||||
if (arti) {
|
||||
@ -53,6 +57,46 @@ class Artifact extends Base {
|
||||
attrMap
|
||||
}
|
||||
}
|
||||
|
||||
static getMainById (id, level = 20, star = 5) {
|
||||
let key = mainIdMap[id]
|
||||
if (!key) {
|
||||
return false
|
||||
}
|
||||
let attrCfg = attrMap[Format.isElem(key) ? 'dmg' : key]
|
||||
let posEff = ['hpPlus', 'atkPlus', 'defPlus'].includes(key) ? 2 : 1
|
||||
let starEff = { 1: 0.21, 2: 0.36, 3: 0.6, 4: 0.9, 5: 1 }
|
||||
return {
|
||||
key,
|
||||
value: attrCfg.value * (1.2 + 0.34 * level) * posEff * (starEff[star || 5])
|
||||
}
|
||||
}
|
||||
|
||||
static getAttrsByIds (ids, star = 5) {
|
||||
let ret = []
|
||||
let tmp = {}
|
||||
let starEff = { 1: 0.21, 2: 0.36, 3: 0.6, 4: 0.8, 5: 1 }
|
||||
lodash.forEach(ids, (id) => {
|
||||
let cfg = attrIdMap[id]
|
||||
if (!cfg) {
|
||||
return true
|
||||
}
|
||||
let { key, value } = cfg
|
||||
if (!tmp[key]) {
|
||||
tmp[key] = {
|
||||
key,
|
||||
eff: 0,
|
||||
upNum: 0,
|
||||
value: 0
|
||||
}
|
||||
ret.push(tmp[key])
|
||||
}
|
||||
tmp[key].eff += value / attrMap[key].value * starEff[star]
|
||||
tmp[key].value += value * (attrMap[key].format === 'pct' ? 100 : 1) * starEff[star]
|
||||
tmp[key].upNum++
|
||||
})
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
export default Artifact
|
||||
|
211
models/Avatar.js
211
models/Avatar.js
@ -1,211 +0,0 @@
|
||||
/*
|
||||
* 用户角色封装
|
||||
* 兼容处理面板 Profile Data 及 Mys Avatar 数据
|
||||
* */
|
||||
import Base from './Base.js'
|
||||
import lodash from 'lodash'
|
||||
import { Profile } from '../components/index.js'
|
||||
import { Artifact, Character, Weapon, ArtifactSet } from './index.js'
|
||||
import moment from 'moment'
|
||||
|
||||
const charKey = 'name,abbr,sName,star,imgs,face,side,gacha,weaponTypeName,elem'.split(',')
|
||||
|
||||
export default class Avatar extends Base {
|
||||
constructor (data = {}, pd = false, hasCk = true) {
|
||||
super()
|
||||
if (!data.name) {
|
||||
return false
|
||||
}
|
||||
let char = Character.get(data.name)
|
||||
if (!char || char.isCustom) {
|
||||
return false
|
||||
}
|
||||
this.meta = data
|
||||
this.char = char
|
||||
let isProfile = data.isProfile
|
||||
this.dataType = isProfile ? 'profile' : 'avatar'
|
||||
this.hasCk = hasCk
|
||||
let profile
|
||||
let uid
|
||||
if (isProfile) {
|
||||
profile = data
|
||||
} else if (pd) {
|
||||
if (pd.isProfile) {
|
||||
profile = pd
|
||||
} else if (/\d{9}/.test(pd)) {
|
||||
uid = pd
|
||||
profile = Profile.get(pd, char.id, true)
|
||||
}
|
||||
}
|
||||
if (profile && profile.isProfile && profile.hasData) {
|
||||
this.profile = profile
|
||||
}
|
||||
this.elem = ((profile && profile.elem) || data.element || data.elem || char.elem || 'anemo').toLowerCase()
|
||||
if (char.isTraveler) {
|
||||
this.char = Character.get({ id: data.id || char.id, elem: this.elem })
|
||||
uid && char.setTraveler(uid)
|
||||
}
|
||||
}
|
||||
|
||||
_get (key) {
|
||||
if (charKey.includes(key)) {
|
||||
return this.char[key]
|
||||
}
|
||||
return this.meta[key]
|
||||
}
|
||||
|
||||
get dataSourceName () {
|
||||
if (!this.hasCk && this.profile) {
|
||||
return this.profile.dataSourceName
|
||||
}
|
||||
return this.meta.dataSourceName || '米游社'
|
||||
}
|
||||
|
||||
get updateTime () {
|
||||
if ((!this.hasCk || this.isProfile) && this.profile) {
|
||||
return this.profile.updateTime
|
||||
}
|
||||
return moment(new Date()).format('MM-DD HH:mm')
|
||||
}
|
||||
|
||||
get isProfile () {
|
||||
return this.dataType === 'profile'
|
||||
}
|
||||
|
||||
get isAvatar () {
|
||||
return this.dataType === 'avatar'
|
||||
}
|
||||
|
||||
get artis () {
|
||||
let ret = {}
|
||||
if (!this.isProfile) {
|
||||
lodash.forEach(this.meta.reliquaries, (ds) => {
|
||||
let arti = Artifact.get(ds.name)
|
||||
ret[arti.idx] = {
|
||||
name: arti.name,
|
||||
set: arti.setName,
|
||||
img: arti.img,
|
||||
level: ds.level
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
if (this.profile && this.profile?.artis) {
|
||||
return this.profile.artis.getArtisData()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
get cons () {
|
||||
let data = this.meta
|
||||
let profile = this.profile
|
||||
return data?.cons || data?.actived_constellation_num || profile?.cons || 0
|
||||
}
|
||||
|
||||
get weapon () {
|
||||
let wd = this.meta?.weapon || this.profile?.weapon
|
||||
if (!wd || !wd.name) {
|
||||
return {}
|
||||
}
|
||||
let weapon = Weapon.get(wd.name)
|
||||
return {
|
||||
name: wd.name,
|
||||
abbr: weapon.abbr,
|
||||
star: weapon.star,
|
||||
level: wd.level || 1,
|
||||
affix: wd.affix || wd.affix_level || 0,
|
||||
type: weapon.type,
|
||||
img: weapon.img
|
||||
}
|
||||
}
|
||||
|
||||
async getTalent (mys) {
|
||||
if (!this.isProfile && mys && mys.isSelfCookie) {
|
||||
let char = this.char
|
||||
let id = char.id
|
||||
let talent = {}
|
||||
let talentRes = await mys.getDetail(id)
|
||||
// { data: null, message: '请先登录', retcode: -100, api: 'detail' }
|
||||
let avatar = this.meta
|
||||
if (!char || !avatar) {
|
||||
return false
|
||||
}
|
||||
if (talentRes && talentRes.skill_list) {
|
||||
let talentList = lodash.orderBy(talentRes.skill_list, ['id'], ['asc'])
|
||||
for (let val of talentList) {
|
||||
let { max_level: maxLv, level_current: lv } = val
|
||||
if (val.name.includes('普通攻击')) {
|
||||
talent.a = lv
|
||||
continue
|
||||
}
|
||||
if (maxLv >= 10 && !talent.e) {
|
||||
talent.e = lv
|
||||
continue
|
||||
}
|
||||
if (maxLv >= 10 && !talent.q) {
|
||||
talent.q = lv
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
let ret = char.getAvatarTalent(talent, this.cons, 'original')
|
||||
if (ret) {
|
||||
ret.id = id
|
||||
}
|
||||
return ret
|
||||
}
|
||||
if (this.profile) {
|
||||
let profile = this.profile
|
||||
let talent = profile.talent
|
||||
talent.id = profile.id
|
||||
return talent
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
get artisSet () {
|
||||
if (this._artisSet) {
|
||||
return this._artisSet
|
||||
}
|
||||
this._artisSet = false
|
||||
if (!this.isProfile) {
|
||||
let artis = this.artis
|
||||
let setCount = {}
|
||||
lodash.forEach(artis, (arti, idx) => {
|
||||
let set = arti?.set
|
||||
if (set) {
|
||||
setCount[set] = (setCount[set] || 0) + 1
|
||||
}
|
||||
})
|
||||
let sets = {}
|
||||
let names = []
|
||||
let abbrs = []
|
||||
let abbrs2 = []
|
||||
let imgs = []
|
||||
for (let set in setCount) {
|
||||
if (setCount[set] >= 2) {
|
||||
let value = setCount[set] >= 4 ? 4 : 2
|
||||
sets[set] = value
|
||||
let artiSet = ArtifactSet.get(set)
|
||||
names.push(artiSet.name)
|
||||
abbrs.push(artiSet.abbr + value)
|
||||
abbrs2.push(artiSet.name + value)
|
||||
imgs.push(artiSet.img)
|
||||
}
|
||||
}
|
||||
this._artisSet = {
|
||||
sets,
|
||||
names,
|
||||
abbrs: [...abbrs, ...abbrs2],
|
||||
imgs,
|
||||
name: (abbrs.length > 1 || abbrs2[0]?.length > 7) ? abbrs.join('+') : abbrs2[0],
|
||||
sName: abbrs.join('+')
|
||||
}
|
||||
}
|
||||
if (this.profile) {
|
||||
let profile = this.profile
|
||||
this._artisSet = profile.artis ? profile.artis.getSetData() : false
|
||||
}
|
||||
return this._artisSet || {}
|
||||
}
|
||||
}
|
268
models/AvatarArtis.js
Normal file
268
models/AvatarArtis.js
Normal file
@ -0,0 +1,268 @@
|
||||
/**
|
||||
* 面板圣遗物
|
||||
*/
|
||||
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/ArtisMark.js'
|
||||
|
||||
export default class AvatarArtis extends Base {
|
||||
constructor (charid = 0) {
|
||||
super()
|
||||
this.charid = charid
|
||||
this.artis = {}
|
||||
}
|
||||
|
||||
get sets () {
|
||||
return this.getSetData().sets || {}
|
||||
}
|
||||
|
||||
get names () {
|
||||
return this.getSetData().names || []
|
||||
}
|
||||
|
||||
get hasArtis () {
|
||||
return !lodash.isEmpty(this.artis)
|
||||
}
|
||||
|
||||
get hasAttr () {
|
||||
return ArtisMark.hasAttr(this.artis)
|
||||
}
|
||||
|
||||
static _eachArtisSet (sets, fn) {
|
||||
lodash.forEach(sets || [], (v, k) => {
|
||||
let artisSet = ArtifactSet.get(k)
|
||||
if (artisSet) {
|
||||
if (v >= 4) {
|
||||
fn(artisSet, 2)
|
||||
}
|
||||
fn(artisSet, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static getArtisKeyTitle () {
|
||||
return ArtisMark.getKeyTitleMap()
|
||||
}
|
||||
|
||||
setArtisData (ds = {}, profile = false) {
|
||||
// let force = !this.hasArtis || ArtisMark.hasAttr(ds) || !ArtisMark.hasAttr(this.artis)
|
||||
if (!profile || (profile && ArtisMark.hasAttr(ds))) {
|
||||
for (let idx = 1; idx <= 5; idx++) {
|
||||
if (ds[idx] || ds[`arti${idx}`]) {
|
||||
this.setArtis(idx, ds[idx] || ds[`arti${idx}`], profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setArtis (idx = 1, ds = {}, profile = false) {
|
||||
idx = idx.toString().replace('arti', '')
|
||||
this.artis[idx] = this.artis[idx] || {}
|
||||
let arti = this.artis[idx]
|
||||
if (profile) {
|
||||
arti.name = ds._name || ds.name || arti.name || ''
|
||||
arti.set = ds._set || Artifact.getSetNameByArti(arti._name) || ds.set || ''
|
||||
arti.level = ds._level || ds.level || 1
|
||||
arti.star = ds._star || ds.star || 5
|
||||
arti.main = ds.main
|
||||
arti.attrs = ds.attrs
|
||||
return true
|
||||
}
|
||||
arti.name = ds.name || arti.name || ''
|
||||
arti.set = ds.set || Artifact.getSetNameByArti(arti.name) || ''
|
||||
arti.level = ds.level || 1
|
||||
arti.star = ds.star || 5
|
||||
|
||||
if (ds.mainId || ds.main) {
|
||||
arti._name = ds.name || arti.name
|
||||
arti._set = ds.set || Artifact.getSetNameByArti(arti.name) || arti.set || ''
|
||||
arti._level = ds.level || arti.level
|
||||
arti._star = ds.star || arti.star || 5
|
||||
}
|
||||
|
||||
// 存在面板数据,更新面板数据
|
||||
if (ds.mainId && ds.attrIds) {
|
||||
arti.mainId = ds.mainId
|
||||
arti.attrIds = ds.attrIds
|
||||
arti.main = Artifact.getMainById(ds.mainId, arti._level, arti._star)
|
||||
arti.attrs = Artifact.getAttrsByIds(ds.attrIds, arti._star)
|
||||
} else if (ds.main && ds.attrs) {
|
||||
arti.main = ArtisMark.formatAttr(ds.main || {})
|
||||
arti.attrs = []
|
||||
for (let attrIdx in ds.attrs || []) {
|
||||
if (ds.attrs[attrIdx]) {
|
||||
arti.attrs.push(ArtisMark.formatAttr(ds.attrs[attrIdx]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forEach (fn) {
|
||||
lodash.forEach(this.artis, (ds, idx) => {
|
||||
if (ds.name) {
|
||||
fn(ds, idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_get (key) {
|
||||
let artis = this.artis
|
||||
switch (key) {
|
||||
case 'length':
|
||||
return lodash.keys(artis).length
|
||||
}
|
||||
if (artis[key]) {
|
||||
return artis[key]
|
||||
}
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
let ret = {}
|
||||
for (let idx = 1; idx <= 5; idx++) {
|
||||
let ds = this.artis[idx]
|
||||
if (ds) {
|
||||
let tmp = {
|
||||
name: ds.name || '',
|
||||
level: ds.level || 1,
|
||||
star: ds.star || 5
|
||||
}
|
||||
if ((ds.mainId && ds.attrIds) || (ds.main && ds.attrs)) {
|
||||
if ((ds._name && ds._name !== ds.name) || (ds._level && ds._level !== ds.level) || (ds._star && ds._star !== ds.star)) {
|
||||
tmp._name = ds._name || null
|
||||
tmp._level = ds._level || null
|
||||
tmp._star = ds._star || null
|
||||
}
|
||||
}
|
||||
if (ds.mainId && ds.attrIds) {
|
||||
tmp.mainId = ds.mainId || null
|
||||
tmp.attrIds = ds.attrIds
|
||||
} else if (ds.main && ds.attrs) {
|
||||
tmp.main = ds.main || null
|
||||
tmp.attrs = []
|
||||
for (let attrIdx in ds.attrs || []) {
|
||||
if (ds.attrs[attrIdx]) {
|
||||
tmp.attrs.push(ArtisMark.formatAttr(ds.attrs[attrIdx]))
|
||||
}
|
||||
}
|
||||
}
|
||||
ret[idx] = tmp
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
getDetail (profile = false) {
|
||||
let ret = {}
|
||||
for (let idx = 1; idx <= 5; idx++) {
|
||||
let ds = this.artis[idx]
|
||||
if (ds) {
|
||||
let artis = Artifact.get(profile ? ds._name : ds.name)
|
||||
let tmp = {
|
||||
...artis?.getData('img,name,set'),
|
||||
level: (profile ? ds._level : ds.level) || 1
|
||||
}
|
||||
if (ds.main && ds.attrs) {
|
||||
tmp.main = ds.main || null
|
||||
tmp.attrs = []
|
||||
for (let attrIdx in ds.attrs || []) {
|
||||
if (ds.attrs[attrIdx]) {
|
||||
tmp.attrs.push(ArtisMark.formatAttr(ds.attrs[attrIdx]))
|
||||
}
|
||||
}
|
||||
}
|
||||
ret[idx] = tmp
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
mainAttr (idx = '') {
|
||||
if (!idx) {
|
||||
let ret = {}
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
ret[i] = this.mainAttr(i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
let main = this.artis[idx]?.main
|
||||
if (!main) {
|
||||
return ''
|
||||
}
|
||||
return main.key || ''
|
||||
}
|
||||
|
||||
is (check, pos = '') {
|
||||
if (pos) {
|
||||
return this.isAttr(check, pos)
|
||||
}
|
||||
let sets = this.getSetData()?.abbrs || []
|
||||
let ret = false
|
||||
Data.eachStr(check, (s) => {
|
||||
if (sets.includes(s)) {
|
||||
ret = true
|
||||
return false
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
isAttr (attr, pos = '3,4,5') {
|
||||
let mainAttr = this.mainAttr()
|
||||
let check = true
|
||||
Data.eachStr(pos.toString(), (p) => {
|
||||
let attrs = attr.split(',')
|
||||
if (!attrs.includes(mainAttr[p]) && (p === '4' && !attrs.includes('dmg') && Format.isElem(mainAttr[p]))) {
|
||||
check = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
return check
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取圣遗物套装数据
|
||||
* @returns {*|{imgs: *[], names: *[], sets: {}, abbrs: *[], sName: string, name: (string|*)}}
|
||||
* sets: 套装名:2/4
|
||||
* names: [套装名]
|
||||
* imgs: [img]
|
||||
* abbrs:[别名]
|
||||
* name: '组合名字', 若为4件套会使用套装完整名
|
||||
* sName: '简写名字',若为4件套也会使用简写
|
||||
*/
|
||||
getSetData (profile = false) {
|
||||
let setCount = {}
|
||||
this.forEach((arti, idx) => {
|
||||
setCount[profile ? arti._set : arti.set] = (setCount[profile ? arti._set : arti.set] || 0) + 1
|
||||
})
|
||||
let sets = {}
|
||||
let names = []
|
||||
let imgs = []
|
||||
let abbrs = []
|
||||
let abbrs2 = []
|
||||
for (let set in setCount) {
|
||||
if (setCount[set] >= 2) {
|
||||
let count = setCount[set] >= 4 ? 4 : 2
|
||||
sets[set] = count
|
||||
let artiSet = ArtifactSet.get(set)
|
||||
names.push(artiSet.name)
|
||||
imgs.push(artiSet.img)
|
||||
abbrs.push(artiSet.abbr + count)
|
||||
abbrs2.push(artiSet.name + count)
|
||||
}
|
||||
}
|
||||
return {
|
||||
sets,
|
||||
names,
|
||||
imgs,
|
||||
abbrs: [...abbrs, ...abbrs2],
|
||||
name: (abbrs.length > 1 || abbrs2[0]?.length > 7) ? abbrs.join('+') : abbrs2[0],
|
||||
sName: abbrs.join('+')
|
||||
}
|
||||
}
|
||||
|
||||
eachArtisSet (fn) {
|
||||
AvatarArtis._eachArtisSet(this.sets, fn)
|
||||
}
|
||||
}
|
205
models/AvatarData.js
Normal file
205
models/AvatarData.js
Normal file
@ -0,0 +1,205 @@
|
||||
import lodash from 'lodash'
|
||||
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/AttrCalc.js'
|
||||
import Profile from './player/Profile.js'
|
||||
|
||||
const charKey = 'name,abbr,sName,star,imgs,face,side,gacha,weaponTypeName'.split(',')
|
||||
|
||||
export default class AvatarData extends Base {
|
||||
constructor (ds = {}, source) {
|
||||
super()
|
||||
let char = Character.get({ id: ds.id, elem: ds.elem })
|
||||
if (!char) {
|
||||
return
|
||||
}
|
||||
this.id = char.id
|
||||
this.char = char
|
||||
this.initArtis()
|
||||
this.setAvatar(ds, source)
|
||||
}
|
||||
|
||||
get hasTalent () {
|
||||
return this.talent && !lodash.isEmpty(this.talent) && !!this._talent
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this.char?.name || ''
|
||||
}
|
||||
|
||||
get hasData () {
|
||||
return !!(this.level > 1 || this?.weapon?.name || this?.talent?.a)
|
||||
}
|
||||
|
||||
// 是否是合法面板数据
|
||||
get isProfile () {
|
||||
return Profile.isProfile(this)
|
||||
}
|
||||
|
||||
get costume () {
|
||||
let costume = this._costume
|
||||
if (lodash.isArray(costume)) {
|
||||
costume = costume[0]
|
||||
}
|
||||
return costume
|
||||
}
|
||||
|
||||
get originalTalent () {
|
||||
return lodash.mapValues(this.talent, (ds) => ds.original)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取圣遗物套装属性
|
||||
* @returns {boolean|*|{imgs: *[], names: *[], sets: {}, abbrs: *[], sName: string, name: (string|*)}|{}}
|
||||
*/
|
||||
get artisSet () {
|
||||
return this.artis.getSetData()
|
||||
}
|
||||
|
||||
get dataSource () {
|
||||
return {
|
||||
enka: 'Enka.Network',
|
||||
miao: '喵喵Api',
|
||||
mys: '米游社'
|
||||
}[this._source] || this._source
|
||||
}
|
||||
|
||||
get updateTime () {
|
||||
let time = this._time
|
||||
if (!time) {
|
||||
return ''
|
||||
}
|
||||
if (lodash.isString(time)) {
|
||||
return moment(time).format('MM-DD HH:mm')
|
||||
}
|
||||
if (lodash.isNumber(time)) {
|
||||
return moment(new Date(time)).format('MM-DD HH:mm')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
static create (ds, source) {
|
||||
let avatar = new AvatarData(ds)
|
||||
if (!avatar) {
|
||||
return false
|
||||
}
|
||||
return avatar
|
||||
}
|
||||
|
||||
initArtis () {
|
||||
this.artis = new AvatarArtis(this.id)
|
||||
}
|
||||
|
||||
_get (key) {
|
||||
if (charKey.includes(key)) {
|
||||
return this.char[key]
|
||||
}
|
||||
}
|
||||
|
||||
setAvatar (ds, source = '') {
|
||||
this._now = new Date() * 1
|
||||
this.setBasic(ds, source)
|
||||
ds.weapon && this.setWeapon(ds.weapon)
|
||||
ds.talent && this.setTalent(ds.talent, 'original', source)
|
||||
ds.artis && this.setArtis(ds)
|
||||
delete this._now
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置角色基础数据
|
||||
* @param ds
|
||||
* @param source
|
||||
*/
|
||||
setBasic (ds = {}, source = '') {
|
||||
const now = this._now || (new Date()) * 1
|
||||
this.level = ds.lv || ds.level || this.level || 1
|
||||
this.cons = ds.cons || this.cons || 0
|
||||
this.fetter = ds.fetter || this.fetter || 0
|
||||
this._costume = ds.costume || this._costume || 0
|
||||
this.elem = ds.elem || this.elem || this.char.elem || ''
|
||||
this.promote = lodash.isUndefined(ds.promote) ? (this.promote || AttrCalc.calcPromote(this.level)) : (ds.promote || 0)
|
||||
|
||||
this._source = ds._source || ds.dataSource || this._source || ''
|
||||
this._time = ds._time || this._time || now
|
||||
this._update = ds._update || this._update || ds._time || now
|
||||
this._talent = ds._talent || this._talent || ds._time || now
|
||||
// 存在数据源时更新时间
|
||||
if (source) {
|
||||
this._update = now
|
||||
if (source !== 'mys') {
|
||||
this._source = source
|
||||
this._time = now
|
||||
} else {
|
||||
this._source = this._source || source
|
||||
this._time = this._source !== 'mys' ? (this._time || now) : now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWeapon (ds = {}) {
|
||||
let w = Weapon.get(ds.name)
|
||||
if (!w) {
|
||||
return false
|
||||
}
|
||||
this.weapon = {
|
||||
name: ds.name,
|
||||
level: ds.level || ds.lv || 1,
|
||||
promote: lodash.isUndefined(ds.promote) ? AttrCalc.calcPromote(ds.level || ds.lv || 1) : (ds.promote || 0),
|
||||
affix: ds.affix,
|
||||
...w.getData('star,abbr,type,img')
|
||||
}
|
||||
if (this.weapon.level < 20) {
|
||||
this.weapon.promote = 0
|
||||
}
|
||||
}
|
||||
|
||||
setTalent (ds = false, mode = 'original', updateTime = '') {
|
||||
const now = this._now || (new Date()) * 1
|
||||
if (ds) {
|
||||
let ret = this.char.getAvatarTalent(ds, this.cons, mode)
|
||||
if (ret) {
|
||||
this.talent = ret || this.talent
|
||||
// 设置天赋更新时间
|
||||
this._talent = ds._talent || this._talent || ds._time || now
|
||||
}
|
||||
}
|
||||
if (updateTime) {
|
||||
this._talent = now
|
||||
}
|
||||
}
|
||||
|
||||
setArtis (ds, source) {
|
||||
this.artis.setArtisData(ds.artis, source)
|
||||
}
|
||||
|
||||
getProfile () {
|
||||
if (!this.isProfile) {
|
||||
return false
|
||||
}
|
||||
return ProfileData.create(this)
|
||||
}
|
||||
|
||||
// 判断当前profileData是否具备有效圣遗物信息
|
||||
hasArtis () {
|
||||
return this.isProfile && this.artis.length > 0
|
||||
}
|
||||
|
||||
// toJSON 供保存使用
|
||||
toJSON () {
|
||||
return {
|
||||
...this.getData('name,id,elem,level,promote,fetter,costume,cons,talent:originalTalent'),
|
||||
weapon: Data.getData(this.weapon, 'name,level,promote,affix'),
|
||||
...this.getData('artis,_source,_time,_update,_talent')
|
||||
}
|
||||
}
|
||||
|
||||
getDetail (keys = '') {
|
||||
return this.getData(keys || 'id,name,level,star,cons,fetter,elem,face,side,gacha,abbr,weapon,talent,artisSet') || {}
|
||||
}
|
||||
|
||||
getArtisDetail () {
|
||||
return this.artis.getDetail()
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* 用户角色列表
|
||||
*
|
||||
* 批量管理及天赋等数据获取
|
||||
* 使用 Avatar Model实现兼容处理面板数据及Mys角色数据
|
||||
* */
|
||||
import Base from './Base.js'
|
||||
import lodash from 'lodash'
|
||||
import { Data, Common, Profile } from '../components/index.js'
|
||||
import { Avatar, MysApi } from './index.js'
|
||||
|
||||
export default class AvatarList extends Base {
|
||||
constructor (uid, datas = [], withProfile = false) {
|
||||
super()
|
||||
if (!uid) {
|
||||
return false
|
||||
}
|
||||
this.uid = uid
|
||||
let avatars = {}
|
||||
let profiles = {}
|
||||
if (withProfile) {
|
||||
profiles = Profile.getAll(uid)
|
||||
}
|
||||
lodash.forEach(datas, (ds) => {
|
||||
let avatar = new Avatar(ds, profiles[ds.id] || false)
|
||||
if (avatar) {
|
||||
avatars[avatar.id] = avatar
|
||||
}
|
||||
})
|
||||
// 使用面板数据补全
|
||||
lodash.forEach(profiles, (profile) => {
|
||||
if (!avatars[profile.id]) {
|
||||
let avatar = new Avatar(profile)
|
||||
if (avatar) {
|
||||
avatars[avatar.id] = avatar
|
||||
}
|
||||
}
|
||||
})
|
||||
this.avatars = avatars
|
||||
}
|
||||
|
||||
getData (ids, keys = '') {
|
||||
let rets = {}
|
||||
keys = keys || 'id,name,level,star,cons,fetter,elem,face,side,gacha,abbr,weapon,artisSet'
|
||||
let avatars = this.avatars
|
||||
lodash.forEach(ids, (id) => {
|
||||
rets[id] = avatars[id].getData(keys) || {}
|
||||
})
|
||||
return rets
|
||||
}
|
||||
|
||||
getAvatar (id) {
|
||||
return this.avatars[id]
|
||||
}
|
||||
|
||||
getProfile (id) {
|
||||
return this.avatars[id]?.profile
|
||||
}
|
||||
|
||||
getIds () {
|
||||
let rets = []
|
||||
lodash.forEach(this.avatars, (ds) => {
|
||||
rets.push(ds.id)
|
||||
})
|
||||
return rets
|
||||
}
|
||||
|
||||
async getTalentData (ids = '', mys = false, keys = '') {
|
||||
if (!ids) {
|
||||
ids = this.getIds()
|
||||
}
|
||||
mys = mys || this._mys
|
||||
let avatarTalent = await Data.getCacheJSON(`miao:avatar-talent:${this.uid}`)
|
||||
let needReq = {}
|
||||
lodash.forEach(ids, (id) => {
|
||||
if (!avatarTalent[id] || !avatarTalent[id]?.a) {
|
||||
needReq[id] = true
|
||||
}
|
||||
})
|
||||
let avatars = this.avatars
|
||||
let needReqIds = lodash.keys(needReq)
|
||||
if (needReqIds.length > 0) {
|
||||
if (needReqIds.length > 8) {
|
||||
this.e && this.e.reply('正在获取角色信息,请稍候...')
|
||||
}
|
||||
let num = 10
|
||||
let ms = 100
|
||||
let skillRet = []
|
||||
let avatarArr = lodash.chunk(needReqIds, num)
|
||||
for (let val of avatarArr) {
|
||||
for (let id of val) {
|
||||
let avatar = avatars[id]
|
||||
skillRet.push(await avatar.getTalent(mys))
|
||||
}
|
||||
skillRet = await Promise.all(skillRet)
|
||||
skillRet = skillRet.filter(item => item.id)
|
||||
await Common.sleep(ms)
|
||||
}
|
||||
lodash.forEach(skillRet, (talent) => {
|
||||
avatarTalent[talent.id] = talent
|
||||
})
|
||||
await Data.setCacheJSON(`miao:avatar-talent:${this.uid}`, avatarTalent, 3600 * 2)
|
||||
}
|
||||
let ret = this.getData(ids, keys)
|
||||
lodash.forEach(ret, (avatar, id) => {
|
||||
avatar.talent = avatarTalent[id] || {}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
get isSelfCookie () {
|
||||
return !!this._mys?.isSelfCookie
|
||||
}
|
||||
}
|
||||
|
||||
AvatarList.hasTalentCache = async function (uid) {
|
||||
return !!await redis.get(`miao:avatar-talent:${uid}`)
|
||||
}
|
||||
|
||||
AvatarList.getAll = async function (e, mys = false) {
|
||||
if (!mys) {
|
||||
mys = await MysApi.init(e)
|
||||
}
|
||||
if (!mys || !mys.uid) return false
|
||||
let uid = mys.uid
|
||||
let data = await mys.getCharacter()
|
||||
if (!data) {
|
||||
return false
|
||||
}
|
||||
let ret = new AvatarList(uid, data.avatars, true)
|
||||
ret.e = e
|
||||
ret._mys = mys
|
||||
return ret
|
||||
}
|
@ -73,17 +73,23 @@ export default class Base {
|
||||
// 设置超时时间
|
||||
_expire (time = 10 * 60) {
|
||||
let id = this._uuid
|
||||
let self = this
|
||||
reFn[id] && clearTimeout(reFn[id])
|
||||
if (time > 0) {
|
||||
if (id) {
|
||||
reFn[id] = setTimeout(() => {
|
||||
reFn[id] && clearTimeout(reFn[id])
|
||||
delete reFn[id]
|
||||
delete cacheMap[id]
|
||||
delete metaMap[id]
|
||||
self._delCache()
|
||||
}, time * 1000)
|
||||
}
|
||||
return cacheMap[id]
|
||||
}
|
||||
}
|
||||
|
||||
_delCache () {
|
||||
let id = this._uuid
|
||||
reFn[id] && clearTimeout(reFn[id])
|
||||
delete reFn[id]
|
||||
delete cacheMap[id]
|
||||
delete metaMap[id]
|
||||
}
|
||||
}
|
||||
|
@ -7,19 +7,22 @@
|
||||
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 { abbrMap, wifeMap, idSort, idMap } = CharId
|
||||
let { wifeMap, idSort, idMap } = CharId
|
||||
|
||||
let getMeta = function (name) {
|
||||
return Data.readJSON(`resources/meta/character/${name}/data.json`)
|
||||
}
|
||||
|
||||
class Character extends Base {
|
||||
// 默认获取的数据
|
||||
_dataKey = 'id,name,abbr,title,star,elem,allegiance,weapon,birthday,astro,cncv,jpcv,ver,desc,talentCons'
|
||||
|
||||
constructor ({ id, name = '', elem = '' }) {
|
||||
super()
|
||||
// 检查缓存
|
||||
@ -40,9 +43,6 @@ class Character extends Base {
|
||||
return this._cache()
|
||||
}
|
||||
|
||||
// 默认获取的数据
|
||||
_dataKey = 'id,name,abbr,title,star,elem,allegiance,weapon,birthday,astro,cncv,jpcv,ver,desc,talentCons'
|
||||
|
||||
// 是否为官方角色
|
||||
get isOfficial () {
|
||||
return /[12]0\d{6}/.test(this._id)
|
||||
@ -140,17 +140,6 @@ class Character extends Base {
|
||||
return this.meta?.talentCons || {}
|
||||
}
|
||||
|
||||
// 获取attr列表
|
||||
getAttrList () {
|
||||
let { meta } = this
|
||||
return CharMeta.getAttrList(meta.baseAttr, meta.growAttr, this.elemName)
|
||||
}
|
||||
|
||||
// 获取素材
|
||||
getMaterials (type = 'all') {
|
||||
return CharMeta.getMaterials(this, type)
|
||||
}
|
||||
|
||||
// 获取生日
|
||||
get birthday () {
|
||||
let birth = this.birth
|
||||
@ -161,130 +150,6 @@ class Character extends Base {
|
||||
return `${birth[0]}月${birth[1]}日`
|
||||
}
|
||||
|
||||
// 获取角色character-img图片
|
||||
getCardImg (se = false, def = true) {
|
||||
if (this.name === '旅行者') {
|
||||
return CharImg.getCardImg(['空', '荧'], se, def)
|
||||
}
|
||||
return CharImg.getCardImg(this.name, se, def)
|
||||
}
|
||||
|
||||
getAvatarTalent (talent = {}, cons = 0, mode = 'original') {
|
||||
return CharTalent.getAvatarTalent(this.id, talent, cons, mode, this.talentCons)
|
||||
}
|
||||
|
||||
// 检查老婆类型
|
||||
checkWifeType (type) {
|
||||
return !!wifeMap[type][this.id]
|
||||
}
|
||||
|
||||
// 检查时装
|
||||
checkCostume (id) {
|
||||
let costume = this.meta?.costume || []
|
||||
return costume.includes(id * 1)
|
||||
}
|
||||
|
||||
// 判断是否为某种元素角色
|
||||
isElem (elem = '') {
|
||||
elem = elem.toLowerCase()
|
||||
return this.elem === elem || this.elemName === elem
|
||||
}
|
||||
|
||||
// 获取角色插画
|
||||
getImgs (costume = '') {
|
||||
if (!lodash.isArray(costume)) {
|
||||
costume = [costume, false]
|
||||
}
|
||||
let costumeCfg = [this.checkCostume(costume[0]) ? '2' : '', costume[1] || 'normal']
|
||||
|
||||
let cacheId = `costume${costume[0]}`
|
||||
if (!this._imgs) {
|
||||
this._imgs = {}
|
||||
}
|
||||
if (!this._imgs[cacheId]) {
|
||||
this._imgs[cacheId] = CharImg.getImgs(this.name, costumeCfg, this.isTraveler ? this.elem : '', this.source === 'amber' ? 'png' : 'webp')
|
||||
}
|
||||
let ret = this._imgs[cacheId]
|
||||
let nPath = `meta/character/${this.name}`
|
||||
if (costumeCfg[1] === 'super') {
|
||||
ret.splash0 = CharImg.getRandomImg(
|
||||
[`profile/super-character/${this.name}`, `profile/normal-character/${this.name}`],
|
||||
[`${nPath}/imgs/splash0.webp`, `${nPath}/imgs/splash${costumeCfg[0]}.webp`, `/${nPath}/imgs/splash.webp`]
|
||||
)
|
||||
} else {
|
||||
ret.splash0 = CharImg.getRandomImg(
|
||||
[`profile/normal-character/${this.name}`],
|
||||
[`${nPath}/imgs/splash${costumeCfg[0]}.webp`, `/${nPath}/imgs/splash.webp`]
|
||||
)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// 获取详情数据
|
||||
getDetail (elem = '') {
|
||||
if (this._detail) {
|
||||
return this._detail
|
||||
}
|
||||
if (this.isCustom) {
|
||||
return {}
|
||||
}
|
||||
const path = 'resources/meta/character'
|
||||
|
||||
try {
|
||||
if (this.isTraveler) {
|
||||
this._detail = Data.readJSON(`${path}/旅行者/${this.elem}/detail.json`)
|
||||
} else {
|
||||
this._detail = Data.readJSON(`${path}/${this.name}/detail.json`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
return this._detail
|
||||
}
|
||||
|
||||
// 设置旅行者数据
|
||||
// TODO:迁移至Avatar
|
||||
setTraveler (uid = '') {
|
||||
if (this.isTraveler && uid && uid.toString().length === 9) {
|
||||
Data.setCacheJSON(`miao:uid-traveler:${uid}`, {
|
||||
id: CharId.getTravelerId(this.id),
|
||||
elem: this.elem
|
||||
}, 3600 * 24 * 120)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取旅行者数据
|
||||
async getTraveler (uid) {
|
||||
if (this.isTraveler) {
|
||||
let tData = await Data.getCacheJSON(`miao:uid-traveler:${uid}`)
|
||||
return Character.get({
|
||||
id: CharId.getTravelerId(tData.id || this.id),
|
||||
elem: tData.elem || (this.elem !== 'multi' ? this.elem : 'anemo')
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
async checkAvatars (avatars, uid = '') {
|
||||
if (!this.isTraveler) {
|
||||
return this
|
||||
}
|
||||
if (lodash.isObject(avatars) && avatars.id) {
|
||||
avatars = [avatars]
|
||||
}
|
||||
for (let avatar of avatars) {
|
||||
if (CharId.isTraveler(avatar.id)) {
|
||||
let char = Character.get({
|
||||
id: avatar.id,
|
||||
elem: (avatar.elem || avatar.element || 'anemo').toLowerCase()
|
||||
})
|
||||
char.setTraveler(uid)
|
||||
return char
|
||||
}
|
||||
}
|
||||
return await this.getTraveler(uid)
|
||||
}
|
||||
|
||||
// 基于角色名获取Character
|
||||
static get (val) {
|
||||
let id = CharId.getId(val, Character.gsCfg)
|
||||
@ -307,30 +172,93 @@ class Character extends Base {
|
||||
})
|
||||
}
|
||||
|
||||
// 基于角色名获取Character
|
||||
// 当获取角色为旅行者时,会考虑当前uid的账号情况返回对应旅行者
|
||||
static async getAvatar (name, uid) {
|
||||
let char = Character.get(name)
|
||||
return await char.getTraveler(uid)
|
||||
}
|
||||
|
||||
// 获取别名数据
|
||||
// TODO:待废弃
|
||||
static getAbbr () {
|
||||
return abbrMap
|
||||
}
|
||||
|
||||
// 检查wife类型
|
||||
// TODO:待废弃
|
||||
static checkWifeType (charid, type) {
|
||||
return !!wifeMap[type][charid]
|
||||
}
|
||||
|
||||
// 获取排序ID
|
||||
static sortIds (arr) {
|
||||
return arr.sort((a, b) => (idSort[a] || 300) - (idSort[b] || 300))
|
||||
}
|
||||
|
||||
// 获取attr列表
|
||||
getAttrList () {
|
||||
let { meta } = this
|
||||
return CharMeta.getAttrList(meta.baseAttr, meta.growAttr, this.elemName)
|
||||
}
|
||||
|
||||
// 获取素材
|
||||
getMaterials (type = 'all') {
|
||||
return CharMeta.getMaterials(this, type)
|
||||
}
|
||||
|
||||
// 获取角色character-img图片
|
||||
getCardImg (se = false, def = true) {
|
||||
if (this.name === '旅行者') {
|
||||
return CharImg.getCardImg(['空', '荧'], se, def)
|
||||
}
|
||||
return CharImg.getCardImg(this.name, se, def)
|
||||
}
|
||||
|
||||
// 设置旅行者数据
|
||||
|
||||
getAvatarTalent (talent = {}, cons = 0, mode = 'original') {
|
||||
return CharTalent.getAvatarTalent(this.id, talent, cons, mode, this.talentCons)
|
||||
}
|
||||
|
||||
// 检查老婆类型
|
||||
checkWifeType (type) {
|
||||
return !!wifeMap[type][this.id]
|
||||
}
|
||||
|
||||
// 检查时装
|
||||
checkCostume (id) {
|
||||
let costume = this.meta?.costume || []
|
||||
return costume.includes(id * 1)
|
||||
}
|
||||
|
||||
// 判断是否为某种元素角色
|
||||
isElem (elem = '') {
|
||||
elem = elem.toLowerCase()
|
||||
return this.elem === elem || this.elemName === elem
|
||||
}
|
||||
|
||||
// 获取角色插画
|
||||
getImgs (costume = '') {
|
||||
if (lodash.isArray(costume)) {
|
||||
costume = costume[0]
|
||||
}
|
||||
let costumeIdx = this.checkCostume(costume) ? '2' : ''
|
||||
let cacheId = `costume${costumeIdx}`
|
||||
if (!this._imgs) {
|
||||
this._imgs = {}
|
||||
}
|
||||
if (!this._imgs[cacheId]) {
|
||||
this._imgs[cacheId] = CharImg.getImgs(this.name, costumeIdx, this.isTraveler ? this.elem : '', this.source === 'amber' ? 'png' : 'webp')
|
||||
}
|
||||
return this._imgs[cacheId]
|
||||
}
|
||||
|
||||
// 基于角色名获取Character
|
||||
|
||||
// 获取详情数据
|
||||
getDetail (elem = '') {
|
||||
if (this._detail) {
|
||||
return this._detail
|
||||
}
|
||||
if (this.isCustom) {
|
||||
return {}
|
||||
}
|
||||
const path = 'resources/meta/character'
|
||||
|
||||
try {
|
||||
if (this.isTraveler) {
|
||||
this._detail = Data.readJSON(`${path}/旅行者/${this.elem}/detail.json`)
|
||||
} else {
|
||||
this._detail = Data.readJSON(`${path}/${this.name}/detail.json`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
return this._detail
|
||||
}
|
||||
|
||||
// 获取伤害计算配置
|
||||
getCalcRule () {
|
||||
if (!this._calcRule && this._calcRule !== false) {
|
||||
|
@ -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
|
||||
|
307
models/Player.js
Normal file
307
models/Player.js
Normal file
@ -0,0 +1,307 @@
|
||||
/**
|
||||
* 用户数据文件
|
||||
* 数据存储在/data/userData/${uid}.json 下
|
||||
* 兼容处理面板户数及Mys数据
|
||||
*
|
||||
*/
|
||||
import lodash from 'lodash'
|
||||
import Base from './Base.js'
|
||||
import { Data } from '../components/index.js'
|
||||
import { AvatarData, ProfileRank, Character } from './index.js'
|
||||
|
||||
import MysAvatar from './player/MysAvatar.js'
|
||||
import Profile from './player/Profile.js'
|
||||
|
||||
Data.createDir('/data/userData', 'root')
|
||||
|
||||
export default class Player extends Base {
|
||||
constructor (uid) {
|
||||
super()
|
||||
uid = uid?._mys?.uid || uid?.uid || uid
|
||||
if (!uid) {
|
||||
return false
|
||||
}
|
||||
let cacheObj = this._getCache(`player:${uid}`)
|
||||
if (cacheObj) {
|
||||
return cacheObj
|
||||
}
|
||||
this.uid = uid
|
||||
this.reload()
|
||||
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
|
||||
let player = new Player(e?._mys?.uid || e.uid)
|
||||
player.e = e
|
||||
return player
|
||||
} else {
|
||||
return new Player(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取面板更新服务名
|
||||
static getProfileServName (uid) {
|
||||
let Serv = Profile.getServ(uid)
|
||||
return Serv.name
|
||||
}
|
||||
|
||||
static delByUid (uid) {
|
||||
let player = Player.create(uid)
|
||||
if (player) {
|
||||
player.del()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载json文件
|
||||
*/
|
||||
reload () {
|
||||
let data
|
||||
data = Data.readJSON(`/data/UserData/${this.uid}.json`, 'root')
|
||||
this.setBasicData(data)
|
||||
if (data.chars) {
|
||||
this.setAvatars(data.chars)
|
||||
// 暂时保留旧数据,防止异常情况
|
||||
this._chars = data.chars
|
||||
}
|
||||
this.setAvatars(data.avatars || [])
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存json文件
|
||||
*/
|
||||
save () {
|
||||
let ret = Data.getData(this, 'uid,name,level,word,face,card,sign,info,_info,_mys,_profile')
|
||||
ret.avatars = {}
|
||||
this.forEachAvatar((avatar) => {
|
||||
ret.avatars[avatar.id] = avatar.toJSON()
|
||||
})
|
||||
// 暂时保留旧数据,防止异常情况
|
||||
if (this._chars) {
|
||||
ret.chars = this._chars
|
||||
}
|
||||
Data.writeJSON(`/data/UserData/${this.uid}.json`, ret, '', 'root')
|
||||
}
|
||||
|
||||
del () {
|
||||
try {
|
||||
Data.delFile(`/data/UserData/${this.uid}.json`, 'root')
|
||||
ProfileRank.delUidInfo(this.uid)
|
||||
this._delCache()
|
||||
Bot.logger.mark(`【面板数据删除】${this.uid}本地文件数据已删除...`)
|
||||
} catch (e) {
|
||||
console.log('del error', e)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置玩家基础数据
|
||||
* @param ds
|
||||
*/
|
||||
setBasicData (ds) {
|
||||
this.name = ds.name || this.name || ''
|
||||
this.level = ds.level || this.level || ''
|
||||
this.word = ds.word || this.word || ''
|
||||
this.face = ds.face || this.face || ''
|
||||
this.card = ds.card || this.card || ''
|
||||
this.sign = ds.sign || this.sign || ''
|
||||
this.info = ds.info || this.info || false
|
||||
this._avatars = this._avatars || {}
|
||||
this._profile = ds._profile || this._profile
|
||||
this._mys = ds._mys || this._mys
|
||||
this._info = ds._info || this._info
|
||||
}
|
||||
|
||||
// 设置角色列表
|
||||
setAvatars (ds) {
|
||||
lodash.forEach(ds, (avatar) => {
|
||||
this.setAvatar(avatar)
|
||||
})
|
||||
}
|
||||
|
||||
// 设置角色数据
|
||||
setAvatar (ds, source = '') {
|
||||
let avatar = this.getAvatar(ds.id, true)
|
||||
avatar.setAvatar(ds, source)
|
||||
}
|
||||
|
||||
// 获取Avatar角色
|
||||
getAvatar (id, create = false) {
|
||||
let char = Character.get(id)
|
||||
let avatars = this._avatars
|
||||
// 兼容处理旅行者的情况
|
||||
if (char.isTraveler && !create) {
|
||||
id = avatars['10000005'] ? 10000005 : 10000007
|
||||
}
|
||||
if (!avatars[id] && create) {
|
||||
avatars[id] = AvatarData.create({ id })
|
||||
}
|
||||
return avatars[id] || false
|
||||
}
|
||||
|
||||
// 异步循环角色
|
||||
async forEachAvatarAsync (fn) {
|
||||
for (let id in this._avatars) {
|
||||
let ret = await fn(this._avatars[id], id)
|
||||
if (ret === false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 循环Avatar
|
||||
forEachAvatar (fn) {
|
||||
for (let id in this._avatars) {
|
||||
let avatar = this._avatars[id]
|
||||
if (avatar && avatar.hasData) {
|
||||
let ret = fn(this._avatars[id])
|
||||
if (ret === false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有Avatar数据
|
||||
getAvatarData (ids = '') {
|
||||
let ret = {}
|
||||
if (!ids) {
|
||||
this.forEachAvatar((avatar) => {
|
||||
ret[avatar.id] = avatar.getDetail()
|
||||
})
|
||||
} else {
|
||||
lodash.forEach(ids, (id) => {
|
||||
let avatar = this.getAvatar(id)
|
||||
if (avatar) {
|
||||
ret[id] = avatar.getDetail()
|
||||
}
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// 获取指定角色的面板数据
|
||||
getProfile (id) {
|
||||
let avatar = this.getAvatar(id)
|
||||
return avatar ? avatar.getProfile() : false
|
||||
}
|
||||
|
||||
// 获取所有面板数据
|
||||
getProfiles () {
|
||||
let ret = {}
|
||||
lodash.forEach(this._avatars, (avatar) => {
|
||||
let profile = avatar.getProfile()
|
||||
if (profile) {
|
||||
ret[profile.id] = profile
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
getUpdateTime () {
|
||||
let ret = {}
|
||||
if (this._profile) {
|
||||
ret.profile = MysAvatar.getDate(this._profile)
|
||||
}
|
||||
if (this._mys) {
|
||||
ret.mys = MysAvatar.getDate(this._mys)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
getInfo () {
|
||||
return MysAvatar.getInfo(this)
|
||||
}
|
||||
|
||||
// 更新面板
|
||||
async refreshProfile (force = 2) {
|
||||
return await Profile.refreshProfile(this, force)
|
||||
}
|
||||
|
||||
// 更新米游社数据
|
||||
/**
|
||||
* 更新米游社数据
|
||||
* @param force: 0:不强制,长超时时间 1:短超时时间 2:无视缓存,强制刷新
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async refreshMysDetail (force = 0) {
|
||||
return MysAvatar.refreshMysDetail(this, force)
|
||||
}
|
||||
|
||||
async refreshMysInfo (force = 0) {
|
||||
return await MysAvatar.refreshMysInfo(this, force)
|
||||
}
|
||||
|
||||
// 通过已有的Mys CharData更新
|
||||
setMysCharData (charData) {
|
||||
MysAvatar.setMysCharData(this, charData)
|
||||
}
|
||||
|
||||
// 使用MysApi刷新指定角色的天赋信息
|
||||
async refreshTalent (ids = '', force = 0) {
|
||||
return await MysAvatar.refreshTalent(this, ids, force)
|
||||
}
|
||||
|
||||
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
|
||||
if (cfg.rank === true && e && e.group_id) {
|
||||
rank = await ProfileRank.create({ group: e.group_id, uid: this.uid, qq: e.user_id })
|
||||
}
|
||||
|
||||
let avatarRet = {}
|
||||
this.forEachAvatar((avatar) => {
|
||||
let { talent } = avatar
|
||||
let ds = avatar.getDetail()
|
||||
ds.aeq = talent?.a?.original + talent?.e?.original + talent?.q?.original || 3
|
||||
avatarRet[ds.id] = ds
|
||||
|
||||
let profile = avatar.getProfile()
|
||||
if (profile) {
|
||||
let mark = profile.getArtisMark(false)
|
||||
ds.artisMark = Data.getData(mark, 'mark,markClass,names')
|
||||
if (rank) {
|
||||
rank.getRank(profile)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (cfg.retType !== 'array') {
|
||||
return avatarRet
|
||||
}
|
||||
avatarRet = lodash.values(avatarRet)
|
||||
if (cfg.sort) {
|
||||
let sortKey = 'level,star,aeq,cons,weapon.level,weapon.star,weapon.affix,fetter'.split(',')
|
||||
avatarRet = lodash.orderBy(avatarRet, sortKey)
|
||||
avatarRet = avatarRet.reverse()
|
||||
}
|
||||
return avatarRet
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import Base from './Base.js'
|
||||
|
||||
export default class Profile extends Base {
|
||||
constructor (uid) {
|
||||
super()
|
||||
if (!uid) {
|
||||
return false
|
||||
}
|
||||
let cacheObj = this._getCache(`profile:${uid}`)
|
||||
if (cacheObj) {
|
||||
return cacheObj
|
||||
}
|
||||
this.uid = uid
|
||||
return this._cache()
|
||||
}
|
||||
|
||||
getProfileData () {
|
||||
|
||||
}
|
||||
|
||||
static create (uid) {
|
||||
let profile = new Profile(uid)
|
||||
return profile
|
||||
}
|
||||
|
||||
static get (uid, id) {
|
||||
let profile = Profile.create(uid)
|
||||
return profile.getProfileData(id)
|
||||
}
|
||||
}
|
@ -2,187 +2,27 @@
|
||||
* 面板圣遗物
|
||||
*/
|
||||
import lodash from 'lodash'
|
||||
import Base from './Base.js'
|
||||
import AvatarArtis from './AvatarArtis.js'
|
||||
import { Artifact, ArtifactSet, Character } from './index.js'
|
||||
import { Format, Data } from '../components/index.js'
|
||||
import ArtisMark from './profile-lib/ArtisMark.js'
|
||||
import { attrMap, attrValue } from '../resources/meta/artifact/index.js'
|
||||
import CharArtis from './profile-lib/CharArtis.js'
|
||||
import { Format } from '../components/index.js'
|
||||
import ArtisMark from './profile/ArtisMark.js'
|
||||
import { attrMap } from '../resources/meta/artifact/index.js'
|
||||
import CharArtis from './profile/CharArtis.js'
|
||||
|
||||
export default class ProfileArtis extends Base {
|
||||
export default class ProfileArtis extends AvatarArtis {
|
||||
constructor (charid = 0, elem = '') {
|
||||
super()
|
||||
this.charid = charid
|
||||
super(charid)
|
||||
this.elem = elem
|
||||
this.artis = {}
|
||||
}
|
||||
|
||||
setProfile (profile, artis) {
|
||||
this.profile = profile
|
||||
this.elem = profile.elem || profile.char?.elem
|
||||
if (artis) {
|
||||
this.setArtisSet(artis)
|
||||
this.setArtisData(artis, true)
|
||||
}
|
||||
}
|
||||
|
||||
setArtisSet (ds) {
|
||||
for (let key in ds) {
|
||||
this.setArtis(key, ds[key] || {})
|
||||
}
|
||||
}
|
||||
|
||||
setArtis (idx = 1, ds = {}) {
|
||||
idx = idx.toString().replace('arti', '')
|
||||
let ret = {}
|
||||
ret.name = ds.name || ArtifactSet.getArtiNameBySet(ds.set, idx) || ''
|
||||
ret.set = ds.set || Artifact.getSetNameByArti(ret.name) || ''
|
||||
ret.level = ds.level || 1
|
||||
ret.main = ArtisMark.formatAttr(ds.main || {})
|
||||
ret.attrs = []
|
||||
for (let attrIdx in ds.attrs || []) {
|
||||
if (ds.attrs[attrIdx]) {
|
||||
ret.attrs.push(ArtisMark.formatAttr(ds.attrs[attrIdx]))
|
||||
}
|
||||
}
|
||||
this.artis[idx] = ret
|
||||
}
|
||||
|
||||
forEach (fn) {
|
||||
lodash.forEach(this.artis, (ds, idx) => {
|
||||
if (ds.name) {
|
||||
fn(ds, idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_get (key) {
|
||||
let artis = this.artis
|
||||
switch (key) {
|
||||
case 'length':
|
||||
return lodash.keys(artis).length
|
||||
}
|
||||
if (artis[key]) {
|
||||
return artis[key]
|
||||
}
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return this.getData('1,2,3,4,5')
|
||||
}
|
||||
|
||||
get sets () {
|
||||
return this.getSetData().sets || {}
|
||||
}
|
||||
|
||||
get names () {
|
||||
return this.getSetData().names || []
|
||||
}
|
||||
|
||||
get hasArtis () {
|
||||
return !lodash.isEmpty(this.artis)
|
||||
}
|
||||
|
||||
mainAttr (idx = '') {
|
||||
if (!idx) {
|
||||
let ret = {}
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
ret[i] = this.mainAttr(i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
let main = this.artis[idx]?.main
|
||||
if (!main) {
|
||||
return ''
|
||||
}
|
||||
return main.key || ''
|
||||
}
|
||||
|
||||
is (check, pos = '') {
|
||||
if (pos) {
|
||||
return this.isAttr(check, pos)
|
||||
}
|
||||
let sets = this.getSetData()?.abbrs || []
|
||||
let ret = false
|
||||
Data.eachStr(check, (s) => {
|
||||
if (sets.includes(s)) {
|
||||
ret = true
|
||||
return false
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
isAttr (attr, pos = '3,4,5') {
|
||||
let mainAttr = this.mainAttr()
|
||||
let check = true
|
||||
Data.eachStr(pos.toString(), (p) => {
|
||||
let attrs = attr.split(',')
|
||||
if (!attrs.includes(mainAttr[p]) && (p === '4' && !attrs.includes('dmg') && Format.isElem(mainAttr[p]))) {
|
||||
check = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
return check
|
||||
}
|
||||
|
||||
// 获取圣遗物数据
|
||||
getArtisData () {
|
||||
let ret = {}
|
||||
this.forEach((ds, idx) => {
|
||||
let arti = Artifact.get(ds.name)
|
||||
ret[idx] = {
|
||||
...ds,
|
||||
name: arti.name,
|
||||
img: arti.img
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取圣遗物套装数据
|
||||
* @returns {*|{imgs: *[], names: *[], sets: {}, abbrs: *[], sName: string, name: (string|*)}}
|
||||
* sets: 套装名:2/4
|
||||
* names: [套装名]
|
||||
* imgs: [img]
|
||||
* abbrs:[别名]
|
||||
* name: '组合名字', 若为4件套会使用套装完整名
|
||||
* sName: '简写名字',若为4件套也会使用简写
|
||||
*/
|
||||
getSetData () {
|
||||
if (this._setData) {
|
||||
return this._setData
|
||||
}
|
||||
let setCount = {}
|
||||
this.forEach((arti, idx) => {
|
||||
setCount[arti.set] = (setCount[arti.set] || 0) + 1
|
||||
})
|
||||
let sets = {}
|
||||
let names = []
|
||||
let imgs = []
|
||||
let abbrs = []
|
||||
let abbrs2 = []
|
||||
for (let set in setCount) {
|
||||
if (setCount[set] >= 2) {
|
||||
let count = setCount[set] >= 4 ? 4 : 2
|
||||
sets[set] = count
|
||||
let artiSet = ArtifactSet.get(set)
|
||||
names.push(artiSet.name)
|
||||
imgs.push(artiSet.img)
|
||||
abbrs.push(artiSet.abbr + count)
|
||||
abbrs2.push(artiSet.name + count)
|
||||
}
|
||||
}
|
||||
this._setData = {
|
||||
sets,
|
||||
names,
|
||||
imgs,
|
||||
abbrs: [...abbrs, ...abbrs2],
|
||||
name: (abbrs.length > 1 || abbrs2[0]?.length > 7) ? abbrs.join('+') : abbrs2[0],
|
||||
sName: abbrs.join('+')
|
||||
}
|
||||
return this._setData
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色配置
|
||||
* @returns {{classTitle: *, weight: *, posMaxMark: {}, mark: {}, attrs: {}}}
|
||||
@ -202,13 +42,13 @@ export default class ProfileArtis extends Base {
|
||||
...attr,
|
||||
weight,
|
||||
fixWeight: weight,
|
||||
mark: weight / attrValue[key]
|
||||
mark: weight / attr.value
|
||||
}
|
||||
if (!k) {
|
||||
ret.mark = weight / attrValue[key]
|
||||
ret.mark = weight / attr.value
|
||||
} else {
|
||||
let plus = k === 'atk' ? 520 : 0
|
||||
ret.mark = weight / attrValue[k] / (baseAttr[k] + plus) * 100
|
||||
ret.mark = weight / attrMap[k].value / (baseAttr[k] + plus) * 100
|
||||
ret.fixWeight = weight * attr.value / attrMap[k].value / (baseAttr[k] + plus) * 100
|
||||
}
|
||||
attrs[key] = ret
|
||||
@ -280,24 +120,4 @@ export default class ProfileArtis extends Base {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
static _eachArtisSet (sets, fn) {
|
||||
lodash.forEach(sets || [], (v, k) => {
|
||||
let artisSet = ArtifactSet.get(k)
|
||||
if (artisSet) {
|
||||
if (v >= 4) {
|
||||
fn(artisSet, 2)
|
||||
}
|
||||
fn(artisSet, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
eachArtisSet (fn) {
|
||||
ProfileArtis._eachArtisSet(this.sets, fn)
|
||||
}
|
||||
|
||||
static getArtisKeyTitle () {
|
||||
return ArtisMark.getKeyTitleMap()
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +1,60 @@
|
||||
import lodash from 'lodash'
|
||||
import Base from './Base.js'
|
||||
import moment from 'moment'
|
||||
import AvatarData from './AvatarData.js'
|
||||
import { Data } from '../components/index.js'
|
||||
import { Character, ProfileArtis, ProfileDmg } from './index.js'
|
||||
import AttrCalc from './profile-lib/AttrCalc.js'
|
||||
import { ProfileArtis, ProfileDmg } from './index.js'
|
||||
import AttrCalc from './profile/AttrCalc.js'
|
||||
import CharImg from './character/CharImg.js'
|
||||
|
||||
export default class ProfileData extends Base {
|
||||
constructor (ds = {}, uid, attrCalc = true) {
|
||||
super()
|
||||
let char = Character.get({ id: ds.id, elem: ds.elem })
|
||||
if (!char) {
|
||||
return false
|
||||
}
|
||||
this.id = char.id
|
||||
this.char = char
|
||||
this.uid = uid || ''
|
||||
this.setBasic(ds)
|
||||
ds.attr && this.setAttr(ds.attr)
|
||||
ds.weapon && this.setWeapon(ds.weapon)
|
||||
ds.talent && this.setTalent(ds.talent)
|
||||
this.artis = new ProfileArtis(this.id, this.elem)
|
||||
ds.artis && this.setArtis(ds.artis)
|
||||
if (attrCalc && this.hasData) {
|
||||
export default class ProfileData extends AvatarData {
|
||||
constructor (ds = {}, calc = true) {
|
||||
super(ds)
|
||||
if (calc) {
|
||||
this.calcAttr()
|
||||
}
|
||||
}
|
||||
|
||||
static create (ds, uid) {
|
||||
let profile = new ProfileData(ds, uid)
|
||||
// 判断当前profileData是否具有有效数据
|
||||
get hasData () {
|
||||
return this.isProfile
|
||||
}
|
||||
|
||||
get costumeSplash () {
|
||||
let costume = this._costume
|
||||
costume = this.char.checkCostume(costume) ? '2' : ''
|
||||
|
||||
let nPath = `meta/character/${this.name}`
|
||||
let isSuper = false
|
||||
let talent = this.talent ? lodash.map(this.talent, (ds) => ds.original).join('') : ''
|
||||
if (this.cons === 6 || ['ACE', 'ACE²'].includes(this.artis?.markClass) || talent === '101010') {
|
||||
isSuper = true
|
||||
}
|
||||
if (isSuper) {
|
||||
return CharImg.getRandomImg(
|
||||
[`profile/super-character/${this.name}`, `profile/normal-character/${this.name}`],
|
||||
[`${nPath}/imgs/splash0.webp`, `${nPath}/imgs/splash${costume}.webp`, `/${nPath}/imgs/splash.webp`]
|
||||
)
|
||||
} else {
|
||||
return CharImg.getRandomImg(
|
||||
[`profile/normal-character/${this.name}`],
|
||||
[`${nPath}/imgs/splash${costume}.webp`, `/${nPath}/imgs/splash.webp`]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
get hasDmg () {
|
||||
return this.hasData && !!ProfileDmg.dmgRulePath(this.name)
|
||||
}
|
||||
|
||||
static create (ds) {
|
||||
let profile = new ProfileData(ds)
|
||||
if (!profile) {
|
||||
return false
|
||||
}
|
||||
return profile
|
||||
}
|
||||
|
||||
calcAttr () {
|
||||
this._attr = AttrCalc.create(this)
|
||||
this.attr = this._attr.calc()
|
||||
}
|
||||
|
||||
setBasic (ds = {}) {
|
||||
this.level = ds.lv || ds.level || 1
|
||||
this.cons = ds.cons || 0
|
||||
this.fetter = ds.fetter || 0
|
||||
this._costume = ds.costume || 0
|
||||
this.elem = ds.elem || this.char.elem || ''
|
||||
this.dataSource = ds.dataSource || 'enka'
|
||||
this.promote = lodash.isUndefined(ds.promote) ? AttrCalc.calcPromote(this.level) : (ds.promote || 0)
|
||||
this._time = ds._time || ds.updateTime || new Date() * 1
|
||||
initArtis () {
|
||||
this.artis = new ProfileArtis(this.id, this.elem)
|
||||
}
|
||||
|
||||
setAttr (ds) {
|
||||
@ -60,104 +67,13 @@ export default class ProfileData extends Base {
|
||||
})
|
||||
}
|
||||
|
||||
setWeapon (ds = {}) {
|
||||
this.weapon = {
|
||||
name: ds.name,
|
||||
star: ds.rank || ds.star || 1,
|
||||
level: ds.level || ds.lv || 1,
|
||||
promote: lodash.isUndefined(ds.promote) ? AttrCalc.calcPromote(ds.level || ds.lv || 1) : (ds.promote || 0),
|
||||
affix: ds.affix
|
||||
}
|
||||
let w = this.weapon
|
||||
if (w.level < 20) {
|
||||
w.promote = 0
|
||||
}
|
||||
calcAttr () {
|
||||
this._attr = AttrCalc.create(this)
|
||||
this.attr = this._attr.calc()
|
||||
}
|
||||
|
||||
setArtis (ds = false) {
|
||||
this.artis.setProfile(this, ds)
|
||||
}
|
||||
|
||||
setTalent (ds = {}, mode = 'original') {
|
||||
this.talent = this.char.getAvatarTalent(ds, this.cons, mode)
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this.char?.name || ''
|
||||
}
|
||||
|
||||
// 判断当前profileData是否具有有效数据
|
||||
get hasData () {
|
||||
// 检查数据源
|
||||
if (!this.dataSource || !['enka', 'change', 'miao'].includes(this.dataSource)) {
|
||||
return false
|
||||
}
|
||||
// 检查属性
|
||||
if (!this.weapon || !this.talent || !this.artis) {
|
||||
return false
|
||||
}
|
||||
// 检查旅行者
|
||||
if (['空', '荧'].includes(this.name)) {
|
||||
return !!this.elem
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 判断当前profileData是否具备有效圣遗物信息
|
||||
hasArtis () {
|
||||
return this.hasData && this.artis.length > 0
|
||||
}
|
||||
|
||||
get costume () {
|
||||
let costume = this._costume
|
||||
if (lodash.isArray(costume)) {
|
||||
costume = costume[0]
|
||||
}
|
||||
let talent = this.talent ? lodash.map(this.talent, (ds) => ds.original).join('') : ''
|
||||
if (this.cons === 6 || ['ACE', 'ACE²'].includes(this.artis?.markClass) || talent === '101010') {
|
||||
return [costume, 'super']
|
||||
}
|
||||
return [costume, 'normal']
|
||||
}
|
||||
|
||||
get originalTalent () {
|
||||
return lodash.mapValues(this.talent, (ds) => ds.original)
|
||||
}
|
||||
|
||||
// toJSON 供保存使用
|
||||
toJSON () {
|
||||
let ret = {
|
||||
...this.getData('id,name,elem,level,promote,fetter,costume,cons,talent,attr,weapon,artis,dataSource,_time')
|
||||
}
|
||||
ret.talent = lodash.mapValues(this.talent, (ds) => ds.original)
|
||||
ret.costume = this.costume[0] || 0
|
||||
return ret
|
||||
}
|
||||
|
||||
get updateTime () {
|
||||
let time = this._time
|
||||
if (!time) {
|
||||
return ''
|
||||
}
|
||||
if (lodash.isString(time)) {
|
||||
return moment(time).format('MM-DD HH:mm')
|
||||
}
|
||||
if (lodash.isNumber(time)) {
|
||||
return moment(new Date(time)).format('MM-DD HH:mm')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
get dataSourceName () {
|
||||
return {
|
||||
enka: 'Enka.Network',
|
||||
miao: '喵喵Api',
|
||||
input: 'Input'
|
||||
}[this.dataSource] || 'Enka.NetWork'
|
||||
}
|
||||
|
||||
get isProfile () {
|
||||
return true
|
||||
this.artis?.setProfile(this, ds.artis?.artis || ds.artis || ds)
|
||||
}
|
||||
|
||||
// 获取当前profileData的圣遗物评分,withDetail=false仅返回简略信息
|
||||
@ -168,10 +84,6 @@ export default class ProfileData extends Base {
|
||||
return {}
|
||||
}
|
||||
|
||||
get hasDmg () {
|
||||
return this.hasData && !!ProfileDmg.dmgRulePath(this.name)
|
||||
}
|
||||
|
||||
// 计算当前profileData的伤害信息
|
||||
async calcDmg ({ enemyLv = 91, mode = 'profile', dmgIdx = 0 }) {
|
||||
if (!this.dmg) {
|
||||
|
@ -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 {
|
||||
|
@ -19,99 +19,6 @@ export default class ProfileRank {
|
||||
return rank
|
||||
}
|
||||
|
||||
key (profile, type) {
|
||||
return `miao:rank:${this.groupId}:${type}:${profile.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取排行信息
|
||||
* @param profile
|
||||
* @param force
|
||||
* @returns {Promise<{}|boolean>}
|
||||
*/
|
||||
async getRank (profile, force = false) {
|
||||
if (!profile || !this.groupId || !this.allowRank || !profile.hasData) {
|
||||
return false
|
||||
}
|
||||
let ret = {}
|
||||
for (let typeKey of ['mark', 'dmg']) {
|
||||
let typeRank = await this.getTypeRank(profile, typeKey, force)
|
||||
ret[typeKey] = typeRank
|
||||
if (!ret.rank || ret.rank >= typeRank.rank) {
|
||||
ret.rank = typeRank.rank
|
||||
ret.rankType = typeKey
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
async getTypeRank (profile, type, force) {
|
||||
if (!profile || !profile.hasData || !type) {
|
||||
return false
|
||||
}
|
||||
if (type === 'dmg' && !profile.hasDmg) {
|
||||
return false
|
||||
}
|
||||
const typeKey = this.key(profile, type)
|
||||
let value
|
||||
let rank
|
||||
if (force) {
|
||||
value = await this.getTypeValue(profile, type)
|
||||
} else {
|
||||
rank = await redis.zRevRank(typeKey, this.uid)
|
||||
if (!lodash.isNumber(rank)) {
|
||||
value = await this.getTypeValue(profile, type)
|
||||
}
|
||||
}
|
||||
if (value && !lodash.isUndefined(value.score)) {
|
||||
await redis.zAdd(typeKey, { score: value.score, value: this.uid })
|
||||
}
|
||||
if (!lodash.isNumber(rank)) {
|
||||
rank = await redis.zRevRank(typeKey, this.uid)
|
||||
}
|
||||
if (rank === null) {
|
||||
rank = 99
|
||||
}
|
||||
if (force) {
|
||||
return {
|
||||
rank: rank + 1,
|
||||
value: value.score,
|
||||
data: value.data
|
||||
}
|
||||
}
|
||||
return {
|
||||
rank: rank + 1
|
||||
}
|
||||
}
|
||||
|
||||
async getTypeValue (profile, type) {
|
||||
if (!profile || !profile.hasData) {
|
||||
return false
|
||||
}
|
||||
if (type === 'mark') {
|
||||
if (!profile?.artis?.hasArtis) {
|
||||
return false
|
||||
}
|
||||
let mark = profile.getArtisMark(false)
|
||||
if (mark && mark._mark) {
|
||||
return {
|
||||
score: mark._mark * 1,
|
||||
data: mark
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type === 'dmg' && profile.hasDmg) {
|
||||
let dmg = await profile.calcDmg({ mode: 'single' })
|
||||
if (dmg && dmg.avg) {
|
||||
return {
|
||||
score: dmg.avg,
|
||||
data: dmg
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群排行UID
|
||||
* @param groupId
|
||||
@ -258,6 +165,20 @@ export default class ProfileRank {
|
||||
await redis.set(`miao:rank:uid-info:${uid}`, JSON.stringify(data), { EX: 3600 * 24 * 365 })
|
||||
}
|
||||
|
||||
static async delUidInfo (uid) {
|
||||
let keys = await redis.keys('miao:rank:*')
|
||||
uid = uid + ''
|
||||
if (!/\d{9}/.test(uid)) {
|
||||
return false
|
||||
}
|
||||
for (let key of keys) {
|
||||
let charRet = /^miao:rank:\d+:(?:mark|dmg):(\d{8})$/.exec(key)
|
||||
if (charRet) {
|
||||
await redis.zRem(key, uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async getUidInfo (uid) {
|
||||
try {
|
||||
let data = await redis.get(`miao:rank:uid-info:${uid}`)
|
||||
@ -304,4 +225,97 @@ export default class ProfileRank {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
key (profile, type) {
|
||||
return `miao:rank:${this.groupId}:${type}:${profile.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取排行信息
|
||||
* @param profile
|
||||
* @param force
|
||||
* @returns {Promise<{}|boolean>}
|
||||
*/
|
||||
async getRank (profile, force = false) {
|
||||
if (!profile || !this.groupId || !this.allowRank || !profile.hasData) {
|
||||
return false
|
||||
}
|
||||
let ret = {}
|
||||
for (let typeKey of ['mark', 'dmg']) {
|
||||
let typeRank = await this.getTypeRank(profile, typeKey, force)
|
||||
ret[typeKey] = typeRank
|
||||
if (!ret.rank || ret.rank >= typeRank.rank) {
|
||||
ret.rank = typeRank.rank
|
||||
ret.rankType = typeKey
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
async getTypeRank (profile, type, force) {
|
||||
if (!profile || !profile.hasData || !type) {
|
||||
return false
|
||||
}
|
||||
if (type === 'dmg' && !profile.hasDmg) {
|
||||
return false
|
||||
}
|
||||
const typeKey = this.key(profile, type)
|
||||
let value
|
||||
let rank
|
||||
if (force) {
|
||||
value = await this.getTypeValue(profile, type)
|
||||
} else {
|
||||
rank = await redis.zRevRank(typeKey, this.uid)
|
||||
if (!lodash.isNumber(rank)) {
|
||||
value = await this.getTypeValue(profile, type)
|
||||
}
|
||||
}
|
||||
if (value && !lodash.isUndefined(value.score)) {
|
||||
await redis.zAdd(typeKey, { score: value.score, value: this.uid })
|
||||
}
|
||||
if (!lodash.isNumber(rank)) {
|
||||
rank = await redis.zRevRank(typeKey, this.uid)
|
||||
}
|
||||
if (rank === null) {
|
||||
rank = 99
|
||||
}
|
||||
if (force) {
|
||||
return {
|
||||
rank: rank + 1,
|
||||
value: value.score,
|
||||
data: value.data
|
||||
}
|
||||
}
|
||||
return {
|
||||
rank: rank + 1
|
||||
}
|
||||
}
|
||||
|
||||
async getTypeValue (profile, type) {
|
||||
if (!profile || !profile.hasData) {
|
||||
return false
|
||||
}
|
||||
if (type === 'mark') {
|
||||
if (!profile?.artis?.hasArtis) {
|
||||
return false
|
||||
}
|
||||
let mark = profile.getArtisMark(false)
|
||||
if (mark && mark._mark) {
|
||||
return {
|
||||
score: mark._mark * 1,
|
||||
data: mark
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type === 'dmg' && profile.hasDmg) {
|
||||
let dmg = await profile.calcDmg({ mode: 'single' })
|
||||
if (dmg && dmg.avg) {
|
||||
return {
|
||||
score: dmg.avg,
|
||||
data: dmg
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,18 @@
|
||||
import lodash from 'lodash'
|
||||
import Base from './Base.js'
|
||||
import ProfileServ from './ProfileServ.js'
|
||||
import fetch from 'node-fetch'
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
export default class ProfileReq extends Base {
|
||||
constructor ({ e, uid }) {
|
||||
constructor (e) {
|
||||
super()
|
||||
this.e = e
|
||||
this.uid = uid
|
||||
this.uid = e.uid
|
||||
}
|
||||
|
||||
static create (e) {
|
||||
if (!e || !e.uid) {
|
||||
return false
|
||||
}
|
||||
return new ProfileReq(e)
|
||||
}
|
||||
|
||||
async setCd (seconds = 60) {
|
||||
@ -32,14 +33,14 @@ export default class ProfileReq extends Base {
|
||||
}
|
||||
|
||||
err (msg = '', cd = 0) {
|
||||
let serv = this.serv
|
||||
let extra = serv.name ? `当前面板服务${serv.name},` : ''
|
||||
const msgs = {
|
||||
error: '请求失败,可能是面板服务升级维护或遇到故障,请稍后重试...',
|
||||
error: `UID${this.uid}更新面板失败,${extra}\n可能是面板服务维护中,请稍后重试...`,
|
||||
empty: '请将角色放置在【游戏内】角色展柜,并打开【显示详情】,等待5分钟重新获取面板'
|
||||
}
|
||||
msg = msgs[msg] || msg
|
||||
if (msg) {
|
||||
this.e.reply(msg)
|
||||
}
|
||||
this.msg(msg)
|
||||
// 设置CD
|
||||
if (cd) {
|
||||
this.setCd(cd)
|
||||
@ -48,21 +49,23 @@ export default class ProfileReq extends Base {
|
||||
}
|
||||
|
||||
msg (msg) {
|
||||
this.e.reply(msg)
|
||||
let e = this.e
|
||||
if (msg && !e._isReplyed) {
|
||||
e.reply(msg)
|
||||
e._isReplyed = true
|
||||
}
|
||||
}
|
||||
|
||||
async request () {
|
||||
let Serv = ProfileReq.getServ(this.uid)
|
||||
let reqParam = await Serv.getReqParam(this.uid)
|
||||
|
||||
async requestProfile (player, serv) {
|
||||
this.serv = serv
|
||||
let reqParam = await serv.getReqParam(this.uid)
|
||||
let cdTime = await this.inCd()
|
||||
if (cdTime) {
|
||||
if (cdTime && !process.argv.includes('web-debug')) {
|
||||
return this.err(`请求过快,请${cdTime}秒后重试..`)
|
||||
}
|
||||
await this.setCd(20)
|
||||
this.msg(`开始获取uid:${this.uid}的数据,可能会需要一定时间~`)
|
||||
await sleep(100)
|
||||
// 发起请求
|
||||
logger.mark(`面板请求UID:${this.uid},面板服务:${serv.name}...`)
|
||||
let data = {}
|
||||
try {
|
||||
let params = reqParam.params || {}
|
||||
@ -83,34 +86,20 @@ export default class ProfileReq extends Base {
|
||||
console.log('面板请求错误', e)
|
||||
data = {}
|
||||
}
|
||||
data = await Serv.response(data, this)
|
||||
data = await serv.response(data, this)
|
||||
// 设置CD
|
||||
cdTime = Serv.getCdTime(data)
|
||||
cdTime = serv.getCdTime(data)
|
||||
if (cdTime) {
|
||||
await this.setCd(cdTime)
|
||||
}
|
||||
if (data === false) {
|
||||
return false
|
||||
}
|
||||
let userData = Serv.getUserData(data)
|
||||
let profiles = Serv.getProfileData(data)
|
||||
cdTime = Serv.getCdTime(data)
|
||||
serv.updatePlayer(player, data)
|
||||
cdTime = serv.getCdTime(data)
|
||||
if (cdTime) {
|
||||
await this.setCd(cdTime)
|
||||
}
|
||||
return lodash.extend({
|
||||
uid: this.uid,
|
||||
chars: profiles
|
||||
}, userData)
|
||||
return player
|
||||
}
|
||||
}
|
||||
|
||||
ProfileReq.serv = {}
|
||||
ProfileReq.regServ = function (serv) {
|
||||
for (let key in serv) {
|
||||
ProfileReq.serv[key] = serv[key]
|
||||
}
|
||||
}
|
||||
ProfileReq.getServ = function (uid) {
|
||||
return ProfileServ.getServ({ uid, serv: ProfileReq.serv })
|
||||
}
|
||||
|
@ -75,22 +75,7 @@ export default class ProfileServ extends Base {
|
||||
return Math.max(cdTime, this.execFn('cdTime', [data], 60))
|
||||
}
|
||||
|
||||
getUserData (data) {
|
||||
return this.execFn('userData', [data], {})
|
||||
}
|
||||
|
||||
getProfileData (data) {
|
||||
return this.execFn('profileData', [data], {})
|
||||
updatePlayer (player, data) {
|
||||
return this.execFn('updatePlayer', [player, data], {})
|
||||
}
|
||||
}
|
||||
|
||||
ProfileServ.getServ = function ({ uid, serv }) {
|
||||
let { Miao, Enka } = serv
|
||||
let token = diyCfg?.miaoApi?.token
|
||||
let qq = diyCfg?.miaoApi?.qq
|
||||
|
||||
if (qq && token && token.length === 32 && !/^test/.test(token) && Cfg.get('profileServ') === 1) {
|
||||
return Miao
|
||||
}
|
||||
return Enka
|
||||
}
|
||||
|
@ -82,10 +82,8 @@ const CharImg = {
|
||||
},
|
||||
|
||||
// 获取角色的图像资源数据
|
||||
getImgs (name, costumeCfg = '', travelerElem = '', fileType = 'webp') {
|
||||
if (!lodash.isArray(costumeCfg)) {
|
||||
costumeCfg = [costumeCfg, 'normal']
|
||||
}
|
||||
getImgs (name, costumeIdx = '', travelerElem = '', fileType = 'webp') {
|
||||
costumeIdx = costumeIdx === '2' ? '2' : ''
|
||||
let imgs = {}
|
||||
if (!['空', '荧', '旅行者'].includes(name)) {
|
||||
travelerElem = ''
|
||||
@ -102,10 +100,10 @@ const CharImg = {
|
||||
let tAdd = (key, path) => {
|
||||
imgs[key] = `${travelerElem ? tPath : nPath}${path}.${fileType}`
|
||||
}
|
||||
add('face', 'imgs/face', `imgs/face${costumeCfg[0]}`)
|
||||
add('side', 'imgs/side', `imgs/side${costumeCfg[0]}`)
|
||||
add('face', 'imgs/face', `imgs/face${costumeIdx}`)
|
||||
add('side', 'imgs/side', `imgs/side${costumeIdx}`)
|
||||
add('gacha', 'imgs/gacha')
|
||||
add('splash', 'imgs/splash', `imgs/splash${costumeCfg[0]}`)
|
||||
add('splash', 'imgs/splash', `imgs/splash${costumeIdx}`)
|
||||
// 检查彩蛋自定义
|
||||
tAdd('card', 'imgs/card')
|
||||
tAdd('banner', 'imgs/banner')
|
@ -2,9 +2,10 @@ import Base from './Base.js'
|
||||
import Character from './Character.js'
|
||||
import Artifact from './Artifact.js'
|
||||
import ArtifactSet from './ArtifactSet.js'
|
||||
import Avatar from './Avatar.js'
|
||||
import AvatarList from './AvatarList.js'
|
||||
import AvatarData from './AvatarData.js'
|
||||
import AvatarArtis from './AvatarArtis.js'
|
||||
import Abyss from './Abyss.js'
|
||||
import Player from './Player.js'
|
||||
import ProfileServ from './ProfileServ.js'
|
||||
import ProfileReq from './ProfileReq.js'
|
||||
import ProfileData from './ProfileData.js'
|
||||
@ -23,8 +24,8 @@ export {
|
||||
Character,
|
||||
Artifact,
|
||||
ArtifactSet,
|
||||
Avatar,
|
||||
AvatarList,
|
||||
AvatarData,
|
||||
AvatarArtis,
|
||||
ProfileServ,
|
||||
ProfileReq,
|
||||
ProfileData,
|
||||
@ -35,5 +36,6 @@ export {
|
||||
Material,
|
||||
Weapon,
|
||||
User,
|
||||
MysApi
|
||||
MysApi,
|
||||
Player
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
import lodash from 'lodash'
|
||||
import { Data } from '../index.js'
|
||||
import { ProfileServ } from '../../models/index.js'
|
||||
import EnkaData from './enka-data.js'
|
||||
import { Data } from '../../components/index.js'
|
||||
import EnkaData from './EnkaData.js'
|
||||
import MiaoData from './MiaoData.js'
|
||||
|
||||
let HttpsProxyAgent = ''
|
||||
|
||||
export default new ProfileServ({
|
||||
export default {
|
||||
id: 'enka',
|
||||
cfgKey: 'enkaApi',
|
||||
|
||||
// 处理请求参数
|
||||
async request (api) {
|
||||
let params = {
|
||||
@ -16,7 +15,6 @@ export default new ProfileServ({
|
||||
}
|
||||
let proxy = this.getCfg('proxyAgent')
|
||||
if (proxy) {
|
||||
|
||||
if (HttpsProxyAgent === '') {
|
||||
HttpsProxyAgent = await import('https-proxy-agent').catch((err) => {
|
||||
logger.error(err)
|
||||
@ -45,8 +43,14 @@ export default new ProfileServ({
|
||||
return data
|
||||
},
|
||||
|
||||
userData (data) {
|
||||
return Data.getData(data, 'name:nickname,avatar:profilePicture.avatarId,level,signature')
|
||||
updatePlayer (player, data) {
|
||||
player.setBasicData(Data.getData(data, 'name:nickname,face:profilePicture.avatarID,card:nameCardID,level,word:worldLevel,sign:signature'))
|
||||
lodash.forEach(data.avatarInfoList, (ds) => {
|
||||
let ret = EnkaData.setAvatar(player, ds)
|
||||
if (ret) {
|
||||
player._update.push(ret.id)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
profileData (data) {
|
||||
@ -64,4 +68,4 @@ export default new ProfileServ({
|
||||
cdTime (data) {
|
||||
return data.ttl || 60
|
||||
}
|
||||
})
|
||||
}
|
126
models/player/EnkaData.js
Normal file
126
models/player/EnkaData.js
Normal file
@ -0,0 +1,126 @@
|
||||
import lodash from 'lodash'
|
||||
import { attrMap, idsMap, artisIdxMap } from './ProfileMeta.js'
|
||||
import { Character, ArtifactSet } from '../index.js'
|
||||
|
||||
let EnkaData = {
|
||||
setAvatar (player, data) {
|
||||
let char = Character.get(data.avatarId)
|
||||
if (!char) {
|
||||
return
|
||||
}
|
||||
let avatar = player.getAvatar(char.id, true)
|
||||
let talentRet = EnkaData.getTalent(char.id, data.skillLevelMap)
|
||||
avatar.setAvatar({
|
||||
level: data.propMap['4001'].val * 1,
|
||||
promote: data.propMap['1002'].val * 1,
|
||||
cons: data.talentIdList ? data.talentIdList.length : 0,
|
||||
fetter: data.fetterInfo.expLevel,
|
||||
costume: char.checkCostume(data.costumeId) ? data.costumeId : 0,
|
||||
elem: talentRet.elem,
|
||||
weapon: EnkaData.getWeapon(data.equipList),
|
||||
talent: talentRet.talent,
|
||||
artis: EnkaData.getArtifact(data.equipList)
|
||||
}, 'enka')
|
||||
return avatar
|
||||
},
|
||||
|
||||
getWeapon (data) {
|
||||
let ds = {}
|
||||
lodash.forEach(data, (temp) => {
|
||||
if (temp.flat && temp.flat.itemType === 'ITEM_WEAPON') {
|
||||
ds = temp
|
||||
return false
|
||||
}
|
||||
})
|
||||
let { weapon, flat } = ds
|
||||
return {
|
||||
name: idsMap[flat.nameTextMapHash],
|
||||
level: weapon.level,
|
||||
promote: weapon.promoteLevel,
|
||||
affix: (lodash.values(weapon.affixMap)[0] || 0) + 1
|
||||
}
|
||||
},
|
||||
|
||||
getTalent (charid, ds = {}) {
|
||||
let char = Character.get(charid)
|
||||
let { talentId = {}, talentElem = {}, talentKey = {} } = char.meta
|
||||
let elem = ''
|
||||
let idx = 0
|
||||
let ret = {}
|
||||
lodash.forEach(ds, (lv, id) => {
|
||||
let key
|
||||
if (talentId[id]) {
|
||||
let tid = talentId[id]
|
||||
key = talentKey[tid]
|
||||
elem = elem || talentElem[tid]
|
||||
ret[key] = lv
|
||||
} else {
|
||||
key = ['a', 'e', 'q'][idx++]
|
||||
ret[key] = ret[key] || lv
|
||||
}
|
||||
})
|
||||
return {
|
||||
elem: elem,
|
||||
talent: ret
|
||||
}
|
||||
},
|
||||
|
||||
getArtifact (data) {
|
||||
let ret = {}
|
||||
lodash.forEach(data, (ds) => {
|
||||
let flat = ds.flat || {}
|
||||
let re = ds.reliquary
|
||||
let idx = artisIdxMap[flat.equipType]
|
||||
if (!idx) {
|
||||
return
|
||||
}
|
||||
let setName = idsMap[flat.setNameTextMapHash] || ''
|
||||
ret[idx] = {
|
||||
name: ArtifactSet.getArtiNameBySet(setName, idx),
|
||||
level: Math.min(20, ((re.level) || 1) - 1),
|
||||
star: flat.rankLevel || 5,
|
||||
mainId: re.mainPropId,
|
||||
attrIds: re.appendPropIdList
|
||||
}
|
||||
})
|
||||
return ret
|
||||
},
|
||||
|
||||
getArtifactBak (data) {
|
||||
let ret = {}
|
||||
let get = function (d) {
|
||||
if (!d) {
|
||||
return {}
|
||||
}
|
||||
let id = d.appendPropId || d.mainPropId || ''
|
||||
id = id.replace('FIGHT_PROP_', '')
|
||||
if (!attrMap[id]) {
|
||||
return {}
|
||||
}
|
||||
return { key: attrMap[id], value: d.statValue }
|
||||
}
|
||||
lodash.forEach(data, (ds) => {
|
||||
let flat = ds.flat || {}
|
||||
let sub = flat.reliquarySubstats || []
|
||||
let idx = artisIdxMap[flat.equipType]
|
||||
if (!idx) {
|
||||
return
|
||||
}
|
||||
let setName = idsMap[flat.setNameTextMapHash] || ''
|
||||
ret[idx] = {
|
||||
name: ArtifactSet.getArtiNameBySet(setName, idx),
|
||||
level: Math.min(20, ((ds.reliquary && ds.reliquary.level) || 1) - 1),
|
||||
main: get(flat.reliquaryMainstat),
|
||||
attrs: [
|
||||
get(sub[0]),
|
||||
get(sub[1]),
|
||||
get(sub[2]),
|
||||
get(sub[3])
|
||||
]
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
export default EnkaData
|
@ -1,9 +1,8 @@
|
||||
import lodash from 'lodash'
|
||||
import { Data } from '../index.js'
|
||||
import { ProfileServ } from '../../models/index.js'
|
||||
import MiaoData from './miao-data.js'
|
||||
import { Data } from '../../components/index.js'
|
||||
import MiaoData from './MiaoData.js'
|
||||
|
||||
export default new ProfileServ({
|
||||
export default {
|
||||
key: 'miao',
|
||||
name: '喵喵Api',
|
||||
cfgKey: 'miaoApi',
|
||||
@ -18,19 +17,14 @@ export default new ProfileServ({
|
||||
return data
|
||||
},
|
||||
|
||||
userData (data) {
|
||||
return Data.getData(data, 'name:nickname,avatar:profilePicture.avatarID,level,signature')
|
||||
},
|
||||
|
||||
profileData (data) {
|
||||
let ret = {}
|
||||
updatePlayer (player, data) {
|
||||
player.setBasicData(Data.getData(data, 'name:nickname,face:profilePicture.avatarID,card:nameCardID,level,word:worldLevel,sign:signature'))
|
||||
lodash.forEach(data.showAvatarInfoList, (ds) => {
|
||||
let profile = MiaoData.getProfile(ds)
|
||||
if (profile && profile.id) {
|
||||
ret[profile.id] = profile
|
||||
let ret = MiaoData.setAvatar(player, ds)
|
||||
if (ret) {
|
||||
player._update.push(ret.id)
|
||||
}
|
||||
})
|
||||
return ret
|
||||
},
|
||||
|
||||
// 获取冷却时间
|
||||
@ -41,4 +35,4 @@ export default new ProfileServ({
|
||||
}
|
||||
return 60
|
||||
}
|
||||
})
|
||||
}
|
101
models/player/MiaoData.js
Normal file
101
models/player/MiaoData.js
Normal file
@ -0,0 +1,101 @@
|
||||
import { Character } from '../index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
import { attrMap, artisIdxMap } from './ProfileMeta.js'
|
||||
|
||||
let MiaoData = {
|
||||
setAvatar (player, ds) {
|
||||
let char = Character.get(ds.id)
|
||||
let avatar = player.getAvatar(ds.id, true)
|
||||
let talentRet = MiaoData.getTalent(char.id, ds.skill)
|
||||
avatar.setAvatar({
|
||||
level: ds.level,
|
||||
cons: ds.constellationNum || 0,
|
||||
promote: ds.promoteLevel,
|
||||
fetter: ds.fetterLevel,
|
||||
costume: char.checkCostume(ds.costumeID) ? ds.costumeID : 0,
|
||||
elem: talentRet.elem,
|
||||
weapon: MiaoData.getWeapon(ds.weapon),
|
||||
talent: talentRet.talent,
|
||||
artis: MiaoData.getArtifact(ds.reliquary)
|
||||
}, 'miao')
|
||||
return avatar
|
||||
},
|
||||
|
||||
getWeapon (weapon) {
|
||||
return {
|
||||
name: weapon.name,
|
||||
level: weapon.level,
|
||||
promote: weapon.promoteLevel,
|
||||
affix: (weapon.affixLevel || 0) + 1
|
||||
}
|
||||
},
|
||||
|
||||
getTalent (charid, data = {}) {
|
||||
let char = Character.get(charid)
|
||||
let { talentId = {}, talentElem = {}, talentKey = {} } = char.meta
|
||||
let elem = ''
|
||||
let idx = 0
|
||||
let ret = {}
|
||||
lodash.forEach(data, (ds) => {
|
||||
let key
|
||||
if (talentId[ds.id]) {
|
||||
let tid = talentId[ds.id]
|
||||
key = talentKey[tid]
|
||||
elem = elem || talentElem[tid]
|
||||
ret[key] = {
|
||||
level: ds.level
|
||||
}
|
||||
} else {
|
||||
key = ['a', 'e', 'q'][idx++]
|
||||
ret[key] = ret[key] || {
|
||||
level: ds.level
|
||||
}
|
||||
}
|
||||
})
|
||||
return {
|
||||
talent: ret,
|
||||
elem
|
||||
}
|
||||
},
|
||||
|
||||
getArtifact (data) {
|
||||
let ret = {}
|
||||
let get = function (d) {
|
||||
if (!d) {
|
||||
return []
|
||||
}
|
||||
let name = d.name
|
||||
name = name.replace('FIGHT_PROP_', '')
|
||||
if (!attrMap[name]) {
|
||||
return []
|
||||
}
|
||||
let value = d.value
|
||||
if (value && value < 1) {
|
||||
value = value * 100
|
||||
}
|
||||
return { key: attrMap[name], value }
|
||||
}
|
||||
lodash.forEach(data, (ds) => {
|
||||
let sub = ds.appendAffix || []
|
||||
let idx = artisIdxMap[ds.type]
|
||||
if (!idx) {
|
||||
return
|
||||
}
|
||||
ret[idx] = {
|
||||
name: ds.name,
|
||||
level: ds.level,
|
||||
star: ds.rank,
|
||||
main: get(ds.mainAffix),
|
||||
attrs: [
|
||||
get(sub[0]),
|
||||
get(sub[1]),
|
||||
get(sub[2]),
|
||||
get(sub[3])
|
||||
]
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
}
|
||||
export default MiaoData
|
303
models/player/MysAvatar.js
Normal file
303
models/player/MysAvatar.js
Normal file
@ -0,0 +1,303 @@
|
||||
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 || force === 2) {
|
||||
return true
|
||||
}
|
||||
if (force === true) {
|
||||
force = 0
|
||||
}
|
||||
let duration = (new Date() * 1 - time * 1) / 1000
|
||||
if (isNaN(duration) || duration < 0) {
|
||||
return true
|
||||
}
|
||||
let reqTime = forceMap[force] === 0 ? 0 : (forceMap[force] || 60)
|
||||
return duration > reqTime * 60
|
||||
},
|
||||
/**
|
||||
* 更新米游社角色信息
|
||||
* @param player
|
||||
* @param mys
|
||||
* @param force
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async refreshMysDetail (player, force = 0) {
|
||||
let mys = player?.e?._mys
|
||||
if (!mys) {
|
||||
return false
|
||||
}
|
||||
if (!MysAvatar.needRefresh(player._mys, force, { 0: 60, 1: 2, 2: 0 })) {
|
||||
return false
|
||||
}
|
||||
let charData = await mys.getCharacter()
|
||||
if (!charData || !charData.avatars) {
|
||||
return false
|
||||
}
|
||||
MysAvatar.setMysCharData(player, charData)
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新米游社统计信息
|
||||
* @param player
|
||||
* @param force
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async refreshMysInfo (player, force = 0) {
|
||||
let mys = player?.e?._mys
|
||||
if (!mys) {
|
||||
return false
|
||||
} // 不必要更新
|
||||
if (!MysAvatar.needRefresh(player._info, force, { 0: 60, 1: 2, 2: 0 })) {
|
||||
return false
|
||||
}
|
||||
let infoData = await mys.getIndex()
|
||||
if (!infoData || !infoData.role) {
|
||||
return false
|
||||
}
|
||||
MysAvatar.setMysInfo(player, infoData)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据已有Mys CharData更新player
|
||||
* @param player
|
||||
* @param charData
|
||||
*/
|
||||
setMysCharData (player, charData) {
|
||||
let role = charData.role
|
||||
player.setBasicData({
|
||||
level: role.level,
|
||||
name: role.nickname
|
||||
})
|
||||
let charIds = {}
|
||||
lodash.forEach(charData.avatars, (ds) => {
|
||||
let avatar = Data.getData(ds, 'id,level,cons:actived_constellation_num,fetter')
|
||||
avatar.elem = ds.element.toLowerCase()
|
||||
// 处理时装数据
|
||||
let costume = (ds?.costumes || [])[0]
|
||||
if (costume && costume.id) {
|
||||
avatar.costume = costume.id
|
||||
}
|
||||
avatar.weapon = Data.getData(ds.weapon, 'name,star:rarity,level,promote:promote_level,affix:affix_level')
|
||||
// 处理圣遗物数据
|
||||
let artis = {}
|
||||
lodash.forEach(ds.reliquaries, (re) => {
|
||||
const posIdx = { 生之花: 1, 死之羽: 2, 时之沙: 3, 空之杯: 4, 理之冠: 5 }
|
||||
if (re && re.name && posIdx[re.pos_name]) {
|
||||
artis[posIdx[re.pos_name]] = {
|
||||
name: re.name,
|
||||
level: re.level
|
||||
}
|
||||
}
|
||||
})
|
||||
avatar.artis = artis
|
||||
player.setAvatar(avatar, 'mys')
|
||||
charIds[avatar.id] = true
|
||||
})
|
||||
// 若角色数据>8,检查缓存,删除错误缓存的数据
|
||||
if (lodash.keys(charIds).length > 8) {
|
||||
player.forEachAvatar((avatar) => {
|
||||
if (!charIds[avatar.id] && !avatar.isProfile) {
|
||||
delete player._avatars[avatar.id]
|
||||
}
|
||||
})
|
||||
}
|
||||
player._mys = new Date() * 1
|
||||
player.save()
|
||||
},
|
||||
|
||||
setMysInfo (player, infoData) {
|
||||
let role = infoData.role
|
||||
// 设置角色信息
|
||||
let homeLevel = ((infoData?.homes || [])[0])?.level
|
||||
if (role) {
|
||||
player.setBasicData({
|
||||
level: role.level,
|
||||
name: role.nickname
|
||||
})
|
||||
}
|
||||
// 设置角色数据
|
||||
lodash.forEach(infoData?.avatars || [], (ds) => {
|
||||
let avatar = Data.getData(ds, 'id,level,cons:actived_constellation_num,fetter')
|
||||
avatar.elem = ds.element.toLowerCase()
|
||||
player.setAvatar(avatar, 'mys')
|
||||
})
|
||||
let stats = {}
|
||||
lodash.forEach(infoData?.stats || [], (num, key) => {
|
||||
key = key.replace('_number', '')
|
||||
if (key !== 'spiral_abyss') {
|
||||
stats[lodash.camelCase(key)] = num
|
||||
}
|
||||
})
|
||||
|
||||
let exploration = {}
|
||||
lodash.forEach(infoData?.world_explorations || [], (ds) => {
|
||||
let { name } = ds
|
||||
if (name === '层岩巨渊') {
|
||||
return true
|
||||
}
|
||||
exploration[name === '层岩巨渊·地下矿区' ? '层岩巨渊' : name] = ds.exploration_percentage
|
||||
})
|
||||
player.info = {
|
||||
homeLevel,
|
||||
stats,
|
||||
exploration
|
||||
}
|
||||
player._info = new Date() * 1
|
||||
player.save()
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前角色需要更新天赋的角色ID
|
||||
* @param player
|
||||
* @param ids 角色列表,若传入则查询指定角色列表,不传入查询全部
|
||||
* @param force
|
||||
* @returns {*[]}
|
||||
*/
|
||||
getNeedRefreshIds (player, ids, force = 0) {
|
||||
let ret = []
|
||||
if (!ids) {
|
||||
ids = lodash.keys(player._avatars)
|
||||
} else if (!lodash.isArray(ids)) {
|
||||
ids = [ids]
|
||||
}
|
||||
lodash.forEach(ids, (id) => {
|
||||
let avatar = player.getAvatar(id)
|
||||
if (!avatar) {
|
||||
return true
|
||||
}
|
||||
if (!avatar.hasTalent || MysAvatar.needRefresh(avatar._talent, force, { 0: 60 * 24, 1: 60, 2: 0 })) {
|
||||
ret.push(avatar.id)
|
||||
}
|
||||
})
|
||||
return ret
|
||||
},
|
||||
|
||||
/**
|
||||
* 使用MysApi刷新指定角色的天赋信息
|
||||
* @param player
|
||||
* @param ids
|
||||
* @param force
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async refreshTalent (player, ids, force = 0) {
|
||||
let e = player?.e
|
||||
let mys = e?._mys
|
||||
if (!e || !mys) {
|
||||
return false
|
||||
}
|
||||
let needReqIds = MysAvatar.getNeedRefreshIds(player, ids, force)
|
||||
if (needReqIds.length > 0) {
|
||||
if (needReqIds.length > 8) {
|
||||
e && e.reply('正在获取角色信息,请稍候...')
|
||||
}
|
||||
let failCount = 0
|
||||
// 并发5,请求天赋数据
|
||||
await Data.asyncPool(5, needReqIds, async (id) => {
|
||||
let avatar = player.getAvatar(id)
|
||||
if (!avatar || failCount > 5) {
|
||||
return false
|
||||
}
|
||||
let ret = await MysAvatar.refreshAvatarTalent(avatar, mys)
|
||||
if (ret === false) {
|
||||
failCount++
|
||||
}
|
||||
})
|
||||
}
|
||||
player.save()
|
||||
},
|
||||
|
||||
async refreshAvatarTalent (avatar, mys) {
|
||||
if (mys && mys.isSelfCookie) {
|
||||
let char = avatar.char
|
||||
if (!char) {
|
||||
return false
|
||||
}
|
||||
let id = char.id
|
||||
let talent = {}
|
||||
let talentRes = await mys.getDetail(id)
|
||||
// { data: null, message: '请先登录', retcode: -100, api: 'detail' }
|
||||
if (talentRes && talentRes.skill_list) {
|
||||
let talentList = lodash.orderBy(talentRes.skill_list, ['id'], ['asc'])
|
||||
for (let val of talentList) {
|
||||
let { max_level: maxLv, level_current: lv } = val
|
||||
if (val.name.includes('普通攻击')) {
|
||||
talent.a = lv
|
||||
continue
|
||||
}
|
||||
if (maxLv >= 10 && !talent.e) {
|
||||
talent.e = lv
|
||||
continue
|
||||
}
|
||||
if (maxLv >= 10 && !talent.q) {
|
||||
talent.q = lv
|
||||
}
|
||||
}
|
||||
}
|
||||
let ret = char.getAvatarTalent(talent, avatar.cons, 'original')
|
||||
avatar.setTalent(ret, 'original', true)
|
||||
return !!ret
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
getDate (time) {
|
||||
return time ? moment(new Date(time)).format('MM-DD HH:mm') : ''
|
||||
},
|
||||
|
||||
getInfo (player) {
|
||||
let chestMap = []
|
||||
Data.eachStr('common,exquisite,precious,luxurious,magic', (key) => {
|
||||
chestMap.push({
|
||||
key: `${key}Chest`,
|
||||
...chestInfo[key]
|
||||
})
|
||||
})
|
||||
let ret = {
|
||||
...(player.info || {}),
|
||||
chestMap
|
||||
}
|
||||
let stats = ret.stats || {}
|
||||
if (stats?.activeDay) {
|
||||
let num = stats?.activeDay
|
||||
let year = Math.floor(num / 365)
|
||||
let month = Math.floor((num % 365) / 30.41)
|
||||
let day = Math.floor((num % 365) % 30.41)
|
||||
let msg = ''
|
||||
if (year > 0) {
|
||||
msg += year + '年'
|
||||
}
|
||||
if (month > 0) {
|
||||
msg += month + '个月'
|
||||
}
|
||||
if (day > 0) {
|
||||
msg += day + '天'
|
||||
}
|
||||
ret.activeDay = msg
|
||||
}
|
||||
let avatarCount = 0
|
||||
let avatar5Count = 0
|
||||
let goldCount = 0
|
||||
player.forEachAvatar((avatar) => {
|
||||
avatarCount++
|
||||
if (avatar.star === 5) {
|
||||
avatar5Count++
|
||||
goldCount += (avatar.cons || 0) + 1
|
||||
}
|
||||
let w = avatar.weapon
|
||||
if (w && w.star === 5) {
|
||||
goldCount += w.affix * 1
|
||||
}
|
||||
})
|
||||
stats.avatar = Math.max(stats.avatar, avatarCount)
|
||||
stats.goldCount = goldCount
|
||||
stats.avatar5 = avatar5Count
|
||||
ret.stats = stats
|
||||
return ret
|
||||
}
|
||||
}
|
||||
export default MysAvatar
|
86
models/player/Profile.js
Normal file
86
models/player/Profile.js
Normal file
@ -0,0 +1,86 @@
|
||||
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'
|
||||
|
||||
let { diyCfg } = await Data.importCfg('profile')
|
||||
|
||||
const Profile = {
|
||||
/**
|
||||
* 根据UID分配请求服务器
|
||||
* @param uid
|
||||
* @returns {ProfileServ}
|
||||
*/
|
||||
getServ (uid) {
|
||||
let token = diyCfg?.miaoApi?.token
|
||||
let qq = diyCfg?.miaoApi?.qq
|
||||
|
||||
if (qq && token && token.length === 32 && !/^test/.test(token) && Cfg.get('profileServ') === 1) {
|
||||
if (!Profile.Miao) {
|
||||
Profile.Miao = new ProfileServ(MiaoApi)
|
||||
}
|
||||
return Profile.Miao
|
||||
}
|
||||
if (!Profile.Enka) {
|
||||
Profile.Enka = new ProfileServ(enkaCfg)
|
||||
}
|
||||
return Profile.Enka
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新面板数据
|
||||
* @param player
|
||||
* @param force
|
||||
* @returns {Promise<boolean|number>}
|
||||
*/
|
||||
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) {
|
||||
return false
|
||||
}
|
||||
let req = ProfileReq.create(e)
|
||||
if (!req) {
|
||||
return false
|
||||
}
|
||||
let serv = Profile.getServ(uid)
|
||||
try {
|
||||
await req.requestProfile(player, serv)
|
||||
player._profile = new Date() * 1
|
||||
player.save()
|
||||
return player._update.length
|
||||
} catch (err) {
|
||||
if (!e._isReplyed) {
|
||||
e.reply(`UID:${uid}更新面板失败,更新服务:${serv.name}`)
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
isProfile (avatar) {
|
||||
// 检查数据源
|
||||
if (!avatar._source || !['enka', 'change', 'miao'].includes(avatar._source)) {
|
||||
return false
|
||||
}
|
||||
// 检查武器及天赋
|
||||
if (!avatar.weapon || !avatar.talent) {
|
||||
return false
|
||||
}
|
||||
// 检查圣遗物词条是否完备
|
||||
if (!avatar.artis || !avatar.artis.hasAttr) {
|
||||
return false
|
||||
}
|
||||
// 检查旅行者
|
||||
if (['空', '荧'].includes(avatar.name)) {
|
||||
return !!avatar.elem
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export default Profile
|
@ -1,4 +1,5 @@
|
||||
export default {
|
||||
// Enka IdsMap
|
||||
export const idsMap = {
|
||||
20848859: '黑岩斩刀',
|
||||
33330467: '元素熟练',
|
||||
37147251: '匣里日月',
|
||||
@ -473,3 +474,40 @@ export default {
|
||||
FIGHT_PROP_WIND_SUB_HURT: '风元素抗性',
|
||||
level: '等级'
|
||||
}
|
||||
|
||||
// 圣遗物词条映射
|
||||
export const attrMap = {
|
||||
HP: 'hpPlus',
|
||||
HP_PERCENT: 'hp',
|
||||
ATTACK: 'atkPlus',
|
||||
ATTACK_PERCENT: 'atk',
|
||||
DEFENSE: 'defPlus',
|
||||
DEFENSE_PERCENT: 'def',
|
||||
FIRE_ADD_HURT: 'pyro',
|
||||
ICE_ADD_HURT: 'cryo',
|
||||
ROCK_ADD_HURT: 'geo',
|
||||
ELEC_ADD_HURT: 'electro',
|
||||
WIND_ADD_HURT: 'anemo',
|
||||
WATER_ADD_HURT: 'hydro',
|
||||
PHYSICAL_ADD_HURT: 'phy',
|
||||
GRASS_ADD_HURT: 'dendro',
|
||||
HEAL_ADD: 'heal',
|
||||
ELEMENT_MASTERY: 'mastery',
|
||||
CRITICAL: 'cpct',
|
||||
CRITICAL_HURT: 'cdmg',
|
||||
CHARGE_EFFICIENCY: 'recharge'
|
||||
}
|
||||
|
||||
// 圣遗物位置映射
|
||||
export const artisIdxMap = {
|
||||
EQUIP_BRACER: 1,
|
||||
EQUIP_NECKLACE: 2,
|
||||
EQUIP_SHOES: 3,
|
||||
EQUIP_RING: 4,
|
||||
EQUIP_DRESS: 5,
|
||||
生之花: 1,
|
||||
死之羽: 2,
|
||||
时之沙: 3,
|
||||
空之杯: 4,
|
||||
理之冠: 5
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import fs from 'node:fs'
|
||||
import { Data } from '../../components/index.js'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const _path = process.cwd()
|
||||
const userPath = `${_path}/data/UserData/`
|
||||
if (!fs.existsSync(userPath)) {
|
||||
fs.mkdirSync(userPath)
|
||||
}
|
||||
|
||||
let ProfileFile = {
|
||||
getData (uid) {
|
||||
let data = Data.readJSON('/data/UserData', 'root')
|
||||
if (data && data.chars) {
|
||||
return data
|
||||
} else {
|
||||
return {
|
||||
uid,
|
||||
chars: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
saveData (profile) {
|
||||
let userData = {}
|
||||
const userFile = `${userPath}/${uid}.json`
|
||||
if (fs.existsSync(userFile)) {
|
||||
userData = JSON.parse(fs.readFileSync(userFile, 'utf8')) || {}
|
||||
}
|
||||
lodash.assignIn(userData, lodash.pick(data, 'uid,name,lv,avatar'.split(',')))
|
||||
userData.chars = userData.chars || {}
|
||||
lodash.forEach(data.chars, (char, charId) => {
|
||||
userData.chars[charId] = char
|
||||
})
|
||||
fs.writeFileSync(userFile, JSON.stringify(userData), '', ' ')
|
||||
return data
|
||||
}
|
||||
}
|
||||
export default ProfileFile
|
@ -5,7 +5,7 @@ import { attrNameMap, mainAttr, subAttr, attrMap } from '../../resources/meta/ar
|
||||
let ArtisMark = {
|
||||
// 根据Key获取标题
|
||||
getKeyByTitle (title, dmg = false) {
|
||||
if (/元素伤害加成/.test(title)) {
|
||||
if (/元素伤害加成/.test(title) || Format.isElem(title)) {
|
||||
let elem = Format.matchElem(title)
|
||||
return dmg ? 'dmg' : elem
|
||||
} else if (title === '物理伤害加成') {
|
||||
@ -67,23 +67,31 @@ let ArtisMark = {
|
||||
let ret = []
|
||||
let totalUpNum = 0
|
||||
let ltArr = []
|
||||
let isIdAttr = false
|
||||
|
||||
lodash.forEach(ds, (d) => {
|
||||
isIdAttr = !!d.eff
|
||||
let arti = ArtisMark.formatArti(d, charAttrCfg)
|
||||
ret.push(arti)
|
||||
if (isIdAttr) {
|
||||
return true
|
||||
}
|
||||
totalUpNum += arti.upNum
|
||||
if (arti.hasLt) {
|
||||
ltArr.push(arti)
|
||||
}
|
||||
ret.push(arti)
|
||||
delete arti.hasLt
|
||||
delete arti.hasGt
|
||||
})
|
||||
ltArr = lodash.sortBy(ltArr, 'upNum').reverse()
|
||||
for (let arti of ltArr) {
|
||||
if (totalUpNum > 9) {
|
||||
arti.upNum = arti.upNum - 1
|
||||
totalUpNum--
|
||||
} else {
|
||||
break
|
||||
if (!isIdAttr) {
|
||||
ltArr = lodash.sortBy(ltArr, 'upNum').reverse()
|
||||
for (let arti of ltArr) {
|
||||
if (totalUpNum > 9) {
|
||||
arti.upNum = arti.upNum - 1
|
||||
totalUpNum--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
@ -103,16 +111,13 @@ let ArtisMark = {
|
||||
if (!key || key === 'undefined') {
|
||||
return {}
|
||||
}
|
||||
|
||||
let arrCfg = attrMap[isDmg ? 'dmg' : key]
|
||||
|
||||
val = Format[arrCfg.format](val, 1)
|
||||
|
||||
let ret = {
|
||||
key,
|
||||
value: val
|
||||
}
|
||||
if (!isMain) {
|
||||
if (!isMain && !ret.eff) {
|
||||
let incRet = ArtisMark.getIncNum(key, value)
|
||||
ret.upNum = incRet.num
|
||||
ret.hasGt = incRet.hasGt
|
||||
@ -166,7 +171,10 @@ let ArtisMark = {
|
||||
getMark (charCfg, posIdx, mainAttr, subAttr, elem = '') {
|
||||
let ret = 0
|
||||
let { attrs, posMaxMark } = charCfg
|
||||
let key = mainAttr.key
|
||||
let key = mainAttr?.key
|
||||
if (!key) {
|
||||
return 0
|
||||
}
|
||||
let fixPct = 1
|
||||
posIdx = posIdx * 1
|
||||
if (posIdx >= 3) {
|
||||
@ -226,6 +234,16 @@ let ArtisMark = {
|
||||
let ret = []
|
||||
lodash.forEach(tmp, (ds) => ret.push(ds.attr))
|
||||
return ret.slice(0, maxLen)
|
||||
},
|
||||
|
||||
hasAttr (artis) {
|
||||
for (let idx = 1; idx <= 5; idx++) {
|
||||
let ds = artis[idx]
|
||||
if (ds && (!ds.name || !ds.main || !ds.attrs || !ds?.main?.key)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,24 @@ class AttrCalc {
|
||||
return new AttrCalc(profile)
|
||||
}
|
||||
|
||||
static calcPromote (lv) {
|
||||
if (lv === 20) {
|
||||
return 1
|
||||
}
|
||||
if (lv === 90) {
|
||||
return 6
|
||||
}
|
||||
let lvs = [1, 20, 40, 50, 60, 70, 80, 90]
|
||||
let promote = 0
|
||||
for (let idx = 0; idx < lvs.length - 1; idx++) {
|
||||
if (lv >= lvs[idx] && lv <= lvs[idx + 1]) {
|
||||
return promote
|
||||
}
|
||||
promote++
|
||||
}
|
||||
return promote
|
||||
}
|
||||
|
||||
/**
|
||||
* 面板属性计算
|
||||
* @returns {{}}
|
||||
@ -37,9 +55,6 @@ class AttrCalc {
|
||||
this.setCharAttr()
|
||||
this.setWeaponAttr()
|
||||
this.setArtisAttr()
|
||||
if (process.argv.includes('web-debug')) {
|
||||
// console.log(this.attr, this.attr.getAttr())
|
||||
}
|
||||
return this.attr.getAttr()
|
||||
}
|
||||
|
||||
@ -103,7 +118,7 @@ class AttrCalc {
|
||||
* 计算武器属性
|
||||
*/
|
||||
setWeaponAttr () {
|
||||
let wData = this.profile?.weapon
|
||||
let wData = this.profile?.weapon || {}
|
||||
let weapon = Weapon.get(wData?.name)
|
||||
let level = wData.level
|
||||
let promote = lodash.isUndefined(wData.promote) ? -1 : wData.promote
|
||||
@ -184,6 +199,9 @@ class AttrCalc {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
calcArtisAttr (ds, char) {
|
||||
if (!ds) {
|
||||
return false
|
||||
}
|
||||
let key = ds.key
|
||||
if (Format.isElem(key) && char.elem === key) {
|
||||
key = 'dmg'
|
||||
@ -196,23 +214,6 @@ class AttrCalc {
|
||||
}
|
||||
this.attr.addAttr(key, ds.value * 1)
|
||||
}
|
||||
|
||||
static calcPromote (lv) {
|
||||
if (lv === 20) {
|
||||
return 1
|
||||
}
|
||||
if (lv === 90) {
|
||||
return 6
|
||||
}
|
||||
let lvs = [1, 20, 40, 50, 60, 70, 80, 90]
|
||||
let promote = 0
|
||||
for (let idx = 0; idx < lvs.length - 1; idx++) {
|
||||
if (lv >= lvs[idx] && lv <= lvs[idx + 1]) {
|
||||
return promote
|
||||
}
|
||||
promote++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AttrCalc
|
@ -19,7 +19,7 @@
|
||||
<div class="cfg-line">
|
||||
{{cfgItem.title}}
|
||||
<span class="cfg-hint"> #喵喵设置{{cfgItem.key}}
|
||||
{{if cfgItem.type==='num'}} {{cfgItem.def}}{{else}} + 开启/关闭{{/if}}
|
||||
{{if cfgItem.type==='num'}} {{cfgItem.def}}{{else}} 开启/关闭{{/if}}
|
||||
</span>
|
||||
{{if cfgItem.type === 'num'}}
|
||||
<div class="cfg-status">{{cfg[cfgKey]}}</div>
|
||||
|
227
resources/character/avatar-list.css
Normal file
227
resources/character/avatar-list.css
Normal file
@ -0,0 +1,227 @@
|
||||
.container {
|
||||
width: 740px;
|
||||
background-size: cover;
|
||||
}
|
||||
.head-box {
|
||||
margin-top: 0;
|
||||
}
|
||||
.user-banner {
|
||||
height: 90px;
|
||||
background-size: auto 100%;
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #f0ece4;
|
||||
border-radius: 50px;
|
||||
padding: 1px;
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
.user-banner .face {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin: 10px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 1px #000, 0 0 5px rgba(0, 0, 0, 0.5);
|
||||
border: 3px solid #fff;
|
||||
overflow: hidden;
|
||||
background: url('../common/item/bg5.png');
|
||||
background-size: cover;
|
||||
}
|
||||
.user-banner .face span {
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-size: auto 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.user-banner .user-info {
|
||||
padding: 15px 5px;
|
||||
color: #414e64;
|
||||
text-shadow: 0 0 2px #f0ece4, 0 0 5px #f0ece4;
|
||||
}
|
||||
.user-banner .user-info .name {
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
}
|
||||
.user-banner .user-info .name strong {
|
||||
font-size: 24px;
|
||||
}
|
||||
.user-banner .user-info .name span {
|
||||
padding-left: 5px;
|
||||
}
|
||||
.user-banner .user-info .uid {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.user-banner .stat {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
margin: 16px;
|
||||
border-radius: 29px;
|
||||
height: 58px;
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
}
|
||||
.user-banner .stat-li {
|
||||
padding: 7px;
|
||||
width: 70px;
|
||||
height: 58px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
text-shadow: 0 0 1px #fff;
|
||||
}
|
||||
.user-banner .stat-li:nth-child(odd) {
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
.user-banner .stat-li:nth-child(even) {
|
||||
background: rgba(220, 220, 220, 0.5);
|
||||
}
|
||||
.user-banner .stat-li:first-child {
|
||||
width: 80px;
|
||||
padding-left: 17px;
|
||||
}
|
||||
.user-banner .stat-li:last-child {
|
||||
width: 80px;
|
||||
padding-right: 17px;
|
||||
}
|
||||
.user-banner .stat-li strong {
|
||||
font-size: 22px;
|
||||
display: block;
|
||||
}
|
||||
.user-banner .stat-li span {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #414e64;
|
||||
}
|
||||
.exploration {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 10px 0;
|
||||
justify-content: center;
|
||||
}
|
||||
.exploration .item {
|
||||
width: 93px;
|
||||
height: 116.25px;
|
||||
background: url('./imgs/exploration.webp') no-repeat;
|
||||
background-size: auto 100%;
|
||||
border-radius: 4px;
|
||||
margin: 3px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
align-items: center;
|
||||
}
|
||||
.exploration .item strong {
|
||||
font-size: 22px;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||
font-weight: normal;
|
||||
}
|
||||
.exploration .item span {
|
||||
margin-top: 58px;
|
||||
font-size: 14px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
display: block;
|
||||
text-shadow: 0 0 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.chest-list {
|
||||
margin: 10px 0;
|
||||
overflow: hidden;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.chest-list .chest {
|
||||
width: 20%;
|
||||
display: flex;
|
||||
padding: 15px 0;
|
||||
}
|
||||
.chest-list .chest:nth-child(even) {
|
||||
background: rgba(50, 50, 50, 0.5);
|
||||
}
|
||||
.chest-list .chest .value {
|
||||
font-size: 24px;
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
padding-right: 8px;
|
||||
text-align: right;
|
||||
width: 70px;
|
||||
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.chest-list .chest .detail {
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.chest-list .chest .info {
|
||||
display: flex;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.chest-list .chest .icon {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 1px;
|
||||
background: url('./imgs/chest.webp') no-repeat;
|
||||
background-size: auto 100%;
|
||||
vertical-align: center;
|
||||
}
|
||||
.chest-list .chest .max {
|
||||
padding-left: 3px;
|
||||
color: #aaa;
|
||||
}
|
||||
.chest-list .chest .title {
|
||||
height: 20px;
|
||||
display: flex;
|
||||
color: #d3bc8e;
|
||||
}
|
||||
.cont-title {
|
||||
padding: 8px 15px;
|
||||
}
|
||||
.avatar-cont-main {
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar-cont {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.avatar-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.avatar-list .avatar-card {
|
||||
margin: 5px;
|
||||
}
|
||||
.cont-notice {
|
||||
text-align: right;
|
||||
}
|
||||
.cont-notice strong {
|
||||
color: #d3bc8e;
|
||||
}
|
||||
.cont-notice span {
|
||||
padding: 0 3px;
|
||||
color: #aaa;
|
||||
}
|
||||
.ck-notice {
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 13px;
|
||||
}
|
||||
.ck-notice strong {
|
||||
color: #d3bc8e;
|
||||
padding: 0 2px;
|
||||
font-weight: normal;
|
||||
}
|
||||
/*# sourceMappingURL=avatar-list.css.map */
|
101
resources/character/avatar-list.html
Normal file
101
resources/character/avatar-list.html
Normal file
@ -0,0 +1,101 @@
|
||||
{{extend elemLayout}}
|
||||
|
||||
{{block 'css'}}
|
||||
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/avatar-list.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="{{_res_path}}/common/tpl.css?v=1.0"/>
|
||||
{{/block}}
|
||||
|
||||
|
||||
{{block 'main'}}
|
||||
<div class="user-banner" style="background-image:url({{_res_path}}{{face?.banner}})">
|
||||
<div class="face">
|
||||
<span style="background-image:url({{_res_path}}{{face?.face}})"></span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="name">
|
||||
<strong>{{face.name}}</strong>
|
||||
{{if face.level && face.level > 1}} Lv.{{face.level}}{{/if}}
|
||||
|
||||
</div>
|
||||
<div class="uid">
|
||||
{{if uid}}<span> #{{uid}}</span>{{/if}}
|
||||
{{ if info.activeDay}}{{info.activeDay}} {{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{if info && info.stats}}
|
||||
<div class="stat">
|
||||
{{each info?.statMap title key}}
|
||||
{{if info.stats[key] }}
|
||||
<div class="stat-li">
|
||||
<strong>{{info.stats[key]}}</strong>
|
||||
<span>{{title}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
||||
{{if !isSelfCookie}}
|
||||
<div class="ck-notice">未绑定CK或CK失效,信息可能不完全。发送<strong>#体力帮助</strong>查看CK绑定方法,发送<strong>#更新面板</strong>更新游戏内角色展柜信息</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
{{if info && info.exploration && info.exploration['蒙德']}}
|
||||
{{set citys = ['蒙德','龙脊雪山','璃月','层岩巨渊','稻妻','渊下宫','须弥'] }}
|
||||
<div class="exploration">
|
||||
{{each citys city idx}}
|
||||
<div class="item city-{{idx+1}}" style="background-position:{{idx}}0% 0">
|
||||
<span>{{city}}</span>
|
||||
<strong>{{ (info.exploration[city]||0)/10 || '0'}}%</strong>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
{{if info?.stats?.commonChest}}
|
||||
{{set ds = info?.stats}}
|
||||
<div class="cont chest-list">
|
||||
{{each info?.chestMap cfg idx}}
|
||||
<div class="chest">
|
||||
<div class="value">{{ds[cfg.key]}}</div>
|
||||
<div class="detail">
|
||||
<div class="info">
|
||||
<div class="icon" style="background-position:{{idx*2}}0% 0"></div>
|
||||
<div class="max">{{cfg.max>ds[cfg.key]?cfg.max:ds[cfg.key]}}</div>
|
||||
</div>
|
||||
<div class="title">{{cfg.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
<div class="cont avatar-cont">
|
||||
<div class="cont-title">角色列表</div>
|
||||
<div class="avatar-list">
|
||||
{{each avatars avatar idx}}
|
||||
<% include(_layout_path+'../tpl/avatar-card.html', [avatar,{_res_path, cardType:'mini'}]) %>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="cont-footer cont-notice">
|
||||
{{set ut = updateTime }}
|
||||
{{if ut.profile || ut.mys}}
|
||||
<strong>数据更新时间</strong>
|
||||
{{if ut.profile}}
|
||||
<span>#更新面板:{{ut.profile}}</span>
|
||||
{{/if}}
|
||||
{{if ut.mys}}
|
||||
<span>米游社:{{ut.mys}}</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
未绑定CK或CK失效,信息可能不完全。发送<strong>#体力帮助</strong>查看CK绑定方法,发送<strong>#更新面板</strong>更新游戏内角色展柜信息
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{/block}}
|
276
resources/character/avatar-list.less
Normal file
276
resources/character/avatar-list.less
Normal file
@ -0,0 +1,276 @@
|
||||
|
||||
.container {
|
||||
width: 740px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.head-box {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
.user-banner {
|
||||
height: 90px;
|
||||
background-size: auto 100%;
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #f0ece4;
|
||||
border-radius: 50px;
|
||||
padding: 1px;
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
|
||||
.face {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin: 10px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 1px #000, 0 0 5px rgba(0, 0, 0, .5);
|
||||
border: 3px solid #fff;
|
||||
overflow: hidden;
|
||||
background: url('../common/item/bg5.png');
|
||||
background-size: cover;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-size: auto 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
padding: 15px 5px;
|
||||
color: #414e64;
|
||||
text-shadow: 0 0 2px #f0ece4, 0 0 5px #f0ece4;
|
||||
|
||||
.name {
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
|
||||
strong {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.uid {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
margin: 16px;
|
||||
border-radius: 29px;
|
||||
height: 58px;
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, .4);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-li {
|
||||
@width: 70px;
|
||||
@padding: 10px;
|
||||
|
||||
padding: 7px;
|
||||
width: @width;
|
||||
height: 58px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
text-shadow: 0 0 1px #fff;
|
||||
|
||||
|
||||
&:nth-child(odd) {
|
||||
background: rgba(255, 255, 255, .65);
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background: rgba(220, 220, 220, .5);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
width: @width+@padding;
|
||||
padding-left: 7px+@padding;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
width: @width+@padding;
|
||||
padding-right: 7px+@padding;
|
||||
}
|
||||
|
||||
|
||||
strong {
|
||||
font-size: 22px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #414e64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.exploration {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 10px 0;
|
||||
justify-content: center;
|
||||
|
||||
.item {
|
||||
width: 93px;
|
||||
height: 93*1.25px;
|
||||
background: url('./imgs/exploration.webp') no-repeat;
|
||||
background-size: auto 100%;
|
||||
border-radius: 4px;
|
||||
margin: 3px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
align-items: center;
|
||||
|
||||
strong {
|
||||
font-size: 22px;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, .5);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 58px;
|
||||
font-size: 14px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
display: block;
|
||||
text-shadow: 0 0 1px rgba(0, 0, 0, .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chest-list {
|
||||
margin: 10px 0;
|
||||
overflow: hidden;
|
||||
background: rgba(0, 0, 0, .6);
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.chest {
|
||||
width: 20%;
|
||||
display: flex;
|
||||
padding: 15px 0;
|
||||
|
||||
&:nth-child(even) {
|
||||
background: rgba(50, 50, 50, .5);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 24px;
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
padding-right: 8px;
|
||||
text-align: right;
|
||||
width: 70px;
|
||||
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.detail {
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 1px;
|
||||
background: url('./imgs/chest.webp') no-repeat;
|
||||
background-size: auto 100%;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.max {
|
||||
padding-left: 3px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.title {
|
||||
height: 20px;
|
||||
display: flex;
|
||||
color: #d3bc8e;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.cont-title {
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
.avatar-cont-main {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar-cont {
|
||||
background: rgba(0, 0, 0, .5);
|
||||
padding: 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.avatar-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
|
||||
.avatar-card {
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.cont-notice {
|
||||
text-align: right;
|
||||
|
||||
strong {
|
||||
color: #d3bc8e;
|
||||
}
|
||||
|
||||
span {
|
||||
padding: 0 3px;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.ck-notice {
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, .9);
|
||||
font-size: 13px;
|
||||
|
||||
strong {
|
||||
color: #d3bc8e;
|
||||
padding: 0 2px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@
|
||||
</div>
|
||||
|
||||
<div class="copyright data-source">
|
||||
数据源:{{data.dataSourceName}} {{data.updateTime}}
|
||||
数据源:{{ {miao:'喵喵API', 'enka':'Enka.Network', mys:'米游社'}[data.source]||data.source }} {{data.updateTime}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{if custom}}
|
||||
|
BIN
resources/character/imgs/chest.webp
Normal file
BIN
resources/character/imgs/chest.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
resources/character/imgs/exploration.webp
Normal file
BIN
resources/character/imgs/exploration.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 236 KiB |
@ -11,7 +11,7 @@
|
||||
{{block 'main'}}
|
||||
<div class="basic">
|
||||
<div class="main-pic"
|
||||
style="background-image:url({{_res_path}}{{imgs.splash0}})"></div>
|
||||
style="background-image:url({{_res_path}}{{costumeSplash || imgs?.splash}})"></div>
|
||||
<div class="detail">
|
||||
<div class="char-name">{{data.name}}</div>
|
||||
<div class="char-lv">UID {{uid}} - Lv.{{data.level}}
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||
<div class="data-info">
|
||||
{{if data.dataSource}}
|
||||
<span>数据源:{{ {miao:"喵喵Api",enka:"Enka.Network",change:"面板变换"}[data.dataSource]||data.dataSource }}</span>
|
||||
<span>数据源:{{ {change:"面板变换"}[data.dataSource]||data.dataSource }}</span>
|
||||
{{/if}}
|
||||
{{if data.updateTime}}
|
||||
<span class="time">{{data.updateTime}}</span>
|
||||
|
@ -12,7 +12,9 @@
|
||||
<div class="title">#面板列表
|
||||
<div class="label">UID:{{uid}}</div>
|
||||
</div>
|
||||
<div class="label">{{msg+", "}}更新角色时请不要出场对应角色,以获取准确面板数据</div>
|
||||
{{if msg}}
|
||||
<div class="label">{{msg}}</div>
|
||||
{{/if}}
|
||||
<div class="label">你可以使用<span>#{{demo}}面板</span>、<span>#{{demo}}伤害</span>、<span>#{{demo}}圣遗物</span>命令来查看面板信息了</div>
|
||||
</div>
|
||||
<div class="cont group-rank-tip {{groupRank?'has-rank':'no-rank'}}">
|
||||
@ -41,7 +43,7 @@
|
||||
<span class="name">{{char.abbr}}<span class="cons cons-{{char.cons}}">{{char.cons}}</span></span>
|
||||
{{if char.groupRank}}
|
||||
{{set gr = char.groupRank}}
|
||||
{{set rank = gr.rank >= 15 ? 10:(gr.rank <=3 ? gr.rank : 4)}}
|
||||
{{set rank = gr.rank >= (rankCfg.number || 15) ? 10:(gr.rank <=3 ? gr.rank : 4)}}
|
||||
<div class="group-rank rank-{{rank}} rank-type-{{gr.rankType}}">
|
||||
<span>{{gr.rank}}</span>
|
||||
</div>
|
||||
@ -55,9 +57,13 @@
|
||||
{{else}}
|
||||
<span></span>
|
||||
{{/if}}
|
||||
<span class="serv">当前更新服务:{{servName}}</span>
|
||||
|
||||
<span class="serv">
|
||||
当前更新服务:{{servName}}
|
||||
{{if updateTime.profile }}
|
||||
<span>,更新时间:{{updateTime.profile }}</span>
|
||||
{{/if}}
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/block}}
|
@ -358,14 +358,16 @@ body {
|
||||
margin-right: -60px;
|
||||
}
|
||||
.cont-notice {
|
||||
color: #888;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
padding: 8px 12px 8px 8px;
|
||||
}
|
||||
.cont-notice strong {
|
||||
color: #d3bc8e;
|
||||
font-weight: normal;
|
||||
}
|
||||
.cont-notice span {
|
||||
padding: 0 3px;
|
||||
color: #555;
|
||||
margin-left: 5px;
|
||||
}
|
||||
/*# sourceMappingURL=profile-stat.css.map */
|
@ -49,6 +49,7 @@
|
||||
<span class="fetter fetter{{['空','荧','旅行者'].includes(avatar.name)?10:avatar.fetter}}"></span>
|
||||
</div>
|
||||
|
||||
{{set talentLvMap = [0,1,1,1,2,2,3,3,3,4,5] }}
|
||||
{{each tk talentKey}}
|
||||
{{set curr = (avatar.talent||{})[talentKey] || {original:1,level:'-'} }}
|
||||
<div class="td-talent lv{{talentLvMap[curr.original]}} {{curr.level>curr.original?'talent-plus':''}}">
|
||||
@ -94,9 +95,22 @@
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{if talentNotice}}
|
||||
<p class="cont-notice">{{@talentNotice}}</p>
|
||||
{{/if}}
|
||||
|
||||
<div class="cont-notice">
|
||||
{{set ut = updateTime }}
|
||||
{{if ut.profile || ut.mys}}
|
||||
<strong>数据更新时间</strong>
|
||||
{{if ut.profile}}
|
||||
<span>#更新面板: {{ut.profile}}</span>
|
||||
{{/if}}
|
||||
{{if ut.mys}}
|
||||
<span>米游社: {{ut.mys}}</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
未绑定CK或CK失效,信息可能不完全。发送<strong>#体力帮助</strong>查看CK绑定方法,发送<strong>#更新面板</strong>更新游戏内角色展柜信息
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -402,14 +402,17 @@ body {
|
||||
}
|
||||
|
||||
.cont-notice {
|
||||
color: #888;
|
||||
background: rgba(255, 255, 255, .4);
|
||||
background: rgba(0, 0, 0, .7);
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
padding: 8px 12px 8px 8px;
|
||||
|
||||
strong {
|
||||
color: #d3bc8e;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
span {
|
||||
padding: 0 3px;
|
||||
color: #555;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user