aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commands/config/config.ts153
-rw-r--r--src/lib/models/instance/Guild.ts65
2 files changed, 117 insertions, 101 deletions
diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts
index 88e83ec..9169180 100644
--- a/src/commands/config/config.ts
+++ b/src/commands/config/config.ts
@@ -1,5 +1,6 @@
import {
BushCommand,
+ GuildNoArraySetting,
guildSettingsObj,
settingsArr,
type ArgType,
@@ -30,6 +31,16 @@ import {
type MessageOptions
} from 'discord.js';
import _ from 'lodash';
+const { camelCase, snakeCase } = _;
+
+export const arrayActions = ['view' as const, 'add' as const, 'remove' as const, 'clear' as const];
+export type ArrayActions = typeof arrayActions[number];
+export const actionsString = ['view' as const, 'set' as const, 'delete' as const];
+export type ActionsString = typeof actionsString[number];
+export const allActions = [...arrayActions, ...actionsString];
+export type Action = typeof allActions[number];
+type SlashArgType = 'ROLE' | 'STRING' | 'CHANNEL' | 'USER';
+type BaseSettingTypes = 'string' | 'channel' | 'role' | 'user' | 'custom';
export default class ConfigCommand extends BushCommand {
public constructor() {
@@ -37,9 +48,7 @@ export default class ConfigCommand extends BushCommand {
aliases: ['config', 'settings', 'setting', 'configure'],
category: 'config',
description: 'Configure server settings.',
- usage: [
- `settings (${settingsArr.map((s) => `'${s}'`).join(', ')}) (${['view', 'set', 'add', 'remove'].map((s) => `'${s}'`)})`
- ],
+ usage: [`settings (${settingsArr.map((s) => `'${s}'`).join(', ')}) (${allActions.map((s) => `'${s}'`)})`],
examples: ['settings', 'config prefix set -'],
slash: true,
slashOptions: settingsArr.map((setting): SlashOption => {
@@ -62,7 +71,7 @@ export default class ConfigCommand extends BushCommand {
if (enumType instanceof Error) throw enumType;
return {
- name: _.snakeCase(setting),
+ name: snakeCase(setting),
description: `Manage the server's ${loweredName}`,
type: ApplicationCommandOptionType.SubcommandGroup,
options: isArray
@@ -99,6 +108,11 @@ export default class ConfigCommand extends BushCommand {
required: true
}
]
+ },
+ {
+ name: 'clear',
+ description: `Remove all values from a server's ${loweredName}.`,
+ type: ApplicationCommandOptionType.Subcommand
}
]
: [
@@ -120,6 +134,11 @@ export default class ConfigCommand extends BushCommand {
required: true
}
]
+ },
+ {
+ name: 'delete',
+ description: `Delete the server's ${loweredName}.`,
+ type: ApplicationCommandOptionType.Subcommand
}
]
};
@@ -144,13 +163,9 @@ export default class ConfigCommand extends BushCommand {
}
};
- const actionType = setting
- ? guildSettingsObj[setting as GuildSettings]?.type.includes('-array')
- ? ['view', 'add', 'remove']
- : ['view', 'set']
- : undefined;
+ const actionType = setting ? (this.isArrayType(guildSettingsObj[setting].type) ? arrayActions : actionsString) : undefined;
- const action: string = setting
+ const action: Action = setting
? yield {
id: 'action',
type: actionType,
@@ -168,21 +183,12 @@ export default class ConfigCommand extends BushCommand {
}
: undefined;
- const valueType =
- setting && action && action !== 'view'
- ? (guildSettingsObj[setting as GuildSettings].type.replace('-array', '') as 'string' | 'channel' | 'role')
- : undefined;
- const grammar =
- setting && action && action !== 'view'
- ? (action as 'add' | 'remove' | 'set') === 'add'
- ? `to the ${setting} setting`
- : (action as 'remove' | 'set') === 'remove'
- ? `from the ${setting} setting`
- : `the ${setting} setting to`
- : undefined;
+ const valueType = setting && action && action !== 'view' ? this.baseType(guildSettingsObj[setting].type) : undefined;
+
+ const grammar = this.grammar(action, setting);
const value: typeof valueType =
- setting && action && action !== 'view'
+ setting && action !== 'view'
? yield {
id: 'value',
type: valueType,
@@ -213,7 +219,7 @@ export default class ConfigCommand extends BushCommand {
if (!message.member.permissions.has(PermissionFlagsBits.ManageGuild) && !message.member?.user.isOwner())
return await message.util.reply(`${util.emojis.error} You must have the **Manage Server** permission to run this command.`);
- const setting = message.util.isSlash ? (_.camelCase(args.subcommandGroup)! as GuildSettings) : args.setting!;
+ const setting = message.util.isSlash ? (camelCase(args.subcommandGroup)! as GuildSettings) : args.setting!;
const action = message.util.isSlash ? args.subcommand! : args.action!;
const value = args.value;
@@ -230,16 +236,11 @@ export default class ConfigCommand extends BushCommand {
return val;
};
- if (!value)
+ if (!value && !(['clear', 'delete'] as const).includes(action))
return await message.util.reply(
- `${util.emojis.error} You must choose a value to ${action} ${
- action === 'add'
- ? `to the ${setting} setting`
- : action === 'remove'
- ? `from the ${setting} setting`
- : `the ${setting} setting to`
- }`
+ `${util.emojis.error} You must choose a value to ${action} ${this.grammar(action, setting)}`
);
+
switch (action) {
case 'add':
case 'remove': {
@@ -256,8 +257,21 @@ export default class ConfigCommand extends BushCommand {
msg = (await message.util.reply(messageOptions)) as Message;
break;
}
+ case 'clear': {
+ await message.guild.setSetting(setting, [], message.member);
+ const messageOptions = await this.generateMessageOptions(message, setting);
+ msg = (await message.util.reply(messageOptions)) as Message;
+ break;
+ }
+ case 'delete': {
+ await message.guild.setSetting(setting, guildSettingsObj[setting].replaceNullWith, message.member);
+ const messageOptions = await this.generateMessageOptions(message, setting);
+ msg = (await message.util.reply(messageOptions)) as Message;
+ break;
+ }
}
}
+
const collector = msg.createMessageComponentCollector({
time: 300_000,
filter: (i) => i.guildId === msg.guildId && i.message?.id === msg.id
@@ -319,38 +333,27 @@ export default class ConfigCommand extends BushCommand {
settingsEmbed.setTitle(guildSettingsObj[setting].name);
const generateCurrentValue = async (type: GuildSettingType): Promise<string> => {
const feat = await message.guild.getSetting(setting);
- let func = (v: string) => v;
- switch (type.replace('-array', '') as BaseSettingTypes) {
- case 'string': {
- func = (v: string) => util.inspectAndRedact(v);
- break;
- }
- case 'channel': {
- func = (v: string) => `<#${v}>`;
- break;
- }
- case 'role': {
- func = (v: string) => `<@&${v}>`;
- break;
- }
- case 'user': {
- func = (v: string) => `<@${v}>`;
- break;
- }
- case 'custom': {
- return util.inspectAndRedact(feat);
- }
- }
- assert(
- feat === null || typeof feat === 'string' || Array.isArray(feat),
- `feat is not a string or an array: ${util.inspect(feat)}`
- );
- assert(type !== 'custom');
+ const func = ((): ((v: string | any) => string) => {
+ switch (type.replace('-array', '') as BaseSettingTypes) {
+ case 'string':
+ return (v) => util.inspectAndRedact(v);
+ case 'channel':
+ return (v) => `<#${v}>`;
+ case 'role':
+ return (v) => `<@&${v}>`;
+ case 'user':
+ return (v) => `<@${v}>`;
+ case 'custom':
+ return util.inspectAndRedact;
+ default:
+ return (v) => v;
+ }
+ })();
return Array.isArray(feat)
? feat.length
- ? (<string[]>(<unknown[]>feat)).map(func).join('\n')
+ ? (<any[]>feat).map((v) => func(v)).join('\n')
: '[Empty Array]'
: feat !== null
? func(feat)
@@ -366,7 +369,7 @@ export default class ConfigCommand extends BushCommand {
settingsEmbed.setFooter({
text: `Run "${util.prefix(message)}${message.util.parsed?.alias ?? 'config'} ${
- message.util.isSlash ? _.snakeCase(setting) : setting
+ message.util.isSlash ? snakeCase(setting) : setting
} ${guildSettingsObj[setting].type.includes('-array') ? 'add/remove' : 'set'} <value>" to set this setting.`
});
settingsEmbed.addFields({
@@ -376,8 +379,28 @@ export default class ConfigCommand extends BushCommand {
return { embeds: [settingsEmbed], components: [components] };
}
}
-}
-type SlashArgType = 'ROLE' | 'STRING' | 'CHANNEL' | 'USER';
-type BaseSettingTypes = 'string' | 'channel' | 'role' | 'user' | 'custom';
-type Action = 'view' | 'add' | 'remove' | 'set';
+ private isArrayType(type: GuildSettingType): boolean {
+ return type.includes('-array');
+ }
+
+ private baseType(type: GuildSettingType): GuildNoArraySetting {
+ return type.replace('-array', '') as GuildNoArraySetting;
+ }
+
+ private grammar(action: Action, setting: GuildSettings) {
+ if (!setting || !action || action === 'view') return undefined;
+
+ switch (action) {
+ case 'add':
+ return `to the ${setting} setting`;
+ case 'remove':
+ return `from the ${setting} setting`;
+ case 'set':
+ return `the ${setting} setting to`;
+ case 'clear':
+ case 'delete':
+ return `the ${setting} setting`;
+ }
+ }
+}
diff --git a/src/lib/models/instance/Guild.ts b/src/lib/models/instance/Guild.ts
index 5fb507a..1a1ff35 100644
--- a/src/lib/models/instance/Guild.ts
+++ b/src/lib/models/instance/Guild.ts
@@ -11,9 +11,9 @@ export interface GuildModel {
autoPublishChannels: Snowflake[];
blacklistedChannels: Snowflake[];
blacklistedUsers: Snowflake[];
- welcomeChannel: Snowflake;
- muteRole: Snowflake;
- punishmentEnding: string;
+ welcomeChannel: Snowflake | null;
+ muteRole: Snowflake | null;
+ punishmentEnding: string | null;
disabledCommands: string[];
lockdownChannels: Snowflake[];
autoModPhases: BadWordDetails[];
@@ -23,7 +23,7 @@ export interface GuildModel {
bypassChannelBlacklist: Snowflake[];
noXpChannels: Snowflake[];
levelRoles: { [level: number]: Snowflake };
- levelUpChannel: Snowflake;
+ levelUpChannel: Snowflake | null;
}
export interface GuildModelCreationAttributes {
@@ -79,17 +79,17 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
/**
* The channels where the welcome messages are sent
*/
- public declare welcomeChannel: Snowflake;
+ public declare welcomeChannel: Snowflake | null;
/**
* The role given out when muting someone
*/
- public declare muteRole: Snowflake;
+ public declare muteRole: Snowflake | null;
/**
* The message that gets sent after someone gets a punishment dm
*/
- public declare punishmentEnding: string;
+ public declare punishmentEnding: string | null;
/**
* Guild specific disabled commands
@@ -139,7 +139,7 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
/**
* The channel to send level up messages in instead of last channel.
*/
- public declare levelUpChannel: Snowflake;
+ public declare levelUpChannel: Snowflake | null;
/**
* Initializes the model.
@@ -179,7 +179,8 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
}
export type BaseGuildSetting = 'channel' | 'role' | 'user';
-export type GuildSettingType = 'string' | 'custom' | BaseGuildSetting | `${BaseGuildSetting}-array`;
+export type GuildNoArraySetting = 'string' | 'custom' | BaseGuildSetting;
+export type GuildSettingType = GuildNoArraySetting | `${BaseGuildSetting}-array`;
export interface GuildSetting {
name: string;
@@ -187,23 +188,29 @@ export interface GuildSetting {
type: GuildSettingType;
subType: ChannelType[] | undefined;
configurable: boolean;
+ replaceNullWith: string | null;
}
-const asGuildSetting = <T>(et: { [K in keyof T]: GuildSetting }) => et;
+const asGuildSetting = <T>(et: { [K in keyof T]: PartialBy<GuildSetting, 'configurable' | 'subType' | 'replaceNullWith'> }) => {
+ for (const key in et) {
+ et[key].subType ??= undefined;
+ et[key].configurable ??= true;
+ et[key].replaceNullWith ??= null;
+ }
+ return et as { [K in keyof T]: GuildSetting };
+};
export const guildSettingsObj = asGuildSetting({
prefix: {
name: 'Prefix',
description: 'The phrase required to trigger text commands in this server.',
type: 'string',
- subType: undefined,
- configurable: true
+ replaceNullWith: client.config.prefix
},
autoPublishChannels: {
name: 'Auto Publish Channels',
description: 'Channels were every message is automatically published.',
type: 'channel-array',
- subType: [ChannelType.GuildNews],
- configurable: true
+ subType: [ChannelType.GuildNews]
},
welcomeChannel: {
name: 'Welcome Channel',
@@ -215,43 +222,33 @@ export const guildSettingsObj = asGuildSetting({
ChannelType.GuildNewsThread,
ChannelType.GuildPublicThread,
ChannelType.GuildPrivateThread
- ],
- configurable: true
+ ]
},
muteRole: {
name: 'Mute Role',
description: 'The role assigned when muting someone.',
- type: 'role',
- subType: undefined,
- configurable: true
+ type: 'role'
},
punishmentEnding: {
name: 'Punishment Ending',
description: 'The message after punishment information to a user in a dm.',
- type: 'string',
- subType: undefined,
- configurable: true
+ type: 'string'
},
lockdownChannels: {
name: 'Lockdown Channels',
description: 'Channels that are locked down when a mass lockdown is specified.',
type: 'channel-array',
- subType: [ChannelType.GuildText],
- configurable: true
+ subType: [ChannelType.GuildText]
},
joinRoles: {
name: 'Join Roles',
description: 'Roles assigned to users on join who do not have sticky role information.',
- type: 'role-array',
- subType: undefined,
- configurable: true
+ type: 'role-array'
},
bypassChannelBlacklist: {
name: 'Bypass Channel Blacklist',
description: 'These users will be able to use commands in channels blacklisted.',
- type: 'user-array',
- subType: undefined,
- configurable: true
+ type: 'user-array'
},
logChannels: {
name: 'Log Channels',
@@ -264,7 +261,6 @@ export const guildSettingsObj = asGuildSetting({
name: 'Automod Phases',
description: 'Custom phrases to be detected by automod.',
type: 'custom',
- subType: undefined,
configurable: false
},
noXpChannels: {
@@ -277,14 +273,12 @@ export const guildSettingsObj = asGuildSetting({
ChannelType.GuildNewsThread,
ChannelType.GuildPublicThread,
ChannelType.GuildPrivateThread
- ],
- configurable: true
+ ]
},
levelRoles: {
name: 'Level Roles',
description: 'What roles get given to users when they reach certain levels.',
type: 'custom',
- subType: undefined,
configurable: false
},
levelUpChannel: {
@@ -297,8 +291,7 @@ export const guildSettingsObj = asGuildSetting({
ChannelType.GuildNewsThread,
ChannelType.GuildPublicThread,
ChannelType.GuildPrivateThread
- ],
- configurable: true
+ ]
}
});