增加检查discord role的接口

This commit is contained in:
CounterFire2023 2024-04-16 15:21:20 +08:00
parent dba4eddee7
commit 2bf81f8e1c
5 changed files with 136 additions and 24 deletions

View File

@ -1,8 +0,0 @@
.idea
node_modules
build
dist
.DS_Store
tmp
target
boundle.log

View File

@ -23,7 +23,7 @@ class DiscordController extends BaseController {
record.refreshToken = tokenResponse.refresh_token record.refreshToken = tokenResponse.refresh_token
record.scope = tokenResponse.scope record.scope = tokenResponse.scope
record.tokenType = tokenResponse.token_type record.tokenType = tokenResponse.token_type
record.expiresIn = tokenResponse.expires_in || 0 + Date.now() record.expiresIn = (Date.now() / 1000 + tokenResponse.expires_in) | 0
await record.save() await record.save()
if (tokenResponse && tokenResponse.access_token) { if (tokenResponse && tokenResponse.access_token) {
let uinfo = await userInfo(tokenResponse.access_token) let uinfo = await userInfo(tokenResponse.access_token)

View File

@ -3,11 +3,12 @@ import { ZError } from 'common/ZError'
import { role, router } from 'decorators/router' import { role, router } from 'decorators/router'
import logger from 'logger/logger' import logger from 'logger/logger'
import { AuthRecord, PlatEnum } from 'modules/AuthRecord' import { AuthRecord, PlatEnum } from 'modules/AuthRecord'
import { DiscordSvr } from 'services/discord.svr' import { DiscordSvr, getAvableAccessToken, userGuildMember } from 'services/discord.svr'
import { hmacsha256 } from 'utils/security.util' import { hmacsha256 } from 'utils/security.util'
const checkSign = (params: { address?: string; sign?: string }) => { const checkSign = (params: { address?: string; sign?: string }) => {
const { address, sign } = params let { address, sign } = params
address = address.toLowerCase()
if (!address || !sign) { if (!address || !sign) {
throw new ZError(10, 'invalid params') throw new ZError(10, 'invalid params')
} }
@ -119,21 +120,49 @@ class MainController extends BaseController {
return result return result
} }
/**
* guid
*/
@role(ROLE_ANON) @role(ROLE_ANON)
@router('get /activity/discord/svr/:id') @router('post /activity/discord/guild')
async checkDiscordJoin(req) { async checkDiscordJoin(req) {
let { id } = req.params let { gid, address } = req.params
checkSign(req.params) checkSign(req.params)
let verified = new DiscordSvr().checkUser(id) address = address.toLowerCase()
return { verified } let record = await AuthRecord.findOne({ address, platform: PlatEnum.DISCORD })
if (!record) {
throw new ZError(12, 'not found discord record')
}
let accessToken = await getAvableAccessToken(record)
// TODO:: check local cache
let data = await userGuildMember(accessToken, gid)
if (!data || data.code) {
throw new ZError(13, 'not found guild')
}
return { result: true }
} }
/**
* role
*/
@role(ROLE_ANON) @role(ROLE_ANON)
@router('get /activity/discord/role/:id') @router('post /activity/discord/role')
async checkDiscordRole(req) { async checkDiscordRole(req) {
let { id } = req.params let { rid, gid, address } = req.params
checkSign(req.params) checkSign(req.params)
let verified = new DiscordSvr().checkUserRole(id) address = address.toLowerCase()
return { verified } let record = await AuthRecord.findOne({ address, platform: PlatEnum.DISCORD })
if (!record) {
throw new ZError(12, 'not found discord record')
}
let accessToken = await getAvableAccessToken(record)
console.log('discord access token:', accessToken)
// TODO:: check local cache
let data = await userGuildMember(accessToken, gid)
console.log(data?.roles)
if (!data || data.code) {
throw new ZError(13, 'not found guild')
}
let roleSet = new Set(data.roles)
return { result: roleSet.has(rid) }
} }
} }

View File

@ -24,7 +24,7 @@ class TwitterController extends BaseController {
record.refreshToken = tokenResponse.refresh_token record.refreshToken = tokenResponse.refresh_token
record.scope = tokenResponse.scope record.scope = tokenResponse.scope
record.tokenType = tokenResponse.token_type record.tokenType = tokenResponse.token_type
record.expiresIn = tokenResponse.expires_in || 0 + Date.now() record.expiresIn = (Date.now() / 1000 + tokenResponse.expires_in || 0) | 0
await record.save() await record.save()
if (tokenResponse && tokenResponse.access_token) { if (tokenResponse && tokenResponse.access_token) {
const uinfo = await getTwitterUserInfo(tokenResponse.access_token) const uinfo = await getTwitterUserInfo(tokenResponse.access_token)

View File

@ -2,6 +2,11 @@ import { ZError } from 'common/ZError'
import { singleton } from 'decorators/singleton' import { singleton } from 'decorators/singleton'
import { Client, Events, GatewayIntentBits, Guild } from 'discord.js' import { Client, Events, GatewayIntentBits, Guild } from 'discord.js'
import { AuthRecordClass } from 'modules/AuthRecord'
import { DocumentType } from '@typegoose/typegoose'
const DISCORD_API_HOST = 'https://discord.com/api/v10'
const DISCORD_APP_HOST = 'https://discordapp.com/api'
export async function exchangeDiscrodCodeForToken(code: string) { export async function exchangeDiscrodCodeForToken(code: string) {
const clientId = process.env.DISCORD_CLIENT_ID const clientId = process.env.DISCORD_CLIENT_ID
@ -19,7 +24,6 @@ export async function exchangeDiscrodCodeForToken(code: string) {
client_secret: clientSecret, client_secret: clientSecret,
redirect_uri: redirectUri, redirect_uri: redirectUri,
code, code,
scope: 'identify email',
}), }),
}) })
@ -27,15 +31,102 @@ export async function exchangeDiscrodCodeForToken(code: string) {
return data return data
} }
export async function getAvableAccessToken(record: DocumentType<AuthRecordClass>) {
if (record.expiresIn < Date.now() / 1000 + 60 * 5) {
console.log('discord access token expired')
const data = await refreshDiscordToken(record.refreshToken)
if (data.code) {
throw new ZError(10, 'refresh token error')
}
record.accessToken = data.access_token
record.refreshToken = data.refresh_token
record.expiresIn = (Date.now() / 1000 + data.expires_in || 0) | 0
await record.save()
}
return record.accessToken
}
export async function refreshDiscordToken(rtoken: string) {
const clientId = process.env.DISCORD_CLIENT_ID
const clientSecret = process.env.DISCORD_CLIENT_SECRET
const response = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: clientId,
client_secret: clientSecret,
refresh_token: rtoken,
}),
})
const data = await response.json()
return data
}
/**
*
* @param token
* @returns
*/
export async function userInfo(token: string) { export async function userInfo(token: string) {
const response = await fetch('https://discord.com/api/users/@me', { const response = await fetch(`${DISCORD_APP_HOST}/users/@me`, {
headers: { headers: {
authorization: `Bearer ${token}`, authorization: `Bearer ${token}`,
}, },
}) })
const data = await response.json() const data = await response.json()
console.log(data) return data
}
/**
* , oauth2方式
* @param token
* @returns
*/
export async function userGuildList(token: string) {
const url = `${DISCORD_APP_HOST}/users/@me/guilds`
const response = await fetch(url, {
headers: {
authorization: `Bearer ${token}`,
},
})
const data = await response.json()
return data
}
/**
* , oauth2方式
* @param token
* @param guildId
* @returns
* :
* https://discord.com/developers/docs/resources/guild#guild-member-object
*/
export async function userGuildMember(token: string, guildId: string) {
const url = `${DISCORD_APP_HOST}/users/@me/guilds/${guildId}/member`
const response = await fetch(url, {
headers: {
authorization: `Bearer ${token}`,
},
})
const data = await response.json()
return data
}
/**
* discord接口,
* :
* https://discord.com/developers/docs/resources/guild#guild-member-object
*/
export async function getGuildMember(uid: string) {
const url = `${DISCORD_API_HOST}/guilds/${process.env.DISCROD_GUILD_ID}/members/${uid}`
const token = process.env.DISCORD_BOT_TOKEN
const response = await fetch(url, {
headers: {
authorization: `Bot ${token}`,
},
})
const data = await response.json()
return data return data
} }