Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup discord integration code #314

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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