底层数据结构变更

This commit is contained in:
Kokomi 2023-02-09 04:55:54 +08:00
parent 5f6669431a
commit cf5b2253ba
36 changed files with 1077 additions and 875 deletions

View File

@ -1,12 +1,14 @@
import { Character, Avatar, MysApi } from '../../models/index.js'
import { Cfg, Common, Profile } from '../../components/index.js'
import { Character, Avatar, 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)
if (!char) {
return false
@ -17,21 +19,12 @@ export async function renderAvatar (e, avatar, renderType = 'card') {
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 player = Player.create(e)
await player.refreshMys()
await player.refreshTalent(char.id)
avatar = player.getAvatar(char.id)
if (!avatar) {
avatar = { id: char.id, name: char.name, detail: false }
}
}
}
@ -39,8 +32,8 @@ export async function renderAvatar (e, avatar, renderType = 'card') {
}
// 渲染角色卡片
async function renderCard (e, ds, renderType = 'card') {
let char = Character.get(ds)
async function renderCard (e, avatar, renderType = 'card') {
let char = Character.get(avatar.id)
if (!char) {
return false
}
@ -54,11 +47,13 @@ async function renderCard (e, ds, renderType = 'card') {
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 = 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
@ -66,6 +61,7 @@ async function renderCard (e, ds, renderType = 'card') {
} else {
data = char.getData('id,name,sName')
}
let width = 600
let imgCss = ''
let scale = 1.2
@ -92,33 +88,3 @@ async function renderCard (e, ds, renderType = 'card') {
}
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
}

View File

@ -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 { renderAvatar } from './AvatarCard.js'
const relationMap = {
wife: {
@ -74,6 +74,7 @@ export async function wife (e) {
if (!mys || !mys.uid) {
return true
}
let player = Player.create(e)
let selfUser = mys.selfUser
let isSelf = true
let renderType = (action === '卡片' ? 'card' : 'photo')
@ -92,7 +93,7 @@ export async function wife (e) {
if (wifeList && wifeList.length > 0 && isSelf && !e.isPoke) {
if (wifeList[0] === '随机') {
// 如果选择为全部,则从列表中随机选择一个
avatarList = await getAvatarList(e, targetCfg.type, mys)
avatarList = await getAvatarList(player, targetCfg.type, mys)
let avatar = lodash.sample(avatarList)
return renderAvatar(e, avatar, renderType)
} else {
@ -101,20 +102,12 @@ export async function wife (e) {
}
}
}
// 如果未指定过则从列表中排序并随机选择前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)
}
} 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 '设置':
@ -169,3 +162,23 @@ export async function wife (e) {
export async function pokeWife (e, components) {
return await wife(e, components)
}
async function getAvatarList (player, type, mys) {
await player.refreshMys()
let list = []
player.forEachAvatar((avatar) => {
if (type !== false) {
if (!Character.checkWifeType(avatar.id, type)) {
return true
}
}
list.push(avatar)
})
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
}

View File

@ -1,5 +1,5 @@
import { Common, App, Data, Cfg } from '../components/index.js'
import { Character } from '../models/index.js'
import { Character, Player } 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'

View File

@ -3,9 +3,9 @@
*
* */
import lodash from 'lodash'
import { Cfg, Profile, Common } from '../../components/index.js'
import { Cfg, Common } from '../../components/index.js'
import { getTargetUid, profileHelp, autoGetProfile } from './ProfileCommon.js'
import { Artifact, Character, ProfileArtis } from '../../models/index.js'
import { Artifact, Character, ProfileArtis, Player } from '../../models/index.js'
/*
* 角色圣遗物面板
@ -64,14 +64,12 @@ export async function profileArtisList (e) {
}
let artis = []
let profiles = Profile.getAll(uid) || {}
if (!profiles || profiles.length === 0) {
e.reply('暂无角色圣遗物详情')
let player = Player.create(uid)
player.forEachAvatar((avatar) => {
let profile = avatar.getProfile()
if (!profile) {
return true
}
lodash.forEach(profiles || [], (profile) => {
let name = profile.name
let char = Character.get(name)
if (!profile.hasData || !profile.hasArtis()) {

View File

@ -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) {
@ -176,10 +177,11 @@ const ProfileChange = {
return source
}
let cuid = cfg.uid || uid
let cPlayer = Player.create(uid)
let id = cfg.char || source.id
let key = cuid + ':' + id
if (!profiles[key]) {
profiles[key] = Profile.get(cuid, id) || {}
profiles[key] = cPlayer.getProfile(id) || {}
}
return profiles[key]?.id ? profiles[key] : source
}

View File

@ -4,8 +4,8 @@
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
@ -87,7 +87,8 @@ export async function autoRefresh (e) {
e.isRefreshed = true
// 数据更新
let data = await Profile.request(uid, e)
let player = Player.create(uid)
let data = await player.refreshProfile(e)
if (!data) {
return false
}
@ -127,7 +128,7 @@ export async function autoGetProfile (e, uid, avatar, callback) {
return { err: true }
}
let profile = Profile.get(uid, char.id)
let profile = Player.getAvatar(uid, char.id)
if (!profile || !profile.hasData) {
if (await refresh()) {
return { err: true }
@ -155,17 +156,18 @@ export async function getProfile (e) {
}
// 数据更新
let data = await Profile.request(uid, e)
if (!data) {
let player = Player.create(uid)
let ret = await player.refreshProfile(e)
if (!ret) {
return true
}
if (!data.chars) {
if (!player._update.length === 0) {
e.reply('获取角色面板数据失败请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~')
} else {
let ret = {}
lodash.forEach(data.chars, (ds) => {
let char = Character.get(ds.id)
lodash.forEach(player._update, (id) => {
let char = Character.get(id)
if (char) {
ret[char.name] = true
}
@ -180,41 +182,6 @@ export async function getProfile (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
}
/*
* 面板帮助
* */

View File

@ -1,7 +1,7 @@
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 { Common, Format } from '../../components/index.js'
import { MysApi, Avatar, ProfileRank, ProfileArtis, Player } from '../../models/index.js'
export async function renderProfile (e, char, mode = 'profile', params = {}) {
let selfUser = await MysApi.initUser(e)
@ -18,7 +18,8 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
return true
}
let profile = e._profile || Profile.get(uid, char.id)
let player = Player.create(uid)
let profile = e._profile || player.getProfile(char.id)
let refresh = async () => {
let refreshRet = await autoRefresh(e)

View File

@ -1,7 +1,7 @@
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 { ProfileRank, Player } from '../../models/index.js'
import { Common, Data } from '../../components/index.js'
export async function profileList (e) {
let uid = await getTargetUid(e)
@ -14,7 +14,7 @@ export async function profileList (e) {
isSelfUid = uids.join(',').split(',').includes(uid + '')
}
let rank = false
let servName = Profile.getServName(uid)
let servName = Player.getProfileServName(uid)
let hasNew = false
let newCount = 0
@ -27,7 +27,8 @@ export async function profileList (e) {
}
const cfg = await Data.importCfg('cfg')
// 获取面板数据
let profiles = Profile.getAll(uid)
let player = Player.create(uid)
let profiles = player.getProfiles()
// 检测标志位
let qq = (e.at && !e.atBot) ? e.at : e.qq
await ProfileRank.setUidInfo({ uid, profiles, qq, uidType: isSelfUid ? 'ck' : 'bind' })
@ -38,8 +39,9 @@ export async function profileList (e) {
}
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) {
await player.forEachAvatarAsync(async function (avatar) {
let profile = avatar.getProfile()
if (!profile) {
return true
}
let char = profile.char

View File

@ -1,6 +1,6 @@
import { Character, ProfileRank, ProfileDmg, Avatar } from '../../models/index.js'
import { Character, ProfileRank, ProfileDmg, Avatar, 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) {
@ -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 })
@ -156,13 +157,14 @@ 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)
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,

View File

@ -1,41 +1,52 @@
import lodash from 'lodash'
import { Cfg, Common, Data } from '../../components/index.js'
import { AvatarList, ProfileRank } from '../../models/index.js'
import { MysApi, ProfileRank, Player } 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
}
// 缓存时间,单位小时
// 缓存时间,单位小时
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 mys = await MysApi.init(e)
if (!mys || !mys.uid) return false
const uid = mys.uid
let player = Player.create(e)
// 更新角色信息
await player.refreshMys()
// 更新天赋信息
await player.refreshTalent()
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)
player.forEachAvatar((avatar) => {
let { talent } = avatar
let ds = avatar.getDetail()
ds.aeq = talent?.a?.original + talent?.e?.original + talent?.q?.original || 3
avatarRet.push(ds)
let profile = avatar.getProfile()
if (!profile) {
return true
}
if (profile) {
if (profile.hasData) {
let mark = profile.getArtisMark(false)
avatar.artisMark = Data.getData(mark, 'mark,markClass,names')
ds.artisMark = Data.getData(mark, 'mark,markClass,names')
if (rank) {
rank.getRank(profile)
}
@ -47,9 +58,6 @@ export async function profileStat (e) {
avatarRet = lodash.orderBy(avatarRet, sortKey)
avatarRet = avatarRet.reverse()
let talentNotice = ''
if (!avatars.isSelfCookie || avatarRet.length <= 8) {
talentNotice = '未绑定CK信息可能展示不完全。回复<span>#体力帮助</span>获取CK配置帮助'
}
return await Common.render('character/profile-stat', {
save_id: uid,

View File

@ -4,7 +4,7 @@
* */
import lodash from 'lodash'
import { Cfg, Common, App, Data } from '../components/index.js'
import { Abyss, AvatarList, Character, MysApi } from '../models/index.js'
import { Abyss, Character, MysApi, Player } from '../models/index.js'
import HutaoApi from './wiki/HutaoApi.js'
let app = App.init({
@ -195,6 +195,9 @@ async function abyssTeam (e) {
if (!mys || !mys.uid || !mys.isSelfCookie) {
return true
}
let player = Player.create(e)
await player.refreshMys()
await player.refreshTalent()
let abyssData = await HutaoApi.getAbyssTeam()
if (!abyssData || !abyssData.data) {
@ -202,18 +205,7 @@ async function abyssTeam (e) {
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 avatarData = player.getAvatarData()
let avatarRet = {}
let data = {}
let noAvatar = {}
@ -397,6 +389,7 @@ async function uploadData (e) {
}
let ret = {}
let uid = mys.uid
let player = Player.create(e)
let resDetail, resAbyss
try {
resAbyss = await mys.getSpiralAbyss(1)
@ -413,9 +406,6 @@ async function uploadData (e) {
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('角色信息获取失败')
@ -431,6 +421,9 @@ async function uploadData (e) {
} catch (err) {
// console.log(err);
}
// 更新player信息
player.setMysCharData(resDetail)
if (ret && ret.retcode === 0) {
let stat = []
if (ret.data) {
@ -440,7 +433,6 @@ async function uploadData (e) {
}
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) {
@ -484,7 +476,8 @@ async function uploadData (e) {
}
addMsg('最强一击', ret.data?.damage || abyssData?.stat?.dmg || {})
addMsg('最高承伤', ret.data?.takeDamage || abyssData?.stat.takeDmg || {})
let avatarData = await avatars.getTalentData(avatarIds, mys)
await player.refreshTalent(avatarIds)
let avatarData = player.getAvatarData(avatarIds)
return await Common.render('stat/abyss-summary', {
abyss: abyssData,
avatars: avatarData,

View File

@ -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

View File

@ -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 }

View File

@ -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: '充能效率'
}

View File

@ -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
}
}

View File

@ -4,8 +4,7 @@
* */
import Base from './Base.js'
import lodash from 'lodash'
import { Profile } from '../components/index.js'
import { Artifact, Character, Weapon, ArtifactSet } from './index.js'
import { Artifact, Character, Weapon, ArtifactSet, Player } from './index.js'
import moment from 'moment'
const charKey = 'name,abbr,sName,star,imgs,face,side,gacha,weaponTypeName,elem'.split(',')
@ -34,7 +33,7 @@ export default class Avatar extends Base {
profile = pd
} else if (/\d{9}/.test(pd)) {
uid = pd
profile = Profile.get(pd, char.id, true)
profile = Player.getAvatar(pd, char.id, true)
}
}
if (profile && profile.isProfile && profile.hasData) {

257
models/AvatarArtis.js Normal file
View File

@ -0,0 +1,257 @@
/**
* 面板圣遗物
*/
import lodash from 'lodash'
import Base from './Base.js'
import { Artifact, ArtifactSet } from './index.js'
import { Format, Data } from '../components/index.js'
import ArtisMark from './profile-lib/ArtisMark.js'
export default class AvatarArtis extends Base {
constructor (charid = 0) {
super()
this.charid = charid
this.artis = {}
}
static isProfileArtis (ds) {
let ret = true
for (let idx = 1; idx <= 5; idx++) {
if (ds[idx]) {
if (!ds[idx].main || !ds[idx].attrs) {
ret = false
return ret
}
}
}
return ret
}
setArtisData (ds = {}, dataSource) {
if (!this.hasArtis || AvatarArtis.isProfileArtis(ds) || !AvatarArtis.isProfileArtis(this.artis)) {
for (let idx = 1; idx <= 5; idx++) {
if (ds[idx]) {
this.setArtis(idx, ds[idx] || {})
}
}
return true
}
return false
}
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
if (ds.main && ds.attrs) {
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 () {
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
}
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 () {
let ret = {}
for (let idx = 1; idx <= 5; idx++) {
let ds = this.artis[idx]
if (ds) {
let artis = Artifact.get(ds.name)
let tmp = {
...artis?.getData('img,name,set'),
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
}
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件套也会使用简写
*/
geSetData () {
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)
}
}
return {
sets,
names,
imgs,
abbrs: [...abbrs, ...abbrs2],
name: (abbrs.length > 1 || abbrs2[0]?.length > 7) ? abbrs.join('+') : abbrs2[0],
sName: abbrs.join('+')
}
}
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) {
AvatarArtis._eachArtisSet(this.sets, fn)
}
static getArtisKeyTitle () {
return ArtisMark.getKeyTitleMap()
}
}

244
models/AvatarData.js Normal file
View File

@ -0,0 +1,244 @@
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-lib/AttrCalc.js'
const charKey = 'name,abbr,sName,star,imgs,face,side,gacha,weaponTypeName'.split(',')
export default class AvatarData extends Base {
constructor (ds = {}, dataSource) {
super()
let char = Character.get({ id: ds.id, elem: ds.elem })
if (!char) {
return false
}
this.id = char.id
this.char = char
this.artis = new AvatarArtis(this.id)
this.setAvatar(ds, dataSource)
}
static create (ds, dataSource) {
let avatar = new AvatarData(ds)
if (!avatar) {
return false
}
return avatar
}
_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, 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) || {}
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 = {}, source = '', mode = 'original') {
const now = this._now || (new Date()) * 1
let ret = this.char.getAvatarTalent(ds, this.cons, mode)
this.talent = ret || this.talent
// 设置天赋更新时间
this._talent = ds._talent || this._talent || ds._time || now
if (source && ret) {
this._talent = now
}
}
/**
* 当前数据是否需要更新天赋
* @returns {boolean}
*/
needRefreshTalent () {
// 不存在天赋数据
if (!this.hasTalent) {
return true
}
// 超过2个小时的天赋数据进行请求
return (new Date() * 1) - this._talent > 3600 * 2 * 1000
}
async refreshTalent (mys) {
if (mys && mys.isSelfCookie) {
let char = this.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, this.cons, 'original')
if (ret) {
this.setTalent(ret, 'mys')
}
return true
}
return false
}
setArtis (ds, source) {
this.artis.setArtisData(ds.artis, source)
}
get hasTalent () {
return this.talent && !lodash.isEmpty(this.talent) && !!this._talent
}
get name () {
return this.char?.name || ''
}
/**
*
*/
get isProfile () {
// 检查数据源
if (!this._source || !['enka', 'change', 'miao'].includes(this._source)) {
return false
}
// 检查属性
if (!this.weapon || !this.talent || !this.artis) {
return false
}
// 检查旅行者
if (['空', '荧'].includes(this.name)) {
return !!this.elem
}
return true
}
getProfile () {
if (!this.isProfile) {
return false
}
return ProfileData.create(this)
}
// 判断当前profileData是否具备有效圣遗物信息
hasArtis () {
return this.hasData && this.artis.length > 0
}
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)
}
// 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') || {}
}
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 ''
}
/**
* 获取圣遗物套装属性
* @returns {boolean|*|{imgs: *[], names: *[], sets: {}, abbrs: *[], sName: string, name: (string|*)}|{}}
*/
get artisSet () {
return this.artis.geSetData()
}
getArtisDetail () {
return this.artis.getDetail()
}
}

View File

@ -6,8 +6,8 @@
* */
import Base from './Base.js'
import lodash from 'lodash'
import { Data, Common, Profile } from '../components/index.js'
import { Avatar, MysApi } from './index.js'
import { Data, Common } from '../components/index.js'
import { Avatar, MysApi, Player } from './index.js'
export default class AvatarList extends Base {
constructor (uid, datas = [], withProfile = false) {
@ -18,8 +18,9 @@ export default class AvatarList extends Base {
this.uid = uid
let avatars = {}
let profiles = {}
let player = Player.create(uid)
if (withProfile) {
profiles = Profile.getAll(uid)
// TODO
}
lodash.forEach(datas, (ds) => {
let avatar = new Avatar(ds, profiles[ds.id] || false)

236
models/Player.js Normal file
View File

@ -0,0 +1,236 @@
/**
* 用户数据文件
*/
import Base from './Base.js'
import { Data } from '../components/index.js'
import fs from 'fs'
import { ProfileReq, AvatarData } from './index.js'
import Profile from './player-lib/profile.js'
import lodash from 'lodash'
import MysAvatar from './player-lib/MysAvatar.js'
const _path = process.cwd()
const userPath = `${_path}/data/UserData/`
if (!fs.existsSync(userPath)) {
fs.mkdirSync(userPath)
}
export default class Player extends Base {
constructor (uid) {
super()
if (!uid) {
return false
}
let cacheObj = this._getCache(`player:${uid}`)
if (cacheObj) {
return cacheObj
}
this.uid = uid
this.reload()
return this._cache()
}
static create (e) {
if (e?._mys?.uid) {
// 传入为e
let player = new Player(e?._mys?.uid)
player.e = e
return player
} else {
return new Player(e)
}
}
/**
* 重新加载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,_mys,_profile')
ret.avatars = {}
lodash.forEach(this._avatars, (ds) => {
ret.avatars[ds.id] = ds.toJSON()
})
if (this._chars) {
ret.chars = this._chars
}
Data.writeJSON(`/data/UserData/${this.uid}.json`, ret, '', 'root')
}
/**
* 设置玩家基础数据
* @param ds
*/
setBasicData (ds) {
this.name = ds.name || this.name || ''
this.level = ds.level || this.level || 1
this.word = ds.word || this.word || 1
this.face = ds.face || this.face || ''
this.card = ds.card || this.card || ''
this.sign = ds.sign || this.sign || ''
this._avatars = this._avatars || {}
this._profile = ds._profile || this._profile
this._mys = ds._mys || this._mys
}
/**
* 设置角色列表
* @param ds
*/
setAvatars (ds) {
lodash.forEach(ds, (avatar) => {
this.setAvatar(avatar)
})
}
/**
* 设置角色
* @param ds
* @param dataSource
*/
setAvatar (ds, source) {
let avatar = this.getAvatar(ds.id)
avatar.setAvatar(ds, source)
}
/**
* 获取角色
* @param id
* @returns {*}
*/
getAvatar (id) {
if (!this._avatars[id]) {
this._avatars[id] = AvatarData.create({ id })
}
return this._avatars[id]
}
/**
* 循环角色
* @param fn
* @returns {Promise<boolean>}
*/
async forEachAvatarAsync (fn) {
for (let id in this._avatars) {
let ret = await fn(this._avatars[id], id)
if (ret === false) {
return false
}
}
}
forEachAvatar (fn) {
for (let id in this._avatars) {
let ret = fn(this._avatars[id], id)
if (ret === false) {
return false
}
}
}
getAvatarData (ids = '') {
let ret = {}
if (!ids) {
this.forEachAvatar((avatar) => {
ret[avatar.id] = avatar.getDetail()
})
} else {
lodash.forEach(ids, (id) => {
ret[id] = this.getAvatar(id)
})
}
return ret
}
/**
* 获取当前用户指定charid面板数据
* @param id
* @returns {*}
*/
getProfile (id) {
let avatar = this.getAvatar(id)
return avatar.getProfile()
}
/**
* 获取当前用户所有面板数据
* @returns {{}}
*/
getProfiles () {
let ret = {}
lodash.forEach(this._avatars, (avatar) => {
let profile = avatar.getProfile()
if (profile) {
ret[profile.id] = profile
}
})
return ret
}
/**
* 更新面板
* @param e
* @returns {Promise<boolean|*|undefined>}
*/
async refreshProfile (e, force = true) {
this._update = []
let { uid } = this
if (uid.toString().length !== 9) {
return false
}
let req = new ProfileReq({ e, uid })
try {
await req.request(this)
this._profile = new Date() * 1
this.save()
return this._update.length
} catch (err) {
console.log(err)
e.reply('请求失败')
return false
}
}
// 更新米游社数据
async refreshMys (force = false) {
return MysAvatar.refreshMys(this, force)
}
// 通过已有的Mys CharData更新
setMysCharData (charData) {
MysAvatar.setMysCharData(this, charData)
}
// 使用MysApi刷新指定角色的天赋信息
async refreshTalent (ids = '', force = false) {
return await MysAvatar.refreshTalent(this, ids, force)
}
/**
* 获取面板更新服务名
* @param uid
* @returns {*}
*/
static getProfileServName (uid) {
let Serv = ProfileReq.getServ(uid)
return Serv.name
}
static getAvatar (uid, charId, onlyHasData = false) {
return Profile.get(uid, charId, onlyHasData)
}
}

View File

@ -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)
}
}

View File

@ -6,7 +6,7 @@ import { Character, ProfileArtis, ProfileDmg } from './index.js'
import AttrCalc from './profile-lib/AttrCalc.js'
export default class ProfileData extends Base {
constructor (ds = {}, uid, attrCalc = true) {
constructor (ds = {}) {
super()
let char = Character.get({ id: ds.id, elem: ds.elem })
if (!char) {
@ -14,20 +14,16 @@ export default class ProfileData extends Base {
}
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) {
this.calcAttr()
}
}
static create (ds, uid) {
let profile = new ProfileData(ds, uid)
static create (ds) {
let profile = new ProfileData(ds)
if (!profile) {
return false
}
@ -50,16 +46,6 @@ export default class ProfileData extends Base {
this._time = ds._time || ds.updateTime || new Date() * 1
}
setAttr (ds) {
this.attr = lodash.extend(Data.getData(ds, 'atk,atkBase,def,defBase,hp,hpBase,mastery,recharge'), {
heal: ds.heal || ds.hInc || 0,
cpct: ds.cpct || ds.cRate,
cdmg: ds.cdmg || ds.cDmg,
dmg: ds.dmg || ds.dmgBonus || 0,
phy: ds.phy || ds.phyBonus || 0
})
}
setWeapon (ds = {}) {
this.weapon = {
name: ds.name,
@ -75,7 +61,7 @@ export default class ProfileData extends Base {
}
setArtis (ds = false) {
this.artis.setProfile(this, ds)
this.artis.setProfile(this, ds.artis || ds)
}
setTalent (ds = {}, mode = 'original') {

View File

@ -51,16 +51,16 @@ export default class ProfileReq extends Base {
this.e.reply(msg)
}
async request () {
async request (player) {
let Serv = ProfileReq.getServ(this.uid)
let reqParam = await Serv.getReqParam(this.uid)
let cdTime = await this.inCd()
if (cdTime) {
return this.err(`请求过快,请${cdTime}秒后重试..`)
// return this.err(`请求过快,请${cdTime}秒后重试..`)
}
await this.setCd(20)
this.msg(`开始获取uid:${this.uid}的数据,可能会需要一定时间~`)
// this.msg(`开始获取uid:${this.uid}的数据,可能会需要一定时间~`)
await sleep(100)
// 发起请求
let data = {}
@ -92,16 +92,12 @@ export default class ProfileReq extends Base {
if (data === false) {
return false
}
let userData = Serv.getUserData(data)
let profiles = Serv.getProfileData(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
}
}

View File

@ -75,12 +75,8 @@ 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], {})
}
}

View File

@ -3,8 +3,11 @@ import Character from './Character.js'
import Artifact from './Artifact.js'
import ArtifactSet from './ArtifactSet.js'
import Avatar from './Avatar.js'
import AvatarData from './AvatarData.js'
import AvatarArtis from './AvatarArtis.js'
import AvatarList from './AvatarList.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'
@ -24,7 +27,9 @@ export {
Artifact,
ArtifactSet,
Avatar,
AvatarData,
AvatarList,
AvatarArtis,
ProfileServ,
ProfileReq,
ProfileData,
@ -35,5 +40,6 @@ export {
Material,
Weapon,
User,
MysApi
MysApi,
Player
}

View File

@ -0,0 +1,123 @@
import lodash from 'lodash'
import { Common, Data } from '../../components/index.js'
const MysAvatar = {
/**
* 更新米游社角色信息
* @param player
* @param mys
* @param force
* @returns {Promise<boolean>}
*/
async refreshMys (player, force = false) {
let mys = player?.e?._mys
if (!mys) {
return false
}
// 不必要更新
if ((new Date() * 1 - player._mys < 10 * 60 * 1000) && !force) {
return false
}
let charData = await mys.getCharacter()
if (!charData || !charData.avatars) {
return false
}
MysAvatar.setMysCharData(player, charData)
},
/**
* 根据已有Mys CharData更新player
* @param player
* @param charData
*/
setMysCharData (player, charData) {
let role = charData.role
player.setBasicData({
level: role.level,
name: role.nickname
})
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')
})
player._mys = new Date() * 1
player.save()
},
/**
* 获取当前角色需要更新天赋的角色ID
* @param player
* @param ids 角色列表若传入则查询指定角色列表不传入查询全部
* @returns {*[]}
*/
getNeedRefreshIds (player, ids) {
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.needRefreshTalent()) {
ret.push(avatar.id)
}
})
return ret
},
/**
* 使用MysApi刷新指定角色的天赋信息
* @param player
* @param ids
* @param force
* @returns {Promise<boolean>}
*/
async refreshTalent (player, ids, force) {
let e = player?.e
let mys = e?._mys
if (!e || !mys) {
return false
}
let needReqIds = MysAvatar.getNeedRefreshIds(player, ids)
if (needReqIds.length > 0) {
if (needReqIds.length > 8) {
e && 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 = player.getAvatar(id)
skillRet.push(await avatar.refreshTalent(mys))
}
skillRet = await Promise.all(skillRet)
skillRet = skillRet.filter(item => item.id)
await Common.sleep(ms)
}
}
player.save()
}
}
export default MysAvatar

View File

@ -1,6 +1,6 @@
import lodash from 'lodash'
import enkaMeta from './enka-meta.js'
import { Character, ArtifactSet, ProfileData } from '../../models/index.js'
import { Character, ArtifactSet, ProfileData } from '../index.js'
const artiIdx = {
EQUIP_BRACER: 1,

View File

@ -1,6 +1,6 @@
import lodash from 'lodash'
import { Data } from '../index.js'
import { ProfileServ } from '../../models/index.js'
import { Data } from '../../components/index.js'
import { ProfileServ } from '../index.js'
import EnkaData from './enka-data.js'
let HttpsProxyAgent = ''

View File

@ -1,6 +1,28 @@
import { Character, ProfileData } from '../../models/index.js'
import { Character, Artifact } from '../index.js'
import lodash from 'lodash'
import { artiIdx, artiSetMap, attrMap } from './miao-meta.js'
const attrMap = {
HP: 'hpPlus',
HP_PERCENT: 'hp',
ATTACK: 'atkPlus',
ATTACK_PERCENT: 'atk',
DEFENSE: 'defPlus',
DEFENSE_PERCENT: 'def',
FIRE_ADD_HURT: '',
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'
}
let MiaoData = {
key: 'miao',
@ -27,70 +49,21 @@ let MiaoData = {
level: ds.level
}
},
getProfile (ds) {
setAvatar (player, ds) {
let char = Character.get(ds.id)
let profile = new ProfileData({ id: char.id })
profile.setBasic({
let avatar = player.getAvatar(ds.id)
let talentRet = MiaoData.getTalent(char.id, ds.skill)
avatar.setAvatar({
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
elem: talentRet.elem,
weapon: MiaoData.getWeapon(ds.weapon),
talent: talentRet.talent,
artis: MiaoData.getArtifact(ds.reliquary)
}, 'miao')
return avatar
},
getWeapon (weapon) {
return {
@ -116,18 +89,23 @@ let MiaoData = {
if (value && value < 1) {
value = value * 100
}
return [attrMap[name], value]
return { key: attrMap[name], value }
}
lodash.forEach(data, (ds) => {
let sub = ds.appendAffix || []
let idx = artiIdx[ds.type]
let idx = {
生之花: 1,
死之羽: 2,
时之沙: 3,
空之杯: 4,
理之冠: 5
}[ds.type]
if (!idx) {
return
}
ret[`arti${idx}`] = {
ret[idx] = {
name: ds.name,
set: artiSetMap[ds.name] || '',
set: Artifact.getSetNameByArti(ds.name) || '',
level: ds.level,
main: get(ds.mainAffix),
attrs: [
@ -162,7 +140,6 @@ let MiaoData = {
}
}
})
return {
talent: ret,
elem

View File

@ -1,6 +1,6 @@
import lodash from 'lodash'
import { Data } from '../index.js'
import { ProfileServ } from '../../models/index.js'
import { Data } from '../../components/index.js'
import { ProfileServ } from '../index.js'
import MiaoData from './miao-data.js'
export default new ProfileServ({
@ -18,19 +18,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
},
// 获取冷却时间

View File

@ -103,7 +103,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

View File

@ -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

View File

@ -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}}

View File

@ -1,117 +0,0 @@
import fs from 'fs'
import { Profile } from '../components/index.js'
import AttrCalc from '../models/profile-lib/AttrCalc.js'
import lodash from 'lodash'
let _path = process.cwd()
function testCalcAttr (profile) {
if (profile.hasData) {
let attrCalc = AttrCalc.create(profile)
let attr2 = attrCalc.calc()
let char = profile.char
let ret = {}
lodash.forEach(profile.attr, (val, key) => {
let diff = val - (attr2[key] || 0)
if (Math.abs(diff / val) > 0.005 && Math.abs(diff) > 0.99) {
ret[key] = [val, attr2[key]]
}
})
if (!lodash.isEmpty(ret)) {
let retKeys = lodash.keys(ret)
let retKeyStr = lodash.keys(ret).join(',')
let ret2 = lodash.extend({}, ret)
if (retKeyStr === 'hp') {
let [s, d] = ret.hp
let hpBase = profile.attr?.hpBase
let pct = Math.round((s - d) / hpBase * 100)
if ([6, 12, 18, 30, 25, 31, 37, 43, 55].includes(pct)) {
delete ret.hp
}
}
if ((ret.atkBase) || (retKeys.length === 2 && ret.atkBase && ret.atk)) {
if ([1, 20, 40, 50, 60, 70, 80, 90].includes(profile.weapon.level)) {
delete ret.atkBase
delete ret.atk
}
}
if (ret.def && ret.defBase && ret.hp && ret.hpBase) {
let [s, d] = ret.defBase
if (s > d && [1, 20, 40, 50, 60, 70, 80, 90].includes(profile.level)) {
delete ret.def
delete ret.defBase
delete ret.hp
delete ret.hpBase
delete ret[char.detail?.attr?.keys[3]]
}
}
if (retKeyStr === 'recharge') {
if (char.isTraveler && char.isElem('风')) {
delete ret.recharge
}
}
if (retKeyStr === 'dmg') {
let [s, d] = ret.dmg
let dmg = Math.round(Math.abs(s - d - 46.6))
console.log(dmg)
if ([0, 15].includes(dmg) || char.name === '莫娜') {
delete ret.dmg
}
}
let cmd = `#${profile.name}面板${profile.uid}`
if (lodash.isEmpty(ret)) {
console.log(`Calc IGNORE: ${cmd}`)
return true
} else {
console.log(`Calc Diff: ${cmd}`, ret2)
}
return false
} else {
console.log(`Calc OK:${profile.uid}:${profile.id}`)
return true
}
} else {
console.log('!has data')
return true
}
}
async function test (ignore) {
let files = fs.readdirSync(`${_path}/data/UserData`)
let count = 0
let total = 0
for (let file of files) {
if (count > 0) {
break
}
let testRet = /(\d{9}).json/.exec(file)
if (testRet && testRet[1]) {
let uid = testRet[1]
if (ignore.includes(uid * 1)) {
continue
}
Profile.forEach(uid, (profile) => {
if (count > 0) {
return false
}
let cmd = `#${profile.name}面板${profile.uid}`
if (ignore.includes(cmd)) {
return true
}
console.log(profile.id)
let ret = testCalcAttr(profile)
if (ret === false) {
count++
}
total++
})
}
}
console.log(`calc test done total:${total}`)
return true
}
const ignore = ['#荧面板100000023', '#班尼特面板100009630', '#荧面板100147429', '#八重神子面板100160080', '#纳西妲面板100181800', '#荧面板100211780',
'#九条裟罗面板100248492', '#砂糖面板100270260']
await test(ignore)