import { SlashCommandBuilder, Interaction, CommandInteraction, ChatInputCommandInteraction, MessageFlags, GuildMemberRoleManager, InteractionReplyOptions, GuildMember, EmbedBuilder, AutocompleteInteraction, formatEmoji, roleMention, time, userMention } from "discord.js"; import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command"; import {GroupModel} from "../../Models/GroupModel"; import {GroupRepository} from "../../Repositories/GroupRepository"; import {DatabaseConnection} from "../../Database/DatabaseConnection"; import {Container} from "../../Container/Container"; import {GroupSelection} from "../CommandPartials/GroupSelection"; import {UserError} from "../UserError"; import {ArrayUtils} from "../../Utilities/ArrayUtils"; import {GroupConfigurationRenderer} from "../../Groups/GroupConfigurationRenderer"; import {GroupConfigurationHandler} from "../../Groups/GroupConfigurationHandler"; import {GroupConfigurationTransformers} from "../../Groups/GroupConfigurationTransformers"; import {GroupConfigurationRepository} from "../../Repositories/GroupConfigurationRepository"; import {IconCache} from "../../Icons/IconCache"; import {PlaydateRepository} from "../../Repositories/PlaydateRepository"; import playdate from "../../Database/tables/Playdate"; import Commands from "./Commands"; export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand { private static GOODBYE_MESSAGES: string[] = [ 'Sad to see you go.', 'May your next adventure be fruitful.', 'I hope, I served you well.', 'I wish you, good luck on your next adventures.', ] definition(): SlashCommandBuilder { // @ts-ignore return new SlashCommandBuilder() .setName('groups') .setDescription(`Manages groups`) .addSubcommand(create => create.setName("create") .setDescription("Creates a new group, with executing user being the leader") .addStringOption((option) => option.setName("name") .setDescription("Defines the name for the group.") .setRequired(true) ) .addRoleOption((builder) => builder.setName("role") .setDescription("Defines the role, where all the members are located in.") .setRequired(true) ) ) .addSubcommand(listCommand => listCommand .setName("list") .setDescription("Displays the groups you are apart of.") ) .addSubcommand(command => command .setName('config') .setDescription("Starts the config manager for the group.") .addIntegerOption(GroupSelection.createOptionSetup()) ) .addSubcommand(command => command .setName("remove") .setDescription("Deletes a group you are the leader for.") .addIntegerOption(GroupSelection.createOptionSetup()) ) .addSubcommand(command => command .setName("transfer") .setDescription("Transfers leadership of a group to a different person") .addIntegerOption(GroupSelection.createOptionSetup) .addUserOption(option => option .setName("target") .setDescription("The member, that is the new leader") .setRequired(true) ) ); } async execute(interaction: ChatInputCommandInteraction): Promise { switch (interaction.options.getSubcommand()) { case "create": this.create(interaction); break; case "list": this.list(interaction); break; case "remove": await this.remove(interaction); break; case "config": await this.runConfigurator(interaction); break; case "transfer": await this.transferLeadership(interaction); break; default: throw new Error("Unsupported command"); } return Promise.resolve(); } private create(interaction: ChatInputCommandInteraction): void { const name = interaction.options.getString("name") ?? ''; const role = interaction.options.getRole("role", true); if (role.id === interaction.guildId) { throw new UserError("Creating a group for everyone is not permitted!"); } if (!interaction.member?.roles.cache.has(role?.id) ?? true) { throw new UserError( "You are not part of the role, you try to create a group for.", "Add yourself to the group or ask your admin to do so." ); } const group: GroupModel = { id: -1, name: name, leader: { server: interaction.guildId ?? '', memberid: interaction.member?.user.id ?? '' }, role: { server: interaction.guildId ?? '', roleid: role?.id ?? '' } } Container.get(GroupRepository.name).create(group); interaction.reply({content: `:white_check_mark: Created group \`${name}\``, flags: MessageFlags.Ephemeral }) } private list(interaction: ChatInputCommandInteraction) { const repo = Container.get(GroupRepository.name); const groups = repo.findGroupsByMember(interaction.member); const playdateRepo = Container.get(PlaydateRepository.name); const iconCache = Container.get(IconCache.name); const embed = new EmbedBuilder() .setTitle("Your groups on this server:") .setFields( groups.map(group => { const nextPlaydate = playdateRepo.getNextPlaydateForGroup(group); const values = [ `Role: ${iconCache.getEmoji("people_group_solid")} ${roleMention(group.role.roleid)}`, `Leader/GM: ${userMention(group.leader.memberid)}` ]; if (nextPlaydate) { values.push( `Next Playdate: ${iconCache.getEmoji("calendar_days_solid")} ${time(nextPlaydate.from_time, "F")}` ) } return { name: group.name, value: values.join("\n") } }) ) const reply: InteractionReplyOptions = { embeds: [ embed ], allowedMentions: { roles: [] }, flags: MessageFlags.Ephemeral } interaction.reply(reply); } private async remove(interaction: ChatInputCommandInteraction) { const group = GroupSelection.getGroup(interaction); const repo = Container.get(GroupRepository.name); if (group.leader.memberid != interaction.member?.user.id) { throw new UserError("Can't remove group. You are not the leader."); } repo.deleteGroup(group); const embed = new EmbedBuilder() .setTitle("Group deleted.") .setDescription( `:x: Deleted \`${group.name}\`. ${ArrayUtils.chooseRandom(GroupCommand.GOODBYE_MESSAGES)}` ) 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, true); return; } } private async runConfigurator(interaction: ChatInputCommandInteraction) { const group = GroupSelection.getGroup(interaction); const configurationRenderer = new GroupConfigurationRenderer( new GroupConfigurationHandler( Container.get(GroupConfigurationRepository.name), group ), new GroupConfigurationTransformers(), ) await configurationRenderer.setup(interaction); } private async transferLeadership(interaction: ChatInputCommandInteraction) { const group = GroupSelection.getGroup(interaction); const repo = Container.get(GroupRepository.name); if (group.leader.memberid != interaction.member?.user.id) { throw new UserError( "Can't transfer leadership. You are not the leader." ); } const newLeader = interaction.options.getMember("target", true); if (!newLeader.roles.cache.has(group.role.roleid)) { throw new UserError( "Can't transfer leadership: The target member is not part of your group.", "Add the user to the role this group is part in or ask your server admin to do it." ); } group.leader.memberid = newLeader.id repo.update(group); const embed = new EmbedBuilder() .setTitle("Leadership transfered") .setDescription( `Leadership was successfully transfered to ${userMention(newLeader.user.id)}` ) await interaction.reply({ embeds: [ embed ], flags: MessageFlags.Ephemeral, }) } }