Add giveaway, ticket, self-role, starboard, and suggestion features to the bot
- Introduced new models in the Prisma schema for giveaways, tickets, self-role panels, starboard configurations, and suggestions, enhancing the bot's functionality. - Implemented job handling for self-role expiration and ticket management, improving user experience. - Updated command localization to support new features in both German and English. - Enhanced the job processing system to include dedicated workers for managing tickets and self-role expirations. - Documented the new features and their setup processes in PHASE-TRACKING.md for clarity on implementation steps.
This commit is contained in:
201
apps/bot/src/modules/tempvoice/commands.ts
Normal file
201
apps/bot/src/modules/tempvoice/commands.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import { PermissionFlagsBits } from 'discord.js';
|
||||
import { t, tf } from '@nexumi/shared';
|
||||
import type { SlashCommand } from '../../types.js';
|
||||
import { getGuildLocale } from '../../i18n.js';
|
||||
import { requirePermission } from '../../permissions.js';
|
||||
import { voiceCommandData } from './command-definitions.js';
|
||||
import {
|
||||
buildControlPanelEmbed,
|
||||
buildControlPanelRows,
|
||||
claimTempChannel,
|
||||
fetchCategory,
|
||||
getTempVoiceChannelForMember,
|
||||
getTempVoiceConfig,
|
||||
hideTempChannel,
|
||||
kickFromTempChannel,
|
||||
lockTempChannel,
|
||||
renameTempChannel,
|
||||
setTempChannelLimit,
|
||||
transferTempChannelOwnership,
|
||||
updateTempVoiceConfig
|
||||
} from './service.js';
|
||||
|
||||
async function requireOwnedTempChannel(
|
||||
interaction: Parameters<SlashCommand['execute']>[0],
|
||||
context: Parameters<SlashCommand['execute']>[1],
|
||||
locale: 'de' | 'en'
|
||||
) {
|
||||
if (!interaction.inGuild()) {
|
||||
await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
|
||||
return null;
|
||||
}
|
||||
|
||||
const guildMember =
|
||||
interaction.guild!.members.cache.get(interaction.user.id) ??
|
||||
(await interaction.guild!.members.fetch(interaction.user.id));
|
||||
const owned = await getTempVoiceChannelForMember(context.prisma, interaction.guildId!, guildMember);
|
||||
if (!owned || owned.record.ownerId !== interaction.user.id) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.error.notOwner'), ephemeral: true });
|
||||
return null;
|
||||
}
|
||||
return owned;
|
||||
}
|
||||
|
||||
const voiceCommand: SlashCommand = {
|
||||
data: voiceCommandData,
|
||||
async execute(interaction, context) {
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!interaction.inGuild()) {
|
||||
await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const guildId = interaction.guildId!;
|
||||
const sub = interaction.options.getSubcommand();
|
||||
|
||||
if (sub === 'setup') {
|
||||
if (!(await requirePermission(interaction, PermissionFlagsBits.ManageGuild, locale))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hub = interaction.options.getChannel('hub_channel', true);
|
||||
const category = interaction.options.getChannel('category', true);
|
||||
const defaultName = interaction.options.getString('default_name');
|
||||
const limit = interaction.options.getInteger('limit');
|
||||
|
||||
const categoryChannel = await fetchCategory(interaction.guild!, category.id);
|
||||
if (!categoryChannel) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.error.invalidCategory'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
await updateTempVoiceConfig(context.prisma, guildId, {
|
||||
enabled: true,
|
||||
hubChannelId: hub.id,
|
||||
categoryId: category.id,
|
||||
defaultName: defaultName ?? undefined,
|
||||
defaultLimit: limit ?? undefined
|
||||
});
|
||||
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.setup.done'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const config = await getTempVoiceConfig(context.prisma, guildId);
|
||||
if (!config.enabled) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.error.disabled'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'panel') {
|
||||
const owned = await requireOwnedTempChannel(interaction, context, locale);
|
||||
if (!owned) {
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [buildControlPanelEmbed(owned.channel.name, locale)],
|
||||
components: buildControlPanelRows(owned.channel.id, locale),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'claim') {
|
||||
const member = await interaction.guild!.members.fetch(interaction.user.id);
|
||||
const inChannel = await getTempVoiceChannelForMember(context.prisma, guildId, member);
|
||||
if (!inChannel) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.error.notInChannel'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await claimTempChannel(context, inChannel.record, inChannel.channel, member, locale);
|
||||
if (result === 'owner_present') {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.claim.ownerPresent'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
if (result === 'not_in_channel') {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.error.notInChannel'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.claim.success'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const owned = await requireOwnedTempChannel(interaction, context, locale);
|
||||
if (!owned) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'rename') {
|
||||
const name = interaction.options.getString('name', true);
|
||||
await renameTempChannel(owned.channel, name);
|
||||
await interaction.reply({
|
||||
content: tf(locale, 'tempvoice.rename.success', { name: name.slice(0, 100) }),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'limit') {
|
||||
const limit = interaction.options.getInteger('limit', true);
|
||||
await setTempChannelLimit(owned.channel, limit);
|
||||
await interaction.reply({
|
||||
content: tf(locale, 'tempvoice.limit.success', { limit }),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'lock') {
|
||||
await lockTempChannel(owned.channel);
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.lock.success'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'hide') {
|
||||
await hideTempChannel(owned.channel);
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.hide.success'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'kick') {
|
||||
const user = interaction.options.getUser('user', true);
|
||||
if (user.id === interaction.user.id) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.kick.self'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const ok = await kickFromTempChannel(owned.channel, user.id);
|
||||
if (!ok) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.kick.notInChannel'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await interaction.reply({
|
||||
content: tf(locale, 'tempvoice.kick.success', { user: `<@${user.id}>` }),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub === 'transfer') {
|
||||
const user = interaction.options.getUser('user', true);
|
||||
if (user.id === interaction.user.id) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.transfer.self'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const target = await interaction.guild!.members.fetch(user.id).catch(() => null);
|
||||
if (!target || !owned.channel.members.has(target.id)) {
|
||||
await interaction.reply({ content: t(locale, 'tempvoice.transfer.notInChannel'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await transferTempChannelOwnership(context, owned.record, owned.channel, target, locale);
|
||||
await interaction.reply({
|
||||
content: tf(locale, 'tempvoice.transfer.success', { user: `<@${user.id}>` }),
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const tempVoiceCommands: SlashCommand[] = [voiceCommand];
|
||||
Reference in New Issue
Block a user