This commit is contained in:
Ctrlcvs 2023-05-09 21:03:33 +08:00
commit 27f41b9c4a
20 changed files with 1233 additions and 940 deletions

View File

@ -1,3 +1,9 @@
# 3.1.0 Dev
* !!!暂未完成,会有很多错误,不建议升级!!!
* 重构CK与UID管理逻辑
* 底层对星铁查询进行支持
# 3.0.2 # 3.0.2
* 3.6卡池以及图像武器别名等数据更新 **@cvs** * 3.6卡池以及图像武器别名等数据更新 **@cvs**

View File

@ -360,7 +360,6 @@ class PluginsLoader {
// e.isSr = true且命令标准化为 #星铁 开头 // e.isSr = true且命令标准化为 #星铁 开头
let srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/ let srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/
if (srReg.test(msg)) { if (srReg.test(msg)) {
console.log('sr test true')
e.isSr = true e.isSr = true
msg = msg.replace(srReg, '#星铁') msg = msg.replace(srReg, '#星铁')
} }

View File

@ -29,7 +29,7 @@ export class user extends plugin {
fnc: 'noLogin' fnc: 'noLogin'
}, },
{ {
reg: '^#?我的(ck|cookie)$', reg: '^#?(原神|星铁)?我的(ck|cookie)$',
event: 'message', event: 'message',
fnc: 'myCk' fnc: 'myCk'
}, },
@ -38,7 +38,7 @@ export class user extends plugin {
fnc: 'delCk' fnc: 'delCk'
}, },
{ {
reg: '^#绑定(uid|UID)?[1-9][0-9]{8}$', reg: '^#(原神|星铁)?绑定(uid|UID)?[1-9][0-9]{8}$',
fnc: 'bingUid' fnc: 'bingUid'
}, },
{ {
@ -157,7 +157,8 @@ export class user extends plugin {
/** 加载旧的绑定ck json */ /** 加载旧的绑定ck json */
loadOldData () { loadOldData () {
this.User.loadOldData() this.User.loadOldDataV2()
this.User.loadOldDataV3()
} }
/** 检查用户CK状态 **/ /** 检查用户CK状态 **/

View File

@ -14,7 +14,7 @@ export default class blueprint extends base {
let uid = await MysInfo.getUid(this.e, false) let uid = await MysInfo.getUid(this.e, false)
if (!uid) return false if (!uid) return false
/** 判断是否绑定了ck */ /** 判断是否绑定了ck */
let ck = await MysInfo.checkUidBing(uid) let ck = await MysInfo.checkUidBing(uid, this.e)
if (!ck) { if (!ck) {
await this.e.reply(MysInfo.tips) await this.e.reply(MysInfo.tips)
return false return false

View File

@ -18,7 +18,7 @@ export default class Calculator extends base {
if (!uid) return false if (!uid) return false
/** 判断是否绑定了ck */ /** 判断是否绑定了ck */
let ck = await MysInfo.checkUidBing(uid) let ck = await MysInfo.checkUidBing(uid, this.e)
if (!ck) { if (!ck) {
await this.e.reply(MysInfo.tips) await this.e.reply(MysInfo.tips)
return false return false

View File

@ -0,0 +1,26 @@
import { Sequelize, DataTypes, Model } from 'sequelize'
import { Data } from '#miao'
Data.createDir('/data/db', 'root')
let dbPath = process.cwd() + '/data/db/data.db'
// TODO DB自定义
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: dbPath,
logging: false
})
await sequelize.authenticate()
export default class BaseModel extends Model {
static Types = DataTypes
static initDB (model, columns) {
let name = model.name
console.log('Model Name', name)
name = name.replace(/DB$/, 's')
model.init(columns, { sequelize, tableName: name })
model.COLUMNS = columns
}
}

View File

@ -0,0 +1,57 @@
import BaseModel from './BaseModel.js'
const { Types } = BaseModel
const COLUMNS = {
// 用户IDqq为数字
ltuid: {
type: Types.INTEGER,
primaryKey: true
},
// MysUser类型mys / hoyolab
type: {
type: Types.STRING,
defaultValue: 'mys',
notNull: true
},
// CK
ck: Types.STRING,
device: Types.STRING,
gsUids: Types.STRING,
srUids: Types.STRING
}
class MysUserDB extends BaseModel {
static async find (ltuid = '', create = false) {
// DB查询
let mys = await MysUserDB.findByPk(ltuid)
if (!mys && create) {
mys = await MysUserDB.build({
ltuid
})
}
return mys || false
}
async saveDB (mys) {
if (!mys.ck || !mys.device || !mys.db) {
return false
}
let db = this
this.ck = mys.ck
this.type = mys.type
this.device = mys.device
this.gsUids = (mys.gsUids || []).join(',')
this.srUids = (mys.srUids || []).join(',')
await db.save()
}
}
BaseModel.initDB(MysUserDB, COLUMNS)
await MysUserDB.sync()
export default MysUserDB

View File

@ -0,0 +1,72 @@
import BaseModel from './BaseModel.js'
import lodash from 'lodash'
const { Types } = BaseModel
const COLUMNS = {
// 用户IDqq为数字
id: {
type: Types.STRING,
autoIncrement: false,
primaryKey: true
},
type: {
type: Types.STRING,
defaultValue: 'qq',
notNull: true
},
// 昵称
name: Types.STRING,
// 头像
face: Types.STRING,
ltuids: Types.STRING,
// 原神UID
gsUid: Types.STRING,
gsRegUids: Types.STRING,
// 星铁UID
srUid: Types.STRING,
srRegUids: Types.STRING
}
class UserDB extends BaseModel {
static async find (id, type = 'qq') {
// user_id
id = type === 'qq' ? '' + id : type + id
// DB查询
let user = await UserDB.findByPk(id)
if (!user) {
user = await UserDB.build({
id,
type
})
}
return user
}
async saveDB (user) {
let db = this
let ltuids = []
lodash.forEach(user.mysUsers, (mys) => {
if (mys.ck) {
ltuids.push(mys.ltuid)
}
})
db.ltuids = ltuids.join(',')
lodash.forEach(['gs', 'sr'], (key) => {
db[`${key}Uid`] = user[`${key}Uid`] ? user[`${key}Uid`] : user.uids[key]?.[0] || ''
db[`${key}RegUids`] = JSON.stringify(user.uidMap[key])
})
await this.save()
}
}
BaseModel.initDB(UserDB, COLUMNS)
await UserDB.sync()
export default UserDB

View File

@ -0,0 +1,12 @@
import UserDB from './UserDB.js'
import MysUserDB from './MysUserDB.js'
UserDB.belongsToMany(MysUserDB, {
through: 'UserLtuids'
})
MysUserDB.belongsToMany(UserDB, {
through: 'UserLtuids'
})
export { UserDB, MysUserDB }

View File

@ -1,6 +1,8 @@
/** /**
* 基础类提供实例缓存等一些基础方法 * 基础类提供实例缓存等一些基础方法
*/ */
import MysUtil from './MysUtil.js'
let cacheMap = {} let cacheMap = {}
let reFn = {} let reFn = {}
@ -12,19 +14,20 @@ export default class BaseModel {
// 获取缓存 // 获取缓存
_getThis (model, id = '', time = 10 * 60) { _getThis (model, id = '', time = 10 * 60) {
const uuid = `${model}:${id}` const uuid = `${model}:${id}`
this._uuid = uuid
if (uuid && cacheMap[uuid]) { if (uuid && cacheMap[uuid]) {
return cacheMap[uuid]._expire(time) return cacheMap[uuid]._expire(time)
} }
this._uuid = uuid
} }
// 设置缓存 // 设置缓存
_cacheThis (time = 10 * 60) { _cacheThis (model, id, time = 10 * 60) {
let id = this._uuid const uuid = this._uuid || `${model}:${id}`
if (id) { this._uuid = uuid
if (uuid) {
this._expire(time) this._expire(time)
cacheMap[id] = this cacheMap[uuid] = this
return cacheMap[id] return cacheMap[uuid]
} }
return this return this
} }
@ -44,4 +47,23 @@ export default class BaseModel {
return cacheMap[id] return cacheMap[id]
} }
} }
_delCache () {
let id = this._uuid
reFn[id] && clearTimeout(reFn[id])
delete reFn[id]
delete cacheMap[id]
}
gameKey (game = 'gs') {
return MysUtil.getGameKey(game)
}
isGs (game = 'gs') {
return this.gameKey(game) === 'gs'
}
isSr (game = 'gs') {
return this.gameKey(game) === 'sr'
}
} }

View File

@ -4,12 +4,12 @@ import BaseModel from './BaseModel.js'
const servs = ['mys', 'hoyolab'] const servs = ['mys', 'hoyolab']
// 超时时间不必精确直接定24小时即可 // 超时时间不必精确直接定24小时即可
const EX = 3600 * 24 const EX = 3600 * 24
const redisKeyRoot = 'Yz:genshin:mys:' const redisKeyRoot = 'Yz:cache:'
export default class DailyCache extends BaseModel { export default class DailyCache extends BaseModel {
constructor (uid) { constructor (uid, game = 'config') {
super() super()
const storeKey = DailyCache.getStoreKey(uid) const storeKey = DailyCache.getStoreKey(uid, game)
// 检查实例缓存 // 检查实例缓存
let self = this._getThis('store', storeKey) let self = this._getThis('store', storeKey)
if (self) { if (self) {
@ -22,40 +22,28 @@ export default class DailyCache extends BaseModel {
/** /**
* 传入UID或server标示返回当日存储对象 * 传入UID或server标示返回当日存储对象
* @param uid * @param uid
* @param game
* * 为空则返回与serv无关的dailyCache * * 为空则返回与serv无关的dailyCache
* * 传入UID会返回UID对应serv的cache对象 * * 传入UID会返回UID对应serv的cache对象
* * 传入servKey (mys/hoyolab)会返回指定的servCache * * 传入servKey (mys/hoyolab)会返回指定的servCache
* @returns {DailyCache} * @returns {DailyCache}
*/ */
static create (uid) { static create (uid, game = 'config') {
return new DailyCache(uid) return new DailyCache(uid, game)
}
/** ---- 基础方法 ---- **/
// 内部方法获取redis表key键值
getTableKey (key, sub = '') {
if (sub) {
return `${this.keyPre}:${key}-${sub}`
} else {
return `${this.keyPre}:${key}`
}
}
// 内部方法获取server key
static getServKey (uid) {
// 不传入uid为默认cache
if (!uid || uid === 'cache') {
return 'cache'
}
// 传入uid或sever key判断是mys还是hoyolab
return /^[6-9]|^hoyo|^os/i.test(uid) ? servs[1] : servs[0]
} }
// 内部方法获取redis表前缀 // 内部方法获取redis表前缀
static getStoreKey (uid) { static getStoreKey (uid, game = 'config') {
const serv = DailyCache.getServKey(uid) let key
if (!uid || game === 'config') {
key = 'sys:config'
} else {
game = game === 'sr' ? 'sr' : 'gs'
let serv = /^[6-9]|^hoyo|^os/i.test(uid) ? servs[1] : servs[0]
key = `${game}:${serv}`
}
const date = moment().format('MM-DD') const date = moment().format('MM-DD')
return `${serv}-${date}` return `${key}-${date}`
} }
/** /**
@ -78,8 +66,8 @@ export default class DailyCache extends BaseModel {
static async clearOutdatedData () { static async clearOutdatedData () {
let keys = await redis.keys(`${redisKeyRoot}*`) let keys = await redis.keys(`${redisKeyRoot}*`)
const date = moment().format('MM-DD') const date = moment().format('MM-DD')
const testReg = new RegExp(`^${redisKeyRoot}(mys|hoyo|hoyolab|cache)-\\d{2}-\\d{2}`) const testReg = new RegExp(`^${redisKeyRoot}(mys|hoyolab|config)-\\d{2}-\\d{2}`)
const todayReg = new RegExp(`^${redisKeyRoot}(mys|hoyo|hoyolab|cache)-${date}`) const todayReg = new RegExp(`^${redisKeyRoot}(mys|hoyolab|config)-${date}`)
for (let key of keys) { for (let key of keys) {
if (testReg.test(key) && !todayReg.test(key)) { if (testReg.test(key) && !todayReg.test(key)) {
await redis.del(key) await redis.del(key)
@ -87,6 +75,47 @@ export default class DailyCache extends BaseModel {
} }
} }
// 内部方法用于decode value
static decodeValue (value, decode = false) {
if (value && decode) {
try {
return JSON.parse(value)
} catch (e) {
return false
}
}
return value
}
// 内部方法用于encode value
static encodeValue (value) {
if (typeof (value) === 'object') {
return JSON.stringify(value) || ''
}
if (typeof (value) === 'undefined') {
return ''
}
return '' + value
}
/** ---- 基础方法 ---- **/
// 内部方法获取redis表key键值
getTableKey (key, sub = '') {
if (sub) {
return `${this.keyPre}:${key}-${sub}`
} else {
return `${this.keyPre}:${key}`
}
}
/**
* 基础数据结构Key-Value
*
* 每个key对应一个Value
* 使用redis kv存储,所有操作需要指定表名
*
* **/
/** /**
* 设置指定表的过期时间 * 设置指定表的过期时间
* @param table * @param table
@ -110,14 +139,6 @@ export default class DailyCache extends BaseModel {
await redis.del(this.getTableKey(table, 'count')) await redis.del(this.getTableKey(table, 'count'))
} }
/**
* 基础数据结构Key-Value
*
* 每个key对应一个Value
* 使用redis kv存储,所有操作需要指定表名
*
* **/
/** /**
* 获取表指定key内容 * 获取表指定key内容
* @param table 表名 * @param table 表名
@ -176,29 +197,6 @@ export default class DailyCache extends BaseModel {
return await redis.set(this.getTableKey(table), value, { EX }) return await redis.set(this.getTableKey(table), value, { EX })
} }
// 内部方法用于decode value
static decodeValue (value, decode = false) {
if (value && decode) {
try {
return JSON.parse(value)
} catch (e) {
return false
}
}
return value
}
// 内部方法用于encode value
static encodeValue (value) {
if (typeof (value) === 'object') {
return JSON.stringify(value) || ''
}
if (typeof (value) === 'undefined') {
return ''
}
return '' + value
}
/** /**
* 基础数据结构Key-List * 基础数据结构Key-List
* *

View File

@ -9,8 +9,11 @@ import DailyCache from './DailyCache.js'
import BaseModel from './BaseModel.js' import BaseModel from './BaseModel.js'
import NoteUser from './NoteUser.js' import NoteUser from './NoteUser.js'
import MysApi from './mysApi.js' import MysApi from './mysApi.js'
import MysUtil from './MysUtil.js'
import lodash from 'lodash' import lodash from 'lodash'
import fetch from 'node-fetch' import fetch from 'node-fetch'
import { MysUserDB, UserDB } from '../db/index.js'
import { Data } from '../../../miao-plugin/components/index.js'
const tables = { const tables = {
// ltuid-uid 查询表 // ltuid-uid 查询表
@ -36,9 +39,9 @@ const tables = {
} }
export default class MysUser extends BaseModel { export default class MysUser extends BaseModel {
constructor (data) {
constructor (ltuid) {
super() super()
let ltuid = data.ltuid
if (!ltuid) { if (!ltuid) {
return false return false
} }
@ -47,66 +50,33 @@ export default class MysUser extends BaseModel {
if (!self) { if (!self) {
self = this self = this
} }
// 单日有效缓存,不区分服务器 this.ltuid = ltuid
self.cache = self.cache || DailyCache.create()
self.uids = self.uids || []
self.ltuid = data.ltuid
self.ck = self.ck || data.ck
self.qq = self.qq || data.qq || 'pub'
if (data.uid || data.uids) {
self.addUid(data.uid || data.uids)
}
if (data.ck && data.ltuid) {
self.ckData = data
}
// 单日有效缓存使用uid区分不同服务器
self.servCache = self.servCache || DailyCache.create(self.uids[0] || 'mys')
return self._cacheThis() return self._cacheThis()
} }
// 可传入ltuid、cookie、ck对象来创建MysUser实例 // 可传入ltuid、cookie、ck对象来创建MysUser实例
// 在仅传入ltuid时必须是之前传入过的才能被识别 // 在仅传入ltuid时必须是之前传入过的才能被识别
static async create (data) { static async create (ltuid, db = false) {
if (!data) { ltuid = MysUtil.getLtuid(ltuid)
if (!ltuid) {
return false return false
} }
if (lodash.isPlainObject(data)) { let mys = new MysUser(ltuid)
return new MysUser(data) await mys.initDB(db)
} return mys
// 传入cookie }
let testRet = /ltuid=(\d{4,9})/g.exec(data)
if (testRet && testRet[1]) { static async forEach (fn) {
let ltuid = testRet[1] let dbs = await MysUserDB.findAll()
// 尝试使用ltuid创建 await Data.forEach(dbs, async (db) => {
let ckUser = await MysUser.create(ltuid) let mys = await MysUser.create(db.ltuid, db)
if (ckUser) { return await fn(mys)
return ckUser })
}
let uids = await MysUser.getCkUid(data)
if (uids) {
return new MysUser({
ltuid,
ck: data,
type: 'ck',
uids
})
}
}
// 传入ltuid
if (/^\d{4,9}$/.test(data)) {
// 查找ck记录
let cache = DailyCache.create()
let ckData = await cache.kGet(tables.ck, data, true)
if (ckData && ckData.ltuid) {
return new MysUser(ckData)
}
}
return false
} }
// 根据uid获取查询MysUser // 根据uid获取查询MysUser
static async getByQueryUid (uid, onlySelfCk = false) { static async getByQueryUid (uid, game = 'gs', onlySelfCk = false) {
let servCache = DailyCache.create(uid) let servCache = DailyCache.create(uid, game)
// 查找已经查询过的ltuid || 分配最少查询的ltuid // 查找已经查询过的ltuid || 分配最少查询的ltuid
// 根据ltuid获取mysUser 封装 // 根据ltuid获取mysUser 封装
@ -120,7 +90,7 @@ export default class MysUser extends BaseModel {
} }
// 若声明只获取自己ck则判断uid是否为本人所有 // 若声明只获取自己ck则判断uid是否为本人所有
if (onlySelfCk && !await ckUser.ownUid(uid)) { if (onlySelfCk && !ckUser.ownUid(uid, game)) {
return false return false
} }
@ -148,11 +118,12 @@ export default class MysUser extends BaseModel {
} }
static async eachServ (fn) { static async eachServ (fn) {
let servs = ['mys', 'hoyolab'] await MysUtil.eachServ(async (serv) => {
for (let serv of servs) { await MysUtil.eachGame(async (game) => {
let servCache = DailyCache.create(serv) let servCache = DailyCache.create(serv, game)
await fn(servCache, serv) await fn(servCache, serv, game)
} })
})
} }
// 清除当日缓存 // 清除当日缓存
@ -223,106 +194,6 @@ export default class MysUser extends BaseModel {
return count return count
} }
static async getGameRole (ck, serv = 'mys') {
let url = {
mys: 'https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie',
hoyolab: 'https://api-os-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_global'
}
let res = await fetch(url[serv], { method: 'get', headers: { Cookie: ck } })
if (!res.ok) return false
res = await res.json()
return res
}
// 获取米游社通行证id
static async getUserFullInfo (ck, serv = 'mys') {
let url = {
mys: 'https://bbs-api.mihoyo.com/user/wapi/getUserFullInfo?gids=2',
hoyolab: ''
}
let res = await fetch(url[serv], {
method: 'get',
headers: {
Cookie: ck,
Accept: 'application/json, text/plain, */*',
Connection: 'keep-alive',
Host: 'bbs-api.mihoyo.com',
Origin: 'https://m.bbs.mihoyo.com',
Referer: ' https://m.bbs.mihoyo.com/'
}
})
if (!res.ok) return res
res = await res.json()
return res
}
/**
* 获取ck对应uid列表
* @param ck 需要获取的ck
* @param withMsg false:uids / true: {uids, msg}
* @param force 忽略缓存强制更新
* @returns {Promise<{msg: *, uids}>}
*/
static async getCkUid (ck, withMsg = false, force = false) {
let ltuid = ''
let testRet = /ltuid=(\w{0,9})/g.exec(ck)
if (testRet && testRet[1]) {
ltuid = testRet[1]
}
let uids = []
let ret = (msg, retUid) => {
retUid = lodash.map(retUid, (a) => a + '')
return withMsg ? { msg, uids: retUid } : retUid
}
if (!ltuid) {
return ret('无ltuid', false)
}
if (!force) {
// 此处不使用DailyCache做长期存储
uids = await redis.get(`Yz:genshin:mys:ltuid-uids:${ltuid}`)
if (uids) {
uids = DailyCache.decodeValue(uids, true)
if (uids && uids.length > 0) {
return ret('', uids)
}
}
}
uids = []
let res = null
let msg = 'error'
for (let serv of ['mys', 'hoyolab']) {
let roleRes = await MysUser.getGameRole(ck, serv)
if (roleRes?.retcode === 0) {
res = roleRes
break
}
if (roleRes.retcode * 1 === -100) {
msg = '该ck已失效请重新登录获取'
}
msg = roleRes.message || 'error'
}
if (!res) return ret(msg, false)
if (!res.data.list || res.data.list.length <= 0) {
return ret('该账号尚未绑定原神或星穹角色', false)
}
for (let val of res.data.list) {
if (/\d{9}/.test(val.game_uid) && val.game_biz === 'hk4e_cn') {
uids.push(val.game_uid + '')
}
}
if (uids.length > 0) {
await redis.set(`Yz:genshin:mys:ltuid-uids:${ltuid}`, JSON.stringify(uids), { EX: 3600 * 24 * 90 })
return ret('', uids)
}
return ret(msg, false)
}
/** /**
* 检查CK状态 * 检查CK状态
* @param ck 需要检查的CK * @param ck 需要检查的CK
@ -376,81 +247,199 @@ export default class MysUser extends BaseModel {
} }
} }
// 不建议使用,为了兼容老数据格式,后续废弃
getCkInfo (game = 'gs') {
let gameKey = this.gameKey(game)
return {
ck: this.ck,
uid: this.getUid(game),
qq: '',
ltuid: this.ltuid
}
}
getUid (game = 'gs') {
return this.getUids(game)[0]
}
getUids (game = 'gs') {
let gameKey = this.gameKey(game)
return this[`${gameKey}Uids`] || []
}
/**
* 刷新mysUser的UID列表
* @returns {Promise<{msg: string, status: number}>}
*/
async reqMysUid () {
let err = (msg = 'error', status = 1) => {
return { status, msg }
}
let res = null
let msg = 'error'
for (let serv of ['mys', 'hoyolab']) {
let roleRes = await this.getGameRole(serv)
if (roleRes?.retcode === 0) {
res = roleRes
if (serv === 'hoyolab') {
this.type = 'hoyolab'
}
break
}
if (roleRes.retcode * 1 === -100) {
msg = '该ck已失效请重新登录获取'
}
msg = roleRes.message || 'error'
}
if (!res) return err(msg)
let playerList = res?.data?.list || []
playerList = playerList.filter(v => ['hk4e_cn', 'hkrpg_cn', 'hk4e_global', 'hkrpg_global'].includes(v.game_biz))
if (!playerList || playerList.length <= 0) {
return err('该账号尚未绑定原神或星穹角色')
}
this.gsUids = []
this.srUids = []
/** 米游社默认展示的角色 */
for (let val of playerList) {
this.addUid(val.game_uid, ['hk4e_cn', 'hk4e_global'].includes(val.game_biz) ? 'gs' : 'sr')
}
await this.save()
return { status: 0, msg: '' }
}
async getGameRole (serv = 'mys') {
let ck = this.ck
let url = {
mys: 'https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie',
hoyolab: 'https://api-os-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie'
}
let res = await fetch(url[serv], { method: 'get', headers: { Cookie: ck } })
if (!res.ok) return false
res = await res.json()
return res
}
// 获取米游社通行证id
async getUserFullInfo (serv = 'mys') {
let ck = this.ck
let url = {
mys: 'https://bbs-api.mihoyo.com/user/wapi/getUserFullInfo?gids=2',
hoyolab: ''
}
let res = await fetch(url[serv], {
method: 'get',
headers: {
Cookie: ck,
Accept: 'application/json, text/plain, */*',
Connection: 'keep-alive',
Host: 'bbs-api.mihoyo.com',
Origin: 'https://m.bbs.mihoyo.com',
Referer: ' https://m.bbs.mihoyo.com/'
}
})
if (!res.ok) return res
res = await res.json()
return res
}
getCache (game = 'gs') {
if (!this.cache) {
this.cache = {}
}
const { cache } = this
if (game !== 'config') {
game = this.gameKey(game)
}
if (!cache[game]) {
cache[game] = DailyCache.create(this.type, game)
}
return cache[game]
}
// 初始化数据
async initDB (db = false) {
if (this.db && !db) {
return
}
db = db && db !== true ? db : await MysUserDB.find(this.ltuid, true)
this.db = db
this.setCkData(db)
}
// 设置ck数据
setCkData (data = {}) {
this.ck = data.ck || this.ck || ''
this.type = data.type || this.type || 'mys'
this.device = data.device || this.device || MysUtil.getDeviceGuid()
let self = this
MysUtil.eachGame((game) => {
let key = `${game}Uids`
self[key] = lodash.isString(data[key]) ? (data[key] || '').split(',') : (lodash.isArray(data[key]) ? data[key] : (self[key] || []))
})
}
async save () {
await this.db.saveDB(this)
}
// 为当前MysUser绑定uid // 为当前MysUser绑定uid
addUid (uid) { addUid (uid, game = 'gs') {
if (lodash.isArray(uid)) { if (lodash.isArray(uid)) {
for (let u of uid) { for (let u of uid) {
this.addUid(u) this.addUid(u, game)
} }
return true return true
} }
uid = '' + uid uid = '' + uid
if (/\d{9}/.test(uid) || uid === 'pub') { if (/\d{9}/.test(uid)) {
if (!this.uids.includes(uid)) { let gameKey = this.gameKey(game)
this.uids.push(uid) let uids = this[`${gameKey}Uids`]
if (!uids.includes(uid)) {
uids.push(uid)
} }
} }
return true return true
} }
hasGame (game = 'gs') {
return (this.isGs(game) ? this.gsUids : this.srUids).length > 0
}
// 初始化当前MysUser缓存记录 // 初始化当前MysUser缓存记录
async initCache (user) { async initCache () {
if (!this.ltuid || !this.servCache || !this.ck) { if (!this.ltuid || !this.ck) {
return return
} }
let self = this
// 为当前MysUser添加uid查询记录 await MysUtil.eachGame(async (game) => {
if (!lodash.isEmpty(this.uids)) { let uids = self[`${game}Uids`]
for (let uid of this.uids) { await this.addQueryUid(uids, game)
if (uid !== 'pub') { let cache = self.getCache(game)
await this.addQueryUid(uid) let cacheSearchList = await cache.get(tables.del, this.ltuid, true)
// 添加ltuid-uid记录用于判定ltuid绑定个数及自ltuid查询 // 这里不直接插入,只插入当前查询记录中没有的值
await this.cache.zAdd(tables.uid, this.ltuid, uid) if (cacheSearchList && cacheSearchList.length > 0) {
for (let searchedUid of cacheSearchList) {
// 检查对应uid是否有新的查询记录
if (!await this.getQueryLtuid(searchedUid, game)) {
await this.addQueryUid(searchedUid, game)
}
} }
} }
} else { })
console.log(`ltuid:${this.ltuid}暂无uid信息请检查...`)
// 公共ck暂无uid信息不添加
if (user?.qq === 'pub') {
return false
}
}
// 缓存ckData供后续缓存使用
// ltuid关系存储到与server无关的cache中方便后续检索
if (this.ckData && this.ckData.ck) {
await this.cache.kSet(tables.ck, this.ltuid, this.ckData)
}
// 缓存qq用于删除ltuid时查找
if (user && user.qq) {
let qq = user.qq === 'pub' ? 'pub' : user.qq * 1
let qqArr = await this.cache.kGet(tables.qq, this.ltuid, true)
if (!lodash.isArray(qqArr)) {
qqArr = []
}
if (!qqArr.includes(qq)) {
qqArr.push(qq)
await this.cache.kSet(tables.qq, this.ltuid, qqArr)
}
}
// 从删除记录中查找并恢复查询记录
let cacheSearchList = await this.servCache.get(tables.del, this.ltuid, true)
// 这里不直接插入,只插入当前查询记录中没有的值
if (cacheSearchList && cacheSearchList.length > 0) {
for (let searchedUid of cacheSearchList) {
// 检查对应uid是否有新的查询记录
if (!await this.getQueryLtuid(searchedUid)) {
await this.addQueryUid(searchedUid)
}
}
}
return true return true
} }
async disable () { async disable (game = 'gs') {
await this.servCache.zDel(tables.detail, this.ltuid) let cache = this.getCache(game)
logger.mark(`[标记无效ck][ltuid:${this.ltuid}]`) await cache.zDel(tables.detail, this.ltuid)
logger.mark(`[标记无效ck][game:${game}, ltuid:${this.ltuid}`)
} }
// //
@ -498,28 +487,38 @@ export default class MysUser extends BaseModel {
} }
// 为当前用户添加uid查询记录 // 为当前用户添加uid查询记录
async addQueryUid (uid) { async addQueryUid (uid, game = 'gs') {
if (lodash.isArray(uid)) {
for (let u of uid) {
await this.addQueryUid(u, game)
}
return
}
if (uid) { if (uid) {
await this.servCache.zAdd(tables.detail, this.ltuid, uid) let cache = this.getCache(game)
await cache.zAdd(tables.detail, this.ltuid, uid)
} }
} }
// 获取当前用户已查询uid列表 // 获取当前用户已查询uid列表
async getQueryUids () { async getQueryUids (game = 'gs') {
return await this.servCache.zList(tables.detail, this.ltuid) let cache = this.getCache('game')
return await cache.zList(tables.detail, this.ltuid)
} }
// 根据uid获取查询ltuid // 根据uid获取查询ltuid
async getQueryLtuid (uid) { async getQueryLtuid (uid, game = 'gs') {
return await this.servCache.zKey(tables.detail, uid) let cache = this.getCache('game')
return await cache.zKey(tables.detail, uid)
} }
// 检查指定uid是否为当前MysUser所有 // 检查指定uid是否为当前MysUser所有
async ownUid (uid) { ownUid (uid, game = 'gs') {
if (!uid) { if (!uid) {
return false return false
} }
let uidArr = await this.cache.zList(tables.uid, this.ltuid) || [] let gameKey = this.gameKey(game)
return uid && uidArr.join(',').split(',').includes(uid + '') let uids = this[`${gameKey}Uids`]
return uids.includes(uid + '')
} }
} }

View File

@ -0,0 +1,47 @@
import { Data } from '#miao'
const MysUtil = {
// 获取标准ltuid
getLtuid (data) {
if (!data) {
return false
}
if (/^\d{4,9}$/.test(data)) {
return data
}
let testRet = /ltuid=(\d{4,9})/g.exec(data.ck || data)
if (testRet && testRet[1]) {
return testRet[1]
}
return false
},
// 获取标准gameKey
getGameKey (game) {
// 兼容e的处理
if (game.user_id) {
return game.isSr ? 'sr' : 'gs'
}
return ['sr', 'star'].includes(game) ? 'sr' : 'gs'
},
// 生成设备guid
getDeviceGuid () {
function S4 () {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
}
return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
},
// 循环game
async eachGame (fn) {
await Data.forEach(['gs', 'sr'], fn)
},
// 循环server
async eachServ (fn) {
await Data.forEach(['mys', 'hoyolab'], fn)
}
}
export default MysUtil

View File

@ -9,9 +9,11 @@ import BaseModel from './BaseModel.js'
import lodash from 'lodash' import lodash from 'lodash'
import MysUser from './MysUser.js' import MysUser from './MysUser.js'
import gsCfg from '../gsCfg.js' import gsCfg from '../gsCfg.js'
import { UserDB } from '../db/index.js'
import { Data } from '#miao'
export default class NoteUser extends BaseModel { export default class NoteUser extends BaseModel {
constructor (qq, data = null) { constructor (qq) {
super() super()
// 检查实例缓存 // 检查实例缓存
let cacheObj = this._getThis('user', qq) let cacheObj = this._getThis('user', qq)
@ -19,77 +21,22 @@ export default class NoteUser extends BaseModel {
return cacheObj return cacheObj
} }
this.qq = qq this.qq = qq
if (data) {
this.ckData = this.ckData || {}
for (let uid in data) {
let ck = data[uid]
if (uid && ck.uid) {
this.ckData[uid] = ck
}
}
} else if (!this.ckData) {
this._getCkData()
}
// 缓存实例
return this._cacheThis() return this._cacheThis()
} }
// 初始化 user
/**
* 创建NoteUser实例
* @param qq NoterUser对应idqq
* * 若传入e对象则会识别e.user_id并将user对象添加至e.user
* @param data 用户对应MysCookie数据为空则自动读取
* @returns {Promise<NoteUser|*>}
*/
static async create (qq, data = null) {
// 兼容处理传入e
if (qq && qq.user_id) {
let e = qq
let user = await NoteUser.create(e.user_id)
e.user = user
return user
}
let user = new NoteUser(qq, data)
// 检查绑定uid (regUid)
await user.getRegUid()
// 传入data则使用否则读取
return user
}
static async forEach (fn) {
// 初始化用户缓存
let res = await gsCfg.getBingCk()
for (let qq in res.noteCk) {
let cks = res.noteCk[qq]
if (!lodash.isEmpty(cks)) {
let user = await NoteUser.create(qq, cks)
if (user && fn) {
if (await fn(user) === false) {
break
}
}
}
}
}
/** /**
* 获取当前用户uid * 获取当前用户uid
* 如果为绑定用户优先获取ck对应uid否则获取绑定uid * 如果为绑定用户优先获取ck对应uid否则获取绑定uid
*/ */
get uid () { get uid () {
// 如果绑定有CK return this.getUid()
if (this.hasCk) {
return this.mainCk?.uid
}
return this._regUid || ''
} }
/** /**
* 当前用户是否具备CK * 当前用户是否具备CK
*/ */
get hasCk () { get hasCk () {
return this.ckData && !lodash.isEmpty(this.ckData) return !lodash.isEmpty(this.mysUsers)
} }
/** /**
@ -119,139 +66,242 @@ export default class NoteUser extends BaseModel {
* @returns { {ltuid:{ckData, ck, uids}} } * @returns { {ltuid:{ckData, ck, uids}} }
*/ */
get cks () { get cks () {
console.log('NoteUser.cks 即将废弃')
let game = 'gs'
let cks = {} let cks = {}
if (!this.hasCk) { if (!this.hasCk) {
return cks return cks
} }
for (let uid in this.ckData) { for (let ltuid in this.mysUsers) {
let ck = this.ckData[uid] let mys = this.mysUsers[ltuid]
if (ck && ck.ltuid && ck.uid) { if (mys && mys.ltuid && mys.uid) {
cks[ck.ltuid] = cks[ck.ltuid] || { cks[ltuid] = cks[ltuid] || {
ckData: ck, ckData: mys.getCkInfo(game),
ck: ck.ck, ck: mys.ck,
uids: [] uids: mys.getUids(game)
} }
cks[ck.ltuid].uids.push(ck.uid)
} }
} }
return cks return cks
} }
/**
* 创建NoteUser实例
* @param qq NoterUser对应idqq
* @param db
* * 若传入e对象则会识别e.user_id并将user对象添加至e.user
* @param data 用户对应MysCookie数据为空则自动读取
* @returns {Promise<NoteUser|*>}
*/
static async create (qq, db = false) {
// 兼容处理传入e
if (qq && qq.user_id) {
let e = qq
let user = await NoteUser.create(e.user_id)
e.user = user
return user
}
let user = new NoteUser(qq)
await user.initDB(db)
// 检查绑定uid (regUid)
await user.getRegUid()
// 传入data则使用否则读取
return user
}
static async forEach (fn) {
let dbs = await UserDB.findAll()
await Data.forEach(users, async (db) => {
let user = await NoteUser.create(db.id, db)
return await fn(user)
})
}
// 初始化数据
async initDB (db = false) {
if (this.db && !db) {
return
}
// 为后续多类型用户兼容
this.db = db && db !== true ? db : await UserDB.find(this.qq, 'qq')
await this.initMysUser()
this.initUids()
}
// 初始化MysUser对象
async initMysUser () {
let ltuids = this.db?.ltuids || ''
this.mysUsers = {}
for (let ltuid of ltuids.split(',')) {
let mys = await MysUser.create(ltuid)
if (mys) {
this.mysUsers[ltuid] = mys
}
}
}
// 初始化Uid
initUids () {
let self = this
self.uids = {}
self.uidMap = {}
const { db, uids, uidMap, mysUsers } = self
lodash.forEach(['gs', 'sr'], (key) => {
// 绑定UID
uidMap[key] = {}
uids[key] = []
// 设置CK UID
lodash.forEach(mysUsers, (mys) => {
lodash.forEach(mys[`${key}Uids`], (uid) => {
if (uid && !uidMap[key][uid]) {
uidMap[key][uid] = { uid, type: 'ck', ltuid: mys.ltuid }
uids[key].push(uid)
}
})
})
let regUids = db[`${key}RegUids`] || '{}'
try {
regUids = JSON.parse(regUids)
} catch (e) {
regUids = {}
}
lodash.forEach(['verify', 'reg'], (uidType) => {
lodash.forEach(regUids, (ds, uid) => {
if (uid && ds.type === uidType && !uidMap[key][uid]) {
uidMap[key][uid] = { uid, type: ds.type }
uids[key].push(uid)
}
})
})
self[`${key}Uid`] = self[`${key}Uid`] || db[`${key}Uid`] || uids[key]?.[0] || ''
})
}
async save () {
await this.db.saveDB(this)
}
// 获取当前UID
getUid (game = 'gs') {
return this.isGs(game) ? this.gsUid : this.srUid
}
getSelfUid (game = 'gs') {
let gameKey = this.gameKey(game)
let uids = this[`${gameKey}UidMap`].filter((v) => v.type === 'ck')
if (uids.length === 0) {
return false
}
let find = lodash.find(uids, (v) => v.uid + '' === uid + '', 0)
return find ? find.uid : uids[0].uid
}
// 获取UID列表
getUidList (game = 'gs') {
let ret = []
let gameKey = this.gameKey(game)
lodash.forEach(this.uids[gameKey], (uid) => {
ret.push(this.uidMap[gameKey][uid])
})
return ret
}
// 获取当前UID数据
getUidData (game = 'gs') {
let gameKey = this.gameKey(game)
let uid = this.getUid(game)
return this.uidMap[gameKey][uid]
}
// 获取当前的MysUser对象
getMysUser (game = 'gs') {
if (lodash.isEmpty(this.mysUsers)) {
return false
}
let uidData = this.getUidData(game)
let ltuid = lodash.keys(this.mysUsers)[0]
if (uidData.type === 'ck') {
ltuid = uidData.ltuid
}
return this.mysUsers[ltuid]
}
// 添加UID
addRegUid (uid, game = 'gs') {
let gameKey = this.gameKey(game)
if (!this.uidMap[gameKey][uid]) {
this.uidMap[gameKey][uid] = { uid, type: 'reg' }
this.uids[gameKey].push(uid)
this.setMainUid(uid, game)
}
}
// 删除UID
delRegUid (uid, game = 'gs') {
let gameKey = this.gameKey(game)
if (this.uidMap[gameKey][uid] && this.uidMap[gameKey][uid].type !== 'ck') {
delete this.uidMap[gameKey][uid]
lodash.remove(this.uids[gameKey], (u) => u === uid)
}
}
/** /**
* 获取当前用户的绑定UID * 获取当前用户的绑定UID
* 主要供内部调用建议使用 user.uid 获取用户uid * 主要供内部调用建议使用 user.uid 获取用户uid
* @returns {Promise<*>} * @returns {Promise<*>}
*/ */
async getRegUid (redisKey = `Yz:genshin:mys:qq-uid:${this.qq}`) { async getRegUid (game = 'gs') {
let uid = await redis.get(redisKey) let gameKey = this.gameKey(game)
if (uid) { return this[`${gameKey}Uid`] || ''
await redis.setEx(redisKey, 3600 * 24 * 30, uid)
}
this._regUid = uid
return this._regUid
} }
/** /**
* 设置当前用户的绑定uid * 设置当前用户的绑定uid
* @param uid 要绑定的uid * @param uid 要绑定的uid
* @param game
* @param force 若已存在绑定uid关系是否强制更新 * @param force 若已存在绑定uid关系是否强制更新
*/ */
async setRegUid (uid = '', force = false) { async setRegUid (uid = '', game = 'gs', force = false) {
let redisKey = `Yz:genshin:mys:qq-uid:${this.qq}` if (this.getRegUid(game) && false) {
if (uid && /[1|2|5-9][0-9]{8}/.test(uid)) { return uid
uid = String(uid)
const oldUid = await this.getRegUid()
// force true、不存在绑定UIDUID一致时存储并更新有效期
if (force || !oldUid || oldUid === uid) {
await redis.setEx(redisKey, 3600 * 24 * 30, uid)
}
this._regUid = uid
return String(this._regUid) || ''
} }
return '' await this.addRegUid(uid, game)
return uid
} }
/** // 切换绑定CK生效的UID
* 切换绑定CK生效的UID setMainUid (uid = '', game = 'gs') {
* @param uid 要切换的UID let gameKey = this.gameKey(game)
*/
async setMainUid (uid = '') {
// 兼容传入index // 兼容传入index
if (uid * 1 <= this.ckUids.length) uid = this.ckUids[uid] if (uid < 100 && this.uids[gameKey][uid]) {
// 非法值以及未传入时使用当前或首个有效uid uid = this.uids[gameKey][uid]
uid = (uid || this.uid) * 1 }
// 设置主uid
lodash.forEach(this.ckData, (ck) => {
ck.isMain = ck.uid * 1 === uid * 1
})
// 保存CK数据
this._saveCkData()
await this.setRegUid(uid, true)
}
/** if (this.uidMap[gameKey][uid]) {
* 初始化或重置 当前用户缓存 if (this.isGs(game)) {
*/ this.gsUid = uid
async initCache () { } else {
// 刷新绑定CK的缓存 this.srUid = uid
let count = 0
let cks = this.cks
for (let ltuid in cks) {
let { ckData, uids } = cks[ltuid]
let mysUser = await MysUser.create(ckData)
for (let uid of uids) {
mysUser.addUid(uid)
}
if (mysUser && await mysUser.initCache(this)) {
count++
} }
} }
return count
} }
/** // 添加MysUser
* 为当前用户增加CK addMysUser (mysUser) {
* @param cks 绑定的ck this.mysUsers[mysUser.ltuid] = mysUser
*/ this.initUids()
async addCk (cks) {
let ckData = this.ckData
lodash.forEach(cks, (ck, uid) => {
ckData[uid] = ck
})
this._saveCkData()
await this.initCache()
} }
/** // 删除当前用户绑定CK
* 删除当前用户绑定CK
* @param ltuid 根据ltuid删除未传入则删除当前生效uid对应ltuid
* @param needRefreshCache 是否需要刷新cache默认刷新
*/
async delCk (ltuid = '', needRefreshCache = true) { async delCk (ltuid = '', needRefreshCache = true) {
if (!ltuid) { if (!this.mysUsers[ltuid]) {
ltuid = this.mainCk.ltuid return false
} }
let ret = [] delete this.mysUsers[ltuid]
ltuid = ltuid * 1 this.initUids()
let ckData = this.ckData
for (let uid in ckData) {
let ck = ckData[uid]
if (ltuid && ck.ltuid * 1 === ltuid) {
ret.push(uid)
delete ckData[uid]
}
}
// 刷新主ck并保存ckData
await this.setMainUid()
// 刷新MysUser缓存
if (needRefreshCache) {
let ckUser = await MysUser.create(ltuid)
if (ckUser) {
await ckUser.del(this)
}
}
return ret
} }
/** /**
@ -291,15 +341,4 @@ export default class NoteUser extends BaseModel {
} }
return ret return ret
} }
// 内部方法读取CK数据
_getCkData () {
this.ckData = gsCfg.getBingCkSingle(this.qq)
return this.ckData
}
// 内部方法写入CK数据
_saveCkData () {
gsCfg.saveBingCk(this.qq, this.ckData)
}
} }

View File

@ -4,140 +4,140 @@
* 临时处理后续大概率重写 主要原因 * 临时处理后续大概率重写 主要原因
*/ */
export default class apiTool { export default class apiTool {
/** /**
* *
* @param {用户uid} uid * @param {用户uid} uid
* @param {区服} server * @param {区服} server
* @param {是否为星穹铁道或其他游戏? type(bool or string)} isSr * @param {是否为星穹铁道或其他游戏? type(bool or string)} isSr
*/ */
constructor(uid, server, isSr = false) { constructor (uid, server, isSr = false) {
this.uid = uid this.uid = uid
this.isSr = isSr this.isSr = isSr
this.server = server this.server = server
this.game='genshin' this.game = 'genshin'
if(isSr) this.game='honkaisr' if (isSr) this.game = 'honkaisr'
if(typeof isSr !== 'boolean'){ if (typeof isSr !== 'boolean') {
this.game=isSr this.game = isSr
}
}
getUrlMap = (data = {}) => {
let host, hostRecord
if (['cn_gf01', 'cn_qd01', 'prod_gf_cn', 'prod_qd_cn'].includes(this.server)) {
host = 'https://api-takumi.mihoyo.com/'
hostRecord = 'https://api-takumi-record.mihoyo.com/'
} else if (['os_usa', 'os_euro', 'os_asia', 'os_cht'].includes(this.server)) {
host = 'https://api-os-takumi.mihoyo.com/'
hostRecord = 'https://bbs-api-os.mihoyo.com/'
}
let urlMap = {
genshin: {
/** 首页宝箱 */
index: {
url: `${hostRecord}game_record/app/genshin/api/index`,
query: `role_id=${this.uid}&server=${this.server}`
},
/** 深渊 */
spiralAbyss: {
url: `${hostRecord}game_record/app/genshin/api/spiralAbyss`,
query: `role_id=${this.uid}&schedule_type=${data.schedule_type || 1}&server=${this.server}`
},
/** 角色详情 */
character: {
url: `${hostRecord}game_record/app/genshin/api/character`,
body: { role_id: this.uid, server: this.server }
},
/** 树脂 */
dailyNote: {
url: `${hostRecord}game_record/app/genshin/api/dailyNote`,
query: `role_id=${this.uid}&server=${this.server}`
},
/** 详情 */
detail: {
url: `${host}event/e20200928calculate/v1/sync/avatar/detail`,
query: `uid=${this.uid}&region=${this.server}&avatar_id=${data.avatar_id}`
},
/** 札记 */
ys_ledger: {
url: 'https://hk4e-api.mihoyo.com/event/ys_ledger/monthInfo',
query: `month=${data.month}&bind_uid=${this.uid}&bind_region=${this.server}`
},
/** 养成计算器 */
compute: {
url: `${host}event/e20200928calculate/v2/compute`,
body: data
},
blueprintCompute: {
url: `${host}event/e20200928calculate/v1/furniture/compute`,
body: data
},
/** 养成计算器 */
blueprint: {
url: `${host}event/e20200928calculate/v1/furniture/blueprint`,
query: `share_code=${data.share_code}&region=${this.server}`
},
/** 角色技能 */
avatarSkill: {
url: `${host}event/e20200928calculate/v1/avatarSkill/list`,
query: `avatar_id=${data.avatar_id}`
},
/** 七圣召唤数据 */
basicInfo: {
url: `${hostRecord}game_record/app/genshin/api/gcg/basicInfo`,
query: `role_id=${this.uid}&server=${this.server}`
},
/**使用兑换码 目前仅限国际服,来自于国服的uid请求已在myinfo.js的init方法提前拦截 */
useCdk: {
url: 'PLACE_HOLDER',
query: null
} }
},
honkaisr: {
/** 首页宝箱 */
index: {
url: `${hostRecord}game_record/app/hkrpg/api/index`,
query: `role_id=${this.uid}&server=${this.server}`
},
UserGame: {
url: `${host}common/badge/v1/login/account`,
body: { uid: this.uid, region: this.server, lang: 'zh-cn', game_biz: 'hkrpg_cn' }
},
/**
* 开拓阅历接口
*/
ys_ledger: {
url: `${host}/event/srledger/month_info`,
query: `region=${this.server}&uid=${this.uid}&month=${data.month}`
},
/** 角色详情 */
character: {
url: `${hostRecord}game_record/app/hkrpg/api/avatar/info`,
body: { role_id: this.uid, server: this.server }
},
/** 树脂 */
dailyNote: {
url: `${hostRecord}game_record/app/hkrpg/api/note`,
query: `role_id=${this.uid}&server=${this.server}`
}
}
} }
if (this.server.startsWith('os')) {
getUrlMap = (data = {}) => { urlMap.genshin.detail.url = 'https://sg-public-api.hoyolab.com/event/calculateos/sync/avatar/detail'// 角色天赋详情
let host, hostRecord urlMap.genshin.detail.query = `lang=zh-cn&uid=${this.uid}&region=${this.server}&avatar_id=${data.avatar_id}`
if (['cn_gf01', 'cn_qd01', 'prod_gf_cn','prod_qd_cn'].includes(this.server)) { urlMap.genshin.avatarSkill.url = 'https://sg-public-api.hoyolab.com/event/calculateos/avatar/skill_list'// 查询未持有的角色天赋
host = 'https://api-takumi.mihoyo.com/' urlMap.genshin.avatarSkill.query = `lang=zh-cn&avatar_id=${data.avatar_id}`
hostRecord = 'https://api-takumi-record.mihoyo.com/' urlMap.genshin.compute.url = 'https://sg-public-api.hoyolab.com/event/calculateos/compute'// 已支持养成计算
} else if (['os_usa', 'os_euro', 'os_asia', 'os_cht'].includes(this.server)) { urlMap.genshin.blueprint.url = 'https://sg-public-api.hoyolab.com/event/calculateos/furniture/blueprint'
host = 'https://api-os-takumi.mihoyo.com/' urlMap.genshin.blueprint.query = `share_code=${data.share_code}&region=${this.server}&lang=zh-cn`
hostRecord = 'https://bbs-api-os.mihoyo.com/' urlMap.genshin.blueprintCompute.url = 'https://sg-public-api.hoyolab.com/event/calculateos/furniture/compute'
} urlMap.genshin.blueprintCompute.body = { lang: 'zh-cn', ...data }
let urlMap = { urlMap.genshin.ys_ledger.url = 'https://hk4e-api-os.mihoyo.com/event/ysledgeros/month_info'// 支持了国际服札记
genshin: { urlMap.genshin.ys_ledger.query = `lang=zh-cn&month=${data.month}&uid=${this.uid}&region=${this.server}`
/** 首页宝箱 */ urlMap.genshin.useCdk.url = 'https://sg-hk4e-api.hoyoverse.com/common/apicdkey/api/webExchangeCdkey'
index: { urlMap.genshin.useCdk.query = `uid=${this.uid}&region=${this.server}&lang=zh-cn&cdkey=${data.cdk}&game_biz=hk4e_global`
url: `${hostRecord}game_record/app/genshin/api/index`,
query: `role_id=${this.uid}&server=${this.server}`
},
/** 深渊 */
spiralAbyss: {
url: `${hostRecord}game_record/app/genshin/api/spiralAbyss`,
query: `role_id=${this.uid}&schedule_type=${data.schedule_type || 1}&server=${this.server}`
},
/** 角色详情 */
character: {
url: `${hostRecord}game_record/app/genshin/api/character`,
body: { role_id: this.uid, server: this.server }
},
/** 树脂 */
dailyNote: {
url: `${hostRecord}game_record/app/genshin/api/dailyNote`,
query: `role_id=${this.uid}&server=${this.server}`
},
/** 详情 */
detail: {
url: `${host}event/e20200928calculate/v1/sync/avatar/detail`,
query: `uid=${this.uid}&region=${this.server}&avatar_id=${data.avatar_id}`
},
/** 札记 */
ys_ledger: {
url: 'https://hk4e-api.mihoyo.com/event/ys_ledger/monthInfo',
query: `month=${data.month}&bind_uid=${this.uid}&bind_region=${this.server}`
},
/** 养成计算器 */
compute: {
url: `${host}event/e20200928calculate/v2/compute`,
body: data
},
blueprintCompute: {
url: `${host}event/e20200928calculate/v1/furniture/compute`,
body: data
},
/** 养成计算器 */
blueprint: {
url: `${host}event/e20200928calculate/v1/furniture/blueprint`,
query: `share_code=${data.share_code}&region=${this.server}`
},
/** 角色技能 */
avatarSkill: {
url: `${host}event/e20200928calculate/v1/avatarSkill/list`,
query: `avatar_id=${data.avatar_id}`
},
/** 七圣召唤数据 */
basicInfo: {
url: `${hostRecord}game_record/app/genshin/api/gcg/basicInfo`,
query: `role_id=${this.uid}&server=${this.server}`
},
/**使用兑换码 目前仅限国际服,来自于国服的uid请求已在myinfo.js的init方法提前拦截 */
useCdk: {
url: 'PLACE_HOLDER',
query: null
}
},
honkaisr: {
/** 首页宝箱 */
index: {
url: `${hostRecord}game_record/app/hkrpg/api/index`,
query: `role_id=${this.uid}&server=${this.server}`
},
UserGame:{
url:`${host}common/badge/v1/login/account`,
body: { uid: this.uid, region: this.server,lang:'zh-cn',game_biz:'hkrpg_cn' }
},
/**
* 开拓阅历接口
*/
ys_ledger:{
url:`${host}/event/srledger/month_info`,
query:`region=${this.server}&uid=${this.uid}&month=${data.month}`
},
/** 角色详情 */
character: {
url: `${hostRecord}game_record/app/hkrpg/api/avatar/info`,
body: { role_id: this.uid, server: this.server }
},
/** 树脂 */
dailyNote: {
url: `${hostRecord}game_record/app/hkrpg/api/note`,
query: `role_id=${this.uid}&server=${this.server}`
},
}
}
if (this.server.startsWith('os')) {
urlMap.genshin.detail.url = 'https://sg-public-api.hoyolab.com/event/calculateos/sync/avatar/detail'// 角色天赋详情
urlMap.genshin.detail.query = `lang=zh-cn&uid=${this.uid}&region=${this.server}&avatar_id=${data.avatar_id}`
urlMap.genshin.avatarSkill.url = 'https://sg-public-api.hoyolab.com/event/calculateos/avatar/skill_list'// 查询未持有的角色天赋
urlMap.genshin.avatarSkill.query = `lang=zh-cn&avatar_id=${data.avatar_id}`
urlMap.genshin.compute.url = 'https://sg-public-api.hoyolab.com/event/calculateos/compute'// 已支持养成计算
urlMap.genshin.blueprint.url = 'https://sg-public-api.hoyolab.com/event/calculateos/furniture/blueprint'
urlMap.genshin.blueprint.query = `share_code=${data.share_code}&region=${this.server}&lang=zh-cn`
urlMap.genshin.blueprintCompute.url = 'https://sg-public-api.hoyolab.com/event/calculateos/furniture/compute'
urlMap.genshin.blueprintCompute.body = { lang: 'zh-cn', ...data }
urlMap.genshin.ys_ledger.url = 'https://hk4e-api-os.mihoyo.com/event/ysledgeros/month_info'// 支持了国际服札记
urlMap.genshin.ys_ledger.query = `lang=zh-cn&month=${data.month}&uid=${this.uid}&region=${this.server}`
urlMap.genshin.useCdk.url = 'https://sg-hk4e-api.hoyoverse.com/common/apicdkey/api/webExchangeCdkey'
urlMap.genshin.useCdk.query = `uid=${this.uid}&region=${this.server}&lang=zh-cn&cdkey=${data.cdk}&game_biz=hk4e_global`
}
return urlMap[this.game]
} }
return urlMap[this.game]
}
} }

View File

@ -25,7 +25,7 @@ export default class MysInfo {
} }
// ck对应MysUser对象 // ck对应MysUser对象
this.ckUser = null this.ckUser = null
this.auth = ['dailyNote', 'bbs_sign_info', 'bbs_sign_home', 'bbs_sign', 'ys_ledger', 'compute', 'avatarSkill', 'detail', 'blueprint'] this.auth = ['dailyNote', 'bbs_sign_info', 'bbs_sign_home', 'bbs_sign', 'ys_ledger', 'compute', 'avatarSkill', 'detail', 'blueprint', 'UserGame']
} }
static async init (e, api) { static async init (e, api) {
@ -62,7 +62,7 @@ export default class MysInfo {
mysInfo.e.uid = mysInfo.uid mysInfo.e.uid = mysInfo.uid
/** 获取ck */ /** 获取ck */
await mysInfo.getCookie(onlySelfCk) await mysInfo.getCookie(e, onlySelfCk)
/** 判断回复 */ /** 判断回复 */
await mysInfo.checkReply() await mysInfo.checkReply()
@ -75,7 +75,7 @@ export default class MysInfo {
let user = await NoteUser.create(e) let user = await NoteUser.create(e)
if (user) { if (user) {
// 强制读取一次ck防止一些问题 // 强制读取一次ck防止一些问题
user._getCkData() user.initDB(true)
return user return user
} }
return false return false
@ -91,7 +91,7 @@ export default class MysInfo {
let user = await NoteUser.create(e) let user = await NoteUser.create(e)
if (e.uid && matchMsgUid) { if (e.uid && matchMsgUid) {
/** 没有绑定的自动绑定 */ /** 没有绑定的自动绑定 */
return await user.setRegUid(e.uid, false) return await user.setRegUid(e.uid, e, false)
} }
let { msg = '', at = '' } = e let { msg = '', at = '' } = e
@ -143,24 +143,7 @@ export default class MysInfo {
return false return false
} }
return selfUser.uid return selfUser.getUid(e)
}
/** 判断绑定ck才能查询 */
checkAuth (api) {
if (api === 'cookie') {
return true
}
if (lodash.isObject(api)) {
for (let i in api) {
if (this.auth.includes(i)) {
return true
}
}
} else if (this.auth.includes(api)) {
return true
}
return false
} }
/** /**
@ -187,7 +170,7 @@ export default class MysInfo {
if (!mysInfo.uid || !mysInfo.ckInfo.ck) return false if (!mysInfo.uid || !mysInfo.ckInfo.ck) return false
e.uid = mysInfo.uid e.uid = mysInfo.uid
let mysApi = new MysApi(mysInfo.uid, mysInfo.ckInfo.ck, option,e.isSr) let mysApi = new MysApi(mysInfo.uid, mysInfo.ckInfo.ck, option, e.isSr)
let res let res
if (lodash.isObject(api)) { if (lodash.isObject(api)) {
@ -220,50 +203,11 @@ export default class MysInfo {
return res return res
} }
async checkReply () {
if (this.e.noTips === true) return
if (!this.uid) {
this.e.reply('请先#绑定uid')
}
if (!this.ckInfo.ck) {
this.e.reply('暂无可用CK请绑定更多用户或设置公共ck..')
}
this.e.noTips = true
}
/* 获取请求所需ck */
/**
* 获取请求所需CK
* @param onlySelfCk 是否只获取uid自己对应的ck为true则只获取uid对应ck若无则返回为空
* @returns {Promise<string|string|*>} 查询ck获取失败则返回空
*/
async getCookie(onlySelfCk = false) {
if (this.ckInfo.ck) return this.ckInfo.ck
let mysUser = await MysUser.getByQueryUid(this.uid, onlySelfCk)
if (mysUser) {
if (mysUser.ckData?.ck) {
this.ckInfo = mysUser.ckData
this.ckUser = mysUser
// 暂时直接记录请求uid后期优化分析MysApi请求结果分状态记录结果
await mysUser.addQueryUid(this.uid)
} else {
// 重新分配
await mysUser.disable()
return onlySelfCk ? '' : await this.getCookie()
}
}
return this.ckInfo.ck
}
/** /**
* 初始化公共CK * 初始化公共CK
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async initPubCk() { static async initPubCk () {
// 初始化公共CK // 初始化公共CK
let pubCount = 0 let pubCount = 0
let pubCks = GsCfg.getConfig('mys', 'pubCk') || [] let pubCks = GsCfg.getConfig('mys', 'pubCk') || []
@ -287,11 +231,14 @@ export default class MysInfo {
* 默认会将用户CK加入查询池 * 默认会将用户CK加入查询池
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async initUserCk() { static async initUserCk () {
// 初始化用户缓存 // 初始化用户缓存
let userCount = 0 let userCount = 0
await NoteUser.forEach(async function (user) { await MysUser.forEach(async (mys) => {
userCount += await user.initCache(true) let ret = await mys.initCache()
if (ret) {
userCount++
}
}) })
logger.mark(`加载用户UID${userCount}个,加入查询池`) logger.mark(`加载用户UID${userCount}个,加入查询池`)
} }
@ -302,7 +249,7 @@ export default class MysInfo {
* @param clearData 强制初始化时是否清除已有数据 (刷新/重置) * @param clearData 强制初始化时是否清除已有数据 (刷新/重置)
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
static async initCache(force = false, clearData = false) { static async initCache (force = false, clearData = false) {
// 检查缓存标记 // 检查缓存标记
let cache = DailyCache.create() let cache = DailyCache.create()
if (!force && await cache.get('cache-ready')) { if (!force && await cache.get('cache-ready')) {
@ -325,7 +272,82 @@ export default class MysInfo {
return true return true
} }
async checkCode(res, type) { static async getBingCkUid () {
let res = await GsCfg.getBingCk()
return { ...res.ck }
}
// 获取uid绑定的ck信息
static async checkUidBing (uid, game = 'gs') {
let ckUser = await MysUser.getByQueryUid(uid, game, true)
if (ckUser && ckUser.ck) {
return ckUser
}
return false
}
static async delDisable () {
return await MysUser.delDisable()
}
/** 判断绑定ck才能查询 */
checkAuth (api) {
if (api === 'cookie') {
return true
}
if (lodash.isObject(api)) {
for (let i in api) {
if (this.auth.includes(i)) {
return true
}
}
} else if (this.auth.includes(api)) {
return true
}
return false
}
async checkReply () {
if (this.e.noTips === true) return
if (!this.uid) {
this.e.reply('请先#绑定uid')
}
if (!this.ckInfo.ck) {
this.e.reply('暂无可用CK请绑定更多用户或设置公共ck..')
}
this.e.noTips = true
}
/* 获取请求所需ck */
/**
* 获取请求所需CK
* @param game 游戏
* @param onlySelfCk 是否只获取uid自己对应的ck为true则只获取uid对应ck若无则返回为空
* @returns {Promise<string|string|*>} 查询ck获取失败则返回空
*/
async getCookie (game = 'gs', onlySelfCk = false) {
if (this.ckUser?.ck) return this.ckUser?.ck
let mysUser = await MysUser.getByQueryUid(this.uid, game, onlySelfCk)
if (mysUser) {
if (mysUser.ck) {
this.ckInfo = mysUser.getCkInfo()
this.ckUser = mysUser
// 暂时直接记录请求uid后期优化分析MysApi请求结果分状态记录结果
await mysUser.addQueryUid(this.uid, game)
} else {
// 重新分配
await mysUser.disable(game)
return onlySelfCk ? '' : await this.getCookie()
}
}
return this.ckUser?.ck
}
async checkCode (res, type) {
if (!res) { if (!res) {
this.e.reply('米游社接口请求失败,暂时无法查询') this.e.reply('米游社接口请求失败,暂时无法查询')
return false return false
@ -394,7 +416,7 @@ export default class MysInfo {
} }
/** 删除失效ck */ /** 删除失效ck */
async delCk() { async delCk () {
if (!this.ckUser) { if (!this.ckUser) {
return false return false
} }
@ -404,26 +426,8 @@ export default class MysInfo {
} }
/** 查询次数满,今日内标记失效 */ /** 查询次数满,今日内标记失效 */
async disableToday() { async disableToday (game = 'gs') {
/** 统计次数设为超限 */ /** 统计次数设为超限 */
await this.ckUser.disable() await this.ckUser.disable(game)
}
static async getBingCkUid() {
let res = await GsCfg.getBingCk()
return { ...res.ck }
}
// 获取uid绑定的ck信息
static async checkUidBing(uid) {
let ckUser = await MysUser.getByQueryUid(uid, true)
if (ckUser && ckUser.ckData) {
return ckUser.ckData
}
return false
}
static async delDisable() {
return await MysUser.delDisable()
} }
} }

View File

@ -1,182 +1,187 @@
import moment from "moment"; import moment from 'moment'
import lodash from "lodash"; import lodash from 'lodash'
import base from "./base.js"; import base from './base.js'
import MysInfo from "./mys/mysInfo.js"; import MysInfo from './mys/mysInfo.js'
export default class Note extends base { export default class Note extends base {
constructor(e) { constructor (e) {
super(e); super(e)
this.model = "dailyNote"; this.model = 'dailyNote'
} }
/** 生成体力图片 */ /** 生成体力图片 */
static async get(e) { static async get (e) {
let note = new Note(e); let note = new Note(e)
return await note.getData(); return await note.getData()
} }
async getData() { async getData () {
let res = await MysInfo.get(this.e, "dailyNote"); let res = await MysInfo.get(this.e, 'dailyNote')
let resUser; let resUser
if (!res || res.retcode !== 0) return false; if (!res || res.retcode !== 0) return false
console.log(this.e.isSr, res)
/** 截图数据 */ /** 截图数据 */
let data = this.e.isSr ? this.noteSr(res) : this.noteData(res); let data = this.e.isSr ? this.noteSr(res) : this.noteData(res)
let screenData = this.screenData; console.log(data)
let screenData = this.screenData
if (this.e.isSr) { if (this.e.isSr) {
screenData.tplFile = "./plugins/genshin/resources/StarRail/html/dailyNote/dailyNote.html"; screenData.tplFile = './plugins/genshin/resources/StarRail/html/dailyNote/dailyNote.html'
resUser = await MysInfo.get(this.e,'UserGame') resUser = await MysInfo.get(this.e, 'UserGame')
if (!resUser || resUser.retcode !== 0) return false; console.log('resUser', resUser)
if (!resUser || resUser.retcode !== 0) return false
} }
return { return {
name: this.e.sender.card, name: this.e.sender.card,
quality: 80, quality: 80,
...screenData, ...screenData,
...data,...resUser?.data ...data, ...resUser?.data
}; }
} }
noteSr(res) {
let { data } = res; noteSr (res) {
let nowDay = moment().date(); let { data } = res
let nowUnix = Number(moment().format("X")); let nowDay = moment().date()
let nowUnix = Number(moment().format('X'))
/** 树脂 */ /** 树脂 */
let resinMaxTime; let resinMaxTime
if (data.stamina_recover_time > 0) { if (data.stamina_recover_time > 0) {
let d = moment.duration(data.stamina_recover_time, 'seconds'); let d = moment.duration(data.stamina_recover_time, 'seconds')
let day= Math.floor(d.asDays()); let day = Math.floor(d.asDays())
let hours =d.hours() let hours = d.hours()
let minutes =d.minutes() let minutes = d.minutes()
let seconds =d.seconds() let seconds = d.seconds()
resinMaxTime = hours+'小时'+minutes+'分钟'+seconds+'秒' resinMaxTime = hours + '小时' + minutes + '分钟' + seconds + '秒'
//精确到秒。。。。 //精确到秒。。。。
if(day>0){ if (day > 0) {
resinMaxTime =day+'天'+hours+'小时'+minutes+'分钟'+seconds+'秒' resinMaxTime = day + '天' + hours + '小时' + minutes + '分钟' + seconds + '秒'
}else if(hours>0){ } else if (hours > 0) {
resinMaxTime = hours+'小时'+minutes+'分钟'+seconds+'秒' resinMaxTime = hours + '小时' + minutes + '分钟' + seconds + '秒'
}else if(minutes>0){ } else if (minutes > 0) {
resinMaxTime = minutes+'分钟'+seconds+'秒' resinMaxTime = minutes + '分钟' + seconds + '秒'
}else if(seconds>0){ } else if (seconds > 0) {
resinMaxTime = seconds+'秒' resinMaxTime = seconds + '秒'
} }
} }
data.bfStamina = data.current_stamina / data.max_stamina * 100 +'%'; data.bfStamina = data.current_stamina / data.max_stamina * 100 + '%'
/** 派遣 */ /** 派遣 */
for(let item of data.expeditions){ for (let item of data.expeditions) {
let d = moment.duration(item.remaining_time, 'seconds'); let d = moment.duration(item.remaining_time, 'seconds')
let day= Math.floor(d.asDays()); let day = Math.floor(d.asDays())
let hours =d.hours() let hours = d.hours()
let minutes =d.minutes() let minutes = d.minutes()
item.dateTime=([day+'天',hours+'时',minutes+'分'].filter(v => !['0天','0时','0分'].includes(v))).join('') item.dateTime = ([day + '天', hours + '时', minutes + '分'].filter(v => !['0天', '0时', '0分'].includes(v))).join('')
item.bfTime=(72000-item.remaining_time)/72000 *100 +'%' item.bfTime = (72000 - item.remaining_time) / 72000 * 100 + '%'
if(item.avatars.length==1){ if (item.avatars.length == 1) {
item.avatars.push('派遣头像') item.avatars.push('派遣头像')
} }
} }
// 标识属性图标~ // 标识属性图标~
let icon = lodash.sample(['希儿','白露','艾丝妲','布洛妮娅','姬子','卡芙卡','克拉拉','停云','佩拉','黑塔','希露瓦','银狼']) let icon = lodash.sample(['希儿', '白露', '艾丝妲', '布洛妮娅', '姬子', '卡芙卡', '克拉拉', '停云', '佩拉', '黑塔', '希露瓦', '银狼'])
let week = [ let week = [
"星期日", '星期日',
"星期一", '星期一',
"星期二", '星期二',
"星期三", '星期三',
"星期四", '星期四',
"星期五", '星期五',
"星期六", '星期六'
]; ]
let day = `${week[moment().day()]}`; let day = `${week[moment().day()]}`
return { return {
uid: this.e.uid, uid: this.e.uid,
saveId: this.e.uid,icon,day, saveId: this.e.uid, icon, day,
resinMaxTime,nowDay:moment(new Date()).format('YYYY年MM月DD日'), resinMaxTime, nowDay: moment(new Date()).format('YYYY年MM月DD日'),
...data, ...data
}; }
} }
noteData(res) {
let { data } = res;
let nowDay = moment().date(); noteData (res) {
let nowUnix = Number(moment().format("X")); let { data } = res
let nowDay = moment().date()
let nowUnix = Number(moment().format('X'))
/** 树脂 */ /** 树脂 */
let resinMaxTime; let resinMaxTime
if (data.resin_recovery_time > 0) { if (data.resin_recovery_time > 0) {
resinMaxTime = nowUnix + Number(data.resin_recovery_time); resinMaxTime = nowUnix + Number(data.resin_recovery_time)
let maxDate = moment.unix(resinMaxTime); let maxDate = moment.unix(resinMaxTime)
resinMaxTime = maxDate.format("HH:mm"); resinMaxTime = maxDate.format('HH:mm')
if (maxDate.date() != nowDay) { if (maxDate.date() != nowDay) {
resinMaxTime = `明天 ${resinMaxTime}`; resinMaxTime = `明天 ${resinMaxTime}`
} else { } else {
resinMaxTime = ` ${resinMaxTime}`; resinMaxTime = ` ${resinMaxTime}`
} }
} }
/** 派遣 */ /** 派遣 */
let remainedTime = ""; let remainedTime = ''
if (data.expeditions && data.expeditions.length >= 1) { if (data.expeditions && data.expeditions.length >= 1) {
remainedTime = lodash.map(data.expeditions, "remained_time"); remainedTime = lodash.map(data.expeditions, 'remained_time')
remainedTime = lodash.min(remainedTime); remainedTime = lodash.min(remainedTime)
if (remainedTime > 0) { if (remainedTime > 0) {
remainedTime = nowUnix + Number(remainedTime); remainedTime = nowUnix + Number(remainedTime)
let remainedDate = moment.unix(remainedTime); let remainedDate = moment.unix(remainedTime)
remainedTime = remainedDate.format("HH:mm"); remainedTime = remainedDate.format('HH:mm')
if (remainedDate.date() != nowDay) { if (remainedDate.date() != nowDay) {
remainedTime = `明天 ${remainedTime}`; remainedTime = `明天 ${remainedTime}`
} else { } else {
remainedTime = ` ${remainedTime}`; remainedTime = ` ${remainedTime}`
} }
} }
} }
/** 宝钱 */ /** 宝钱 */
let coinTime = ""; let coinTime = ''
if (data.home_coin_recovery_time > 0) { if (data.home_coin_recovery_time > 0) {
let coinDay = Math.floor(data.home_coin_recovery_time / 3600 / 24); let coinDay = Math.floor(data.home_coin_recovery_time / 3600 / 24)
let coinHour = Math.floor((data.home_coin_recovery_time / 3600) % 24); let coinHour = Math.floor((data.home_coin_recovery_time / 3600) % 24)
let coinMin = Math.floor((data.home_coin_recovery_time / 60) % 60); let coinMin = Math.floor((data.home_coin_recovery_time / 60) % 60)
if (coinDay > 0) { if (coinDay > 0) {
coinTime = `${coinDay}${coinHour}小时${coinMin}分钟`; coinTime = `${coinDay}${coinHour}小时${coinMin}分钟`
} else { } else {
let coinDate = moment.unix( let coinDate = moment.unix(
nowUnix + Number(data.home_coin_recovery_time) nowUnix + Number(data.home_coin_recovery_time)
); )
if (coinDate.date() != nowDay) { if (coinDate.date() != nowDay) {
coinTime = `明天 ${coinDate.format("HH:mm")}`; coinTime = `明天 ${coinDate.format('HH:mm')}`
} else { } else {
coinTime = coinDate.format("HH:mm"); coinTime = coinDate.format('HH:mm')
} }
} }
} }
let week = [ let week = [
"星期日", '星期日',
"星期一", '星期一',
"星期二", '星期二',
"星期三", '星期三',
"星期四", '星期四',
"星期五", '星期五',
"星期六", '星期六'
]; ]
let day = `${moment().format("MM-DD HH:mm")} ${week[moment().day()]}`; let day = `${moment().format('MM-DD HH:mm')} ${week[moment().day()]}`
/** 参量质变仪 */ /** 参量质变仪 */
if (data?.transformer?.obtained) { if (data?.transformer?.obtained) {
data.transformer.reached = data.transformer.recovery_time.reached; data.transformer.reached = data.transformer.recovery_time.reached
let recoveryTime = ""; let recoveryTime = ''
if (data.transformer.recovery_time.Day > 0) { if (data.transformer.recovery_time.Day > 0) {
recoveryTime += `${data.transformer.recovery_time.Day}`; recoveryTime += `${data.transformer.recovery_time.Day}`
} }
if (data.transformer.recovery_time.Hour > 0) { if (data.transformer.recovery_time.Hour > 0) {
recoveryTime += `${data.transformer.recovery_time.Hour}小时`; recoveryTime += `${data.transformer.recovery_time.Hour}小时`
} }
if (data.transformer.recovery_time.Minute > 0) { if (data.transformer.recovery_time.Minute > 0) {
recoveryTime += `${data.transformer.recovery_time.Minute}分钟`; recoveryTime += `${data.transformer.recovery_time.Minute}分钟`
} }
data.transformer.recovery_time = recoveryTime; data.transformer.recovery_time = recoveryTime
} }
return { return {
@ -186,7 +191,7 @@ export default class Note extends base {
remainedTime, remainedTime,
coinTime, coinTime,
day, day,
...data, ...data
}; }
} }
} }

View File

@ -28,7 +28,7 @@ export default class RoleList extends base {
if (avatars.length <= 0) return false if (avatars.length <= 0) return false
/** 判断是否绑定了ck */ /** 判断是否绑定了ck */
this.ck = await MysInfo.checkUidBing(uid) this.ck = await MysInfo.checkUidBing(uid, this.e)
let skill = [] let skill = []
if (this.ck) { if (this.ck) {

View File

@ -145,7 +145,7 @@ export default class Today extends base {
} }
/** 判断是否绑定了ck */ /** 判断是否绑定了ck */
this.ck = await MysInfo.checkUidBing(this.e.uid) this.ck = await MysInfo.checkUidBing(this.e.uid, this.e)
let skill = {} let skill = {}
if (this.ck) { if (this.ck) {

View File

@ -3,8 +3,12 @@ import gsCfg from './gsCfg.js'
import lodash from 'lodash' import lodash from 'lodash'
import fs from 'node:fs' import fs from 'node:fs'
import common from '../../../lib/common/common.js' import common from '../../../lib/common/common.js'
import MysUser from './mys/MysUser.js'
import MysInfo from './mys/mysInfo.js' import MysInfo from './mys/mysInfo.js'
import NoteUser from './mys/NoteUser.js'
import MysUser from './mys/MysUser.js'
import { promisify } from 'node:util'
import YAML from 'yaml'
import { Data } from '#miao'
export default class User extends base { export default class User extends base {
constructor (e) { constructor (e) {
@ -23,7 +27,7 @@ export default class User extends base {
// 获取当前user实例 // 获取当前user实例
async user () { async user () {
return await MysInfo.getNoteUser(this.e) return await NoteUser.create(this.e)
} }
async resetCk () { async resetCk () {
@ -41,9 +45,9 @@ export default class User extends base {
return return
} }
let ck = this.e.ck.replace(/#|'|"/g, '') let ckStr = this.e.ck.replace(/#|'|"/g, '')
let param = {} let param = {}
ck.split(';').forEach((v) => { ckStr.split(';').forEach((v) => {
// 处理分割特殊cookie_token // 处理分割特殊cookie_token
let tmp = lodash.trim(v).replace('=', '~').split('~') let tmp = lodash.trim(v).replace('=', '~').split('~')
param[tmp[0]] = tmp[1] param[tmp[0]] = tmp[1]
@ -54,56 +58,61 @@ export default class User extends base {
return return
} }
this.ck = `ltoken=${param.ltoken};ltuid=${param.ltuid || param.login_uid};cookie_token=${param.cookie_token || param.cookie_token_v2}; account_id=${param.ltuid || param.login_uid};` let mys = await MysUser.create(param.ltuid)
let data = {}
data.ck = `ltoken=${param.ltoken};ltuid=${param.ltuid || param.login_uid};cookie_token=${param.cookie_token || param.cookie_token_v2}; account_id=${param.ltuid || param.login_uid};`
let flagV2 = false let flagV2 = false
if (param.cookie_token_v2 && (param.account_mid_v2 || param.ltmid_v2)) { // if (param.cookie_token_v2 && (param.account_mid_v2 || param.ltmid_v2)) { //
// account_mid_v2 为版本必须带的字段不带的话会一直提示绑定cookie失败 请重新登录 // account_mid_v2 为版本必须带的字段不带的话会一直提示绑定cookie失败 请重新登录
flagV2 = true flagV2 = true
this.ck = `account_mid_v2=${param.account_mid_v2};cookie_token_v2=${param.cookie_token_v2};ltoken_v2=${param.ltoken_v2};ltmid_v2=${param.ltmid_v2};` data.ck = `account_mid_v2=${param.account_mid_v2};cookie_token_v2=${param.cookie_token_v2};ltoken_v2=${param.ltoken_v2};ltmid_v2=${param.ltmid_v2};`
}
if (param.mi18nLang) {
data.ck += ` mi18nLang=${param.mi18nLang};`
} }
/** 拼接ck */ /** 拼接ck */
this.ltuid = param.ltuid || param.ltmid_v2 data.ltuid = param.ltuid || param.ltmid_v2
/** 米游币签到字段 */ /** 米游币签到字段 */
this.login_ticket = param.login_ticket ?? '' data.login_ticket = param.login_ticket ?? ''
mys.setCkData(data)
/** 检查ck是否失效 */ /** 检查ck是否失效 */
if (!await this.checkCk(param)) { let uidRet = await mys.reqMysUid()
console.log('uidRet', uidRet)
if (uidRet.status !== 0) {
logger.mark(`绑定cookie错误1${this.checkMsg || 'cookie错误'}`) logger.mark(`绑定cookie错误1${this.checkMsg || 'cookie错误'}`)
await this.e.reply(`绑定cookie失败${this.checkMsg || 'cookie错误'}`) return await this.e.reply(`绑定cookie失败${this.checkMsg || 'cookie错误'}`)
return
} }
if (flagV2) { if (flagV2) {
// 获取米游社通行证id // 获取米游社通行证id
let userFullInfo = await this.getUserInfo() let userFullInfo = await mys.getUserFullInfo()
if (userFullInfo?.data?.user_info) { if (userFullInfo?.data?.user_info) {
let userInfo = userFullInfo?.data?.user_info let userInfo = userFullInfo?.data?.user_info
/*
this.ltuid = userInfo.uid this.ltuid = userInfo.uid
this.ck = `${this.ck}ltuid=${this.ltuid};` this.ck = `${this.ck}ltuid=${this.ltuid};`
*/
} else { } else {
logger.mark(`绑定cookie错误2${userFullInfo.message || 'cookie错误'}`) logger.mark(`绑定cookie错误2${userFullInfo.message || 'cookie错误'}`)
await this.e.reply(`绑定cookie失败${userFullInfo.message || 'cookie错误'}`) return await this.e.reply(`绑定cookie失败${userFullInfo.message || 'cookie错误'}`)
return
} }
} }
logger.mark(`${this.e.logFnc} 检查cookie正常 [uid:${this.uid}]`) logger.mark(`${this.e.logFnc} 检查cookie正常 [ltuid:${mys.ltuid}]`)
await user.addCk(this.getCk()) await user.addMysUser(mys)
await user.save()
logger.mark(`${this.e.logFnc} 保存cookie成功 [uid:${this.uid}] [ltuid:${this.ltuid}]`) logger.mark(`${this.e.logFnc} 保存cookie成功 [ltuid:${mys.ltuid}]`)
let uidMsg = [`绑定cookie成功\n${this.region_name}${this.uid}`] let uidMsg = [`绑定cookie成功`]
if (!lodash.isEmpty(this.allUid)) {
this.allUid.forEach(v => {
uidMsg.push(`${v.region_name}${v.uid}`)
})
}
await this.e.reply(uidMsg.join('\n')) await this.e.reply(uidMsg.join('\n'))
let msg = '' let msg = ''
this.region_name += lodash.map(this.allUid, 'region_name').join(',') if (mys.hasGame('gs')) {
if (/天空岛|世界树|America Server|Europe Server|Asia Server/.test(this.region_name)) {
msg += '原神模块支持:\n【#体力】查询当前树脂' msg += '原神模块支持:\n【#体力】查询当前树脂'
msg += '\n【#签到】米游社原神自动签到' msg += '\n【#签到】米游社原神自动签到'
msg += '\n【#关闭签到】开启或关闭原神自动签到' msg += '\n【#关闭签到】开启或关闭原神自动签到'
@ -115,7 +124,7 @@ export default class User extends base {
msg += '\n【#我的ck】查看当前绑定ck' msg += '\n【#我的ck】查看当前绑定ck'
msg += '\n【#删除ck】删除当前绑定ck' msg += '\n【#删除ck】删除当前绑定ck'
} }
if (/星穹列车/.test(this.region_name)) { if (mys.hasGame('sr')) {
msg += '\n星穹铁道支持\n功能还在咕咕咕~' msg += '\n星穹铁道支持\n功能还在咕咕咕~'
} }
msg += '\n 支持绑定多个ck' msg += '\n 支持绑定多个ck'
@ -125,15 +134,16 @@ export default class User extends base {
} }
/** 检查ck是否可用 */ /** 检查ck是否可用 */
async checkCk (param) { async checkCk (mys, param = {}) {
let res let res
for (let type of ['mys', 'hoyolab']) { for (let type of ['mys', 'hoyolab']) {
let roleRes = await this.getGameRoles(type) let roleRes = await mys.getGameRole(type)
if (roleRes?.retcode === 0) { if (roleRes?.retcode === 0) {
res = roleRes res = roleRes
/** 国际服的标记 */ /** 国际服的标记 */
if (type === 'hoyolab' && typeof (param.mi18nLang) === 'string') { if (type === 'hoyolab' && typeof (param.mi18nLang) === 'string' && !/mi18nLang/.test(mys.ck)) {
this.ck += ` mi18nLang=${param.mi18nLang};` mys.ck += ` mi18nLang=${param.mi18nLang};`
mys.type = 'hoyolab'
} }
break break
} }
@ -145,82 +155,20 @@ export default class User extends base {
} }
if (!res) return false if (!res) return false
let playerList = res?.data?.list || []
if (!res.data.list || res.data.list.length <= 0) { if (!playerList || playerList.length <= 0) {
this.checkMsg = '该账号尚未绑定原神或星穹角色!' this.checkMsg = '该账号尚未绑定原神或星穹角色!'
return false return false
} else { } else {
res.data.list = res.data.list.filter(v => ['hk4e_cn', 'hkrpg_cn', 'hk4e_global', 'hkrpg_global'].includes(v.game_biz)) playerList = playerList.filter(v => ['hk4e_cn', 'hkrpg_cn', 'hk4e_global', 'hkrpg_global'].includes(v.game_biz))
} }
//避免同时多个默认展示角色时候只绑定一个
let is_chosen = false
/** 米游社默认展示的角色 */ /** 米游社默认展示的角色 */
for (let val of res.data.list) { for (let val of playerList) {
if (val.is_chosen && !is_chosen) { mys.addUid(val.game_uid, ['hk4e_cn', 'hk4e_global'].includes(val.game_biz) ? 'gs' : 'sr')
this.uid = val.game_uid
this.region_name = val.region_name
is_chosen = true
} else {
this.allUid.push({
uid: val.game_uid,
region_name: val.region_name
})
}
} }
await mys.save()
return mys
if (!this.uid && res.data?.list?.length > 0) {
this.uid = res.data.list[0].game_uid
this.region_name = res.data.list[0].region_name
if (this.allUid[0].uid == this.uid) delete this.allUid[0]
}
return this.uid
}
async getGameRoles (server = 'mys') {
return await MysUser.getGameRole(this.ck, server)
}
// 获取米游社通行证id
async getUserInfo (server = 'mys') {
return await MysUser.getUserFullInfo(this.ck, server)
}
/** 保存ck */
getCk () {
let ck = gsCfg.getBingCkSingle(this.e.user_id)
lodash.map(ck, o => {
o.isMain = false
return o
})
ck[this.uid] = {
uid: this.uid,
qq: this.e.user_id,
ck: this.ck,
ltuid: this.ltuid,
login_ticket: this.login_ticket,
region_name: this.region_name,
device_id: this.getGuid(),
isMain: true
}
this.allUid.forEach((v) => {
if (!v.uid) return
ck[v.uid] = {
uid: v.uid,
qq: this.e.user_id,
ck: this.ck,
ltuid: this.ltuid,
region_name: v.region_name,
device_id: this.getGuid(),
isMain: false
}
})
return ck
} }
/** 删除绑定ck */ /** 删除绑定ck */
@ -236,57 +184,49 @@ export default class User extends base {
if (!uid) return if (!uid) return
uid = uid[0] uid = uid[0]
let user = await this.user() let user = await this.user()
await user.setRegUid(uid, true) user.addRegUid(uid, this.e)
return await this.e.reply(`绑定成功uid:${uid}`, false, { at: true }) await user.save()
return await this.showUid()
} }
/** #uid */ /** #uid */
async showUid () { async showUid () {
let user = await this.user() let user = await this.user()
if (!user.hasCk) { let msg = ['通过【#uid+序号】来切换uid']
await this.e.reply(`当前绑定uid${user.uid || '无'}`, false, { at: true }) lodash.forEach({ genshin: '原神', star: '星穹铁道' }, (gameName, game) => {
return let uidList = user.getUidList(game)
} let currUid = user.getUid(game)
let uids = user.ckUids if (uidList.length === 0) {
let ckData = user.ckData return true
let uid = user.uid * 1
let msg = [`当前uid${uid}`, '当前绑定cookie Uid列表', '通过【#uid+序号】来切换uid']
let region_name = []
Object.keys(ckData).forEach((v) => {
if (!region_name.includes(ckData[v].region_name)) {
region_name.push(ckData[v].region_name)
} }
}) msg.push(`${gameName}`)
let count = 0 lodash.forEach(uidList, (ds, idx) => {
for (let n of region_name) { let tmp = `${++idx}: ${ds.uid} (${ds.type})`
msg.push(n) if (currUid * 1 === ds.uid * 1) {
for (let i in uids) { tmp += ' ☑'
if (ckData[uids[i]].region_name == n) {
let tmp = `${++count}: ${uids[i]}`
if (uids[i] * 1 === uid) {
tmp += ' ☑'
}
msg.push(tmp)
} }
} msg.push(tmp)
} })
})
await this.e.reply(msg.join('\n')) await this.e.reply(msg.join('\n'))
} }
/** 切换uid */ /** 切换uid */
async toggleUid (index) { async toggleUid (index) {
let user = await this.user() let user = await this.user()
let uidList = user.ckUids let game = this.e
let uidList = user.getUidList(game)
if (index > uidList.length) { if (index > uidList.length) {
return await this.e.reply('uid序号输入错误') return await this.e.reply('uid序号输入错误')
} }
index = Number(index) - 1 index = Number(index) - 1
await user.setMainUid(index) await user.setMainUid(index, game)
return await this.e.reply(`切换成功当前uid${user.uid}`) await user.save()
return await this.showUid()
} }
/** 加载ck */ /** 加载V2ck */
async loadOldData () { async loadOldDataV2 () {
let file = [ let file = [
'./data/MysCookie/NoteCookie.json', './data/MysCookie/NoteCookie.json',
'./data/NoteCookie/NoteCookie.json', './data/NoteCookie/NoteCookie.json',
@ -324,22 +264,96 @@ export default class User extends base {
qq, qq,
ck: ck.cookie, ck: ck.cookie,
ltuid, ltuid,
isMain, isMain
device_id: this.getGuid()
} }
}) })
lodash.forEach(arr, (ck, qq) => { let count = await this.loadOldData(arr)
let saveFile = `./data/MysCookie/${qq}.yaml` if (count > 0) {
if (fs.existsSync(saveFile)) return logger.mark(logger.green(`DB导入V2用户ck${count}`))
gsCfg.saveBingCk(qq, ck) }
})
logger.mark(logger.green(`加载用户ck完成${lodash.size(arr)}`))
fs.unlinkSync(json) fs.unlinkSync(json)
} }
/** 加载V3ck */
async loadOldDataV3 (data) {
let dir = './data/MysCookie/'
Data.createDir('./data/MysCookieBak')
let files = fs.readdirSync(dir).filter(file => file.endsWith('.yaml'))
const readFile = promisify(fs.readFile)
let promises = []
files.forEach((v) => promises.push(readFile(`${dir}${v}`, 'utf8')))
const res = await Promise.all(promises)
let ret = {}
for (let v of res) {
v = YAML.parse(v)
let qq
for (let k in v) {
qq = qq || v[k]?.qq
}
if (qq) {
ret[qq] = v
}
}
let count = await this.loadOldData(ret)
if (count > 0) {
logger.mark(logger.green(`DB导入V3用户ck${count}`))
}
}
async loadOldData (data) {
let count = 0
if (!lodash.isPlainObject(data)) {
return
}
for (let u in data) {
let ltuids = {}
let v = data[u]
let qq
for (let k in v) {
let data = v[k]
qq = qq || data?.qq
let { uid, ck, ltuid, region_name: region, device_id: device } = data
ltuids[ltuid] = ltuids[ltuid] || {
ck,
device,
ltuid,
star: {},
genshin: {},
type: /America Server|Europe Server|Asia Server/.test(region) ? 'hoyolab' : 'mys'
}
let tmp = ltuids[ltuid]
let game = region === '星穹列车' ? 'star' : 'genshin'
tmp[game][uid] = uid
}
if (!qq) {
continue
}
let user = await NoteUser.create(qq)
for (let ltuid in ltuids) {
let data = ltuids[ltuid]
let mys = await MysUser.create(data.ltuid)
if (mys) {
data.gsUids = lodash.keys(data.genshin)
data.srUids = lodash.keys(data.star)
mys.setCkData(data)
await mys.save()
user.addMysUser(mys)
}
}
await user.save()
if (fs.existsSync(`./data/MysCookie/${qq}.yaml`)) {
/* fs.rename(`./data/MysCookie/${qq}.yaml`, `./data/MysCookieBak/${qq}.yaml`, (err) => {
if (err) {
console.log(err)
}
}) */
}
count++
}
return count
}
/** 我的ck */ /** 我的ck */
async myCk () { async myCk () {
let user = await this.user() let user = await this.user()
@ -386,14 +400,6 @@ export default class User extends base {
await this.e.reply(cks.join('\n----\n'), false, { at: true }) await this.e.reply(cks.join('\n----\n'), false, { at: true })
} }
getGuid () {
function S4 () {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
}
return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
}
async userAdmin () { async userAdmin () {
this.model = 'userAdmin' this.model = 'userAdmin'
await MysInfo.initCache() await MysInfo.initCache()