import express from "express"; import { createServer } from "http"; import { ModuleRpcServer } from "rpc_ts/lib/server"; import { ModuleRpcProtocolServer } from "rpc_ts/lib/protocol/server"; import { NoctBotService } from "@shared/rpc/backend"; import { eventLogger, logger } from "./logging"; import { client } from "./client"; import { tryDo } from "@shared/common/async_utils"; import { getRepository } from "typeorm"; import { GuildVerification } from "@shared/db/entity/GuildVerification"; import { isAuthorisedAsync } from "./util"; import { GuildMember } from "discord.js"; const PORT = +(process.env.RPC_PORT ?? "8181"); const app = express(); async function checkUser(action: string, userId: string, check: (user: GuildMember, verification: GuildVerification) => Promise): Promise { const verificationGuildRepo = getRepository(GuildVerification); const guilds = await verificationGuildRepo.find({ select: [ "guildId", "verifiedRoleId", ] }); for (const guild of guilds) { const guildInstance = await tryDo(client.bot.guilds.fetch(guild.guildId)); if (!guildInstance.ok) { logger.error("Failed to fetch guild instance for guild %s: %s", guild.guildId, guildInstance.error); continue; } const user = await tryDo(guildInstance.result.members.fetch(userId)); if (!user.ok) { const userInfo = await tryDo(client.bot.users.fetch(userId)); const userName = userInfo.ok ? `${userInfo.result.username}#${userInfo.result.discriminator}` : "???"; logger.warn("Couldn't %s for user %s (%s) on server %s because: %s", action, userId, userName, guild.guildId, user.error); } if (user.ok && await check(user.result, guild)) { return true; } } return false; } const handler: ModuleRpcServer.ServiceHandlerFor = { async getPing({ ping }): Promise<{ text: string }> { return { text: `pong: ${ping}` }; }, async userInServer({ userId }): Promise<{ exists: boolean }> { return { exists: await checkUser("check access" ,userId, async () => true) }; }, async userAuthorised({ userId }): Promise<{ authorised: boolean }> { return { authorised: await checkUser("check auth", userId, (user) => isAuthorisedAsync(user)) }; }, async userVerified({ userId }): Promise<{ verified: boolean }> { return { verified: await checkUser("check verified", userId, async (user, guild) => user.roles.cache.has(guild.verifiedRoleId)) }; }, async verifyUser({ userId }): Promise<{ ok: boolean }> { return { ok: !(await checkUser("verify", userId, async (user, guild) => { const result = await tryDo(user.roles.add(guild.verifiedRoleId)); if (result.ok) { eventLogger.info("Verifying user %s#%s (%s)", user.user.username, user.user.discriminator, user.user.id); logger.info("Verifying user %s#%s (%s) on guild %s", user.user.username, user.user.discriminator, user.user.id, guild.guildId); } else { eventLogger.warn("Failed to verify user %s#%s (%s): %s", user.user.username, user.user.discriminator, user.user.id, result.error); logger.warn("Failed to verify user %s#%s (%s) on guild %s: %s", user.user.username, user.user.discriminator, user.user.id, guild.guildId, result.error); } return !result.ok; }))}; } }; app.use(ModuleRpcProtocolServer.registerRpcRoutes(NoctBotService, handler)); export function startRpcServer(): void { logger.info(`Starting RPC at *:${PORT}`); createServer(app).listen(PORT); }