Adds transfer of leadership of a group

This commit is contained in:
Michel Fedde 2025-05-27 20:00:10 +02:00
parent fccc30626a
commit 13f2bf30fa
4 changed files with 67 additions and 4 deletions

View file

@ -14,7 +14,7 @@ export class GroupSelection {
public static createOptionSetup(): SlashCommandIntegerOption { public static createOptionSetup(): SlashCommandIntegerOption {
return new SlashCommandIntegerOption() return new SlashCommandIntegerOption()
.setName("group") .setName("group")
.setDescription("Defines the group you want to manage the playdates for") .setDescription("Defines the group this action is for")
.setRequired(true) .setRequired(true)
.setAutocomplete(true) .setAutocomplete(true)
} }

View file

@ -9,7 +9,7 @@ import {
GuildMember, GuildMember,
EmbedBuilder, EmbedBuilder,
AutocompleteInteraction, AutocompleteInteraction,
formatEmoji, roleMention, time formatEmoji, roleMention, time, userMention
} from "discord.js"; } from "discord.js";
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command"; import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
import {GroupModel} from "../../Models/GroupModel"; import {GroupModel} from "../../Models/GroupModel";
@ -26,6 +26,7 @@ import {GroupConfigurationRepository} from "../../Repositories/GroupConfiguratio
import {IconCache} from "../../Icons/IconCache"; import {IconCache} from "../../Icons/IconCache";
import {PlaydateRepository} from "../../Repositories/PlaydateRepository"; import {PlaydateRepository} from "../../Repositories/PlaydateRepository";
import playdate from "../../Database/tables/Playdate"; import playdate from "../../Database/tables/Playdate";
import Commands from "./Commands";
export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand { export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand {
private static GOODBYE_MESSAGES: string[] = [ private static GOODBYE_MESSAGES: string[] = [
@ -68,6 +69,16 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
.setName("remove") .setName("remove")
.setDescription("Deletes a group you are the leader for.") .setDescription("Deletes a group you are the leader for.")
.addIntegerOption(GroupSelection.createOptionSetup()) .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)
)
); );
} }
@ -85,6 +96,9 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
case "config": case "config":
await this.runConfigurator(interaction); await this.runConfigurator(interaction);
break; break;
case "transfer":
await this.transferLeadership(interaction);
break;
default: default:
throw new Error("Unsupported command"); throw new Error("Unsupported command");
} }
@ -201,4 +215,40 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
await configurationRenderer.setup(interaction); 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,
})
}
} }

View file

@ -6,7 +6,7 @@ import {
ChatInputCommandInteraction, ChatInputCommandInteraction,
MessageFlags, MessageFlags,
Activity, Activity,
ActivityType, REST ActivityType, REST, inlineCode
} from "discord.js"; } from "discord.js";
import Commands from "./Commands/Commands"; import Commands from "./Commands/Commands";
import {Container} from "../Container/Container"; import {Container} from "../Container/Container";
@ -96,6 +96,12 @@ export class DiscordClient {
let userMessage = ":x: There was an error while executing this command!"; let userMessage = ":x: There was an error while executing this command!";
if (e.constructor.name === UserError.name) { if (e.constructor.name === UserError.name) {
userMessage = `:x: \`${e.message}\` - Please validate your request!` userMessage = `:x: \`${e.message}\` - Please validate your request!`
if (e.tryInstead) {
userMessage += `
You can try the following:
${inlineCode(e.tryInstead)}`
}
} }
if (interaction.replied || interaction.deferred) { if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: userMessage, flags: MessageFlags.Ephemeral }); await interaction.followUp({ content: userMessage, flags: MessageFlags.Ephemeral });

View file

@ -1,3 +1,10 @@
export class UserError extends Error { export class UserError extends Error {
constructor(
message: string,
public readonly tryInstead: string|null = null
) {
super(message);
}
} }