refactor(configuration): Setup configuration and menu to be reuseable
This commit is contained in:
parent
863ae3fab2
commit
d46bbd84c5
21 changed files with 551 additions and 452 deletions
71
source/Configuration/ConfigurationHandler.ts
Normal file
71
source/Configuration/ConfigurationHandler.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import {ConfigurationProvider} from "./ConfigurationProvider";
|
||||||
|
import {ConfigurationModel} from "../Database/Models/ConfigurationModel";
|
||||||
|
// @ts-expect-error set-path is provided
|
||||||
|
import setPath from 'object-path-set';
|
||||||
|
import deepmerge from "deepmerge";
|
||||||
|
// @ts-expect-error Any is fine
|
||||||
|
import {isPlainObject} from "is-plain-object";
|
||||||
|
import {Nullable} from "../types/Nullable";
|
||||||
|
import {ConfigurationTransformer, TransformerResults} from "./ConfigurationTransformer";
|
||||||
|
|
||||||
|
export class ConfigurationHandler<
|
||||||
|
TProviderModel extends ConfigurationModel = ConfigurationModel,
|
||||||
|
TRuntimeConfiguration extends object = Record<string, string>,
|
||||||
|
> {
|
||||||
|
public readonly transformer: ConfigurationTransformer
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly provider: ConfigurationProvider<TProviderModel>
|
||||||
|
) {
|
||||||
|
this.transformer = provider.getTransformer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public save(path: string, value: string): void {
|
||||||
|
const configuration = this.provider.get(path);
|
||||||
|
|
||||||
|
if (configuration) {
|
||||||
|
this.provider.save({
|
||||||
|
...configuration,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.provider.save({
|
||||||
|
key: path,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCompleteConfiguration(): TRuntimeConfiguration {
|
||||||
|
return deepmerge(
|
||||||
|
this.provider.defaults,
|
||||||
|
this.getCompleteDatabaseConfig(),
|
||||||
|
{
|
||||||
|
isMergeableObject: isPlainObject
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfigurationByPath(path: string): Nullable<TransformerResults> {
|
||||||
|
const configuration = this.provider.get(path);
|
||||||
|
if (!configuration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.transformer.getValue(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCompleteDatabaseConfig(): Partial<TRuntimeConfiguration> {
|
||||||
|
const values = this.provider.getAll();
|
||||||
|
const configuration: Partial<TRuntimeConfiguration> = {};
|
||||||
|
|
||||||
|
values.forEach((configValue) => {
|
||||||
|
const value = this.transformer.getValue(configValue);
|
||||||
|
setPath(configuration, configValue.key, value);
|
||||||
|
})
|
||||||
|
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
source/Configuration/ConfigurationProvider.ts
Normal file
20
source/Configuration/ConfigurationProvider.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {ConfigurationModel} from "../Database/Models/ConfigurationModel";
|
||||||
|
import {Model} from "../Database/Models/Model";
|
||||||
|
import {ValueOf} from "../types/Class";
|
||||||
|
import {Nullable} from "../types/Nullable";
|
||||||
|
import {ConfigurationTransformer} from "./ConfigurationTransformer";
|
||||||
|
|
||||||
|
|
||||||
|
export interface ConfigurationProvider<
|
||||||
|
TProviderModel extends ConfigurationModel = ConfigurationModel,
|
||||||
|
TRuntimeConfiguration extends object = object
|
||||||
|
> {
|
||||||
|
get(path: string): Nullable<TProviderModel>;
|
||||||
|
getAll(): TProviderModel[];
|
||||||
|
|
||||||
|
get defaults(): TRuntimeConfiguration;
|
||||||
|
|
||||||
|
save(value: Omit<ConfigurationModel, "id"> & Partial<Model>): void;
|
||||||
|
|
||||||
|
getTransformer(): ConfigurationTransformer;
|
||||||
|
}
|
||||||
55
source/Configuration/ConfigurationTransformer.ts
Normal file
55
source/Configuration/ConfigurationTransformer.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import {ChannelId} from "../types/DiscordTypes";
|
||||||
|
import {Nullable} from "../types/Nullable";
|
||||||
|
import {ArrayUtils} from "../Utilities/ArrayUtils";
|
||||||
|
import {ConfigurationModel} from "../Database/Models/ConfigurationModel";
|
||||||
|
|
||||||
|
export enum TransformerType {
|
||||||
|
Channel,
|
||||||
|
PermissionBoolean,
|
||||||
|
String,
|
||||||
|
Paragraph,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigurationTransformerItem = {
|
||||||
|
path: string[];
|
||||||
|
type: TransformerType,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TransformerResults =
|
||||||
|
ChannelId | boolean | string | null
|
||||||
|
|
||||||
|
export class ConfigurationTransformer {
|
||||||
|
constructor(
|
||||||
|
private readonly transformers: ConfigurationTransformerItem[]
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValue(configValue: ConfigurationModel): TransformerResults {
|
||||||
|
const transformerType = this.getTransformerType(configValue.key);
|
||||||
|
if (transformerType === undefined || transformerType === null) {
|
||||||
|
throw new Error(`Can't find transformer for ${configValue.key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (transformerType) {
|
||||||
|
case TransformerType.Channel:
|
||||||
|
return <ChannelId>configValue.value;
|
||||||
|
case TransformerType.PermissionBoolean:
|
||||||
|
return configValue.value === '1';
|
||||||
|
case TransformerType.Paragraph:
|
||||||
|
case TransformerType.String:
|
||||||
|
return configValue.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTransformerType(configKey: string): Nullable<TransformerType> {
|
||||||
|
const path = configKey.split('.');
|
||||||
|
return this.transformers.find(
|
||||||
|
transformer => {
|
||||||
|
return ArrayUtils.arraysEqual(transformer.path, path);
|
||||||
|
}
|
||||||
|
)?.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
import {
|
|
||||||
AnyMenuItem, FieldMenuItem,
|
|
||||||
FieldMenuItemContext, FieldMenuItemSaveValue, MenuItem,
|
|
||||||
MenuItemType, PromptMenuItem,
|
|
||||||
RowBuilderFieldMenuItemContext
|
|
||||||
} from "../../Menu/MenuRenderer.types";
|
|
||||||
import {GroupConfigurationTransformers} from "./GroupConfigurationTransformers";
|
|
||||||
import {GroupConfigurationHandler} from "./GroupConfigurationHandler";
|
|
||||||
import {
|
|
||||||
channelMention,
|
|
||||||
ChannelSelectMenuBuilder,
|
|
||||||
ChannelType,
|
|
||||||
inlineCode,
|
|
||||||
italic,
|
|
||||||
Snowflake,
|
|
||||||
StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextInputBuilder,
|
|
||||||
TextInputStyle
|
|
||||||
} from "discord.js";
|
|
||||||
import {ChannelId} from "../../types/DiscordTypes";
|
|
||||||
import {MessageActionRowComponentBuilder} from "@discordjs/builders";
|
|
||||||
import {Prompt} from "../../Menu/Modals/Prompt";
|
|
||||||
|
|
||||||
export class ConfigurationMenuHandler {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly configuration: GroupConfigurationHandler,
|
|
||||||
private readonly transformer: GroupConfigurationTransformers
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public getMenuItems(): AnyMenuItem[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
traversalKey: "channels",
|
|
||||||
label: "Channels",
|
|
||||||
description: "Provides settings to define in what channels the bot sends messages, when not directly interacting with it.",
|
|
||||||
type: MenuItemType.Collection,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
traversalKey: "newPlaydates",
|
|
||||||
label: "New Playdates",
|
|
||||||
description: "Sets the channel, where the group gets notified, when new Playdates are set.",
|
|
||||||
type: MenuItemType.Field,
|
|
||||||
getCurrentValue: this.getChannelValue.bind(this),
|
|
||||||
getActionRowBuilder: this.getChannelMenuBuilder.bind(this),
|
|
||||||
setValue: this.setValue.bind(this)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
traversalKey: "playdateReminders",
|
|
||||||
label: 'Playdate Reminders',
|
|
||||||
description: "Sets the channel, where the group gets reminded of upcoming playdates.",
|
|
||||||
type: MenuItemType.Field,
|
|
||||||
getCurrentValue: this.getChannelValue.bind(this),
|
|
||||||
getActionRowBuilder: this.getChannelMenuBuilder.bind(this),
|
|
||||||
setValue: this.setValue.bind(this)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
traversalKey: "permissions",
|
|
||||||
label: "Permissions",
|
|
||||||
description: "Allows customization, how the members are allowed to interact with the data stored in the group.",
|
|
||||||
type: MenuItemType.Collection,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
traversalKey: "allowMemberManagingPlaydates",
|
|
||||||
label: "Manage Playdates",
|
|
||||||
description: "Defines if the members are allowed to manage playdates like adding or deleting them.",
|
|
||||||
type: MenuItemType.Field,
|
|
||||||
getCurrentValue: this.getPermissionBooleanValue.bind(this),
|
|
||||||
getActionRowBuilder: this.getPermissionBooleanBuilder.bind(this),
|
|
||||||
setValue: this.setValue.bind(this)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
traversalKey: "calendar",
|
|
||||||
label: "Calendar",
|
|
||||||
description: "Provides settings for the metadata contained in the playdate exports.",
|
|
||||||
type: MenuItemType.Collection,
|
|
||||||
children: [
|
|
||||||
this.createStringMenuItem({
|
|
||||||
traversalKey: "title",
|
|
||||||
label: "Title",
|
|
||||||
description: "Defines how the calendar entry should be called.",
|
|
||||||
}),
|
|
||||||
this.createTextareaMenuItem({
|
|
||||||
traversalKey: "description",
|
|
||||||
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 {
|
|
||||||
const value = this.configuration.getConfigurationByPath(context.path.join('.'));
|
|
||||||
if (value === undefined) {
|
|
||||||
return italic("None");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return inlineCode("None");
|
|
||||||
}
|
|
||||||
return channelMention(<ChannelId>value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getChannelMenuBuilder(context: FieldMenuItemContext): MessageActionRowComponentBuilder {
|
|
||||||
return new ChannelSelectMenuBuilder()
|
|
||||||
.setChannelTypes(ChannelType.GuildText)
|
|
||||||
.setPlaceholder("New Value");
|
|
||||||
}
|
|
||||||
|
|
||||||
private getLocaleValue(context: FieldMenuItemContext): string {
|
|
||||||
const value = this.configuration.getConfigurationByPath(context.path.join('.'));
|
|
||||||
if (value === undefined) {
|
|
||||||
return italic("None");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return inlineCode("Default");
|
|
||||||
}
|
|
||||||
|
|
||||||
const displaynames = new Intl.DisplayNames(["en"], {type: "language"});
|
|
||||||
return displaynames.of((<Intl.Locale>value)?.baseName) ?? "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
private getLocaleMenuBuilder(context: FieldMenuItemContext): MessageActionRowComponentBuilder {
|
|
||||||
const options = [
|
|
||||||
'en-US',
|
|
||||||
'fr-FR',
|
|
||||||
'it-IT',
|
|
||||||
'de-DE'
|
|
||||||
]
|
|
||||||
const displaynames = new Intl.DisplayNames(["en"], {type: "language"});
|
|
||||||
return new StringSelectMenuBuilder()
|
|
||||||
.setOptions(
|
|
||||||
options.map(intl => new StringSelectMenuOptionBuilder()
|
|
||||||
.setLabel(displaynames.of(intl) ?? '')
|
|
||||||
.setValue(intl)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPermissionBooleanValue(context: FieldMenuItemContext) {
|
|
||||||
const value = this.configuration.getConfigurationByPath(context.path.join('.'));
|
|
||||||
if (value === undefined) {
|
|
||||||
return italic("None");
|
|
||||||
}
|
|
||||||
|
|
||||||
return value ? 'Allowed' : "Disallowed";
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPermissionBooleanBuilder(context: FieldMenuItemContext) {
|
|
||||||
return new StringSelectMenuBuilder()
|
|
||||||
.setOptions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
label: "Allow",
|
|
||||||
value: "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Disallow",
|
|
||||||
value: "0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStringValue(context: FieldMenuItemContext): string {
|
|
||||||
const value = this.configuration.getConfigurationByPath(context.path.join('.'));
|
|
||||||
if (!value) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
throw new TypeError(`Value of type ${typeof value} can't be used for a string value!`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStringBuilder(context: FieldMenuItemContext): TextInputBuilder {
|
|
||||||
return new TextInputBuilder()
|
|
||||||
.setStyle(TextInputStyle.Short)
|
|
||||||
.setMaxLength(100)
|
|
||||||
}
|
|
||||||
private getTextareaBuilder(context: FieldMenuItemContext): TextInputBuilder {
|
|
||||||
return new TextInputBuilder()
|
|
||||||
.setStyle(TextInputStyle.Paragraph)
|
|
||||||
.setMaxLength(2048)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private setValue(value: FieldMenuItemSaveValue[]|string, context: FieldMenuItemContext): void {
|
|
||||||
const savedValue = typeof value !== 'string' ?
|
|
||||||
value.join('; ') :
|
|
||||||
value;
|
|
||||||
|
|
||||||
this.configuration.saveConfiguration(context.path.join('.'), savedValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import {RuntimeGroupConfiguration} from "./RuntimeGroupConfiguration";
|
|
||||||
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
|
||||||
import {GroupModel} from "../../Database/Models/GroupModel";
|
|
||||||
import {GroupConfigurationResult, GroupConfigurationTransformers} from "./GroupConfigurationTransformers";
|
|
||||||
// @ts-expect-error set-path is provided
|
|
||||||
import setPath from 'object-path-set';
|
|
||||||
import deepmerge from "deepmerge";
|
|
||||||
import {Nullable} from "../../types/Nullable";
|
|
||||||
// @ts-expect-error Any is fine
|
|
||||||
import {isPlainObject} from "is-plain-object";
|
|
||||||
|
|
||||||
export class GroupConfigurationHandler {
|
|
||||||
static DEFAULT_CONFIGURATION: RuntimeGroupConfiguration = {
|
|
||||||
channels: null,
|
|
||||||
permissions: {
|
|
||||||
allowMemberManagingPlaydates: false
|
|
||||||
},
|
|
||||||
calendar: {
|
|
||||||
title: null,
|
|
||||||
description: null,
|
|
||||||
location: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly transformers: GroupConfigurationTransformers = new GroupConfigurationTransformers();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly repository: GroupConfigurationRepository,
|
|
||||||
private readonly group: GroupModel
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public saveConfiguration(path: string, value: string): void {
|
|
||||||
const configuration = this.repository.findConfigurationByPath(this.group, path);
|
|
||||||
|
|
||||||
if (configuration) {
|
|
||||||
this.repository.update(
|
|
||||||
{
|
|
||||||
...configuration,
|
|
||||||
value: value
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.repository.create({
|
|
||||||
group: this.group,
|
|
||||||
key: path,
|
|
||||||
value: value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getConfiguration(): RuntimeGroupConfiguration {
|
|
||||||
return deepmerge(GroupConfigurationHandler.DEFAULT_CONFIGURATION, this.getDatabaseConfiguration(), {
|
|
||||||
isMergeableObject: isPlainObject
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getConfigurationByPath(path: string): Nullable<GroupConfigurationResult> {
|
|
||||||
const configuration = this.repository.findConfigurationByPath(this.group, path);
|
|
||||||
if (!configuration) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.transformers.getValue(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDatabaseConfiguration(): Partial<RuntimeGroupConfiguration> {
|
|
||||||
const values = this.repository.findGroupConfigurations(this.group);
|
|
||||||
const configuration: Partial<RuntimeGroupConfiguration> = {};
|
|
||||||
|
|
||||||
values.forEach((configValue) => {
|
|
||||||
const value = this.transformers.getValue(configValue);
|
|
||||||
setPath(configuration, configValue.key, value);
|
|
||||||
})
|
|
||||||
|
|
||||||
return configuration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
104
source/Configuration/Groups/GroupConfigurationProvider.ts
Normal file
104
source/Configuration/Groups/GroupConfigurationProvider.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import {ConfigurationProvider} from "../ConfigurationProvider";
|
||||||
|
import {GroupConfigurationModel} from "../../Database/Models/GroupConfigurationModel";
|
||||||
|
import {Nullable} from "../../types/Nullable";
|
||||||
|
import {ChannelId} from "../../types/DiscordTypes";
|
||||||
|
import { ConfigurationModel } from "../../Database/Models/ConfigurationModel";
|
||||||
|
import { Model } from "../../Database/Models/Model";
|
||||||
|
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
||||||
|
import {GroupModel} from "../../Database/Models/GroupModel";
|
||||||
|
import {ConfigurationTransformer, TransformerType} from "../ConfigurationTransformer";
|
||||||
|
|
||||||
|
export type RuntimeGroupConfiguration = {
|
||||||
|
channels: Nullable<ChannelRuntimeGroupConfiguration>,
|
||||||
|
permissions: PermissionRuntimeGroupConfiguration,
|
||||||
|
calendar: CalendarRuntimeGroupConfiguration
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChannelRuntimeGroupConfiguration = {
|
||||||
|
newPlaydates: ChannelId,
|
||||||
|
playdateReminders: ChannelId
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PermissionRuntimeGroupConfiguration = {
|
||||||
|
allowMemberManagingPlaydates: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CalendarRuntimeGroupConfiguration = {
|
||||||
|
title: null | string,
|
||||||
|
description: null | string,
|
||||||
|
location: null | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GroupConfigurationResult =
|
||||||
|
ChannelId | Intl.Locale | boolean | string | null
|
||||||
|
|
||||||
|
export class GroupConfigurationProvider implements ConfigurationProvider<
|
||||||
|
GroupConfigurationModel,
|
||||||
|
RuntimeGroupConfiguration
|
||||||
|
> {
|
||||||
|
constructor(
|
||||||
|
private readonly repository: GroupConfigurationRepository,
|
||||||
|
private readonly group: GroupModel
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaults(): RuntimeGroupConfiguration {
|
||||||
|
return {
|
||||||
|
channels: null,
|
||||||
|
permissions: {
|
||||||
|
allowMemberManagingPlaydates: false
|
||||||
|
},
|
||||||
|
calendar: {
|
||||||
|
title: null,
|
||||||
|
description: null,
|
||||||
|
location: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(path: string): Nullable<GroupConfigurationModel> {
|
||||||
|
return this.repository.findConfigurationByPath(this.group, path);
|
||||||
|
}
|
||||||
|
getAll(): GroupConfigurationModel[] {
|
||||||
|
return this.repository.findGroupConfigurations(this.group);
|
||||||
|
}
|
||||||
|
save(value: Omit<ConfigurationModel, "id"> & Partial<Model>): void {
|
||||||
|
if (value.id) {
|
||||||
|
// @ts-expect-error id is set, due to the check on line above
|
||||||
|
this.repository.update(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repository.create(value);
|
||||||
|
}
|
||||||
|
getTransformer(): ConfigurationTransformer {
|
||||||
|
return new ConfigurationTransformer(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: ['channels', 'newPlaydates'],
|
||||||
|
type: TransformerType.Channel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['channels', 'playdateReminders'],
|
||||||
|
type: TransformerType.Channel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['permissions', 'allowMemberManagingPlaydates'],
|
||||||
|
type: TransformerType.PermissionBoolean
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['calendar', 'title'],
|
||||||
|
type: TransformerType.String
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['calendar', 'description'],
|
||||||
|
type: TransformerType.Paragraph,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ['calendar', 'location'],
|
||||||
|
type: TransformerType.String
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import {ChannelId} from "../../types/DiscordTypes";
|
|
||||||
import {GroupConfigurationModel} from "../../Database/Models/GroupConfigurationModel";
|
|
||||||
import {Nullable} from "../../types/Nullable";
|
|
||||||
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
|
||||||
|
|
||||||
export enum TransformerType {
|
|
||||||
Locale,
|
|
||||||
Channel,
|
|
||||||
PermissionBoolean,
|
|
||||||
String
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupConfigurationTransformer = {
|
|
||||||
path: string[];
|
|
||||||
type: TransformerType,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GroupConfigurationResult =
|
|
||||||
ChannelId | Intl.Locale | boolean | string | null
|
|
||||||
|
|
||||||
export class GroupConfigurationTransformers {
|
|
||||||
static TRANSFORMERS: GroupConfigurationTransformer[] = [
|
|
||||||
{
|
|
||||||
path: ['channels', 'newPlaydates'],
|
|
||||||
type: TransformerType.Channel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ['channels', 'playdateReminders'],
|
|
||||||
type: TransformerType.Channel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ['permissions', 'allowMemberManagingPlaydates'],
|
|
||||||
type: TransformerType.PermissionBoolean
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ['calendar', 'title'],
|
|
||||||
type: TransformerType.String
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ['calendar', 'description'],
|
|
||||||
type: TransformerType.String,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ['calendar', 'location'],
|
|
||||||
type: TransformerType.String
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
public getValue(configValue: GroupConfigurationModel): GroupConfigurationResult {
|
|
||||||
const transformerType = this.getTransformerType(configValue.key);
|
|
||||||
if (transformerType === undefined || transformerType === null) {
|
|
||||||
throw new Error(`Can't find transformer for ${configValue.key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (transformerType) {
|
|
||||||
case TransformerType.Locale:
|
|
||||||
return new Intl.Locale(configValue.value)
|
|
||||||
case TransformerType.Channel:
|
|
||||||
return <ChannelId>configValue.value;
|
|
||||||
case TransformerType.PermissionBoolean:
|
|
||||||
return configValue.value === '1';
|
|
||||||
case TransformerType.String:
|
|
||||||
return configValue.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTransformerType(configKey: string): Nullable<TransformerType> {
|
|
||||||
const path = configKey.split('.');
|
|
||||||
return GroupConfigurationTransformers.TRANSFORMERS.find(
|
|
||||||
transformer => {
|
|
||||||
return ArrayUtils.arraysEqual(transformer.path, path);
|
|
||||||
}
|
|
||||||
)?.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import {ChannelId} from "../../types/DiscordTypes";
|
|
||||||
import {Nullable} from "../../types/Nullable";
|
|
||||||
|
|
||||||
export type RuntimeGroupConfiguration = {
|
|
||||||
channels: Nullable<ChannelRuntimeGroupConfiguration>,
|
|
||||||
permissions: PermissionRuntimeGroupConfiguration,
|
|
||||||
calendar: CalendarRuntimeGroupConfiguration
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ChannelRuntimeGroupConfiguration = {
|
|
||||||
newPlaydates: ChannelId,
|
|
||||||
playdateReminders: ChannelId
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PermissionRuntimeGroupConfiguration = {
|
|
||||||
allowMemberManagingPlaydates: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CalendarRuntimeGroupConfiguration = {
|
|
||||||
title: null|string,
|
|
||||||
description: null|string,
|
|
||||||
location: null|string
|
|
||||||
}
|
|
||||||
177
source/Configuration/MenuHandler.ts
Normal file
177
source/Configuration/MenuHandler.ts
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
import {ConfigurationHandler} from "./ConfigurationHandler";
|
||||||
|
import {
|
||||||
|
AnyMenuItem,
|
||||||
|
CollectionMenuItem,
|
||||||
|
FieldMenuItem, FieldMenuItemContext, FieldMenuItemSaveValue,
|
||||||
|
MenuItem,
|
||||||
|
MenuItemType,
|
||||||
|
PromptMenuItem
|
||||||
|
} from "../Menu/MenuRenderer.types";
|
||||||
|
import {TraversalPath} from "../Menu/MenuTraversal.types";
|
||||||
|
import {
|
||||||
|
channelMention,
|
||||||
|
ChannelSelectMenuBuilder,
|
||||||
|
ChannelType,
|
||||||
|
inlineCode,
|
||||||
|
italic,
|
||||||
|
StringSelectMenuBuilder, TextInputBuilder, TextInputStyle
|
||||||
|
} from "discord.js";
|
||||||
|
import {ChannelId} from "../types/DiscordTypes";
|
||||||
|
import {MessageActionRowComponentBuilder} from "@discordjs/builders";
|
||||||
|
import {TransformerType} from "./ConfigurationTransformer";
|
||||||
|
|
||||||
|
type FieldHandlerMenuItem = MenuItem & (Partial<PromptMenuItem> | Partial<FieldMenuItem>);
|
||||||
|
type HandlerMenuItem =
|
||||||
|
FieldHandlerMenuItem | CollectionMenuItem<HandlerMenuItem>
|
||||||
|
|
||||||
|
export class MenuHandler {
|
||||||
|
constructor(
|
||||||
|
private readonly config: ConfigurationHandler,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public fillMenuItems(
|
||||||
|
menuItems: HandlerMenuItem[]
|
||||||
|
): AnyMenuItem[] {
|
||||||
|
const handler = this;
|
||||||
|
|
||||||
|
function resolve(path: TraversalPath, menuItem: HandlerMenuItem): AnyMenuItem {
|
||||||
|
path = [...path, menuItem.traversalKey];
|
||||||
|
|
||||||
|
if (menuItem.type === MenuItemType.Collection) {
|
||||||
|
menuItem.children = menuItem.children.map((nextMenuItem) => resolve(path, nextMenuItem))
|
||||||
|
return <CollectionMenuItem>menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformerType = handler.config.transformer.getTransformerType(path.join('.'));
|
||||||
|
if (transformerType === undefined || transformerType === null) {
|
||||||
|
throw new Error(`Can't resolve type for '${path.join('.')}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItem.type = handler.findType(transformerType);
|
||||||
|
|
||||||
|
menuItem.setValue = handler.setValue.bind(handler);
|
||||||
|
menuItem.getCurrentValue = handler.findCurrentValueMethod(transformerType);
|
||||||
|
menuItem.getActionRowBuilder = handler.findActionRowBuilderMethod(transformerType);
|
||||||
|
|
||||||
|
return <AnyMenuItem>menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuItems.map((menuItem) => resolve([], menuItem))
|
||||||
|
}
|
||||||
|
|
||||||
|
private findType(transformer: TransformerType): MenuItemType.Field | MenuItemType.Prompt
|
||||||
|
{
|
||||||
|
switch (transformer) {
|
||||||
|
case TransformerType.String:
|
||||||
|
case TransformerType.Paragraph:
|
||||||
|
return MenuItemType.Prompt;
|
||||||
|
default:
|
||||||
|
return MenuItemType.Field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private findCurrentValueMethod(transformer: TransformerType): (context: FieldMenuItemContext) => string
|
||||||
|
{
|
||||||
|
switch (transformer) {
|
||||||
|
case TransformerType.Channel:
|
||||||
|
return this.getChannelValue.bind(this);
|
||||||
|
case TransformerType.PermissionBoolean:
|
||||||
|
return this.getPermissionBooleanValue.bind(this);
|
||||||
|
case TransformerType.String:
|
||||||
|
case TransformerType.Paragraph:
|
||||||
|
return this.getStringValue.bind(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private findActionRowBuilderMethod(transformer: TransformerType):
|
||||||
|
((context: FieldMenuItemContext) => MessageActionRowComponentBuilder) | ((context: FieldMenuItemContext) => TextInputBuilder)
|
||||||
|
{
|
||||||
|
switch (transformer) {
|
||||||
|
case TransformerType.Channel:
|
||||||
|
return this.getChannelMenuBuilder.bind(this);
|
||||||
|
case TransformerType.PermissionBoolean:
|
||||||
|
return this.getPermissionBooleanBuilder.bind(this);
|
||||||
|
case TransformerType.String:
|
||||||
|
return this.getStringBuilder.bind(this);
|
||||||
|
case TransformerType.Paragraph:
|
||||||
|
return this.getTextareaBuilder.bind(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getChannelValue(context: FieldMenuItemContext): string {
|
||||||
|
const value = this.config.getConfigurationByPath(context.path.join('.'));
|
||||||
|
if (value === undefined) {
|
||||||
|
return italic("None");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
return inlineCode("None");
|
||||||
|
}
|
||||||
|
return channelMention(<ChannelId>value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getChannelMenuBuilder(): MessageActionRowComponentBuilder {
|
||||||
|
return new ChannelSelectMenuBuilder()
|
||||||
|
.setChannelTypes(ChannelType.GuildText)
|
||||||
|
.setPlaceholder("New Value");
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPermissionBooleanValue(context: FieldMenuItemContext) {
|
||||||
|
const value = this.config.getConfigurationByPath(context.path.join('.'));
|
||||||
|
if (value === undefined) {
|
||||||
|
return italic("None");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value ? 'Allowed' : "Disallowed";
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPermissionBooleanBuilder() {
|
||||||
|
return new StringSelectMenuBuilder()
|
||||||
|
.setOptions(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: "Allow",
|
||||||
|
value: "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Disallow",
|
||||||
|
value: "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStringValue(context: FieldMenuItemContext): string {
|
||||||
|
const value = this.config.getConfigurationByPath(context.path.join('.'));
|
||||||
|
if (!value) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new TypeError(`Value of type ${typeof value} can't be used for a string value!`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStringBuilder(): TextInputBuilder {
|
||||||
|
return new TextInputBuilder()
|
||||||
|
.setStyle(TextInputStyle.Short)
|
||||||
|
.setMaxLength(100)
|
||||||
|
}
|
||||||
|
private getTextareaBuilder(): TextInputBuilder {
|
||||||
|
return new TextInputBuilder()
|
||||||
|
.setStyle(TextInputStyle.Paragraph)
|
||||||
|
.setMaxLength(2048)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private setValue(value: FieldMenuItemSaveValue[]|string, context: FieldMenuItemContext): void {
|
||||||
|
const savedValue = typeof value !== 'string' ?
|
||||||
|
value.join('; ') :
|
||||||
|
value;
|
||||||
|
|
||||||
|
this.config.save(context.path.join('.'), savedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
source/Database/Models/ConfigurationModel.ts
Normal file
6
source/Database/Models/ConfigurationModel.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import {Model} from "./Model";
|
||||||
|
|
||||||
|
export type ConfigurationModel = Model & {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import {Model} from "./Model";
|
import {Model} from "./Model";
|
||||||
import {GroupModel} from "./GroupModel";
|
import {GroupModel} from "./GroupModel";
|
||||||
|
import {ConfigurationModel} from "./ConfigurationModel";
|
||||||
|
|
||||||
export interface GroupConfigurationModel extends Model {
|
export type GroupConfigurationModel = ConfigurationModel & {
|
||||||
group: GroupModel;
|
group: GroupModel;
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {Model} from "./Model";
|
import {Model} from "./Model";
|
||||||
import {GuildMember, Role} from "../../types/DiscordTypes";
|
import {GuildMember, Role} from "../../types/DiscordTypes";
|
||||||
|
|
||||||
export interface GroupModel extends Model {
|
export type GroupModel = Model & {
|
||||||
name: string;
|
name: string;
|
||||||
leader: GuildMember;
|
leader: GuildMember;
|
||||||
role: Role;
|
role: Role;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export interface Model {
|
export type Model = {
|
||||||
id: number | bigint;
|
id: number | bigint;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import {Model} from "./Model";
|
||||||
import {GroupModel} from "./GroupModel";
|
import {GroupModel} from "./GroupModel";
|
||||||
import {Nullable} from "../../types/Nullable";
|
import {Nullable} from "../../types/Nullable";
|
||||||
|
|
||||||
export interface PlaydateModel extends Model {
|
export type PlaydateModel = Model & {
|
||||||
group: Nullable<GroupModel>
|
group: Nullable<GroupModel>
|
||||||
from_time: Date,
|
from_time: Date,
|
||||||
to_time: Date,
|
to_time: Date,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class Repository<ModelType extends Model, IntermediateModelType = unknown
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(instance: Partial<ModelType> & { id: number }): boolean {
|
public update(instance: Partial<ModelType> & Model): boolean {
|
||||||
const columnNames = this.schema.columns.filter((column) => {
|
const columnNames = this.schema.columns.filter((column) => {
|
||||||
return !column.primaryKey
|
return !column.primaryKey
|
||||||
}).map((column) => {
|
}).map((column) => {
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,6 @@ import {Container} from "../../Container/Container";
|
||||||
import {GroupSelection} from "../CommandPartials/GroupSelection";
|
import {GroupSelection} from "../CommandPartials/GroupSelection";
|
||||||
import {UserError} from "../UserError";
|
import {UserError} from "../UserError";
|
||||||
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
||||||
import {GroupConfigurationRenderer} from "../../Configuration/Groups/GroupConfigurationRenderer";
|
|
||||||
import {GroupConfigurationHandler} from "../../Configuration/Groups/GroupConfigurationHandler";
|
|
||||||
import {GroupConfigurationTransformers} from "../../Configuration/Groups/GroupConfigurationTransformers";
|
|
||||||
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
||||||
import {PlaydateRepository} from "../../Database/Repositories/PlaydateRepository";
|
import {PlaydateRepository} from "../../Database/Repositories/PlaydateRepository";
|
||||||
import {Nullable} from "../../types/Nullable";
|
import {Nullable} from "../../types/Nullable";
|
||||||
|
|
@ -28,6 +25,9 @@ import {MenuRenderer} from "../../Menu/MenuRenderer";
|
||||||
import {MenuItemType} from "../../Menu/MenuRenderer.types";
|
import {MenuItemType} from "../../Menu/MenuRenderer.types";
|
||||||
import {ConfigurationMenuHandler} from "../../Configuration/Groups/ConfigurationMenuHandler";
|
import {ConfigurationMenuHandler} from "../../Configuration/Groups/ConfigurationMenuHandler";
|
||||||
import {MenuTraversal} from "../../Menu/MenuTraversal";
|
import {MenuTraversal} from "../../Menu/MenuTraversal";
|
||||||
|
import {ConfigurationHandler} from "../../Configuration/ConfigurationHandler";
|
||||||
|
import {GroupConfigurationProvider} from "../../Configuration/Groups/GroupConfigurationProvider";
|
||||||
|
import {MenuHandler} from "../../Configuration/MenuHandler";
|
||||||
|
|
||||||
export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand {
|
export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand {
|
||||||
private static GOODBYE_MESSAGES: string[] = [
|
private static GOODBYE_MESSAGES: string[] = [
|
||||||
|
|
@ -238,17 +238,75 @@ export class GroupCommand implements Command, ChatInteractionCommand, Autocomple
|
||||||
|
|
||||||
private async runConfigurator(interaction: ChatInputCommandInteraction) {
|
private async runConfigurator(interaction: ChatInputCommandInteraction) {
|
||||||
const group = GroupSelection.getGroup(interaction);
|
const group = GroupSelection.getGroup(interaction);
|
||||||
const menuHandler = new ConfigurationMenuHandler(
|
const menuHandler = new MenuHandler(
|
||||||
new GroupConfigurationHandler(
|
new ConfigurationHandler(
|
||||||
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
new GroupConfigurationProvider(
|
||||||
group
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
),
|
group
|
||||||
new GroupConfigurationTransformers(),
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const menu = new MenuRenderer(
|
const menu = new MenuRenderer(
|
||||||
new MenuTraversal(
|
new MenuTraversal(
|
||||||
menuHandler.getMenuItems(),
|
menuHandler.fillMenuItems(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
traversalKey: "channels",
|
||||||
|
label: "Channels",
|
||||||
|
description: "Provides settings to define in what channels the bot sends messages, when not directly interacting with it.",
|
||||||
|
type: MenuItemType.Collection,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
traversalKey: "newPlaydates",
|
||||||
|
label: "New Playdates",
|
||||||
|
description: "Sets the channel, where the group gets notified, when new Playdates are set.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
traversalKey: "playdateReminders",
|
||||||
|
label: 'Playdate Reminders',
|
||||||
|
description: "Sets the channel, where the group gets reminded of upcoming playdates.",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
traversalKey: "permissions",
|
||||||
|
label: "Permissions",
|
||||||
|
description: "Allows customization, how the members are allowed to interact with the data stored in the group.",
|
||||||
|
type: MenuItemType.Collection,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
traversalKey: "allowMemberManagingPlaydates",
|
||||||
|
label: "Manage Playdates",
|
||||||
|
description: "Defines if the members are allowed to manage playdates like adding or deleting them.",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
traversalKey: "calendar",
|
||||||
|
label: "Calendar",
|
||||||
|
description: "Provides settings for the metadata contained in the playdate exports.",
|
||||||
|
type: MenuItemType.Collection,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
traversalKey: "title",
|
||||||
|
label: "Title",
|
||||||
|
description: "Defines how the calendar entry should be called.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
traversalKey: "description",
|
||||||
|
label: "Description",
|
||||||
|
description: "Sets the description for the calendar entry.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
traversalKey: "location",
|
||||||
|
label: "Location",
|
||||||
|
description: "Sets the location where the calendar should point to."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
),
|
||||||
'Group Configuration',
|
'Group Configuration',
|
||||||
"This UI allows you to change settings for your group."
|
"This UI allows you to change settings for your group."
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ import {
|
||||||
ChatInputCommandInteraction,
|
ChatInputCommandInteraction,
|
||||||
time,
|
time,
|
||||||
AttachmentBuilder,
|
AttachmentBuilder,
|
||||||
ActivityFlagsBitField,
|
|
||||||
Options,
|
|
||||||
User,
|
|
||||||
GuildMember
|
GuildMember
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
|
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
|
||||||
|
|
@ -21,10 +18,10 @@ import {PlaydateRepository} from "../../Database/Repositories/PlaydateRepository
|
||||||
import {GroupModel} from "../../Database/Models/GroupModel";
|
import {GroupModel} from "../../Database/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 "../../Configuration/Groups/GroupConfigurationHandler";
|
|
||||||
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
||||||
import {privateDecrypt} from "node:crypto";
|
|
||||||
import {GroupRepository} from "../../Database/Repositories/GroupRepository";
|
import {GroupRepository} from "../../Database/Repositories/GroupRepository";
|
||||||
|
import {GroupConfigurationProvider} from "../../Configuration/Groups/GroupConfigurationProvider";
|
||||||
|
import { ConfigurationHandler } from "../../Configuration/ConfigurationHandler";
|
||||||
|
|
||||||
export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand {
|
export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand {
|
||||||
definition(): SlashCommandBuilder {
|
definition(): SlashCommandBuilder {
|
||||||
|
|
@ -314,10 +311,12 @@ 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(
|
const groupConfig = new ConfigurationHandler(
|
||||||
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
new GroupConfigurationProvider(
|
||||||
group
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
).getConfiguration();
|
group
|
||||||
|
)
|
||||||
|
).getCompleteConfiguration();
|
||||||
|
|
||||||
const playdates = this.getExportTargets(interaction, group);
|
const playdates = this.getExportTargets(interaction, group);
|
||||||
|
|
||||||
|
|
@ -396,10 +395,12 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = new GroupConfigurationHandler(
|
const config = new ConfigurationHandler(
|
||||||
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
new GroupConfigurationProvider(
|
||||||
group
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
|
group
|
||||||
|
)
|
||||||
);
|
);
|
||||||
return config.getConfiguration().permissions.allowMemberManagingPlaydates;
|
return config.getConfigurationByPath("permissions.allowMemberManagingPlaydates") === true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {Container} from "../../Container/Container";
|
import {Container} from "../../Container/Container";
|
||||||
import {PlaydateRepository} from "../../Database/Repositories/PlaydateRepository";
|
import {PlaydateRepository} from "../../Database/Repositories/PlaydateRepository";
|
||||||
import {GroupConfigurationHandler} from "../../Configuration/Groups/GroupConfigurationHandler";
|
|
||||||
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
||||||
import {PlaydateModel} from "../../Database/Models/PlaydateModel";
|
import {PlaydateModel} from "../../Database/Models/PlaydateModel";
|
||||||
import {ChannelId} from "../../types/DiscordTypes";
|
import {ChannelId} from "../../types/DiscordTypes";
|
||||||
|
|
@ -8,6 +7,12 @@ import {DiscordClient} from "../../Discord/DiscordClient";
|
||||||
import {EmbedBuilder, roleMention, time} from "discord.js";
|
import {EmbedBuilder, roleMention, time} from "discord.js";
|
||||||
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
||||||
import {EventConfiguration, EventType, TimedEvent} from "../EventHandler.types";
|
import {EventConfiguration, EventType, TimedEvent} from "../EventHandler.types";
|
||||||
|
import {ConfigurationHandler} from "../../Configuration/ConfigurationHandler";
|
||||||
|
import {
|
||||||
|
GroupConfigurationProvider,
|
||||||
|
RuntimeGroupConfiguration
|
||||||
|
} from "../../Configuration/Groups/GroupConfigurationProvider";
|
||||||
|
import {GroupConfigurationModel} from "../../Database/Models/GroupConfigurationModel";
|
||||||
|
|
||||||
export class ReminderEvent implements TimedEvent {
|
export class ReminderEvent implements TimedEvent {
|
||||||
private static REMINDER_INTERVALS = [
|
private static REMINDER_INTERVALS = [
|
||||||
|
|
@ -61,12 +66,14 @@ export class ReminderEvent implements TimedEvent {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
const configurationHandler = new GroupConfigurationHandler(
|
const groupConfig = new ConfigurationHandler<GroupConfigurationModel, RuntimeGroupConfiguration>(
|
||||||
this.groupConfigurationRepository,
|
new GroupConfigurationProvider(
|
||||||
playdate.group
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
|
playdate.group
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const config = configurationHandler.getConfiguration();
|
const config = groupConfig.getCompleteConfiguration();
|
||||||
const targetChannel = config.channels?.playdateReminders;
|
const targetChannel = config.channels?.playdateReminders;
|
||||||
|
|
||||||
if (!targetChannel) {
|
if (!targetChannel) {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,15 @@ import {PlaydateModel} from "../../Database/Models/PlaydateModel";
|
||||||
import PlaydateTableConfiguration from "../../Database/tables/Playdate";
|
import PlaydateTableConfiguration from "../../Database/tables/Playdate";
|
||||||
import {EmbedBuilder, roleMention, time} from "discord.js";
|
import {EmbedBuilder, roleMention, time} from "discord.js";
|
||||||
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
||||||
import {GroupConfigurationHandler} from "../../Configuration/Groups/GroupConfigurationHandler";
|
|
||||||
import {Container} from "../../Container/Container";
|
import {Container} from "../../Container/Container";
|
||||||
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
import {GroupConfigurationRepository} from "../../Database/Repositories/GroupConfigurationRepository";
|
||||||
import {DiscordClient} from "../../Discord/DiscordClient";
|
import {DiscordClient} from "../../Discord/DiscordClient";
|
||||||
|
import {ConfigurationHandler} from "../../Configuration/ConfigurationHandler";
|
||||||
|
import {GroupConfigurationModel} from "../../Database/Models/GroupConfigurationModel";
|
||||||
|
import {
|
||||||
|
GroupConfigurationProvider,
|
||||||
|
RuntimeGroupConfiguration
|
||||||
|
} from "../../Configuration/Groups/GroupConfigurationProvider";
|
||||||
|
|
||||||
const NEW_PLAYDATE_MESSAGES = [
|
const NEW_PLAYDATE_MESSAGES = [
|
||||||
'A new playdate was added. Lets hope, your GM has not planned to kill you. >:]',
|
'A new playdate was added. Lets hope, your GM has not planned to kill you. >:]',
|
||||||
|
|
@ -25,12 +30,14 @@ export async function sendCreatedNotificationEventHandler(event: ElementCreatedE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const configurationHandler = new GroupConfigurationHandler(
|
const groupConfig = new ConfigurationHandler<GroupConfigurationModel, RuntimeGroupConfiguration>(
|
||||||
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
new GroupConfigurationProvider(
|
||||||
playdate.group
|
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||||
|
playdate.group
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const targetChannel = configurationHandler.getConfigurationByPath('channels.newPlaydates');
|
const targetChannel = groupConfig.getConfigurationByPath('channels.newPlaydates');
|
||||||
if (!targetChannel) {
|
if (!targetChannel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ export type MenuItem = {
|
||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CollectionMenuItem = MenuItem & {
|
export type CollectionMenuItem<TCollection extends MenuItem = AnyMenuItem> = MenuItem & {
|
||||||
type: MenuItemType.Collection,
|
type: MenuItemType.Collection,
|
||||||
children: AnyMenuItem[]
|
children: TCollection[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FieldMenuItem = MenuItem & {
|
export type FieldMenuItem = MenuItem & {
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
export type Class = { constructor: { name: string } }
|
export type Class = { constructor: { name: string } }
|
||||||
export type ClassNamed = { name: string }
|
export type ClassNamed = { name: string }
|
||||||
|
|
||||||
|
export type ValueOf<T> = T[keyof T]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue