import { SlashCommandBuilder, CommandInteraction, AutocompleteInteraction, EmbedBuilder, MessageFlags, ChatInputCommandInteraction, time } from "discord.js"; import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command"; import {Container} from "../../Container/Container"; import {GroupSelection} from "../CommandPartials/GroupSelection"; import {UserError} from "../UserError"; import {PlaydateModel} from "../../Models/PlaydateModel"; import {PlaydateRepository} from "../../Repositories/PlaydateRepository"; import {GroupModel} from "../../Models/GroupModel"; export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand { static REGEX = [ ] definition(): SlashCommandBuilder { // @ts-expect-error Command builder is improperly marked as incomplete. return new SlashCommandBuilder() .setName("playdates") .setDescription("Manage your playdates") .addSubcommand((subcommand) => subcommand .setName("create") .setDescription("Creates a new playdate") .addIntegerOption(GroupSelection.createOptionSetup()) .addStringOption((option) => option .setName("from") .setDescription("Defines the start date & time. Format: YYYY-MM-DD HH:mm") ) .addStringOption((option) => option .setName("to") .setDescription("Defines the end date & time. Format: YYYY-MM-DD HH:mm") ) ) .addSubcommand((subcommand) => subcommand .setName("list") .setDescription("Lists all playdates") .addIntegerOption(GroupSelection.createOptionSetup()) ) .addSubcommand((subcommand) => subcommand .setName("remove") .setDescription("Removes a playdate") .addIntegerOption(GroupSelection.createOptionSetup()) .addIntegerOption((option) => option .setName("playdate") .setDescription("Selects a playdate") .setRequired(true) .setAutocomplete(true) ) ); } async execute(interaction: ChatInputCommandInteraction): Promise { const group = GroupSelection.getGroup(interaction); switch (interaction.options.getSubcommand()) { case "create": await this.create(interaction, group); break; case "remove": await this.delete(interaction, group); break; case "list": await this.list(interaction, group); break; default: throw new UserError("This subcommand is not yet implemented."); } } async create(interaction: CommandInteraction, group: GroupModel): Promise { const fromDate = Date.parse(interaction.options.get("from")?.value ?? ''); const toDate = Date.parse(interaction.options.get("to")?.value ?? ''); if (isNaN(fromDate)) { throw new UserError("No date or invalid date format for the from parameter."); } if (isNaN(toDate)) { throw new UserError("No date or invalid date format for the to parameter."); } if (fromDate > toDate) { throw new UserError("The to-date can't be earlier than the from-date"); } const playdateRepo = Container.get(PlaydateRepository.name); const collidingTimes = playdateRepo.findPlaydatesInRange(fromDate, toDate, group); if (collidingTimes.length > 0) { throw new UserError("The playdate collides with another playdate. Please either remove the old one or choose a different time.") } const playdate: Partial = { group: group, from_time: new Date(fromDate), to_time: new Date(toDate), } playdateRepo.create(playdate); const embed = new EmbedBuilder() .setTitle("Created a play-date.") .setDescription(":white_check_mark: Your playdate has been created! You and your group get notified, when its time.") .setFields({ name: "Created playdate", value: `${time(new Date(fromDate),'F')} - ${time(new Date(toDate), 'F')}`, }) .setFooter({ text: `Group: ${group.name}` }) await interaction.reply({ embeds: [ embed ], flags: MessageFlags.Ephemeral, }) } async handleAutocomplete(interaction: AutocompleteInteraction): Promise { const option = interaction.options.getFocused(true); if (option.name == "group") { await GroupSelection.handleAutocomplete(interaction); return; } if (option.name != 'playdate') { return; } const group = GroupSelection.getGroup(interaction); const playdates = Container.get(PlaydateRepository.name).findFromGroup(group); await interaction.respond( playdates.map(playdate => { return { name: `${playdate.from_time.toLocaleString()} - ${playdate.to_time.toLocaleString()}`, value: playdate.id } }) ) } private async list(interaction: ChatInputCommandInteraction, group: GroupModel) { const playdates = Container.get(PlaydateRepository.name).findFromGroup(group); const embed = new EmbedBuilder() .setTitle("The next playdates:") .setFields( playdates.map((playdate) => { return { name: `${time(playdate.from_time, 'F')} - ${time(playdate.to_time, 'F')}`, value: `${time(playdate.from_time, 'R')}` } }) ) .setFooter({ text: `Group: ${group.name}` }) await interaction.reply({ embeds: [ embed ], flags: MessageFlags.Ephemeral, }) } private async delete(interaction: ChatInputCommandInteraction, group: GroupModel): Promise { const playdateId = interaction.options.getInteger("playdate", true) const repo = Container.get(PlaydateRepository.name); const selected = repo.getById(playdateId); if (!selected) { throw new UserError("No playdate found"); } if (selected.group?.id != group.id) { throw new UserError("No playdate found"); } repo.delete(playdateId); const embed = new EmbedBuilder() .setTitle("Playdate deleted") .setDescription( `:x: Deleted \`${selected.from_time.toLocaleString()} - ${selected.to_time.toLocaleString()}\`` ) .setFooter({ text: `Group: ${group.name}` }) await interaction.reply({ embeds: [ embed ], flags: MessageFlags.Ephemeral, }) } }