diff --git a/prisma/migrations/20250806130822_add_invites/migration.sql b/prisma/migrations/20250806130822_add_invites/migration.sql new file mode 100644 index 0000000..49cc2bc --- /dev/null +++ b/prisma/migrations/20250806130822_add_invites/migration.sql @@ -0,0 +1,9 @@ +-- CreateTable +CREATE TABLE "Invite" ( + "id" TEXT NOT NULL PRIMARY KEY, + "slackId" TEXT NOT NULL, + "invitedBy" TEXT NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "Invite_slackId_key" ON "Invite"("slackId"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 78720a8..217b429 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -86,3 +86,9 @@ model AsAccount { updatedAt DateTime @updatedAt lastResetDate DateTime? } + +model Invite { + id String @id @default(cuid()) + slackId String @unique + invitedBy String // slack id +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 558ad38..aafa3d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import { Airports } from './util/airports'; import { AdsBDB } from './util/adsbdb'; import { FlightUpdater } from './util/flightUpdater'; import { largestMortalsId } from './util/largestMortalsId'; +import { isInvited } from './util/isInvited'; export const app = new App({ token: process.env.SLACK_BOT_TOKEN, @@ -27,6 +28,14 @@ app.command('/flight-add', async ({ command, ack, respond }) => { const EXAMPLE = `_Example: \`/flight-add AGP today 11\` looks for flights from AGP today at 11:00 (24-hour UTC)._`; await ack(); + if (!await isInvited(command.user_id)) { + await respond({ + text: '❌ You are not invited to use this command.', + response_type: 'ephemeral', + }); + return; + } + if ( !command.channel_id || (!command.channel_id.startsWith('C') && !command.channel_id.startsWith('G')) @@ -133,6 +142,48 @@ app.command('/flight-add', async ({ command, ack, respond }) => { } }); +app.command('/flight-invite', async ({ command, ack, respond }) => { + await ack(); + if (command.user_id !== process.env.OWNER_ID) { + await respond({ + text: '❌ You are not allowed to use this command.', + response_type: 'ephemeral', + }); + return; + } + + const args = command.text.split(' '); + // claude generated regex for this... yeah + const inviteeId = args[0]?.replace(/^<@([^|>]+).*>$/, '$1'); + const rm = args[1] === "remove"; + if (!inviteeId) { + await respond({ + text: 'provide a slack id idioiot', + response_type: 'ephemeral', + }); + return; + } + if (rm) { + await db.invite.delete({ + where: { + slackId: inviteeId, + } + }) + } else { + await db.invite.create({ + data: { + slackId: inviteeId, + invitedBy: command.user_id, + }, + }) + } + + await respond({ + text: `✅ ${rm ? 'Removed' : 'Added'} invite for <@${inviteeId}>.`, + response_type: 'ephemeral', + }); +}); + async function showFlightPage( respond: any, flights: ScheduledDeparture[], diff --git a/src/util/isInvited.ts b/src/util/isInvited.ts new file mode 100644 index 0000000..91bba31 --- /dev/null +++ b/src/util/isInvited.ts @@ -0,0 +1,9 @@ +import { db } from ".."; + +export async function isInvited(slackId: string) { + return process.env.OWNER_ID === slackId || (await db.invite.count({ + where: { + slackId, + } + })) > 0; +} \ No newline at end of file