Refactor thread command

This commit is contained in:
Rigidity
2024-05-24 12:52:53 -04:00
parent d754b053dd
commit b4be026419
2 changed files with 106 additions and 64 deletions

View File

@@ -1,12 +1,18 @@
import { command } from 'jellycommands';
import { wrap_in_embed } from '../utils/embed_helpers';
import { ChannelType, Message } from 'discord.js';
import {
AnyThreadChannel,
Channel,
ChannelType,
ChatInputCommandInteraction,
Collection,
GuildTextBasedChannel,
} from 'discord.js';
import { HELP_THREAD_CHANNELS } from '../config';
import { wrapErrors } from '../utils/errors';
export default command({
name: 'threads',
description: 'Manage all threads',
options: [
{
name: 'list',
@@ -14,70 +20,88 @@ export default command({
type: 'Subcommand',
},
],
global: true,
defer: {
ephemeral: true,
},
defer: { ephemeral: true },
run: wrapErrors(async (interaction) => {
const subcommand = interaction.options.getSubcommand(true);
run: async ({ interaction }) => {
try {
const subcommand = interaction.options.getSubcommand(true);
// Get all active non-private threads in the guild that the user has access to
const threads = (
await interaction.guild.channels.fetchActiveThreads()
).threads
.map((x) => x)
.filter((thread) =>
thread
.permissionsFor(interaction.user)
.has(['ReadMessageHistory', 'ViewChannel']),
);
const activeThreads =
await interaction.guild.channels.fetchActiveThreads();
switch (subcommand) {
case 'list': {
// Get the parent channel if we're using it inside a thread
const parentChannel =
interaction.channel.type === ChannelType.GuildText
? interaction.channel
: interaction.channel.parent;
// Filter all threads based on the channel the command was ran in
let listThreads = threads.filter(
(thread) => thread.parentId === parentChannel.id,
);
if (HELP_THREAD_CHANNELS.includes(parentChannel.id)) {
listThreads = listThreads.filter((thread) =>
thread.name.startsWith('❔'),
);
}
// Set a title for the DM
let message = `**Here's a list of all currently active threads in <#${parentChannel.id}>**\n`;
if (listThreads.length === 0) {
message = `**There are currently no active threads in <#${parentChannel.id}>**`;
} else {
// Add all chat threads to the message
message += listThreads
.map((thread) => `<#${thread.id}>`)
.join('\n');
}
// Send the message to the user
await interaction.followUp(message);
break;
}
}
} catch (e) {
// Send the error
const reply = (await interaction.followUp(
wrap_in_embed((e as Error).message),
)) as Message;
// Delete the error after 15 seconds
try {
setTimeout(async () => {
reply.delete();
}, 15000);
} catch (e) {
console.error(e);
const visibleThreads = activeThreads.threads.filter((thread) =>
thread
.permissionsFor(interaction.user)
.has(['ReadMessageHistory', 'ViewChannel']),
);
switch (subcommand) {
case 'list': {
await handleList(interaction, visibleThreads);
break;
}
}
},
}),
});
// If the channel is a text channel, it doesn't have a parent.
// For other kinds of channels, we return null since it's not a thread.
// If the channel is a thread, we return the parent channel.
function getParentChannel(channel: GuildTextBasedChannel): Channel | null {
switch (channel.type) {
case ChannelType.GuildText:
return channel;
case ChannelType.PublicThread:
return channel.parent;
case ChannelType.PrivateThread:
return channel.parent;
default:
return null;
}
}
async function handleList(
interaction: ChatInputCommandInteraction,
visibleThreads: Collection<string, AnyThreadChannel<boolean>>,
) {
const parentChannel = getParentChannel(interaction.channel);
if (!parentChannel) {
return interaction.reply({
ephemeral: true,
content:
'You can only use this command in a text channel or thread.',
});
}
if (!parentChannel.isTextBased()) {
return interaction.reply({
ephemeral: true,
content: 'The parent of the thread must be a text channel.',
});
}
const isHelpChannel = HELP_THREAD_CHANNELS.includes(parentChannel.id);
const threadsWithinChannel = visibleThreads.filter(
(thread) => thread.parentId === parentChannel.id,
);
const threads = isHelpChannel
? threadsWithinChannel
: threadsWithinChannel.filter((thread) => thread.name.startsWith('❔'));
if (!threads.size) {
return interaction.reply({
ephemeral: true,
content: `There are no active threads in <#${parentChannel.id}>.`,
});
}
const threadList = threads.map((thread) => `<#${thread.id}>`).join(', ');
await interaction.reply({
content: `Active threads in <#${parentChannel.id}>: ${threadList}`,
});
}

18
src/utils/errors.ts Normal file
View File

@@ -0,0 +1,18 @@
import { ChatInputCommandInteraction } from 'discord.js';
export function wrapErrors(
handler: (interaction: ChatInputCommandInteraction) => Promise<void>,
): (args: { interaction: ChatInputCommandInteraction }) => Promise<void> {
return async ({ interaction }) => {
try {
await handler(interaction);
} catch (error) {
console.error(error);
await interaction.reply({
content: `Runtime error: ${error}`,
ephemeral: true,
});
}
};
}