Skip to content

Commit

Permalink
perf: 定时任务429时可切换其他api查询
Browse files Browse the repository at this point in the history
  • Loading branch information
XasYer committed Jan 2, 2025
1 parent 34f93e8 commit 3026c3c
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 20 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

## **注意**

1. 一定要填**Steam Web API Key**,否则无法使用绝大部分功能,通常会返回 401 或 403 错误,请前往[Steam API](https://steamcommunity.com/dev/apikey)申请API Key
1. 一定要填**Steam Web API Key**,否则无法使用绝大部分功能,通常会返回 401 或 403 错误,请前往[Steam API](https://steamcommunity.com/dev/apikey)申请API Key, 域名随意填写

相关链接:

Expand Down
8 changes: 4 additions & 4 deletions apps/push.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,6 @@ const rule = {
tips: true
},
fnc: async e => {
if (!e.group_id) {
return false
}
const isAll = e.msg.includes('全部')
let list = []
if (isAll) {
Expand All @@ -112,6 +109,9 @@ const rule = {
await e.reply([Config.tips.noSteamIdTips])
return true
}
} else if (!e.group_id) {
await e.reply('请在群内使用')
return true
} else {
const memberList = await utils.bot.getGroupMemberList(e.self_id, e.group_id)
list = memberList.length
Expand Down Expand Up @@ -154,7 +154,7 @@ const rule = {
name: nickname,
appid: i.personaname,
desc: utils.steam.getPersonaState(i.personastate),
image: await utils.bot.getUserAvatar(userInfo.botId, userInfo.userId, userInfo.groupId) || i.avatarfull,
image: await utils.bot.getUserAvatar(userInfo.botId, userInfo.userId, userInfo.groupId) || (Config.other.steamAvatar ? i.avatarfull : `https://q.qlogo.cn/g?b=qq&s=100&nk=${userInfo.userId}`),
isAvatar: true,
descBgColor: getColor(i.personastate)
})
Expand Down
13 changes: 9 additions & 4 deletions apps/setting.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ const rule = {
return false
}

const toggleKeys = [
'随机Bot',
'推送api'
]
const key = (() => {
if (/随机Bot/i.test(regRet[1])) {
return '随机Bot'
} else {
return regRet[1]
for (const i of toggleKeys) {
if (new RegExp(i, 'i').test(regRet[1])) {
return i
}
}
return regRet[1]
})()

if (key == '全部') {
Expand Down
10 changes: 7 additions & 3 deletions components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export default class App {
if (options.recallMsg) {
setTimeout(() => {
if (e.group?.recallMsg) {
e.group.recallMsg(res.message_id).catch(() => {})
e.group.recallMsg(res.message_id)?.catch?.(() => {})
} else if (e.friend?.recallMsg) {
e.friend.recallMsg(res.message_id).catch(() => {})
e.friend.recallMsg(res.message_id)?.catch?.(() => {})
}
}, options.recallMsg * 1000)
}
Expand Down Expand Up @@ -94,7 +94,11 @@ export default class App {
App.reply(e, Config.tips.loadingTips, { recallMsg: 5, at: true })
}
const res = await fnc(e).catch(error => {
logger.error(error.message)
if (error.isAxiosError) {
logger.error(error.message)
} else {
logger.error(error)
}
let message = error.message
const keyMap = [
{ key: 'apiProxy', title: 'api反代' },
Expand Down
1 change: 1 addition & 0 deletions components/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class Config {
* @returns {{
* enable: boolean,
* stateChange: boolean,
* pushApi: number,
* pushMode: number,
* time: number,
* defaultPush: boolean,
Expand Down
22 changes: 22 additions & 0 deletions config/default_config/push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ enable: true
# 是否开启状态改变推送功能 比如上线 下线等
stateChange: true

# 设置每次检查时请求的api

# 1: ISteamUserOAuth/GetUserSummaries/v2
# 此接口需要access_token 429情况未知
# 和2接口参数返回值一样, 但是使用access_token鉴权
# 需要有人扫码登录获取access_token后才可以调用

# 2: ISteamUser/GetPlayerSummaries/v2
# 此接口会有429限制, 经测试 40+steamid 3min 会出现 steamid越多越容易出现
# 429 是根据apiKey进行限制, 可配置多个apiKey

# 3: IPlayerService/GetPlayerLinkDetails/v1
# 429情况暂时未知, 但是这个接口只会返回正在玩的appid不会返回name, 所以需要再请求一个接口获得游戏名

# 4: 随机

# tips: 依次进行获取 如果选择1出现429则尝试使用2 2出现429则尝试使用3 3出现429则停止尝试
# 1如果没有access_token则跳过

# more api please wait or issue/pr...
pushApi: 2

# 推送模式
# 1: 文字推送 一条消息就是一个群友 xxx正在玩xxx
# 2: 图片推送 一张图片展示所有群友
Expand Down
40 changes: 40 additions & 0 deletions models/api/ISteamUserOAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import _ from 'lodash'
import { utils } from '#models'

/**
* 获取用户相关信息
* @param {string} accessToken
* @param {string|string[]} steamIds
* @returns {Promise<{
* steamid: string,
* communityvisibilitystate: number,
* profilestate: number,
* personaname: string,
* profileurl: string,
* avatar: string,
* avatarmedium: string,
* avatarfull: string,
* lastlogoff?: number,
* personastate: number,
* timecreated: string,
* gameid?: string,
* gameextrainfo?: string,
* }[]>}
*/
export async function GetUserSummaries (accessToken, steamIds) {
!Array.isArray(steamIds) && (steamIds = [steamIds])
const result = []
// 一次只能获取100个用户信息
for (const items of _.chunk(steamIds, 100)) {
const res = await utils.request.get('ISteamUserOAuth/GetUserSummaries/v2', {
params: {
access_token: accessToken,
steamIds: items.join(',')
}
})
if (res.players?.length) {
result.push(...res.players)
}
}
return result
}
1 change: 1 addition & 0 deletions models/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * as store from './store.js'
export * as ISteamUser from './ISteamUser.js'
export * as IStoreService from './IStoreService.js'
export * as IPlayerService from './IPlayerService.js'
export * as ISteamUserOAuth from './ISteamUserOAuth.js'
export * as ISteamUserStats from './ISteamUserStats.js'
export * as IWishlistService from './IWishlistService.js'
export * as ICheckoutService from './ICheckoutService.js'
Expand Down
8 changes: 8 additions & 0 deletions models/db/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,11 @@ export async function TokenTableDeleteByUserIdAndSteamId (userId, steamId) {
}
})
}

/**
* 查询所有accessToken
* @returns {Promise<TokenColumns[]>}
*/
export async function TokenTableGetAll () {
return await TokenTable.findAll().then(res => res.map(item => item.dataValues))
}
23 changes: 23 additions & 0 deletions models/setting/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,29 @@ export const cfgSchema = {
def: true,
desc: '是否推送游戏状态改变 比如上线 下线等'
},
pushApi: {
title: '推送请求api',
key: '推送api',
type: 'number',
def: 2,
min: 1,
max: 4,
input: (n) => {
if (n >= 1 && n <= 4) {
return n * 1
} else {
return 2
}
},
component: 'RadioGroup',
options: [
{ label: '需要access_token', value: 1 },
{ label: '默认 可能会出现429', value: 2 },
{ label: '需要请求两个接口', value: 3 },
{ label: '随机', value: 4 }
],
desc: '推送消息的api 请查看default_config/push.yaml的注释'
},
pushMode: {
title: '推送模式',
key: '推送模式',
Expand Down
6 changes: 3 additions & 3 deletions models/task/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Config, Version, Render } from '#components'
import { Bot, logger, redis, segment } from '#lib'
import { api, db, utils } from '#models'
import { db, utils } from '#models'
import _ from 'lodash'

let timer = null
Expand All @@ -26,7 +26,7 @@ export function startTimer () {
// 所有的steamId
const steamIds = _.uniq(PushData.map(i => i.steamId))
// 获取所有steamId现在的状态
const result = await api.ISteamUser.GetPlayerSummaries(steamIds)
const result = await utils.steam.getUserSummaries(steamIds)
const userList = {}
for (const player of result) {
// 获取上一次的状态
Expand Down Expand Up @@ -104,7 +104,7 @@ export function startTimer () {
desc: `已${utils.steam.getPersonaState(player.personastate)}`,
image: await utils.bot.getUserAvatar(i.botId, i.userId, i.groupId) || (Config.other.steamAvatar ? i.avatarfull : ''),
isAvatar: true,
descBgColor: getColor(i.personastate)
descBgColor: getColor(player.personastate)
})
if (player.personastate === 0) {
db.StatsTableUpdate(i.userId, i.groupId, i.botId, i.steamId, player.gameid, player.gameextrainfo, 'onlineTime', time).catch(e => logger.error('更新统计数据失败', e.message))
Expand Down
108 changes: 103 additions & 5 deletions models/utils/steam.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { api, db } from '#models'
import _ from 'lodash'
import moment from 'moment'
import { api, db } from '#models'
import { Config } from '#components'

const steamIdOffset = 76561197960265728n
/**
Expand Down Expand Up @@ -110,25 +112,121 @@ export function decodeAccessTokenJwt (jwt) {

/**
* 获取对应用户的access_token
* @param {*} userId
* @param {*} steamId
* @param {string} userId
* @param {string} steamId
* @returns {Promise<string>}
*/
export async function getAccessToken (userId, steamId) {
if (!userId || !steamId) return ''
const token = await db.TokenTableGetByUserIdAndSteamId(userId, steamId)
return await refreshAccessToken(token)
}

/**
* 刷新access_token
* @param {import('models/db').TokenColumns} token
* @returns
*/
export async function refreshAccessToken (token) {
if (!token) return ''
const now = moment.unix()
const now = moment().unix()
// 提前30分钟刷新access_token
const exp = token.accessTokenExpires - 60 * 30
if (exp > now) return token.accessToken
// 判断refresh_token是否过期
const rtExp = token.refreshTokenExpires - 60 * 30
if (rtExp < now) {
await db.TokenTableDeleteByUserIdAndSteamId(token.userId, token.steamId)
throw new Error('refresh_token已过期, 请重新登录')
}
const accessToken = (await api.IAuthenticationService.GenerateAccessTokenForApp(token.refreshToken, token.steamId)).access_token
if (!accessToken) throw new Error('刷新access_token失败')
await db.TokenTableAddData(token.userId, token.accessToken)
await db.TokenTableAddData(token.userId, accessToken)
return accessToken
}

/**
* 获取用户相关信息
* @param {string|string[]} steamIds
* @returns {Promise<{
* steamid: string,
* communityvisibilitystate: number,
* profilestate: number,
* personaname: string,
* avatar: string,
* avatarmedium: string,
* avatarfull: string,
* lastlogoff?: number,
* personastate: number,
* timecreated: string,
* gameid?: string,
* gameextrainfo?: string,
* }[]>}
*/
export async function getUserSummaries (steamIds) {
if (_.isEmpty(steamIds)) return []
let type = Math.floor(Number(Config.push.pushApi)) || 2
if (type > 4 || type < 1) type = 2
if (type === 4) {
type = _.random(1, 3)
}
if (type === 1) {
let accessToken = null
const tokenList = await db.TokenTableGetAll()
while (!accessToken) {
const token = _.sample(tokenList)
if (!token) {
break
}
_.pull(tokenList, token)
accessToken = await refreshAccessToken(token)
}
if (accessToken) {
const data = await api.ISteamUserOAuth.GetUserSummaries(accessToken, steamIds).catch(err => {
if ([429, 401, 403].includes(err.status)) {
logger.info(`请求 ISteamUserOAuth.GetUserSummaries/v2 失败: ${err.status} 尝试使用 ISteamUser.GetPlayerSummaries/v2`)
return false
}
throw err
})
if (data !== false) {
return data
}
}
type = 2
}
if (type === 2) {
const data = await api.ISteamUser.GetPlayerSummaries(steamIds).catch(err => {
if (err.status === 429) {
logger.info('请求 ISteamUser/GetPlayerSummaries/v2 失败: 429 尝试使用 IPlayerService.GetPlayerLinkDetails 接口不同返回的参数会有不同')
return false
}
throw err
})
if (data !== false) {
return data
}
}
return await api.IPlayerService.GetPlayerLinkDetails(steamIds).then(async res => {
const appids = res.map(i => i.private_data.game_id).filter(Boolean)
const appInfo = await api.IStoreBrowseService.GetItems(appids)
return res.map(i => {
const avatarhash = Buffer.from(i.public_data.sha_digest_avatar, 'base64').toString('hex')
const gameid = i.private_data.game_id
return {
steamid: i.public_data.steamid,
communityvisibilitystate: i.public_data.visibility_state,
profilestate: i.public_data.profile_state,
personaname: i.public_data.persona_name,
avatar: `https://avatars.steamstatic.com/${avatarhash}.jpg`,
avatarmedium: `https://avatars.steamstatic.com/${avatarhash}_medium.jpg`,
avatarfull: `https://avatars.steamstatic.com/${avatarhash}_full.jpg`,
avatarhash,
personastate: i.private_data.persona_state ?? 0,
timecreated: i.private_data.time_created,
gameid,
gameextrainfo: appInfo[gameid]?.name
}
})
})
}

0 comments on commit 3026c3c

Please sign in to comment.