Skip to content

Instantly share code, notes, and snippets.

@Phxntxm
Last active April 21, 2024 22:42
Show Gist options
  • Save Phxntxm/5027106223b366616823553cb03f4482 to your computer and use it in GitHub Desktop.
Save Phxntxm/5027106223b366616823553cb03f4482 to your computer and use it in GitHub Desktop.
A pretty simplified way to implement custom commands into your bot.
from discord.ext import commands
# The check to ensure this is the guild used where the command was made
def guild_check(_custom_commands):
async def predicate(ctx):
return _custom_commands.get(ctx.command.qualified_name) and ctx.guild.id in _custom_commands.get(ctx.command.qualified_name)
return commands.check(predicate)
class CustomCommands(commands.Cog):
""" Each entry in _custom_commands will look like this:
{
"command_name": {
guild_id: "This guild's output",
guild_id2: "This other guild's output",
}
}
"""
_custom_commands = {}
@commands.command()
async def add_command(self, ctx, name, *, output):
# First check if there's a custom command with that name already
existing_command = self._custom_commands.get(name)
# Check if there's a built in command, we don't want to override that
if existing_command is None and ctx.bot.get_command(name):
return await ctx.send(f"A built in command with the name {name} is already registered")
# Now, if the command already exists then we just need to add/override the message for this guild
if existing_command:
self._custom_commands[name][ctx.guild.id] = output
# Otherwise, we need to create the command object
else:
@commands.command(name=name, help=f"Custom command: Outputs your custom provided output")
@guild_check(self._custom_commands)
async def cmd(self, ctx):
await ctx.send(self._custom_commands[ctx.invoked_with][ctx.guild.id])
cmd.cog = self
# And add it to the cog and the bot
self.__cog_commands__ = self.__cog_commands__ + (cmd,)
ctx.bot.add_command(cmd)
# Now add it to our list of custom commands
self._custom_commands[name] = {ctx.guild.id: output}
await ctx.send(f"Added a command called {name}")
@commands.command()
async def remove_command(self, ctx, name):
# Make sure it's actually a custom command, to avoid removing a real command
if name not in self._custom_commands or ctx.guild.id not in self._custom_commands[name]:
return await ctx.send(f"There is no custom command called {name}")
# All that technically has to be removed, is our guild from the dict for the command
del self._custom_commands[name][ctx.guild.id]
await ctx.send(f"Removed a command called {name}")
def setup(bot):
bot.add_cog(CustomCommands(bot))
@Phxntxm
Copy link
Author

Phxntxm commented Dec 6, 2019

Note: There is one issue that this will encounter that I am aware of. If someone adds a custom command, and you later load a cog (or add a command any other way) that has a command with that same name...that cog will fail to load, as there's already a command registered with that name.

I can't figure out how to solve this, so just noting it here as a warning in case anyone uses this

@Eraldo
Copy link

Eraldo commented Feb 26, 2020

How about forcing a different prefix for custom commands created that way?

@Phxntxm
Copy link
Author

Phxntxm commented Feb 26, 2020

If that's in direct response to the issue I mentioned in the comment above, it wouldn't fix the issue. Commands are registered and added to the bot with the name of the command, the prefix is not relevant in any way during this step...it is only used during actual processing of a message.

@z3r0xxx
Copy link

z3r0xxx commented Mar 30, 2020

How can i paste info about custom commands in database?

@Phxntxm
Copy link
Author

Phxntxm commented Jun 29, 2020

How can i paste info about custom commands in database?

Never saw this, sorry. That CustomCommands._custom_commands is the thing that holds all the information needed to create the custom commands. Save that information in the database, on startup do essentially what is happening in the add_command method.

@Zet0x0
Copy link

Zet0x0 commented Aug 4, 2020

hey, getting an error: [CheckFailure] The check functions for command ok failed., how can i solve that?

@Phxntxm
Copy link
Author

Phxntxm commented Aug 4, 2020

hey, getting an error: [CheckFailure] The check functions for command ok failed., how can i solve that?

You don't? That is saying that the check failed, have a look at what the check is doing on line 5... if it does not match the guild the custom command was set up in, then it is supposed to fail

@lakshaycodes
Copy link

Can anybody tell how can I add this into my code?

@IBgreat1
Copy link

IBgreat1 commented Dec 9, 2020

Can anybody tell how can I add this into my code?

You can make a Cogs file and Make a file for Custom Commands and Put this code there and in your bots ' on_ready event you can load the cog.

@IBgreat1
Copy link

IBgreat1 commented Dec 9, 2020

The Code is really good and its useful , I wanted to know How will I use Custom Arguments for this , Like a Member Convertor or a Role Convertor or like Number of Normal Arguments .

@Phxntxm
Copy link
Author

Phxntxm commented Dec 9, 2020

The Code is really good and its useful , I wanted to know How will I use Custom Arguments for this , Like a Member Convertor or a Role Convertor or like Number of Normal Arguments .

This gets a bit complicated if you want it to be dynamic.

            @commands.command(name=name, help=f"Custom command: Outputs your custom provided output")
            @guild_check(self._custom_commands)
            async def cmd(self, ctx):
                await ctx.send(self._custom_commands[ctx.invoked_with][ctx.guild.id])

This line is where the command itself is defined, you're going to have to do some funky stuff if you want a user to somehow be able to arguments to the add_command command to specify what kind of arguments you want. For something like this, I would recommend the extension discord.ext.flags with nargs="+" for converters and arguments. Note... it is complicated, that's gonna take some work to make it actually function.

If you don't want it to be dynamic, the custom commands only take whatever specific arguments you want them to always have then that's easy. Just change the async def cmd(self, ctx): line to have arguments/converters just like any normal command.

@toseev
Copy link

toseev commented Oct 22, 2021

I'm sorry, but how i can get list of all custom commands for help command?

@Phxntxm
Copy link
Author

Phxntxm commented Oct 22, 2021

I'm sorry, but how i can get list of all custom commands for help command?

By default this implementation has the commands already show up in the "custom command" category in the help command (that's actually the entire purpose of this slightly complicated implementation - to have them automatically work with the help command). There should be nothing special you have to do. If you're doing a help command from scratch then that's not the recommended way. I'd recommend going over this walk through.

If for some reason though you are doing something abnormal with the help command, and still need the custom commands directly - then the ._custom_commands attribute on the cog instance can be used.

@aepmqr
Copy link

aepmqr commented Oct 30, 2021

im having a error whenever i run the cmd: discord.ext.commands.errors.CommandNotFound: Command "add" is not found

@Phxntxm
Copy link
Author

Phxntxm commented Nov 2, 2021

im having a error whenever i run the cmd: discord.ext.commands.errors.CommandNotFound: Command "add" is not found

There is no command called "add" in this gist. It's called "add_command" just as an example, if you want to name it something different you'll have to understand how the commands extension works. Documentation can be found here:

https://discordpy.readthedocs.io/en/stable/ext/commands/api.html
"Subcommands" probably make sense for this, but this gist is purely an example - you're meant to customize it to your liking

@benthetechguy
Copy link

This is no longer compatible with discord.py 2.0 and later. See this forum post in the discord.py server for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment