Writing a function for custom subcommands discord.py - python

So, I want to write a function for all subcommands for my python bot but I'm not sure how they work in discord.py. For instance, I have this mute code here.
#bot.command()
async def mute(ctx,member : discord.Member, *, reason=None):
with open("role.json", "r") as f:
roles = json.load(f)
muted_role = discord.utils.get(ctx.guild.roles, id=roles[f"{ctx.guild.id}"]["role_id"])
embed = discord.Embed(color=discord.Color(0xFF0000), title=f"{member} has been muted.")
#sent to DM
embedC=discord.Embed(title=f"{member} has been muted.",color=discord.Color(0xff0000))
embedC.add_field(name=f"Member",value=f"{member}",inline=False)
embedC.add_field(name=f"Moderator",value=f"{ctx.author}",inline=False)
embedC.add_field(name=f"Reason",value=f"{reason}",inline=False)
embedC.set_footer(text=f"Mute was successfully invoked. Expiration is indefinite.")
await member.add_roles(muted_role)
await ctx.send(embed=embed)
await member.send(embed=embedC)
In the code above is my mute command. I know this is a bit irrelevant to my question but I want to make a custom subcommand for bot. You can remove built in command help by doing bot bot.remove_command("help") but how do I remove custom help subcommand? Now this leads to the second part of the question, which is also the main question. How do I write a function for custom subcommand so I won't have to write and repeat the same process over for every subcommand?
I tried doing def subcommand(command, error) where the command is the command needed for help and error is the error message I want to put. So in case my mute command fails, it will ask user to use my subcommand and my subcommand will provide them with information on how this command works. Thank you!

There's an built-in subcommand support:
#bot.group()
async def cmd(ctx):
if ctx.invoked_subcommand is None:
await ctx.send("Command can't be invoked without a subcommand")
#cmd.command()
async def subcommand1(ctx, *args):
#...
#cmd.command()
async def subcommand2(ctx, *args):
#...
To invoke:
{prefix}cmd subcommand1 arg1 arg2
Docs

Related

Discord Python cog_check issue

New to coding. I want to prevent my bot from responding to itself/other bots, and I'm trying to invoke a cog_check but can't make it work properly. Help would be greatly appreciated.
class CommandsMisc(commands.Cog):
def __init__(self, client):
self.client = client
async def cog_check(self, ctx):
if ctx.author.bot: # also tried: if ctx.author == self.client.user:
await exit()
#commands.Cog.listener("on_message") # example cog command
async def print(self, message):
if '!guide' in message.content:
await message.reply(Guide_Text)
else:
pass
Since some people seem to lack reading comprehension skills, my question is how can I best utilize "cog_check" in Discord.py so that all subsequent commands/listeners in the cog will check whether the message.author is the bot and then won't execute if it is?
It seems like what you want is a custom command decorator. You can do this as follows:
def is_not_bot():
async def predicate(ctx):
return not ctx.author.bot
return commands.check(predicate)
You place this in your cog class, then on any command inside the cog you can use the decorator #is_not_bot()
What does this do, exactly? Well, we create a function which can be used as a command decorator (#is_not_bot()). Inside this, we create a function called predicate. This predicate will return True if the user is not a bot. We then call return commands.check(predicate) inside the parent function. This will allow us to use this as a decorator.
discord.py docs reference
what you can have is a on_message event, which can do checks on the message before processing the command, something like this
bot = commands.Bot(command_prefix="!", intents=discord.Intents.all())
# or if you made your subclass of Bot or its variants
class CustomBot(commands.Bot):
def __init__(self):
...
bot = CustomBot()
#bot.event
async def on_message(message):
if message.author.bot:
return # this is a bot, so we can early exit
# any other checks
await bot.process_commands(message)
exit() will just make your entire program close. cog_check should return a Boolean that indicates if the command should run or not. In your case, you don't return anything if it's okay, so it returns None which is falsey, so your check always fails.
Make it return True if it should succeed, and False if it should fail.
Edit
The ext.commands framework already prevents bots from running your commands, so this is not something you should even have to worry about.
The reason your cog_check still doesn't do anything is because you don't have any commands. You're using an on_message listener to manually parse messages, instead of creating commands. Your "example cog command" is not a command, it's just an event listener. Those are two very different concepts.
cog_check is only invoked when actual commands are run, not before dispatching events like on_message, so this doesn't have any effect here. If you parse commands in on_message, checks aren't used at all.
See the docs for ext.commands for how to create commands: https://discordpy.readthedocs.io/en/stable/ext/commands/index.html

Can an event execute a command? If so, how can I make my one do so?

So I am trying to have an event where, once the user types a specific word (not a command, literally a word/string), the event will trigger an existing command. Yeah you may wonder "Why not have the user type the command itself?" well, the reason why this isn't the case it's kinda hard to explain. Check this out:
My event will work only if the person types "nothing" (literally the word nothing). Eventually, the person won't expect the bot to actually take this as a command, and so he/she won't type it as command (with the prefix and all that) This is my code:
#client.command()
async def menu(ctx)
#here, well, goes what I want the command to do but it's not the issue
#client.event
async def on_message(message):
if message.content.startswith("nothing"):
#here idk how to execute the command up there. That's my question
I hope I am being clear with my issue. Don't worry about what the command exectues, or why the message for the event is "nothing". I just really want to know how to make this work.
Some friends suggested me to invoke the command, but I didn't really know how to do that, and everytime I would try it wouldn't work. Others suggested to call the function, but I also tried that and it wouldn't work. I don't know if I typed it correctly or if it simply won't work. I hope someone helps me out here.
Thanks in advance.
get_context, this takes a message object. Then invoke. Keep in mind, there are 3 downsides to using this method.
The converters (the type hints) won't be triggered. You need to pass the correct types into the arguments.
Checks will be bypassed. You could invoke an owner only command with a non-owner and it would still work.
If you still want to run all the checks, see can_run, which will run all the checks and raise an error if any checks fail.
If ctx.invoke was invoked outside of a command (eg eval), the error handler won't fire.
#client.command()
async def menu(ctx):
await ctx.send("Hello")
#client.event
async def on_message(message):
if message.content.startswith("nothing"):
ctx = await client.get_context(message)
await ctx.invoke(menu)
await client.process_commands(message)
If your client is a Bot instance you can use Bot.get_context() to create your own context and invoke the command from there:
import discord
from discord.ext import commands
bot = commands.Bot(command_prefix='!')
#bot.command()
async def menu(ctx):
await ctx.send('bar')
#bot.event
async def on_message(message):
if message.content.startswith('foo'):
ctx = await bot.get_context(message, cls=commands.Context)
ctx.command = bot.get_command('menu')
await bot.invoke(ctx)
await bot.process_commands(message)

How do I resend user input after a command (e.g. User: !send 1234 Bot: 1234) in discord.py?

I have recently started programming my own discord bot as many other people are doing...
So far what I have got is this:
#bot.command
async def on_message(message):
if message.content.startswith("t!send"):
await client.send_message(message.content)
It doesn't crash but it doesn't work either...
It seems as if you're using a tutorial for an old version of discord.py.
There are some big changes in the most recent version - rewrite. Please take some time to look for more updated tutorials, or read the documentation linked above.
Here are the cases for using both the on_message event and commands:
#bot.command()
async def send(ctx, *, sentence):
await ctx.send(sentence)
######################################
#bot.event
async def on_message(message):
args = message.content.split(" ")[1:]
if message.content.startswith("t!send"):
await message.channel.send(" ".join(args))
else:
await bot.process_commands(message) # only add this if you're also using command decorators
References:
commands.Context() - if not familiar with using command decorators
discord.on_message()
Bot.process_commands()
So first of all, on_message doesn't belong here and you also don't have to use it. (on_message() would use the decorator #bot.event) Assuming you have setup a prefix for your bot using bot = commands.Bot(command_prefix = 't!') you could do something like this:
#bot.command()
async def send(ctx, *args):
message = " ".join(args)
await ctx.send(message)
*args is everything the user types in after t!send. So for example: if the user types in t!send Hello world! args would be ["Hello", "world!"]. With .join we can join them together into a single string (message)

Place the default help command of a Discord bot in a category (Python)

When I used the default help command, it displayed all my commands categorically except for the help command which was under No Category. How do I add this to a cog?
This might not be the best solution, but you can remove the 'help' command, and re-add the 'help' command within your cog, since discord.py does not allow you to change a command's cog_name. The default help command is stored commands.bot._default_help_command.
from discord.ext import commands
bot = commands.Bot('.')
bot.remove_command('help')
class ACog:
#commands.command(pass_context=True)
async def help(self, ctx, *args: str):
"""Shows this message."""
return await commands.bot._default_help_command(ctx, *args)
bot.add_cog(ACog())
bot.run('TKOEN')

Discord Bot Kick Command

how i can set kick command with a role use just that Moderator role will can use
my kick command :
#client.command(pass_context = True)
async def kick(ctx, userName: discord.User):
"""Kick A User from server"""
await client.kick(userName)
await client.say("__**Successfully User Has Been Kicked!**__")
You can use the commands.has_permissions decorator to ensure the caller has a specific permission.
#client.command(...)
#commands.has_permissions(kick_members=True)
async def kick(ctx, ...):
pass
Just a word of warning though, according to the function docstring, it checks for the user having any required permission instead of all.
It is also recommended to add the bot_has_permissions check too to make sure it can actually kick users too.

Categories

Resources