feat(permissions): Adds server permissions

This commit is contained in:
Michel Fedde 2025-06-24 20:58:46 +02:00
parent d46bbd84c5
commit cf9c88a2d6
24 changed files with 415 additions and 69 deletions

View file

@ -4,11 +4,13 @@ import {GroupCommand} from "./Groups";
import {PlaydatesCommand} from "./Playdates";
import {RESTPostAPIChatInputApplicationCommandsJSONBody} from "discord.js";
import {Nullable} from "../../types/Nullable";
import {ServerCommand} from "./Server";
const commands: Set<Command> = new Set<Command>([
new HelloWorldCommand(),
new GroupCommand(),
new PlaydatesCommand()
new PlaydatesCommand(),
new ServerCommand()
]);
export default class Commands {

View file

@ -5,9 +5,9 @@ import {
GuildMember,
GuildMemberRoleManager,
InteractionReplyOptions,
MessageFlags,
MessageFlags, PermissionFlagsBits,
roleMention,
SlashCommandBuilder,
SlashCommandBuilder, Snowflake,
time,
userMention
} from "discord.js";
@ -28,6 +28,9 @@ import {MenuTraversal} from "../../Menu/MenuTraversal";
import {ConfigurationHandler} from "../../Configuration/ConfigurationHandler";
import {GroupConfigurationProvider} from "../../Configuration/Groups/GroupConfigurationProvider";
import {MenuHandler} from "../../Configuration/MenuHandler";
import {ServerConfigurationProvider} from "../../Configuration/Server/ServerConfigurationProvider";
import {ServerConfigurationRepository} from "../../Database/Repositories/ServerConfigurationRepository";
import {PermissionError} from "../PermissionError";
export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand {
private static GOODBYE_MESSAGES: string[] = [
@ -114,6 +117,10 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
}
private create(interaction: ChatInputCommandInteraction): void {
if (!this.allowedCreate(interaction)) {
throw new PermissionError("You don't have the permissions for it!")
}
const name = interaction.options.getString("name") ?? '';
const role = interaction.options.getRole("role", true);
@ -151,6 +158,22 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
interaction.reply({content: `:white_check_mark: Created group \`${name}\``, flags: MessageFlags.Ephemeral})
}
private allowedCreate(interaction: ChatInputCommandInteraction): boolean {
if ((<GuildMember>interaction.member)?.permissions.has(PermissionFlagsBits.Administrator)) {
return true;
}
const config = new ConfigurationHandler(
new ServerConfigurationProvider(
Container.get<ServerConfigurationRepository>(ServerConfigurationRepository.name),
<Snowflake>interaction.guildId
)
);
const configValue = config.getConfigurationByPath("permissions.groupCreation.allowEveryone").value;
return configValue === true;
}
private validateGroupName(name: string): string | null {
const lowercaseName = name.toLowerCase();
for (const invalidcharactersequence of GroupCommand.INVALID_CHARACTER_SEQUENCES) {
@ -209,7 +232,7 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
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.");
throw new PermissionError("You are not the leader.");
}
repo.deleteGroup(group);
@ -320,8 +343,8 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
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."
throw new PermissionError(
"You are not the leader."
);
}

View file

@ -22,6 +22,7 @@ import {GroupConfigurationRepository} from "../../Database/Repositories/GroupCon
import {GroupRepository} from "../../Database/Repositories/GroupRepository";
import {GroupConfigurationProvider} from "../../Configuration/Groups/GroupConfigurationProvider";
import { ConfigurationHandler } from "../../Configuration/ConfigurationHandler";
import {PermissionError} from "../PermissionError";
export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand {
definition(): SlashCommandBuilder {
@ -216,7 +217,7 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
private async delete(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
if (!this.interactionIsAllowedToManage(<ChatInputCommandInteraction>interaction, group)) {
throw new UserError(
throw new PermissionError(
"You are not allowed to delete playdates for this group.",
"Ask your Game Master to delete the playdate or ask him to allow everyone to do so."
)
@ -401,6 +402,6 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
group
)
);
return config.getConfigurationByPath("permissions.allowMemberManagingPlaydates") === true;
return config.getConfigurationByPath("permissions.allowMemberManagingPlaydates").value === true;
}
}

View file

@ -0,0 +1,79 @@
import {CacheType, ChatInputCommandInteraction, PermissionFlagsBits, SlashCommandBuilder, Snowflake} from "discord.js";
import {ChatInteractionCommand, Command} from "./Command";
import {GroupSelection} from "../CommandPartials/GroupSelection";
import {MenuHandler} from "../../Configuration/MenuHandler";
import {ConfigurationHandler} from "../../Configuration/ConfigurationHandler";
import {GroupConfigurationProvider} from "../../Configuration/Groups/GroupConfigurationProvider";
import {Container} from "../../Container/Container";
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
import {MenuRenderer} from "../../Menu/MenuRenderer";
import {MenuTraversal} from "../../Menu/MenuTraversal";
import {MenuItemType} from "../../Menu/MenuRenderer.types";
import {ServerConfigurationProvider} from "../../Configuration/Server/ServerConfigurationProvider";
import {ServerConfigurationRepository} from "../../Database/Repositories/ServerConfigurationRepository";
export class ServerCommand implements Command, ChatInteractionCommand {
definition(): SlashCommandBuilder {
return new SlashCommandBuilder()
.setName("server")
.setDescription("Allows server administrators to adjust things about the PnP Scheduler bot.")
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addSubcommand(command => command
.setName("config")
.setDescription("Starts the configurator for the server settings")
)
}
async execute(interaction: ChatInputCommandInteraction<CacheType>): Promise<void> {
switch (interaction.options.getSubcommand()) {
case "config":
await this.startConfiguration(interaction);
break
}
}
private async startConfiguration(interaction: ChatInputCommandInteraction) {
const menuHandler = new MenuHandler(
new ConfigurationHandler(
new ServerConfigurationProvider(
Container.get<ServerConfigurationRepository>(ServerConfigurationRepository.name),
<Snowflake>interaction.guildId
)
)
)
const menu = new MenuRenderer(
new MenuTraversal(
menuHandler.fillMenuItems(
[
{
traversalKey: "permissions",
label: "Permissions",
description: "Allows customization, how the server members are allowed to interact with the PnP Scheduler.",
type: MenuItemType.Collection,
children: [
{
traversalKey: "groupCreation",
label: "Group Creation",
description: "Sets the permissions, who is allowed to create groups.",
type: MenuItemType.Collection,
children: [
{
traversalKey: "allowEveryone",
label: "Group Creation",
description: "Defines if all members are allowed to create groups.",
}
]
},
]
},
]
),
'Server Configuration',
"This UI allows you to change settings for your server."
)
)
menu.display(interaction);
}
}