Skip to content

Commit

Permalink
Merge pull request #314 from TFNS/0-cleanup-discord-integration-code
Browse files Browse the repository at this point in the history
Cleanup discord integration code
  • Loading branch information
JJ-8 authored Nov 12, 2024
2 parents 0342947 + 8196865 commit 2f130b3
Show file tree
Hide file tree
Showing 30 changed files with 832 additions and 634 deletions.
9 changes: 9 additions & 0 deletions api/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ type DeepReadOnly<T> = {
: T[k];
};

export enum DiscordChannelHandleStyle {
Agile = "agile",
}

export type CTFNoteConfig = DeepReadOnly<{
env: string;
sessionSecret: string;
Expand Down Expand Up @@ -45,6 +49,7 @@ export type CTFNoteConfig = DeepReadOnly<{
registrationEnabled: string;
registrationAccountRole: string;
registrationRoleId: string;
channelHandleStyle: DiscordChannelHandleStyle;
};
}>;

Expand Down Expand Up @@ -102,6 +107,10 @@ const config: CTFNoteConfig = {
"user_guest"
),
registrationRoleId: getEnv("DISCORD_REGISTRATION_ROLE_ID", ""),
channelHandleStyle: getEnv(
"DISCORD_CHANNEL_HANDLE_STYLE",
"agile"
) as DiscordChannelHandleStyle,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import {
getTaskTitleFromTopic,
sendMessageToChannel,
topicDelimiter,
} from "./messages";
} from "../utils/messages";
import {
isChannelOfCtf,
isTaskChannelOf,
isRoleOfCtf,
isCategoryOfCtf,
} from "./comparison";
} from "../utils/comparison";
import { safeSlugify } from "../../utils/utils";

enum CategoryType {
Expand All @@ -53,6 +53,8 @@ export interface TaskInput {
flag: string;
}

export const challengesTalkChannelName = "challenges-talk";

const newPrefix = "New - ";
const startedPrefix = "Started - ";
const solvedPrefix = "Solved - ";
Expand Down Expand Up @@ -171,7 +173,7 @@ export async function createChannelsAndRolesForCtf(guild: Guild, ctf: CTF) {

// create challenges-talk channel
await guild?.channels.create({
name: `challenges-talk`,
name: challengesTalkChannelName,
type: ChannelType.GuildText,
parent: startedCategory?.id,
});
Expand Down Expand Up @@ -231,7 +233,7 @@ function getTalkChannelForCtf(guild: Guild, ctf: CTF) {
return guild.channels.cache.find(
(channel) =>
channel.type === ChannelType.GuildText &&
channel.name === `challenges-talk` &&
channel.name === challengesTalkChannelName &&
isChannelOfCtf(channel, startedCategoryName(ctf))
) as TextChannel;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Command } from "./command";
import { CreateCtf } from "./commands/createCtf";
import { ArchiveCtf } from "./commands/archiveCtf";
import { SolveTask } from "./commands/solveTask";
Expand All @@ -7,7 +6,7 @@ import { StartWorking, StopWorking } from "./commands/workingOn";
import { DeleteCtf } from "./commands/deleteCtf";
import { Register } from "./commands/register";

export const Commands: Command[] = [
export default [
ArchiveCtf,
CreateCtf,
SolveTask,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@ import {
ActionRowBuilder,
ApplicationCommandType,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
Client,
CommandInteraction,
Interaction,
PermissionFlagsBits,
} from "discord.js";
import { Command } from "../command";
import { Command } from "../../interfaces/command";
import {
createTask,
getAllCtfsFromDatabase,
getCtfFromDatabase,
} from "../database/ctfs";
import { getChannelCategoriesForCtf } from "../utils/channels";
} from "../../database/ctfs";
import { getChannelCategoriesForCtf } from "../channels";
import {
convertMessagesToPadFormat,
createPadWithoutLimit,
getMessagesOfCategories,
} from "../utils/messages";
} from "../../utils/messages";
import { DiscordButtonInteraction } from "../../interfaces/interaction";

export async function handleArchiveInteraction(
interaction: Interaction,
async function handleArchiveInteraction(
interaction: ButtonInteraction,
ctfName: string
) {
const guild = interaction.guild;
Expand Down Expand Up @@ -53,11 +54,35 @@ export async function handleArchiveInteraction(
return true;
}

export const HandleArchiveCtfInteraction: DiscordButtonInteraction = {
customId: "archive-ctf-button",
handle: async (client: Client, interaction: ButtonInteraction) => {
const ctfName = interaction.customId.replace("archive-ctf-button-", "");
await interaction.deferUpdate();
await interaction.editReply({
content: `Archiving the CTF channels and roles for ${ctfName}`,
components: [],
});

if (await handleArchiveInteraction(interaction, ctfName)) {
await interaction.editReply({
content: `Archived the CTF channels and roles for ${ctfName}`,
components: [],
});
} else {
await interaction.editReply({
content: `Failed to archive the CTF channels and roles for ${ctfName}`,
components: [],
});
}
},
};

async function archiveCtfLogic(
client: Client,
interaction: CommandInteraction
) {
// Get current CTFs from the discord categorys
// Get current CTFs from the discord categories
let ctfNames = await getAllCtfsFromDatabase();
const guild = interaction.guild;
if (guild == null) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,57 @@ import {
ActionRowBuilder,
ApplicationCommandType,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
Client,
CommandInteraction,
PermissionFlagsBits,
} from "discord.js";
import { Command } from "../command";
import { getCTFNamesFromDatabase } from "../database/ctfs";
import { getChannelCategoriesForCtf } from "../utils/channels";
import { Command } from "../../interfaces/command";
import {
getCtfFromDatabase,
getCTFNamesFromDatabase,
} from "../../database/ctfs";
import {
createChannelForTaskInCtf,
createChannelsAndRolesForCtf,
getChannelCategoriesForCtf,
} from "../channels";
import { DiscordButtonInteraction } from "../../interfaces/interaction";
import { getChallengesFromDatabase } from "../../database/tasks";

export const HandleCreateCtfInteraction: DiscordButtonInteraction = {
customId: "create-ctf-button",
handle: async (client: Client, interaction: ButtonInteraction) => {
const ctfName = interaction.customId.replace("create-ctf-button-", "");
await interaction.deferUpdate();
await interaction.editReply({
content: `Creating the CTF channels and roles for ${ctfName}`,
components: [],
});

const guild = interaction.guild;
if (guild == null) return;

// assign roles to users already having access to the ctf
const ctf = await getCtfFromDatabase(ctfName);
if (ctf == null) return;

await createChannelsAndRolesForCtf(guild, ctf);

// create for every challenge a channel
const challenges = await getChallengesFromDatabase(ctf.id);

for (const challenge of challenges) {
await createChannelForTaskInCtf(guild, challenge, ctf);
}

await interaction.editReply({
content: `Created the CTF channels and roles for ${ctfName}`,
components: [],
});
},
};

async function createCtfLogic(client: Client, interaction: CommandInteraction) {
let ctfNames = await getCTFNamesFromDatabase();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ import {
ActionRowBuilder,
ApplicationCommandType,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
Client,
CommandInteraction,
Interaction,
PermissionFlagsBits,
} from "discord.js";
import { Command } from "../command";
import { getAllCtfsFromDatabase, getCtfFromDatabase } from "../database/ctfs";
import { getChannelCategoriesForCtf } from "../utils/channels";
import { handleDeleteCtf } from "../../plugins/discordHooks";
import { getTaskByCtfIdAndNameFromDatabase } from "../database/tasks";
import { discordArchiveTaskName } from "../utils/messages";
import { Command } from "../../interfaces/command";
import {
getAllCtfsFromDatabase,
getCtfFromDatabase,
} from "../../database/ctfs";
import { getChannelCategoriesForCtf } from "../channels";
import { handleDeleteCtf } from "../hooks";
import { getTaskByCtfIdAndNameFromDatabase } from "../../database/tasks";
import { discordArchiveTaskName } from "../../utils/messages";
import { DiscordButtonInteraction } from "../../interfaces/interaction";

export async function handleDeleteInteraction(
interaction: Interaction,
Expand Down Expand Up @@ -74,6 +79,37 @@ async function deleteCtfLogic(client: Client, interaction: CommandInteraction) {
});
}

export const HandleDeleteCtfInteraction: DiscordButtonInteraction = {
customId: "delete-ctf-button",
handle: async (client: Client, interaction: ButtonInteraction) => {
const ctfName = interaction.customId.replace("delete-ctf-button-", "");
await interaction.deferUpdate();
await interaction.editReply({
content: `Deleting the CTF channels and roles for ${ctfName}`,
components: [],
});

const done = await handleDeleteInteraction(interaction, ctfName);
if (!done) {
await interaction.editReply({
content: `I insist that you create an \`/archive\` first!`,
});
} else {
try {
await interaction.editReply({
content: `Deleted the CTF channels and roles for ${ctfName}`,
components: [],
});
} catch (error) {
console.debug(
"Failed to update message that CTF was deleted. Probably the command was triggered in a channel that got deleted by the /delete command.",
error
);
}
}
},
};

export const DeleteCtf: Command = {
name: "delete",
description: "Delete the channels and roles for a CTF. This is irreversible!",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import {
Client,
CommandInteraction,
} from "discord.js";
import { Command } from "../command";
import { Command } from "../../interfaces/command";
import {
getDiscordIdFromUserId,
getUserByToken,
setDiscordIdForUser,
} from "../database/users";
import { CTF, getAccessibleCTFsForUser } from "../database/ctfs";
import { getDiscordClient } from "..";
import config from "../../config";
} from "../../database/users";
import { CTF, getAccessibleCTFsForUser } from "../../database/ctfs";
import { getDiscordClient } from "../..";
import config from "../../../config";
import { PoolClient } from "pg";

export async function changeDiscordUserRoleForCTF(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {
CommandInteraction,
GuildMemberRoleManager,
} from "discord.js";
import { Command } from "../command";
import { Command } from "../../interfaces/command";
import {
AllowedRoles,
createInvitationTokenForDiscordId,
getInvitationTokenForDiscordId,
getUserByDiscordId,
} from "../database/users";
import config from "../../config";
} from "../../database/users";
import config from "../../../config";

async function getInvitationUrl(invitationCode: string | null = null) {
if (config.pad.domain == "") return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,42 @@ import {
ApplicationCommandType,
Client,
CommandInteraction,
Guild,
} from "discord.js";
import { Command } from "../command";
import { setFlagForChallengeId } from "../database/tasks";
import { handleTaskSolved } from "../../plugins/discordHooks";
import { getUserByDiscordId } from "../database/users";
import { getCurrentTaskChannelFromDiscord } from "../utils/channels";
import { Command } from "../../interfaces/command";
import { getTaskFromId, setFlagForChallengeId } from "../../database/tasks";
import { getUserByDiscordId } from "../../database/users";
import {
ChannelMovingEvent,
getCurrentTaskChannelFromDiscord,
moveChannel,
} from "../channels";
import { sendMessageToTask } from "../../utils/messages";
import { convertToUsernameFormat } from "../../utils/user";

async function accessDenied(interaction: CommandInteraction) {
await interaction.editReply({
content: "You are not using a valid channel to solve the task",
});
}

export async function handleTaskSolved(
guild: Guild,
id: bigint,
userId: bigint | string
) {
const task = await getTaskFromId(id);
if (task == null) return;

await moveChannel(guild, task, null, ChannelMovingEvent.SOLVED);

return sendMessageToTask(
guild,
id,
`${task.title} is solved by ${await convertToUsernameFormat(userId)}!`
);
}

async function solveTaskLogic(client: Client, interaction: CommandInteraction) {
const r = await getCurrentTaskChannelFromDiscord(interaction);
if (r == null) return accessDenied(interaction);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { ApplicationCommandType, Client, CommandInteraction } from "discord.js";
import { Command } from "../command";
import { Command } from "../../interfaces/command";
import {
userStartsWorkingOnTask,
userStopsWorkingOnTask,
} from "../database/tasks";
import {
sendStartWorkingOnMessage,
sendStopWorkingOnMessage,
} from "../../plugins/discordHooks";
import { getUserByDiscordId } from "../database/users";
import { getCurrentTaskChannelFromDiscord } from "../utils/channels";
} from "../../database/tasks";
import { sendStartWorkingOnMessage, sendStopWorkingOnMessage } from "../hooks";
import { getUserByDiscordId } from "../../database/users";
import { getCurrentTaskChannelFromDiscord } from "../channels";

async function accessDenied(interaction: CommandInteraction) {
await interaction.editReply({
Expand Down
Loading

0 comments on commit 2f130b3

Please sign in to comment.