aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/config/features.ts105
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts93
-rw-r--r--src/lib/extensions/discord.js/BushGuild.ts16
-rw-r--r--src/lib/utils/BushConstants.ts2
-rw-r--r--src/listeners/client/interactionCreate.ts1
-rw-r--r--src/listeners/commands/commandError.ts2
6 files changed, 125 insertions, 94 deletions
diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts
index cb7f4bc..a694483 100644
--- a/src/commands/config/features.ts
+++ b/src/commands/config/features.ts
@@ -1,44 +1,65 @@
-// import { BushCommand, BushMessage, BushSlashMessage, guildFeatures } from '@lib';
-// import { MessageEmbed } from 'discord.js';
+import { BushCommand, BushMessage, BushSlashMessage, GuildFeatures, guildFeatures } from '@lib';
+import { Message, MessageActionRow, MessageEmbed, MessageSelectMenu, SelectMenuInteraction } from 'discord.js';
-// export default class FeaturesCommand extends BushCommand {
-// public constructor() {
-// super('features', {
-// aliases: ['features'],
-// category: 'config',
-// description: {
-// content: 'Toggle features the server.',
-// usage: 'features',
-// examples: ['features']
-// },
-// slash: true,
-// channel: 'guild',
-// clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
-// userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD'],
-// ownerOnly: true
-// });
-// }
-// public override async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
-// if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`);
-// const featureEmbed = await this.generateEmbed(message);
-// return await message.util.reply({ embeds: [featureEmbed] });
-// }
+//todo: fix this so that it doesn't just select one feature but instead toggles it
+export default class FeaturesCommand extends BushCommand {
+ public constructor() {
+ super('features', {
+ aliases: ['features'],
+ category: 'config',
+ description: {
+ content: 'Toggle features the server.',
+ usage: 'features',
+ examples: ['features']
+ },
+ slash: true,
+ channel: 'guild',
+ clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
+ userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD'],
+ ownerOnly: true
+ });
+ }
+ public override async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
+ if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`);
+ const featureEmbed = new MessageEmbed().setTitle(`${message.guild!.name}'s Features`).setColor(util.colors.default);
-// public async handleInteraction(): Promise<void> {
-
-// }
-
-// public async generateEmbed(message: BushMessage | BushSlashMessage): Promise<MessageEmbed> {
-// const featureEmbed = new MessageEmbed().setTitle(`${message.guild!.name}'s Features`).setColor(util.colors.default);
-// const featureList: string[] = [];
-// const enabledFeatures = await message.guild!.getSetting('enabledFeatures');
-// guildFeatures.forEach((feature) => {
-// featureList.push(`**${feature}:** ${enabledFeatures.includes(feature) ? util.emojis.check : util.emojis.cross}`);
-// });
-// return featureEmbed.setDescription(featureList.join('\n'));
-// }
-
-// public async generateButtons(): Promise<void>{
-
-// }
-// }
+ const enabledFeatures = await message.guild!.getSetting('enabledFeatures');
+ let featureList = guildFeatures.map(
+ (feature) => `**${feature}:** ${enabledFeatures.includes(feature) ? util.emojis.check : util.emojis.cross}`
+ );
+ featureEmbed.setDescription(featureList.join('\n'));
+ const components = new MessageActionRow().addComponents(
+ new MessageSelectMenu()
+ .addOptions(...guildFeatures.map((f) => ({ label: f, value: f })))
+ .setPlaceholder('Select A Feature to Toggle')
+ .setMaxValues(1)
+ .setMinValues(1)
+ .setCustomId('featureCommand_selectFeature')
+ );
+ const x = (await message.util.reply({ embeds: [featureEmbed], components: [components] })) as Message;
+ const collector = x.createMessageComponentCollector({
+ channel: message.channel ?? undefined,
+ guild: message.guild,
+ componentType: 'SELECT_MENU',
+ message: message as Message,
+ time: 300_000
+ });
+ collector.on('collect', async (interaction: SelectMenuInteraction) => {
+ if (interaction.user.id == message.author.id || client.config.owners.includes(interaction.user.id)) {
+ if (!message.guild) throw new Error('message.guild is null');
+ const [selected] = interaction.values;
+ if (!guildFeatures.includes(selected)) throw new Error('Invalid guild feature selected');
+ await message.guild.toggleFeature(selected as GuildFeatures);
+ const enabledFeatures = await message.guild!.getSetting('enabledFeatures');
+ featureList = guildFeatures.map(
+ (feature) => `**${feature}:** ${enabledFeatures.includes(feature) ? util.emojis.check : util.emojis.cross}`
+ );
+ featureEmbed.setDescription(featureList.join('\n'));
+ await interaction.update({ embeds: [featureEmbed] }).catch(() => undefined);
+ return;
+ } else {
+ return await interaction?.deferUpdate().catch(() => undefined);
+ }
+ });
+ }
+}
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index 3c3a9c4..26f890a 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -81,6 +81,7 @@ interface MojangProfile {
uuid: string;
}
+// #region codeblock type
export type CodeBlockLang =
| '1c'
| 'abnf'
@@ -391,6 +392,7 @@ export type CodeBlockLang =
| 'yaml'
| 'zephir'
| 'zep';
+//#endregion
/**
* {@link https://nodejs.org/api/util.html#util_util_inspect_object_options}
@@ -825,7 +827,6 @@ export class BushClientUtil extends ClientUtil {
/**
* Surrounds text in a code block with the specified language and puts it in a hastebin if its too long.
- *
* * Embed Description Limit = 4096 characters
* * Embed Field Limit = 1024 characters
*/
@@ -944,7 +945,7 @@ export class BushClientUtil extends ClientUtil {
newResponseOptions = responseOptions;
}
if (interaction.replied || interaction.deferred) {
- //@ts-expect-error: stop being dumb
+ // @ts-expect-error: stop being dumb
delete newResponseOptions.ephemeral; // Cannot change a preexisting message to be ephemeral
return (await interaction.editReply(newResponseOptions)) as Message | APIMessage;
} else {
@@ -962,10 +963,9 @@ export class BushClientUtil extends ClientUtil {
/**
* Takes an array and combines the elements using the supplied conjunction.
- *
- * @param {string[]} array The array to combine.
- * @param {string} conjunction The conjunction to use.
- * @param {string} ifEmpty What to return if the array is empty.
+ * @param array The array to combine.
+ * @param conjunction The conjunction to use.
+ * @param ifEmpty What to return if the array is empty.
* @returns The combined elements or `ifEmpty`
*
* @example
@@ -996,26 +996,20 @@ export class BushClientUtil extends ClientUtil {
return await row.save().catch((e) => client.logger.error('insertOrRemoveFromGlobal', e?.stack || e));
}
- public addOrRemoveFromArray(action: 'add' | 'remove', _array: any[], value: any): any[] {
- const array = new Array(..._array); // prevent modifying the original array
- let newValue: any[];
- if (!array) throw new Error('array is either null or undefined');
- if (action === 'add') {
- if (!array.includes(action)) array.push(value);
- newValue = array;
- } else {
- newValue = Array.from(array).filter((ae) => ae !== value);
- }
- return newValue;
+ /**
+ * Add or remove an item from an array. All duplicates will be removed.
+ */
+ public addOrRemoveFromArray(action: 'add' | 'remove', array: any[], value: any): any[] {
+ const set = new Set(...array);
+ action === 'add' ? set.add(value) : set.delete(value);
+ return [...set];
}
/**
* Surrounds a string to the begging an end of each element in an array.
- *
- * @param {string[]} array The array you want to surround.
- * @param {string} surroundChar1 The character placed in the beginning of the element (or end if surroundChar2 isn't supplied).
- * @param {string} [surroundChar2=surroundChar1] The character placed in the end of the element.
- * @returns {string[]}
+ * @param array - The array you want to surround.
+ * @param surroundChar1 - The character placed in the beginning of the element.
+ * @param surroundChar2 - The character placed in the end of the element. Defaults to `surroundChar1`.
*/
public surroundArray(array: string[], surroundChar1: string, surroundChar2?: string): string[] {
const newArray: string[] = [];
@@ -1036,8 +1030,7 @@ export class BushClientUtil extends ClientUtil {
for (const unit in BushConstants.TimeUnits) {
const regex = BushConstants.TimeUnits[unit].match;
const match = regex.exec(contentWithoutTime);
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- const value = Number(match?.groups?.[unit] || 0);
+ const value = Number(match?.groups?.[unit] ?? 0);
duration += value * BushConstants.TimeUnits[unit].value;
if (remove) contentWithoutTime = contentWithoutTime.replace(regex, '');
@@ -1365,32 +1358,32 @@ export class BushClientUtil extends ClientUtil {
return new Promise((resolve) => setTimeout(resolve, s * 1000));
}
- // modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class
- // answer by Bruno Grieder
- // public getMethods(obj: any): string {
- // let props = [];
-
- // do {
- // const l = Object.getOwnPropertyNames(obj)
- // .concat(Object.getOwnPropertySymbols(obj).map((s) => s.toString()))
- // .sort()
- // .filter(
- // (p, i, arr) =>
- // typeof obj[p] === 'function' && //only the methods
- // p !== 'constructor' && //not the constructor
- // (i == 0 || p !== arr[i - 1]) && //not overriding in this prototype
- // props.indexOf(p) === -1 //not overridden in a child
- // );
- // props = props.concat(
- // l /* .map((p) => (obj[p] && obj[p][Symbol.toStringTag] === 'AsyncFunction' ? 'async ' : '' + p + '();')) */
- // );
- // } while (
- // (obj = Object.getPrototypeOf(obj)) && //walk-up the prototype chain
- // Object.getPrototypeOf(obj) //not the the Object prototype methods (hasOwnProperty, etc...)
- // );
-
- // return props.join('\n');
- // }
+ //~ modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class
+ //~ answer by Bruno Grieder
+ //~ public getMethods(obj: any): string {
+ //~ let props = [];
+
+ //~ do {
+ //~ const l = Object.getOwnPropertyNames(obj)
+ //~ .concat(Object.getOwnPropertySymbols(obj).map((s) => s.toString()))
+ //~ .sort()
+ //~ .filter(
+ //~ (p, i, arr) =>
+ //~ typeof obj[p] === 'function' && //only the methods
+ //~ p !== 'constructor' && //not the constructor
+ //~ (i == 0 || p !== arr[i - 1]) && //not overriding in this prototype
+ //~ props.indexOf(p) === -1 //not overridden in a child
+ //~ );
+ //~ props = props.concat(
+ //~ l /* .map((p) => (obj[p] && obj[p][Symbol.toStringTag] === 'AsyncFunction' ? 'async ' : '' + p + '();')) */
+ //~ );
+ //~ } while (
+ //~ (obj = Object.getPrototypeOf(obj)) && //walk-up the prototype chain
+ //~ Object.getPrototypeOf(obj) //not the the Object prototype methods (hasOwnProperty, etc...)
+ //~ );
+
+ //~ return props.join('\n');
+ //~ }
/**
* Discord.js's Util class
diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts
index 908fcec..4fc27a7 100644
--- a/src/lib/extensions/discord.js/BushGuild.ts
+++ b/src/lib/extensions/discord.js/BushGuild.ts
@@ -20,6 +20,22 @@ export class BushGuild extends Guild {
return features.includes(feature);
}
+ public async addFeature(feature: GuildFeatures): Promise<GuildModel['enabledFeatures']> {
+ const features = await this.getSetting('enabledFeatures');
+ const newFeatures = util.addOrRemoveFromArray('add', features, feature);
+ return (await this.setSetting('enabledFeatures', newFeatures)).enabledFeatures;
+ }
+
+ public async removeFeature(feature: GuildFeatures): Promise<GuildModel['enabledFeatures']> {
+ const features = await this.getSetting('enabledFeatures');
+ const newFeatures = util.addOrRemoveFromArray('remove', features, feature);
+ return (await this.setSetting('enabledFeatures', newFeatures)).enabledFeatures;
+ }
+
+ public async toggleFeature(feature: GuildFeatures): Promise<GuildModel['enabledFeatures']> {
+ return (await this.hasFeature(feature)) ? await this.removeFeature(feature) : await this.addFeature(feature);
+ }
+
public async getSetting<K extends keyof GuildModel>(setting: K): Promise<GuildModel[K]> {
// client.console.debug(`getSetting: ${setting}`);
return (
diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts
index bcc34ae..dcc551e 100644
--- a/src/lib/utils/BushConstants.ts
+++ b/src/lib/utils/BushConstants.ts
@@ -39,7 +39,7 @@ export class BushConstants {
idleCircle: '<:idle:787550520956551218>',
onlineCircle: '<:online:787550449435803658>',
cross: '<:cross:878319362539421777>',
- check: ''
+ check: '<:check:878320135297961995>'
};
public static colors: bushColors = {
diff --git a/src/listeners/client/interactionCreate.ts b/src/listeners/client/interactionCreate.ts
index d76a484..b4cc353 100644
--- a/src/listeners/client/interactionCreate.ts
+++ b/src/listeners/client/interactionCreate.ts
@@ -28,6 +28,7 @@ export default class InteractionCreateListener extends BushListener {
if (interaction.customId.startsWith('paginate_')) return;
return await interaction.reply({ content: 'Buttons go brrr', ephemeral: true });
} else if (interaction.isSelectMenu()) {
+ if (interaction.customId.startsWith('featureCommand_')) return;
return await interaction.reply({
content: `You selected ${
Array.isArray(interaction.values)
diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts
index 029394d..f43e17d 100644
--- a/src/listeners/commands/commandError.ts
+++ b/src/listeners/commands/commandError.ts
@@ -96,7 +96,7 @@ export default class CommandErrorListener extends BushListener {
}
const inspectOptions = {
showHidden: false,
- depth: 4,
+ depth: 5,
colors: false,
customInspect: true,
showProxy: false,