feat(configuration): Adds missing implermentations
This commit is contained in:
parent
b82ab7dbc4
commit
ec0aa5654c
7 changed files with 150 additions and 32 deletions
|
|
@ -2,7 +2,15 @@ import {
|
||||||
SlashCommandBuilder,
|
SlashCommandBuilder,
|
||||||
CommandInteraction,
|
CommandInteraction,
|
||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
EmbedBuilder, MessageFlags, ChatInputCommandInteraction, time, AttachmentBuilder, ActivityFlagsBitField, Options
|
EmbedBuilder,
|
||||||
|
MessageFlags,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
time,
|
||||||
|
AttachmentBuilder,
|
||||||
|
ActivityFlagsBitField,
|
||||||
|
Options,
|
||||||
|
User,
|
||||||
|
GuildMember
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
|
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
|
||||||
import {Container} from "../../Container/Container";
|
import {Container} from "../../Container/Container";
|
||||||
|
|
@ -13,6 +21,10 @@ import {PlaydateRepository} from "../../Repositories/PlaydateRepository";
|
||||||
import {GroupModel} from "../../Models/GroupModel";
|
import {GroupModel} from "../../Models/GroupModel";
|
||||||
import * as ics from 'ics';
|
import * as ics from 'ics';
|
||||||
import ical from 'node-ical';
|
import ical from 'node-ical';
|
||||||
|
import {GroupConfigurationHandler} from "../../Groups/GroupConfigurationHandler";
|
||||||
|
import {GroupConfigurationRepository} from "../../Repositories/GroupConfigurationRepository";
|
||||||
|
import {privateDecrypt} from "node:crypto";
|
||||||
|
import {GroupRepository} from "../../Repositories/GroupRepository";
|
||||||
|
|
||||||
export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand {
|
export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand {
|
||||||
definition(): SlashCommandBuilder {
|
definition(): SlashCommandBuilder {
|
||||||
|
|
@ -100,6 +112,13 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(interaction: CommandInteraction, group: GroupModel): Promise<void> {
|
async create(interaction: CommandInteraction, group: GroupModel): Promise<void> {
|
||||||
|
if (!this.interactionIsAllowedToManage(<ChatInputCommandInteraction>interaction, group)) {
|
||||||
|
throw new UserError(
|
||||||
|
"You are not allowed to create playdates for this group.",
|
||||||
|
"Ask your Game Master to add the playdate or ask him to allow everyone to do so."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const fromDate = Date.parse(<string>interaction.options.get("from")?.value ?? '');
|
const fromDate = Date.parse(<string>interaction.options.get("from")?.value ?? '');
|
||||||
const toDate = Date.parse(<string>interaction.options.get("to")?.value ?? '');
|
const toDate = Date.parse(<string>interaction.options.get("to")?.value ?? '');
|
||||||
|
|
||||||
|
|
@ -199,6 +218,13 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
}
|
}
|
||||||
|
|
||||||
private async delete(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
private async delete(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
||||||
|
if (!this.interactionIsAllowedToManage(<ChatInputCommandInteraction>interaction, group)) {
|
||||||
|
throw new UserError(
|
||||||
|
"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."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const playdateId = interaction.options.getInteger("playdate", true)
|
const playdateId = interaction.options.getInteger("playdate", true)
|
||||||
|
|
||||||
const repo = Container.get<PlaydateRepository>(PlaydateRepository.name);
|
const repo = Container.get<PlaydateRepository>(PlaydateRepository.name);
|
||||||
|
|
@ -231,6 +257,13 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
}
|
}
|
||||||
|
|
||||||
private async import(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
private async import(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
||||||
|
if (!this.interactionIsAllowedToManage(<ChatInputCommandInteraction>interaction, group)) {
|
||||||
|
throw new UserError(
|
||||||
|
"You are not allowed to create playdates for this group.",
|
||||||
|
"Ask your Game Master to add the playdate or ask him to allow everyone to do so."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const file = interaction.options.getAttachment('file', true);
|
const file = interaction.options.getAttachment('file', true);
|
||||||
const mimeType = file.contentType?.split(';')[0];
|
const mimeType = file.contentType?.split(';')[0];
|
||||||
if (mimeType !== "text/calendar") {
|
if (mimeType !== "text/calendar") {
|
||||||
|
|
@ -281,8 +314,25 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
}
|
}
|
||||||
|
|
||||||
private async export(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
private async export(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
||||||
|
const groupConfig = new GroupConfigurationHandler(
|
||||||
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
|
group
|
||||||
|
).getConfiguration();
|
||||||
|
|
||||||
const playdates = this.getExportTargets(interaction, group);
|
const playdates = this.getExportTargets(interaction, group);
|
||||||
|
|
||||||
|
if (playdates.length < 1) {
|
||||||
|
await interaction.reply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle("Export failed")
|
||||||
|
.setDescription(`:x: No playdates found under those filters.`)
|
||||||
|
],
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const result = ics.createEvents(
|
const result = ics.createEvents(
|
||||||
playdates.map((playdate) => {
|
playdates.map((playdate) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -292,7 +342,9 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
end: ics.convertTimestampToArray(playdate.to_time.getTime(), ''),
|
end: ics.convertTimestampToArray(playdate.to_time.getTime(), ''),
|
||||||
endInputType: 'utc',
|
endInputType: 'utc',
|
||||||
endOutputType: 'utc',
|
endOutputType: 'utc',
|
||||||
title: `PnP with ${group.name}`,
|
title: groupConfig.calendar.title ?? `PnP with ${group.name}`,
|
||||||
|
description: groupConfig.calendar.description ?? undefined,
|
||||||
|
location: groupConfig.calendar.location ?? undefined,
|
||||||
status: "CONFIRMED",
|
status: "CONFIRMED",
|
||||||
busyStatus: "FREE",
|
busyStatus: "FREE",
|
||||||
categories: ['PnP']
|
categories: ['PnP']
|
||||||
|
|
@ -332,4 +384,22 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
|
|
||||||
return [playdate];
|
return [playdate];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interactionIsAllowedToManage(interaction: ChatInputCommandInteraction, group: GroupModel): boolean {
|
||||||
|
const interactionMemberId = interaction.member?.user.id;
|
||||||
|
if (group.leader.memberid === interactionMemberId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupRepo = Container.get<GroupRepository>(GroupRepository.name);
|
||||||
|
if (!groupRepo.isMemberInGroup(<GuildMember>interaction.member, group)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = new GroupConfigurationHandler(
|
||||||
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
|
group
|
||||||
|
);
|
||||||
|
return config.getConfiguration().permissions.allowMemberManagingPlaydates;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import {Container} from "../Container/Container";
|
||||||
import {EventHandler} from "../Events/EventHandler";
|
import {EventHandler} from "../Events/EventHandler";
|
||||||
import {ModalInteractionEvent} from "../Events/EventClasses/ModalInteractionEvent";
|
import {ModalInteractionEvent} from "../Events/EventClasses/ModalInteractionEvent";
|
||||||
import {ComponentInteractionEvent} from "../Events/EventClasses/ComponentInteractionEvent";
|
import {ComponentInteractionEvent} from "../Events/EventClasses/ComponentInteractionEvent";
|
||||||
|
import {log} from "node:util";
|
||||||
|
|
||||||
enum InteractionRoutingType {
|
enum InteractionRoutingType {
|
||||||
Unrouted,
|
Unrouted,
|
||||||
|
|
@ -91,9 +92,9 @@ export class InteractionRouter {
|
||||||
|
|
||||||
await command.execute?.call(command, interaction);
|
await command.execute?.call(command, interaction);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.logger.error(e)
|
|
||||||
|
|
||||||
let userMessage = ":x: There was an error while executing this command!";
|
let userMessage = ":x: There was an error while executing this command!";
|
||||||
|
let logErrorMessage = true;
|
||||||
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) {
|
if (e.tryInstead) {
|
||||||
|
|
@ -102,7 +103,14 @@ export class InteractionRouter {
|
||||||
You can try the following:
|
You can try the following:
|
||||||
${inlineCode(e.tryInstead)}`
|
${inlineCode(e.tryInstead)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logErrorMessage = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (logErrorMessage) {
|
||||||
|
this.logger.error(e)
|
||||||
|
}
|
||||||
|
|
||||||
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});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
AnyMenuItem, FieldMenuItem,
|
AnyMenuItem, FieldMenuItem,
|
||||||
FieldMenuItemContext, FieldMenuItemSaveValue,
|
FieldMenuItemContext, FieldMenuItemSaveValue, MenuItem,
|
||||||
MenuItemType,
|
MenuItemType, PromptMenuItem,
|
||||||
RowBuilderFieldMenuItemContext
|
RowBuilderFieldMenuItemContext
|
||||||
} from "../Menu/MenuRenderer.types";
|
} from "../Menu/MenuRenderer.types";
|
||||||
import {GroupConfigurationTransformers} from "./GroupConfigurationTransformers";
|
import {GroupConfigurationTransformers} from "./GroupConfigurationTransformers";
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import {ChannelId} from "../types/DiscordTypes";
|
import {ChannelId} from "../types/DiscordTypes";
|
||||||
import {MessageActionRowComponentBuilder} from "@discordjs/builders";
|
import {MessageActionRowComponentBuilder} from "@discordjs/builders";
|
||||||
|
import {Prompt} from "../Menu/Modals/Prompt";
|
||||||
|
|
||||||
export class ConfigurationMenuHandler {
|
export class ConfigurationMenuHandler {
|
||||||
|
|
||||||
|
|
@ -56,15 +57,6 @@ export class ConfigurationMenuHandler {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
traversalKey: "locale",
|
|
||||||
label: "Locale",
|
|
||||||
description: "Provides locale to be used for this group.",
|
|
||||||
type: MenuItemType.Field,
|
|
||||||
getCurrentValue: this.getLocaleValue.bind(this),
|
|
||||||
getActionRowBuilder: this.getLocaleMenuBuilder.bind(this),
|
|
||||||
setValue: this.setValue.bind(this)
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
traversalKey: "permissions",
|
traversalKey: "permissions",
|
||||||
label: "Permissions",
|
label: "Permissions",
|
||||||
|
|
@ -88,20 +80,45 @@ export class ConfigurationMenuHandler {
|
||||||
description: "Provides settings for the metadata contained in the playdate exports.",
|
description: "Provides settings for the metadata contained in the playdate exports.",
|
||||||
type: MenuItemType.Collection,
|
type: MenuItemType.Collection,
|
||||||
children: [
|
children: [
|
||||||
{
|
this.createStringMenuItem({
|
||||||
traversalKey: "title",
|
traversalKey: "title",
|
||||||
label: "Title",
|
label: "Title",
|
||||||
description: "Defines how the calendar entry should be called.",
|
description: "Defines how the calendar entry should be called.",
|
||||||
type: MenuItemType.Prompt,
|
}),
|
||||||
getCurrentValue: this.getStringValue.bind(this),
|
this.createTextareaMenuItem({
|
||||||
getActionRowBuilder: this.getStringBuilder.bind(this),
|
traversalKey: "description",
|
||||||
setValue: this.setValue.bind(this)
|
label: "Description",
|
||||||
}
|
description: "Sets the description for the calendar entry.",
|
||||||
|
}),
|
||||||
|
this.createStringMenuItem({
|
||||||
|
traversalKey: "location",
|
||||||
|
label: "Location",
|
||||||
|
description: "Sets the location where the calendar should point to."
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createStringMenuItem(metadata: MenuItem): PromptMenuItem {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
type: MenuItemType.Prompt,
|
||||||
|
getCurrentValue: this.getStringValue.bind(this),
|
||||||
|
getActionRowBuilder: this.getStringBuilder.bind(this),
|
||||||
|
setValue: this.setValue.bind(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private createTextareaMenuItem(metadata: MenuItem): PromptMenuItem {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
type: MenuItemType.Prompt,
|
||||||
|
getCurrentValue: this.getStringValue.bind(this),
|
||||||
|
getActionRowBuilder: this.getTextareaBuilder.bind(this),
|
||||||
|
setValue: this.setValue.bind(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private getChannelValue(context: FieldMenuItemContext): string {
|
private getChannelValue(context: FieldMenuItemContext): string {
|
||||||
const value = this.configuration.getConfigurationByPath(context.path.join('.'));
|
const value = this.configuration.getConfigurationByPath(context.path.join('.'));
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
|
|
@ -193,6 +210,11 @@ export class ConfigurationMenuHandler {
|
||||||
return new TextInputBuilder()
|
return new TextInputBuilder()
|
||||||
.setStyle(TextInputStyle.Short)
|
.setStyle(TextInputStyle.Short)
|
||||||
}
|
}
|
||||||
|
private getTextareaBuilder(context: FieldMenuItemContext): TextInputBuilder {
|
||||||
|
return new TextInputBuilder()
|
||||||
|
.setStyle(TextInputStyle.Paragraph)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private setValue(value: FieldMenuItemSaveValue[]|string, context: FieldMenuItemContext): void {
|
private setValue(value: FieldMenuItemSaveValue[]|string, context: FieldMenuItemContext): void {
|
||||||
const savedValue = typeof value !== 'string' ?
|
const savedValue = typeof value !== 'string' ?
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,13 @@ import {isPlainObject} from "is-plain-object";
|
||||||
export class GroupConfigurationHandler {
|
export class GroupConfigurationHandler {
|
||||||
static DEFAULT_CONFIGURATION: RuntimeGroupConfiguration = {
|
static DEFAULT_CONFIGURATION: RuntimeGroupConfiguration = {
|
||||||
channels: null,
|
channels: null,
|
||||||
locale: new Intl.Locale('en-GB'),
|
|
||||||
permissions: {
|
permissions: {
|
||||||
allowMemberManagingPlaydates: false
|
allowMemberManagingPlaydates: false
|
||||||
},
|
},
|
||||||
calendar: {
|
calendar: {
|
||||||
title: null
|
title: null,
|
||||||
|
description: null,
|
||||||
|
location: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ type GroupConfigurationTransformer = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupConfigurationResult =
|
export type GroupConfigurationResult =
|
||||||
ChannelId | Intl.Locale | boolean | string
|
ChannelId | Intl.Locale | boolean | string | null
|
||||||
|
|
||||||
export class GroupConfigurationTransformers {
|
export class GroupConfigurationTransformers {
|
||||||
static TRANSFORMERS: GroupConfigurationTransformer[] = [
|
static TRANSFORMERS: GroupConfigurationTransformer[] = [
|
||||||
|
|
@ -28,10 +28,6 @@ export class GroupConfigurationTransformers {
|
||||||
path: ['channels', 'playdateReminders'],
|
path: ['channels', 'playdateReminders'],
|
||||||
type: TransformerType.Channel,
|
type: TransformerType.Channel,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: ['locale'],
|
|
||||||
type: TransformerType.Locale,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: ['permissions', 'allowMemberManagingPlaydates'],
|
path: ['permissions', 'allowMemberManagingPlaydates'],
|
||||||
type: TransformerType.PermissionBoolean
|
type: TransformerType.PermissionBoolean
|
||||||
|
|
@ -39,6 +35,14 @@ export class GroupConfigurationTransformers {
|
||||||
{
|
{
|
||||||
path: ['calendar', 'title'],
|
path: ['calendar', 'title'],
|
||||||
type: TransformerType.String
|
type: TransformerType.String
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['calendar', 'description'],
|
||||||
|
type: TransformerType.String,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['calendar', 'location'],
|
||||||
|
type: TransformerType.String
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
5
source/Groups/RuntimeGroupConfiguration.d.ts
vendored
5
source/Groups/RuntimeGroupConfiguration.d.ts
vendored
|
|
@ -3,7 +3,6 @@ import {Nullable} from "../types/Nullable";
|
||||||
|
|
||||||
export type RuntimeGroupConfiguration = {
|
export type RuntimeGroupConfiguration = {
|
||||||
channels: Nullable<ChannelRuntimeGroupConfiguration>,
|
channels: Nullable<ChannelRuntimeGroupConfiguration>,
|
||||||
locale: Intl.Locale,
|
|
||||||
permissions: PermissionRuntimeGroupConfiguration,
|
permissions: PermissionRuntimeGroupConfiguration,
|
||||||
calendar: CalendarRuntimeGroupConfiguration
|
calendar: CalendarRuntimeGroupConfiguration
|
||||||
};
|
};
|
||||||
|
|
@ -18,5 +17,7 @@ export type PermissionRuntimeGroupConfiguration = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CalendarRuntimeGroupConfiguration = {
|
export type CalendarRuntimeGroupConfiguration = {
|
||||||
title: null|string
|
title: null|string,
|
||||||
|
description: null|string,
|
||||||
|
location: null|string
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import {Repository} from "./Repository";
|
||||||
import {GroupModel} from "../Models/GroupModel";
|
import {GroupModel} from "../Models/GroupModel";
|
||||||
import Groups, {DBGroup} from "../Database/tables/Groups";
|
import Groups, {DBGroup} from "../Database/tables/Groups";
|
||||||
import {DatabaseConnection} from "../Database/DatabaseConnection";
|
import {DatabaseConnection} from "../Database/DatabaseConnection";
|
||||||
import {GuildMember} from "discord.js";
|
import {GuildMember, UserFlagsBitField} from "discord.js";
|
||||||
import {Nullable} from "../types/Nullable";
|
import {Nullable} from "../types/Nullable";
|
||||||
import {PlaydateRepository} from "./PlaydateRepository";
|
import {PlaydateRepository} from "./PlaydateRepository";
|
||||||
import {Container} from "../Container/Container";
|
import {Container} from "../Container/Container";
|
||||||
|
|
@ -16,7 +16,6 @@ export class GroupRepository extends Repository<GroupModel, DBGroup> {
|
||||||
database,
|
database,
|
||||||
Groups
|
Groups
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public findGroupByName(name: string): Nullable<GroupModel> {
|
public findGroupByName(name: string): Nullable<GroupModel> {
|
||||||
|
|
@ -60,6 +59,19 @@ export class GroupRepository extends Repository<GroupModel, DBGroup> {
|
||||||
return group.leader.memberid === member.id;
|
return group.leader.memberid === member.id;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isMemberInGroup(member: Nullable<GuildMember>, group: GroupModel): boolean
|
||||||
|
{
|
||||||
|
if (!member) {
|
||||||
|
throw new Error("Can't find member for guild: none given");
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups = this.findGroupsByRoles(member.guild.id, [...member.roles.cache.keys()])
|
||||||
|
|
||||||
|
return groups.some((dbGroup) => {
|
||||||
|
return group.id === dbGroup.id;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public deleteGroup(group: GroupModel): void {
|
public deleteGroup(group: GroupModel): void {
|
||||||
this.delete(group.id);
|
this.delete(group.id);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue