From 1c814317c08ebd1783b3b83b2d6c330dfc74efa3 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 18 Jun 2022 22:33:14 +0300 Subject: [PATCH] Access command initial --- .env.example | 4 ++- app/constants.ts | 2 ++ app/db/models/users.server.ts | 16 +++++++++ app/permissions.ts | 10 +++++- app/routes/plus/list.ts | 17 +++++++++ discord-bot/.env.example | 4 ++- discord-bot/commands/access.ts | 45 +++++++++++++++++++++++ discord-bot/commands/index.ts | 3 +- discord-bot/package-lock.json | 66 +++++++++++++++++++++++++++++++--- discord-bot/package.json | 3 +- 10 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 app/routes/plus/list.ts create mode 100644 discord-bot/commands/access.ts diff --git a/.env.example b/.env.example index 565e90672..8d48d915e 100644 --- a/.env.example +++ b/.env.example @@ -5,4 +5,6 @@ DB_PATH=db.sqlite3 // auth SESSION_SECRET=secret DISCORD_CLIENT_ID= -DISCORD_CLIENT_SECRET= \ No newline at end of file +DISCORD_CLIENT_SECRET= + +LOHI_TOKEN=salmon \ No newline at end of file diff --git a/app/constants.ts b/app/constants.ts index e9b366ae1..186011f2a 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -11,3 +11,5 @@ export const PLUS_UPVOTE = 1; export const PLUS_DOWNVOTE = -1; export const ADMIN_DISCORD_ID = "79237403620945920"; + +export const LOHI_TOKEN_HEADER_NAME = "Lohi-Token"; diff --git a/app/db/models/users.server.ts b/app/db/models/users.server.ts index 5b37fe6f3..352c2d2ed 100644 --- a/app/db/models/users.server.ts +++ b/app/db/models/users.server.ts @@ -83,3 +83,19 @@ export function findAll() { "id" | "discordId" | "discordName" | "discordDiscriminator" | "plusTier" >[]; } + +const findAllPlusMembersStm = sql.prepare(` +SELECT "User"."discordId", "PlusTier"."tier" as "plusTier" + FROM "User" + LEFT JOIN "PlusTier" ON "PlusTier"."userId" = "User".id + WHERE "PlusTier"."tier" IS NOT NULL +`); + +export type FindAllPlusMembers = Pick< + UserWithPlusTier, + "discordId" | "plusTier" +>[]; + +export function findAllPlusMembers() { + return findAllPlusMembersStm.all() as FindAllPlusMembers; +} diff --git a/app/permissions.ts b/app/permissions.ts index b9ee82878..750bd5a2a 100644 --- a/app/permissions.ts +++ b/app/permissions.ts @@ -2,7 +2,8 @@ import type * as plusSuggestions from "~/db/models/plusSuggestions.server"; import { monthsVotingRange } from "./modules/plus-server"; import type { PlusSuggestion, User, UserWithPlusTier } from "./db/types"; import { allTruthy } from "./utils/arrays"; -import { ADMIN_DISCORD_ID } from "./constants"; +import { ADMIN_DISCORD_ID, LOHI_TOKEN_HEADER_NAME } from "./constants"; +import invariant from "tiny-invariant"; // TODO: 1) move "root checkers" to one file and utils to one file 2) make utils const for more terseness @@ -206,3 +207,10 @@ export function canPerformAdminActions(user?: Pick) { if (!user) return false; return user.discordId === ADMIN_DISCORD_ID; } + +invariant(process.env["LOHI_TOKEN"], "LOHI_TOKEN is required"); +export function canAccessLohiEndpoint(request: Request) { + return ( + request.headers.get(LOHI_TOKEN_HEADER_NAME) === process.env["LOHI_TOKEN"] + ); +} diff --git a/app/routes/plus/list.ts b/app/routes/plus/list.ts new file mode 100644 index 000000000..82d5bd46d --- /dev/null +++ b/app/routes/plus/list.ts @@ -0,0 +1,17 @@ +import { json } from "@remix-run/node"; +import type { LoaderFunction } from "@remix-run/node"; +import { db } from "~/db"; +import { canAccessLohiEndpoint } from "~/permissions"; +import type { FindAllPlusMembers } from "~/db/models/users.server"; + +export interface PlusListLoaderData { + users: FindAllPlusMembers; +} + +export const loader: LoaderFunction = ({ request }) => { + if (!canAccessLohiEndpoint(request)) { + throw new Response(null, { status: 403 }); + } + + return json({ users: db.users.findAllPlusMembers() }); +}; diff --git a/discord-bot/.env.example b/discord-bot/.env.example index 835a98bf8..15db3efa9 100644 --- a/discord-bot/.env.example +++ b/discord-bot/.env.example @@ -1,3 +1,5 @@ BOT_TOKEN= TEST_GUILD_ID= -BOT_ID= \ No newline at end of file +BOT_ID= +SENDOU_INK_URL=http://localhost:5800 +LOHI_TOKEN=salmon \ No newline at end of file diff --git a/discord-bot/commands/access.ts b/discord-bot/commands/access.ts new file mode 100644 index 000000000..17a00ea93 --- /dev/null +++ b/discord-bot/commands/access.ts @@ -0,0 +1,45 @@ +import { SlashCommandBuilder } from "@discordjs/builders"; +import invariant from "tiny-invariant"; +import { LOHI_TOKEN_HEADER_NAME } from "~/constants"; +import type { PlusListLoaderData } from "~/routes/plus/list"; +import ids from "../ids"; +import type { BotCommand } from "../types"; + +const COMMAND_NAME = "access"; + +export const accessCommand: BotCommand = { + guilds: [ids.guilds.plusServer], + name: COMMAND_NAME, + builder: new SlashCommandBuilder() + .setName(COMMAND_NAME) + .setDescription( + "Get the corresponding Plus Server membership role if you have access" + ), + execute: async ({ interaction }) => { + const { users } = await usersWithAccess(); + + if (!users.some((u) => u.discordId === interaction.user.id)) { + return interaction.reply("You currently don't have access"); + } + + // TODO: logic to change roles here + return interaction.reply("ok"); + }, +}; + +async function usersWithAccess(): Promise { + invariant(process.env["SENDOU_INK_URL"], "SENDOU_INK_URL is not set"); + invariant(process.env["LOHI_TOKEN"], "LOHI_TOKEN is not set"); + + const response = await fetch(`${process.env["SENDOU_INK_URL"]}/plus/list`, { + headers: [[LOHI_TOKEN_HEADER_NAME, process.env["LOHI_TOKEN"]]], + }); + + if (!response.ok) { + throw new Error( + `Failed to fetch users. Response status was ${response.status}` + ); + } + + return response.json(); +} diff --git a/discord-bot/commands/index.ts b/discord-bot/commands/index.ts index 1c32b65f1..aedec6e1c 100644 --- a/discord-bot/commands/index.ts +++ b/discord-bot/commands/index.ts @@ -1,8 +1,9 @@ import { questionCommand } from "./q"; import { lfgRoleCommand } from "./lfg"; +import { accessCommand } from "./access"; import type { BotCommand } from "discord-bot/types"; -export const commands = [questionCommand, lfgRoleCommand]; +export const commands = [questionCommand, lfgRoleCommand, accessCommand]; export const commandsMap = Object.fromEntries(commands.map((c) => [c.name, c])); diff --git a/discord-bot/package-lock.json b/discord-bot/package-lock.json index 9ed5895ab..d9462efe3 100644 --- a/discord-bot/package-lock.json +++ b/discord-bot/package-lock.json @@ -1,13 +1,11 @@ { - "name": "discord-bot", + "name": "lohi", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "discord-bot", - "version": "1.0.0", - "license": "ISC", + "name": "lohi", "dependencies": { "@discordjs/builders": "^0.15.0", "@discordjs/rest": "^0.5.0", @@ -16,6 +14,7 @@ "dotenv": "^16.0.1", "tiny-invariant": "^1.2.0", "ts-node": "^10.8.1", + "tsconfig-paths": "^4.0.0", "typescript": "^4.7.3" } }, @@ -328,6 +327,17 @@ "node": ">= 6" } }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -352,6 +362,11 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -371,6 +386,14 @@ } } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, "node_modules/tiny-invariant": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", @@ -428,6 +451,16 @@ } } }, + "node_modules/tsconfig-paths": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz", + "integrity": "sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q==", + "dependencies": { + "json5": "^2.2.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -752,6 +785,11 @@ "mime-types": "^2.1.12" } }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -770,6 +808,11 @@ "mime-db": "1.52.0" } }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -778,6 +821,11 @@ "whatwg-url": "^5.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, "tiny-invariant": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", @@ -813,6 +861,16 @@ "yn": "3.1.1" } }, + "tsconfig-paths": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz", + "integrity": "sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q==", + "requires": { + "json5": "^2.2.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", diff --git a/discord-bot/package.json b/discord-bot/package.json index e85f2c627..1cbd3a839 100644 --- a/discord-bot/package.json +++ b/discord-bot/package.json @@ -1,7 +1,7 @@ { "name": "lohi", "scripts": { - "dev": "ts-node index.ts", + "dev": "node --experimental-fetch --require ts-node/register --require tsconfig-paths/register index.ts", "commands": "ts-node deploy-commands.ts", "test": "echo \"Error: no test specified\" && exit 1" }, @@ -13,6 +13,7 @@ "dotenv": "^16.0.1", "tiny-invariant": "^1.2.0", "ts-node": "^10.8.1", + "tsconfig-paths": "^4.0.0", "typescript": "^4.7.3" } }