290 lines
No EOL
11 KiB
TypeScript
290 lines
No EOL
11 KiB
TypeScript
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";
|
|
import Groups from "../../Database/tables/Groups";
|
|
|
|
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 and your group good luck on your next adventures.',
|
|
]
|
|
|
|
private static INVALID_CHARACTER_SEQUENCES: string[] = [
|
|
"http://",
|
|
"https://"
|
|
]
|
|
|
|
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)
|
|
.setMaxLength(64)
|
|
)
|
|
.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<void> {
|
|
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 validName = this.validateGroupName(name);
|
|
if (name !== true) {
|
|
throw new UserError(`Your group name contains one or more invalid character sequences: ${validName}`)
|
|
}
|
|
|
|
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>(GroupRepository.name).create(group);
|
|
|
|
interaction.reply({content: `:white_check_mark: Created group \`${name}\``, flags: MessageFlags.Ephemeral })
|
|
}
|
|
|
|
private validateGroupName(name: string): true|string{
|
|
const lowercaseName = name.toLowerCase();
|
|
for (let invalidcharactersequence of GroupCommand.INVALID_CHARACTER_SEQUENCES) {
|
|
if (!lowercaseName.includes(invalidcharactersequence)) {
|
|
continue
|
|
}
|
|
|
|
return invalidcharactersequence
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private list(interaction: ChatInputCommandInteraction) {
|
|
const repo = Container.get<GroupRepository>(GroupRepository.name);
|
|
const groups = repo.findGroupsByMember(<GuildMember>interaction.member);
|
|
|
|
const playdateRepo = Container.get<PlaydateRepository>(PlaydateRepository.name);
|
|
|
|
const iconCache = Container.get<IconCache>(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>(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<void> {
|
|
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>(GroupConfigurationRepository.name),
|
|
group
|
|
),
|
|
new GroupConfigurationTransformers(),
|
|
)
|
|
|
|
await configurationRenderer.setup(interaction);
|
|
}
|
|
|
|
private async transferLeadership(interaction: ChatInputCommandInteraction) {
|
|
const group = GroupSelection.getGroup(interaction);
|
|
|
|
const repo = Container.get<GroupRepository>(GroupRepository.name);
|
|
if (group.leader.memberid != interaction.member?.user.id) {
|
|
throw new UserError(
|
|
"Can't transfer leadership. You are not the leader."
|
|
);
|
|
}
|
|
|
|
const newLeader = <GuildMember>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,
|
|
})
|
|
}
|
|
} |