Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Prefix Command Management #85

Open
wants to merge 51 commits into
base: staging
Choose a base branch
from

Conversation

pdellaert
Copy link
Collaborator

Prefix Command Management

Description

Please read https://github.com/pdellaert/fbw-discord-bot-utils/blob/dynamic-command-management/docs/prefix-commands.md for all the fancy details.

NOTE: The cache refresh system will be rewritten to be safer, right now it isn't perfectly safe.

Test Results

I don't know how many hours, but probably good someone takes their own look...

Discord Username

straks

@pdellaert pdellaert self-assigned this Oct 14, 2024
@pdellaert pdellaert marked this pull request as ready for review October 15, 2024 06:34
Copy link
Member

@benw202 benw202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good work! I like the way this has been done.

src/commands/utils/prefixHelp.ts Outdated Show resolved Hide resolved
src/commands/utils/prefixHelp.ts Outdated Show resolved Hide resolved
src/events/messageCreateHandler.ts Outdated Show resolved Hide resolved
src/events/messageCreateHandler.ts Outdated Show resolved Hide resolved
src/events/messageCreateHandler.ts Outdated Show resolved Hide resolved
@benw202
Copy link
Member

benw202 commented Oct 16, 2024

Some further comments as discussed:

  • When modifying a command name, the mod log embed doesn't show the command description
  • MongoDB __V not incrementing on an update - intentional?
  • On timeout of modal it says "You did not provide the necessary content information and the change was not made.". The user may have provided info, but not in time. Is it worth saying that there is a two minute timeout somewhere in the modal description?

@benw202
Copy link
Member

benw202 commented Oct 16, 2024

Moderator in Prefix Command Cache Update is not a clickable tag. Could make this clickable if we want it to match other mod logs?

@pdellaert
Copy link
Collaborator Author

Addressed the review comments, will look at

  • MongoDB __V not incrementing on an update - intentional?
  • On timeout of modal it says "You did not provide the necessary content information and the change was not made.". The user may have provided info, but not in time. Is it worth saying that there is a two minute timeout somewhere in the modal description?

Also likely to hide the choice buttons if it is a "fallback-to-GENERIC" if a different unavailable version is requested (either explicit, or by channel default)

@pdellaert
Copy link
Collaborator Author

  • On timeout of modal it says "You did not provide the necessary content information and the change was not made.". The user may have provided info, but not in time. Is it worth saying that there is a two minute timeout somewhere in the modal description?

Can this be done? I don't see a way to add a description to the Modal? Doesn't look like we do it anywhere?

@benw202
Copy link
Member

benw202 commented Oct 18, 2024

  • On timeout of modal it says "You did not provide the necessary content information and the change was not made.". The user may have provided info, but not in time. Is it worth saying that there is a two minute timeout somewhere in the modal description?

Can this be done? I don't see a way to add a description to the Modal? Doesn't look like we do it anywhere?

You're right! I saw descriptions somewhere but seems it was only a concept. I guess we will just have to remember that we have a two minute limit. Make sure that people who can add and modify commands write their commands out elsewhere and just copy/paste in.

@ExampleWasTaken
Copy link
Contributor

I just gave this a test and thought I'd share what I found. I have not reviewed the code yet because I wanted to understand the functionality first.

All was tested on e95eae2.

To get the important stuff right out of the way:

  • The bot crashes when trying to set content for an invalid/non-existing version

The rest are just thoughts I had while messing about.

When a custom version is set as a channel default and that version is disabled, nothing is shown.

Example setup with a32nx and a380x being custom versions of the .bg command:

a380x is disabled but set as default version for channel A:

Running .bg results in nothing being shown. IMO this should result in the GENERIC version being shown. The presence of the a380x is not leaked as showing the GENERIC version is the default behavior anyway. Additionally, the way it currently works, there is not way to get a list of all available versions.

Commands without content?

Is there any reason why commands can be "empty" i.e. have no content set? IMO there should at least be a GENERIC version of every command.
This will

  1. ensure that users are able to retrieve a list of all available versions
  2. prevents "ghost" commands that exist but have no content and therefore no use

I might be missing something here but I didn't find an explanation for this and thought I'd raise it. :)

Consistent slash command

As it stands right now there are three different slash-commands for this feature:

  • /prefix-commands
  • /prefix-command-permissions
  • /prefix-help
  • /prefix-commands-cache-update

I think this is pretty inconsistent and confusing to remember (was it /prefix-commands-permissions, /prefix-command-permission or /prefix-command-permissions?).
Instead, I suggest we use a common slash-command /prefix and have everything else be sub commands.

Example:

  • /prefix categories <...>
  • /prefix versions <...>
  • /prefix permissions <...>
  • /prefix help <...>
  • /prefix cache <...>

This way, the user can explore all available commands in the Discord client by just typing /prefix . It also reduces the overall slash-commands used and provides a cleaner command structure imo.


I think overall this is looking pretty awesome! Really nice work!

@ExampleWasTaken
Copy link
Contributor

Another bug I just found:

Aliases are not unique against command names and aliases of other commands.

Example:

These can all exist at the same time.

Command 1

  • Name: beginners_guide
  • Alias: bg

Command 2

  • Name: bg

Command 3

  • Name: test
  • Alias: bg

This results in undefined behavior as there are three possible commands that would all fulfil the conditions to be shown.

@pdellaert
Copy link
Collaborator Author

pdellaert commented Oct 20, 2024

Bunch of changes, most are bug fixes and minor enhancements to make sure that there's no overlap in commands, aliases & versions. Also no empty contents.

Feedback on commands without content: Can't really do anything about it. I can't force setting a GENERIC content, unless we do it during the creation of the command, but would require a bit more work than we have time for right now.

Feeback on different slash commands: As discussed in the channel, Discord has a max length on command definition and subgroup/commands, we would be exceeding it if we put it in one.

Changes since my last comment (4 days ago) pdellaert/fbw-discord-bot-utils@e95eae2...dynamic-command-management

@ExampleWasTaken
Copy link
Contributor

That sounds awesome!

Regarding the commands without content, I think that something we can introduce down the line if we want to. Definitely not a showstopper.

@benw202
Copy link
Member

benw202 commented Oct 21, 2024

That sounds awesome!

Regarding the commands without content, I think that something we can introduce down the line if we want to. Definitely not a showstopper.

Agree on this. There will be a limited amount of people who can add commands so the likelihood of us having rogue empty commands should be none.

* Allowing empty title
* Fixing messageCreateHandler in case of unset permissions on command
*
* Showing buttons for disabled versions on generic content
* Not showing anything if channel default is disabled
* Not properly checking commands and versions
* Not prorperly using defer/reply/followUp
…nds. And version aliases do not overlap with commands
Copy link
Contributor

@ExampleWasTaken ExampleWasTaken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is outstanding work! The way this is thought through and structured is more than impressive!

I've said a few things about it on Discord already so here just the most important stuff:

Currently, all commands that list something (version, categories, commands) will fail when there are more than 25 items to list. This is due to Discord's limitation of max 25 fields per embed. I think these commands migth need a paginated embed.

Absolutely awesome stuff!

Comment on lines +67 to +68
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
let modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS);
if (!modLogsChannel || !modLogsChannel.isTextBased()) {
modLogsChannel = null;

Might be safer to actually check if the channel is a text channel? Especially if we want to offer the codebase to other projects. Then, if it's not a text channel set it to null to prevent accidental attempts to send into a non-text channel.


const doesNotExistsEmbed = (category: string) => makeEmbed({
title: 'Prefix Commands - Modify Category - Does not exist',
description: `The prefix command category ${category} does not exists. Can not modify it.`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: `The prefix command category ${category} does not exists. Can not modify it.`,
description: `The prefix command category ${category} does not exists. Cannot modify it.`,

"Cannot" is generally more common. I suggest we use that but obviously that's personal preference and I'm happy to leave it as is!

Comment on lines +68 to +69
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
let modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS);
if (!modLogsChannel || !modLogsChannel.isTextBased()) {
modLogsChannel = null;

Might be safer to actually check if the channel is a text channel? Especially if we want to offer the codebase to other projects. Then, if it's not a text channel set it to null to prevent accidental attempts to send into a non-text channel.


const doesNotExistsEmbed = (category: string) => makeEmbed({
title: 'Prefix Commands - Delete Category - Does not exist',
description: `The prefix command category ${category} does not exists. Can not delete it.`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: `The prefix command category ${category} does not exists. Can not delete it.`,
description: `The prefix command category ${category} does not exists. Cannot delete it.`,

"Cannot" is generally more common. I suggest we use that but obviously that's personal preference and I'm happy to leave it as is!

Comment on lines 69 to 70
await interaction.followUp({ embeds: [noModLogs], ephemeral: true });
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await interaction.followUp({ embeds: [noModLogs], ephemeral: true });
return;
await interaction.followUp({ embeds: [noModLogs], ephemeral: true });

As discussed, this is just a warning and shouldn't block the creation.

Comment on lines +235 to +238
for (const category of PrefixCommandCategories) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandCategoryToCache(category);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (const category of PrefixCommandCategories) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandCategoryToCache(category);
}
await Promise.all(PrefixCommandCategories.map((category) => loadSinglePrefixCommandCategoryToCache(category)));

Nerd level increasing... xD

Comment on lines +277 to +280
for (const dbCategory of prefixCommandCategories) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandCategoryToCache(dbCategory);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (const dbCategory of prefixCommandCategories) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandCategoryToCache(dbCategory);
}
await Promise.all(prefixCommandCategories.map((dbCategory) => loadSinglePrefixCommandCategoryToCache(dbCategory)));

Awaiting all the promises...

Comment on lines +315 to +318
for (const defaultVersion of PrefixCommandChannelDefaultVersions) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandChannelDefaultVersionToCache(defaultVersion);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (const defaultVersion of PrefixCommandChannelDefaultVersions) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandChannelDefaultVersionToCache(defaultVersion);
}
await Promise.all(
PrefixCommandChannelDefaultVersions
.map((defaultVersion) => loadSinglePrefixCommandChannelDefaultVersionToCache(defaultVersion)),
);

Can't wait till prettier does the formtting for us...

Comment on lines +352 to +355
for (const dbChannelDefaultVersion of prefixCommandChannelDefaultVersions) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandChannelDefaultVersionToCache(dbChannelDefaultVersion);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (const dbChannelDefaultVersion of prefixCommandChannelDefaultVersions) {
// eslint-disable-next-line no-await-in-loop
await loadSinglePrefixCommandChannelDefaultVersionToCache(dbChannelDefaultVersion);
}
await Promise.all(
prefixCommandChannelDefaultVersions
.map(
(dbChannelDefaultVersion) => loadSinglePrefixCommandChannelDefaultVersionToCache(dbChannelDefaultVersion),
),
);

Even longer lmao. But I'm always a fan of descriptive names so I rather take that than guessing what a function may do 👍.

Comment on lines +27 to +30
await refreshAllPrefixCommandVersionsCache();
await refreshAllPrefixCommandCategoriesCache();
await refreshAllPrefixCommandsCache();
await refreshAllPrefixCommandChannelDefaultVersionsCache();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could potentially be refreshed concurrently using Promise.all(), but it is unclear to me if, and if so, how versions and default channel version are dependent on each other. It may be simpler to avoid race conditions and update them sequentially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants