Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
94 changes: 94 additions & 0 deletions src/commands/utility/confess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Command } from '@sapphire/framework';
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
EmbedBuilder,
LabelBuilder,
MessageFlags,
ModalBuilder,
TextInputBuilder,
TextInputStyle,
TextChannel
} from 'discord.js';
import { getSettings } from '#lib/settings.js';
import { storeConfessionContext } from '#lib/confessions.js';
import { emojis } from '#utils/emoji.js';

export class ConfessCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
super(context, { ...options });
}

public override registerApplicationCommands(_registry: Command.Registry) {
// Registration is handled per-guild at runtime in the Ready listener
}

public override async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
if (!interaction.inGuild() || !interaction.guild) {
await interaction.reply({ content: `${emojis.rightArrow2} This command can only be used in a server.`, flags: MessageFlags.Ephemeral });
return;
}

const settings = await getSettings(interaction.guild.id, interaction.guild.name);

if (!settings.confessionChannelId || !settings.confessionEnabled) {
await interaction.reply({ content: `${emojis.rightArrow2} Confessions are not enabled or configured for this server.`, flags: MessageFlags.Ephemeral });
return;
}

const confessionInput = new TextInputBuilder()
.setCustomId('confession-text')
.setStyle(TextInputStyle.Paragraph)
.setRequired(true)
.setMaxLength(1_000);

const confessionLabel = new LabelBuilder().setLabel('Confession').setTextInputComponent(confessionInput);

const modal = new ModalBuilder().setCustomId('create-confession-modal').setTitle('Create Confession').addLabelComponents(confessionLabel);

await interaction.showModal(modal);

let modalSubmit;

try {
modalSubmit = await interaction.awaitModalSubmit({
filter: (m) => m.customId === 'create-confession-modal' && m.user.id === interaction.user.id,
time: 60_000
});
} catch {
return;
}

const confession = modalSubmit.fields.getTextInputValue('confession-text');

const confessionChannel = await interaction.guild.channels.fetch(settings.confessionChannelId).catch(() => null);

if (!(confessionChannel instanceof TextChannel)) {
await modalSubmit.reply({ content: `${emojis.rightArrow2} The configured confession channel is unavailable.`, flags: MessageFlags.Ephemeral });
return;
}

const embed = new EmbedBuilder().setTitle('Confession').setDescription(confession).setTimestamp();

const message = await confessionChannel.send({ embeds: [embed] });

let thread;

try {
thread = await message.startThread({ name: `confession-${message.id}` });
} catch (error) {
await message.delete().catch(() => null);
throw error;
}

const reportButton = new ButtonBuilder().setCustomId(`report-confession:${message.id}`).setLabel('Report').setStyle(ButtonStyle.Danger);
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(reportButton);

await message.edit({ components: [row] });

storeConfessionContext({ guildId: interaction.guild.id, channelId: confessionChannel.id, messageId: message.id, threadId: thread.id });

await modalSubmit.reply({ content: `${emojis.rightArrow2} Confession sent.`, flags: MessageFlags.Ephemeral });
}
}
53 changes: 53 additions & 0 deletions src/commands/utility/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,42 @@ function buildLoggingPanel(settings: ServerSettings, guild: Guild, status?: stri
};
}

function buildConfessionPanel(settings: ServerSettings, guild: Guild, status?: string) {
const currentChannelName = settings.confessionChannelId
? guild.channels.cache.get(settings.confessionChannelId)?.name
: null;

const toggleMenu = new StringSelectMenuBuilder()
.setCustomId('confessionToggle')
.setPlaceholder(`${settings.confessionEnabled ? 'Enabled' : 'Disabled'}`)
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel('Enable')
.setDescription('Enable confessions for this server.')
.setValue('enable'),
new StringSelectMenuOptionBuilder()
.setLabel('Disable')
.setDescription('Disable confessions for this server.')
.setValue('disable')
);

const channelMenu = new ChannelSelectMenuBuilder()
.setCustomId('confessionChannel')
.setPlaceholder(currentChannelName ? `#${currentChannelName}` : 'Select a channel for confessions')
.setChannelTypes(ChannelType.GuildText);

return {
content: status
? `${emojis.rightArrow1} **Confessions** module:
${emojis.rightArrow2} ${status}`
: `${emojis.rightArrow1} **Confessions** module:`,
components: [
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(toggleMenu),
new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(channelMenu)
]
};
}

async function normalizeTicketSettings(guildId: string, guild: Guild, settings: ServerSettings) {
if (!settings.ticketCategoryId) return settings;

Expand Down Expand Up @@ -200,6 +236,11 @@ export class SettingsCommand extends Command {
.setLabel('Logging')
.setDescription('Configure server channel logging.')
.setValue('logging')
,
new StringSelectMenuOptionBuilder()
.setLabel('Confessions')
.setDescription('Configure where confessions are posted and whether they are enabled.')
.setValue('confessions')
);

const response = await interaction.reply({
Expand Down Expand Up @@ -248,6 +289,8 @@ export class SettingsCommand extends Command {
await settingChoice.update(buildTicketPanel(settings, guild));
} else if (settingChoice.values[0] === 'logging') {
await settingChoice.update(buildLoggingPanel(settings, guild));
} else if (settingChoice.values[0] === 'confessions') {
await settingChoice.update(buildConfessionPanel(settings, guild));
} else {
return;
}
Expand Down Expand Up @@ -300,6 +343,16 @@ export class SettingsCommand extends Command {
const next = await updateSettings(guildId, guild.name, { loggingChannelId: channelId });

await i.update(buildLoggingPanel(next, guild, `Logging channel set to <#${channelId}>.`));
} else if (i.customId === 'confessionToggle' && i.isStringSelectMenu()) {
const enable = i.values[0] === 'enable';
const next = await updateSettings(guildId, guild.name, { confessionEnabled: enable });

await i.update(buildConfessionPanel(next, guild, `Confessions **${enable ? 'enabled' : 'disabled'}**.`));
} else if (i.customId === 'confessionChannel' && i.isChannelSelectMenu()) {
const channelId = i.values[0];
const next = await updateSettings(guildId, guild.name, { confessionChannelId: channelId });

await i.update(buildConfessionPanel(next, guild, `Confession channel set to <#${channelId}>.`));
}
});

Expand Down
74 changes: 0 additions & 74 deletions src/commands/utility/setupConfessions.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/lib/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ServerSettings = {
ticketCategoryId: string | null;
staffRole: string | null;
confessionChannelId: string | null;
confessionEnabled: boolean;
loggingEnabled?: boolean;
loggingChannelId?: string | null;
};
Expand All @@ -17,6 +18,7 @@ export const DefaultSettings: ServerSettings = {
ticketCategoryId: null,
staffRole: null,
confessionChannelId: null,
confessionEnabled: false,
loggingEnabled: false,
loggingChannelId: null,
};
Expand Down
2 changes: 1 addition & 1 deletion src/listeners/ready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class ReadyListener extends Listener<typeof Events.ClientReady> {
console.log(`Ready! Logged in as ${client.user.tag}`);

client.user.setActivity({
name: `🤐 /setup-confessions | Shard ${client.shard?.ids?.[0] ?? 0}`,
name: `🤐 Confessions! | Shard ${client.shard?.ids?.[0] ?? 0}`,
type: ActivityType.Custom
});

Expand Down