mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-06-16 10:19:10 +00:00
[bot] break out RPC and shared packages for bot service breakout
This commit is contained in:
parent
544ae65c58
commit
50b5e334a3
31 changed files with 233 additions and 111 deletions
|
@ -34,7 +34,8 @@ export type AppContext = {
|
|||
config: {
|
||||
appUrl: string,
|
||||
dev: boolean,
|
||||
hotReload: boolean
|
||||
hotReload: boolean,
|
||||
sharedSecret: string
|
||||
},
|
||||
ui: Next,
|
||||
uiHandler: Next.Handler,
|
||||
|
@ -85,7 +86,8 @@ class Roleypoly {
|
|||
config: {
|
||||
appUrl,
|
||||
dev,
|
||||
hotReload: process.env.NO_HOT_RELOAD !== '1'
|
||||
hotReload: process.env.NO_HOT_RELOAD !== '1',
|
||||
sharedSecret: process.env.SHARED_SECRET
|
||||
},
|
||||
io,
|
||||
ui,
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
// @flow
|
||||
import type DiscordService from '../services/discord'
|
||||
import logger from '../logger'
|
||||
const log = logger(__filename)
|
||||
|
||||
export default class Bot {
|
||||
svc: DiscordService
|
||||
log: typeof log
|
||||
constructor (DS: DiscordService) {
|
||||
this.svc = DS
|
||||
this.log = log
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"scripts": {
|
||||
"dev": "babel-node index.js",
|
||||
"start": "NODE_ENV=production node dist/index.js",
|
||||
"pretest": "standard --fix",
|
||||
"build": "NODE_ENV=production babel --delete-dir-on-start -d dist ."
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
class RPCError extends Error {
|
||||
constructor (msg, code, ...extra) {
|
||||
super(msg)
|
||||
this.code = code
|
||||
this.extra = extra
|
||||
}
|
||||
|
||||
static fromResponse (body, status) {
|
||||
const e = new RPCError(body.msg, status)
|
||||
e.remoteStack = body.trace
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RPCError
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import { type AppContext } from '../Roleypoly'
|
||||
import { type Context } from 'koa'
|
||||
import RPCError from './_error'
|
||||
import RPCError from '@roleypoly/rpc-client/error'
|
||||
|
||||
import logger from '../logger'
|
||||
const log = logger(__filename)
|
||||
|
@ -122,6 +122,29 @@ export const any = (
|
|||
silent: boolean = false
|
||||
) => (...args: any) => fn(...args)
|
||||
|
||||
export const bot = (
|
||||
$: AppContext,
|
||||
fn: (ctx: Context, ...args: any[]) => any,
|
||||
silent: boolean = false
|
||||
) => (
|
||||
ctx: Context,
|
||||
...args: any
|
||||
) => {
|
||||
const authToken: ?string = ctx.request.headers['Authorization']
|
||||
|
||||
if (authToken != null && authToken.startsWith('Bot ')) {
|
||||
if (authToken === `Bot ${$.config.sharedSecret}`) {
|
||||
return fn(ctx, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
log.info('RPC bot check failed', logFacts(ctx))
|
||||
}
|
||||
|
||||
throw PermissionError
|
||||
}
|
||||
|
||||
type Handler = (ctx: Context, ...args: any[]) => any
|
||||
type Strategy = (
|
||||
$: AppContext,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import fnv from 'fnv-plus'
|
||||
import autoloader from './_autoloader'
|
||||
import RPCError from './_error'
|
||||
import RPCError from '@roleypoly/rpc-client/error'
|
||||
import type Roleypoly, { Router } from '../Roleypoly'
|
||||
import type { Context } from 'koa'
|
||||
// import logger from '../logger'
|
||||
|
@ -121,7 +121,6 @@ export default class RPCServer {
|
|||
if (err instanceof RPCError || err.constructor.name === 'RPCError') {
|
||||
// $FlowFixMe
|
||||
ctx.status = err.code
|
||||
console.log(err.code)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { type AppContext } from '../Roleypoly'
|
|||
import { type Context } from 'koa'
|
||||
import { type Guild } from 'eris'
|
||||
import * as secureAs from './_security'
|
||||
import RPCError from './_error'
|
||||
import RPCError from '@roleypoly/rpc-client/error'
|
||||
|
||||
export default ($: AppContext) => ({
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ import * as secureAs from './_security'
|
|||
export default ($: AppContext) => ({
|
||||
|
||||
getCurrentUser: secureAs.authed($, async (ctx: Context) => {
|
||||
return $.discord.getUserPartial(ctx.session.userId)
|
||||
const u = await $.discord.getUserPartial(ctx.session.userId)
|
||||
return u
|
||||
}),
|
||||
|
||||
isRoot: secureAs.root($, () => {
|
||||
|
|
|
@ -31,20 +31,25 @@ export default class AuthService extends Service {
|
|||
async isLoggedIn (ctx: Context, { refresh = false }: { refresh: boolean } = {}) {
|
||||
const { userId, expiresAt, authType } = ctx.session
|
||||
if (userId == null) {
|
||||
this.log.debug('isLoggedIn failed, no userId', ctx.session)
|
||||
return false
|
||||
}
|
||||
|
||||
if (expiresAt < Date.now()) {
|
||||
this.log.debug('session has expired.', expiresAt, Date.now())
|
||||
if (refresh && authType === 'oauth') {
|
||||
this.log.debug('was oauth and we can refresh')
|
||||
const tokens = await this.ctx.discord.refreshOAuth(ctx.session)
|
||||
this.injectSessionFromOAuth(ctx, tokens, userId)
|
||||
return true
|
||||
}
|
||||
|
||||
this.log.debug('was not oauth, we are destroying the session')
|
||||
ctx.session = null // reset session as well
|
||||
return false
|
||||
}
|
||||
|
||||
this.log.debug('this user is logged in', userId)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
import Service from './Service'
|
||||
import type { AppContext } from '../Roleypoly'
|
||||
import Bot from '../bot'
|
||||
import Eris, { type Member, Role, type Guild, type Permission as ErisPermission } from 'eris'
|
||||
import LRU from 'lru-cache'
|
||||
// $FlowFixMe
|
||||
|
@ -9,13 +8,13 @@ import { OrderedSet } from 'immutable'
|
|||
import superagent from 'superagent'
|
||||
import type { AuthTokens } from './auth'
|
||||
import type { IFetcher } from './discord/types'
|
||||
import RestFetcher from './discord/restFetcher'
|
||||
|
||||
type DiscordServiceConfig = {
|
||||
token: string,
|
||||
clientId: string,
|
||||
clientSecret: string,
|
||||
rootUsers: Set<string>,
|
||||
isBot: boolean
|
||||
rootUsers: Set<string>
|
||||
}
|
||||
|
||||
export type Permissions = {
|
||||
|
@ -56,7 +55,6 @@ export type MemberExt = Member & {
|
|||
|
||||
export default class DiscordService extends Service {
|
||||
ctx: AppContext
|
||||
bot: Bot
|
||||
client: Eris
|
||||
|
||||
cfg: DiscordServiceConfig
|
||||
|
@ -77,8 +75,7 @@ export default class DiscordService extends Service {
|
|||
rootUsers: new Set((process.env.ROOT_USERS || '').split(',')),
|
||||
token: process.env.DISCORD_BOT_TOKEN || '',
|
||||
clientId: process.env.DISCORD_CLIENT_ID || '',
|
||||
clientSecret: process.env.DISCORD_CLIENT_SECRET || '',
|
||||
isBot: process.env.IS_BOT === 'true'
|
||||
clientSecret: process.env.DISCORD_CLIENT_SECRET || ''
|
||||
}
|
||||
|
||||
this.oauthCallback = `${ctx.config.appUrl}/api/oauth/callback`
|
||||
|
@ -86,36 +83,11 @@ export default class DiscordService extends Service {
|
|||
this.ownRoleCache = new LRU()
|
||||
this.topRoleCache = new LRU()
|
||||
|
||||
if (this.cfg.isBot) {
|
||||
this.client = new Eris(this.cfg.token, {
|
||||
disableEveryone: true,
|
||||
maxShards: 'auto',
|
||||
messageLimit: 10,
|
||||
disableEvents: {
|
||||
CHANNEL_PINS_UPDATE: true,
|
||||
USER_SETTINGS_UPDATE: true,
|
||||
USER_NOTE_UPDATE: true,
|
||||
RELATIONSHIP_ADD: true,
|
||||
RELATIONSHIP_REMOVE: true,
|
||||
GUILD_BAN_ADD: true,
|
||||
GUILD_BAN_REMOVE: true,
|
||||
TYPING_START: true,
|
||||
MESSAGE_UPDATE: true,
|
||||
MESSAGE_DELETE: true,
|
||||
MESSAGE_DELETE_BULK: true,
|
||||
VOICE_STATE_UPDATE: true
|
||||
}
|
||||
})
|
||||
this.bot = new Bot(this)
|
||||
const BotFetcher = require('./discord/botFetcher').default
|
||||
this.fetcher = new BotFetcher(this)
|
||||
} else {
|
||||
this.client = new Eris(`Bot ${this.cfg.token}`, {
|
||||
restMode: true
|
||||
})
|
||||
const RestFetcher = require('./discord/restFetcher').default
|
||||
this.fetcher = new RestFetcher(this)
|
||||
}
|
||||
this.client = new Eris(`Bot ${this.cfg.token}`, {
|
||||
restMode: true
|
||||
})
|
||||
|
||||
this.fetcher = new RestFetcher(this)
|
||||
}
|
||||
|
||||
isRoot (id: string): boolean {
|
||||
|
@ -272,6 +244,7 @@ export default class DiscordService extends Service {
|
|||
async getUserPartial (userId: string): Promise<?UserPartial> {
|
||||
const u = await this.fetcher.getUser(userId)
|
||||
if (u == null) {
|
||||
this.log.debug('userPartial got a null user', userId, u)
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
// @flow
|
||||
import type { IFetcher } from './types'
|
||||
import type DiscordSvc from '../discord'
|
||||
import type ErisClient, { User, Member, Guild } from 'eris'
|
||||
|
||||
export default class BotFetcher implements IFetcher {
|
||||
ctx: DiscordSvc
|
||||
client: ErisClient
|
||||
constructor (ctx: DiscordSvc) {
|
||||
this.ctx = ctx
|
||||
this.client = ctx.client
|
||||
}
|
||||
|
||||
getUser = async (id: string): Promise<?User> =>
|
||||
this.client.users.get(id)
|
||||
|
||||
getMember = async (server: string, user: string): Promise<?Member> =>
|
||||
this.client.guilds.get(server)?.members.get(user)
|
||||
|
||||
getGuild = async (server: string): Promise<?Guild> =>
|
||||
this.client.guilds.get(server)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue