Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
fd6407d
Merge branch 'events_command' into dev
DevRuto Apr 15, 2025
cf0c36a
Merge branch 'team_commands' into dev
DevRuto Apr 15, 2025
8591c30
Add info commands and team registration
DevRuto Apr 15, 2025
eed6439
Merge branch 'main' into register
DevRuto Apr 15, 2025
470a993
lint
DevRuto Apr 15, 2025
ac6e97c
Update README
DevRuto Apr 15, 2025
636b3e3
Add Active flag and command for Events
DevRuto Apr 15, 2025
9446d4d
Merge branch 'register' into active_event
DevRuto Apr 15, 2025
52dd094
Use active event for commands
DevRuto Apr 15, 2025
f7bcfbc
Prettier README.md
DevRuto Apr 15, 2025
4b53b82
Merge branch 'register' into dev
DevRuto Apr 15, 2025
c16af02
Add Members to Team info
DevRuto Apr 15, 2025
597750c
Split Team/Members by new line
DevRuto Apr 15, 2025
46917ca
Add add/remove participants to teams
DevRuto Apr 15, 2025
28f6dd7
Merge branch 'active_event' into participant
DevRuto Apr 15, 2025
049abae
Add RSN to EventParticipant
DevRuto Apr 15, 2025
47b0467
Rename event/team info command
DevRuto Apr 15, 2025
973d2bb
Allow submission if registered
DevRuto Apr 15, 2025
0eb63b1
Fix member registration
DevRuto Apr 15, 2025
5cfd5a1
Clarify params
DevRuto Apr 15, 2025
5d72104
Update README
DevRuto Apr 15, 2025
4a13e4c
Update README
DevRuto Apr 15, 2025
370733b
Merge branch 'submission' into dev
DevRuto Apr 15, 2025
4d02ada
Remove custom admin role
DevRuto Apr 15, 2025
b2bfbf2
Use ephemeral flag
DevRuto Apr 16, 2025
689e808
Add note and change id to discordId for participant
DevRuto Apr 16, 2025
9c5e02a
Update registration command to support duo
DevRuto Apr 16, 2025
0bb8915
Lowercase RSN
DevRuto Apr 16, 2025
22f1083
Add status command
DevRuto Apr 16, 2025
1e0c4cb
Don't show duo if it exists
DevRuto Apr 16, 2025
9161e9e
Add log
DevRuto Apr 16, 2025
84a8aaf
Add setting for accepted and signed up channels
DevRuto Apr 16, 2025
28e2448
Forward signups to configured channel
DevRuto Apr 16, 2025
fa0867f
Add leader to their own team
DevRuto Apr 16, 2025
b9536dd
Numbered list in embed
DevRuto Apr 16, 2025
d69aa69
Add color to embed based on name hash
DevRuto Apr 16, 2025
d206153
Check if leader is in a team before creation
DevRuto Apr 16, 2025
69e7583
Update README
DevRuto Apr 16, 2025
e117b72
Add command to deploy discord commands
DevRuto Apr 16, 2025
f655755
Update README
DevRuto Apr 16, 2025
bbc3431
Approve/Deny submissions
DevRuto Apr 16, 2025
3a2470b
Add log
DevRuto Apr 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,40 @@ npm start
## Available Commands

- `/ping` - Bot responds with "Pong!"

## Admin commands

- `/approvechannel` - Set the channel for submissions to be forwarded to for approval
- `/signupchannel` - Set the channel to announce registered members
- `/acceptedchannel` - Set the channel to announced approved submissions

## Mod commands

- `/create-event` `/edit-event` - Create/Edit an event
- `/create-team` `/edit-team` - Create/Edit a team
- `/activate-event` - Activate the event for team commands
- `/register-team` - Register a team for an event

## Public commands

- `/event` - Information for an event and teams
- `/team` - Information for a team and members
- `/register` - Register yourself to the event
- `/status` - Status for yourself

## Event commands (for participants)

- `/submit` - Submit an image to the event

## Usage

1. Setting up event and team
1. /create-event name: CAKE description: yumyum
2. /activate-event event: CAKE
3. /create-team leader: @ruto name: BESTTEAM
- Note: They must already be in discord
4. /register-team team: CAKE
2. Other users must now register
1. /register rsn: noah
3. Mod can now add user to a team
1. /addmember team: CAKE user: @Noah
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"prisma:generate": "prisma generate",
"prisma:migrate": "prisma migrate dev",
"prisma:studio": "prisma studio",
"prisma:seed": "node prisma/seed.js"
"prisma:seed": "node prisma/seed.js",
"commands": "node ./deploy_commands.js"
},
"dependencies": {
"@prisma/client": "^5.10.0",
Expand Down
19 changes: 19 additions & 0 deletions prisma/migrations/20250415182835_active_event/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Event" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"description" TEXT,
"startDate" DATETIME NOT NULL,
"endDate" DATETIME,
"status" TEXT NOT NULL DEFAULT 'PLANNED',
"active" BOOLEAN NOT NULL DEFAULT false,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_Event" ("createdAt", "description", "endDate", "id", "name", "startDate", "status", "updatedAt") SELECT "createdAt", "description", "endDate", "id", "name", "startDate", "status", "updatedAt" FROM "Event";
DROP TABLE "Event";
ALTER TABLE "new_Event" RENAME TO "Event";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventParticipant" ADD COLUMN "rsn" TEXT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventParticipant" ADD COLUMN "note" TEXT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Warnings:

- The primary key for the `EventParticipant` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the column `id` on the `EventParticipant` table. All the data in the column will be lost.

*/
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_EventParticipant" (
"userId" TEXT NOT NULL,
"eventId" TEXT NOT NULL,
"rsn" TEXT,
"note" TEXT,
"status" TEXT NOT NULL DEFAULT 'REGISTERED',

PRIMARY KEY ("userId", "eventId"),
CONSTRAINT "EventParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "DiscordUser" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "EventParticipant_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_EventParticipant" ("eventId", "note", "rsn", "status", "userId") SELECT "eventId", "note", "rsn", "status", "userId" FROM "EventParticipant";
DROP TABLE "EventParticipant";
ALTER TABLE "new_EventParticipant" RENAME TO "EventParticipant";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_EventParticipant" (
"userId" TEXT NOT NULL,
"eventId" TEXT NOT NULL,
"rsn" TEXT,
"note" TEXT,
"status" TEXT NOT NULL DEFAULT 'REGISTERED',

PRIMARY KEY ("userId", "eventId"),
CONSTRAINT "EventParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "DiscordUser" ("discordId") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "EventParticipant_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_EventParticipant" ("eventId", "note", "rsn", "status", "userId") SELECT "eventId", "note", "rsn", "status", "userId" FROM "EventParticipant";
DROP TABLE "EventParticipant";
ALTER TABLE "new_EventParticipant" RENAME TO "EventParticipant";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
8 changes: 5 additions & 3 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ model Event {
startDate DateTime
endDate DateTime?
status String @default("PLANNED") // PLANNED, ONGOING, COMPLETED, CANCELLED
active Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

Expand Down Expand Up @@ -71,14 +72,15 @@ model TeamMember {
}

model EventParticipant {
id String @id @default(uuid())
userId String
eventId String
rsn String? // Registered in-game name
note String?
status String @default("REGISTERED") // REGISTERED, INTERESTED, UNPAID
user DiscordUser @relation(fields: [userId], references: [id])
user DiscordUser @relation(fields: [userId], references: [discordId])
event Event @relation(fields: [eventId], references: [id])

@@unique([userId, eventId])
@@id([userId, eventId])
}

model Submission {
Expand Down
43 changes: 43 additions & 0 deletions src/commands/admin/acceptedchannel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
ChannelType,
ChatInputCommandInteraction,
PermissionFlagsBits,
SlashCommandBuilder,
} from 'discord.js';
import { ConfigService } from '#services/configService.js';
import logger from '#utils/logger.js';

export const data = new SlashCommandBuilder()
.setName('acceptedchannel')
.setDescription('Get or set the accepted submission channel')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addChannelOption((option) =>
option
.setName('channel')
.setDescription('Channel to forward accepted submissions to')
.setRequired(false)
.addChannelTypes(ChannelType.GuildText)
);

/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
const channel = interaction.options.getChannel('channel');

// if channel doesn't exist, get current channel if it exists
if (!channel) {
const currentChannel = await ConfigService.getAcceptedChannel(interaction.guildId);
if (currentChannel) {
await interaction.reply(`Current accepted channel: <#${currentChannel}>`);
} else {
await interaction.reply('No accepted channel set.');
}
return;
}

// if channel exists, set it as the accepted channel
await ConfigService.setAcceptedChannel(interaction.guildId, channel.id);
await interaction.reply(`Accepted channel set to <#${channel.id}>`);
logger.info(`Accepted channel set to ${channel.id} for guild ${interaction.guildId}`);
}
34 changes: 0 additions & 34 deletions src/commands/admin/adminrole.js

This file was deleted.

24 changes: 7 additions & 17 deletions src/commands/admin/approvechannel.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { ChannelType, ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
import {
ChannelType,
ChatInputCommandInteraction,
PermissionFlagsBits,
SlashCommandBuilder,
} from 'discord.js';
import { ConfigService } from '#services/configService.js';
import logger from '#utils/logger.js';

export const data = new SlashCommandBuilder()
.setName('approvechannel')
.setDescription('Get or set the approval submission channel')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addChannelOption((option) =>
option
.setName('channel')
Expand All @@ -30,22 +36,6 @@ export async function execute(interaction) {
return;
}

// Check if user has admin permissions
// Check custom role first
if (!interaction.member.permissions.has('Administrator')) {
const customRole = await ConfigService.getAdminRole(interaction.guildId);
if (customRole) {
const member = await interaction.guild.members.fetch(interaction.user.id);
if (!member.roles.cache.has(customRole)) {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
} else {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
}

// if channel exists, set it as the approval channel
await ConfigService.setApprovalChannel(interaction.guildId, channel.id);
await interaction.reply(`Approval channel set to <#${channel.id}>`);
Expand Down
40 changes: 40 additions & 0 deletions src/commands/admin/event/activateevent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ChatInputCommandInteraction, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
import { EventService } from '#services/eventService.js';
import logger from '#utils/logger.js';

export const data = new SlashCommandBuilder()
.setName('activate-event')
.setDescription('Set the event to active for the bot context')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addStringOption((option) =>
option
.setName('event')
.setDescription('The event to activate')
.setRequired(true)
.setAutocomplete(true)
);

/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
const eventId = interaction.options.getString('event');
try {
const event = await EventService.activateEvent(eventId);
await interaction.reply(`Event ${event.name} has been activated.`);
} catch (error) {
logger.error('Error activating event', error);
await interaction.reply('An error occurred while activating the event.');
}
}

/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function autocomplete(interaction) {
const focusedValue = interaction.options.getFocused();
const events = await EventService.getEvents();
const filtered = events.filter((event) => event.name.startsWith(focusedValue));
const choices = filtered.map((event) => event).slice(0, 25);
await interaction.respond(choices.map((choice) => ({ name: choice.name, value: choice.id })));
}
18 changes: 2 additions & 16 deletions src/commands/admin/event/createevent.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
import { ConfigService } from '#services/configService.js';
import { ChatInputCommandInteraction, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
import logger from '#utils/logger.js';
import { EventService } from '#services/eventService.js';

export const data = new SlashCommandBuilder()
.setName('create-event')
.setDescription('Create an event')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addStringOption((option) =>
option.setName('name').setDescription('The name of the event').setRequired(true)
)
Expand All @@ -23,20 +23,6 @@ export const data = new SlashCommandBuilder()
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
if (!interaction.member.permissions.has('Administrator')) {
const customRole = await ConfigService.getAdminRole(interaction.guildId);
if (customRole) {
const member = await interaction.guild.members.fetch(interaction.user.id);
if (!member.roles.cache.has(customRole)) {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
} else {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
}

const name = interaction.options.getString('name');
const description = interaction.options.getString('description');
const startDate = interaction.options.getString('startdate');
Expand Down
18 changes: 2 additions & 16 deletions src/commands/admin/event/editevent.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
import { ConfigService } from '#services/configService.js';
import { ChatInputCommandInteraction, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
import logger from '#utils/logger.js';
import { EventService } from '#services/eventService.js';

export const data = new SlashCommandBuilder()
.setName('edit-event')
.setDescription('Edit an event')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addStringOption((option) =>
option
.setName('event')
Expand Down Expand Up @@ -42,20 +42,6 @@ export const data = new SlashCommandBuilder()
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
if (!interaction.member.permissions.has('Administrator')) {
const customRole = await ConfigService.getAdminRole(interaction.guildId);
if (customRole) {
const member = await interaction.guild.members.fetch(interaction.user.id);
if (!member.roles.cache.has(customRole)) {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
} else {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
}

const eventId = interaction.options.getString('event');
const name = interaction.options.getString('name');
const description = interaction.options.getString('description');
Expand Down
Loading