So, I was wondering if there could be a command I could write that allows me to set the bots presence and activity (ex. ~~set presence idle or ~~set activity watching "people typing ~~help") or something like that.
Unrelated question: How do I set commands to be used by me only?
I haven't found any example code for this, and i'm a beginner.
You can use the is_owner check to ensure that you are the only person who can invoke a command.
To change the presence or status of the bot, use the change_presence method:
from discord.ext.commands import Bot, is_owner
from discord import Status, Activity, ActivityType
bot = Bot("~~")
def getEnum(enum):
def getter(arg):
return enum[arg]
return getter
#bot.group(invoke_without_command=True)
#is_owner()
async def set(ctx):
await ctx.send("You must provide a subcommand.")
#set.command()
async def presence(ctx, status: getEnum(Status)):
await bot.change_presence(status=status)
#set.command(invoke_without_command=True)
async def activity(ctx, type: getEnum(ActivityType), *, description):
await bot.change_presence(activity=Activity(type=type, name=description))
#set.error
async def set_error(ctx, error):
if isinstance(error, BadArgument):
await ctx.send(error.message)
await ctx.send(error.args)
bot.run("token")
The above will fail silently if you try to provide an unrecognized name to Status or ActivityType, you could also try writing an error handler to provide some feedback.
Related
so i was trying to create my first discord bot, with no experience in Python whatsoever just to learn stuff by doing so and stumbled on an Error (Title) which was answered quiet a lot, but didn't really help me.
Im just gonna share my code:
import discord
from discord.ext import commands
import random
client = commands.Bot(command_prefix="=", intents=discord.Intents.all())
#client.event
async def on_ready():
print("Bot is connected to Discord")
#commands.command()
async def ping(ctx):
await ctx.send("pong")
client.add_command(ping)
#client.command()
async def magic8ball(ctx, *, question):
with open("Discordbot_rank\magicball.txt", "r") as f:
random_responses = f.readlines()
response = random.choice(random_responses)
await ctx.send(response)
client.add_command(magic8ball)
client.run(Token)
I tried to run this multiple times, first without the client.add_command line, but both methods for registering a command didn't work for me. I also turned all options for privileged gateway intents in the discord developer portal on. What am i doing wrong?
Because you have an incorrect decorator for your command ping. It should be #client.command() in your case.
Also, in most of the cases, you don't need to call client.add_command(...) since you use the command() decorator shortcut already.
Only when you have cogs, you will use #commands.command() decorator. Otherwise, all commands in your main py file should have decorator #client.command() in your case.
A little bit of suggestion, since you already uses commands.Bot, you could rename your client variable to bot since it is a little bit ambiguous.
You have to use Slash commands cause user commands are no longer available
Example code using Pycord library
import discord
bot = discord.Bot()
#bot.slash_command()
async def hello(ctx, name: str = None):
name = name or ctx.author.name
await ctx.respond(f"Hello {name}!")
bot.run("token")
I have imported discord.ext and discord.ext.commands
I have defined the owner_id attribute when defining my client variable
#client.group(aliases=['owncmds'])
#commands.is_owner()
async def ownercmds(ctx):
if ctx.invoked_subcommand is None:
em = discord.Embed(title="Hello, owner.",description="Here are the commands that only you can use!",color=discord.Colour.blue())
em.add_field(name="b?clearconsole",value="Clears the console.")
await ctx.send(embed=em)
When I run the command, it doesn't give me a response, nor an error.
Yes,
After a bit of testing, it does work, this is the response I got after copying your code and pasting it to a bot: The working embed from the command that is specified in the question:
If this does not work for you, you could just use an if statement to check whether the id is equal to yours (if you are the owner), to add this if statement to the specified command it would be:
#client.group(aliases=['owncmds'])
async def ownercmds(ctx):
if ctx.author.id = "add your id here without the quotes":
if ctx.invoked_subcommand is None:
em = discord.Embed(title="Hello, owner.",description="Here are the commands that only you can use!",color=discord.Colour.blue())
em.add_field(name="b?clearconsole",value="Clears the console.")
await ctx.send(embed=em)
I am implementing error messages into my Discord.py bot, where am using cogs to implement commands.
When a user incorrectly uses a command, for example passes no arguments into a command that requires them, I would like the bot to inform them of the correct usage of that specific command.
For example, here I have a simple cog test.py;
from discord.ext import commands
class Test(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
async def test_command(self, ctx, arg1: str, arg2: int):
msg = f"{arg1} and {arg2 + 5}"
await ctx.reply(msg)
def setup(client):
client.add_cog(Test(client))
If the user uses the command incorrectly, for example types !test_command foo, I would like the bot to return a message along the lines of
Correct usage: !test_command <arg1> <arg2>
How would I go about doing this? I would like to do it procedurally, and not have to pick from a list of pre-written usage help messages for each command.
Thanks in advance.
EDIT:
Please note I already have the error checking logic in place. I am asking - in the event of an error triggering - how to automatically generate a message to inform the user on how to use the command.
You're looking for Command.signature. This will automatically give you the usage of the command. Just use it in conjunction with the on_command_error and the MissingRequiredArgument error.
Example:
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f"Correct Usage: {ctx.prefix}{ctx.command.name} {ctx.command.signature}")
This will return Correct Usage: !test_command <arg1> <arg2>
You can use try except
#commands.command()
async def test_command(self, ctx, arg1: str, arg2: int):
try:
msg = f"{arg1} and {arg2 + 5}"
await ctx.reply(msg)
except:
await ctx.send('Correct usage: !test_command <arg1> <arg2>')
I see a few errors in your code which might stop the bot from working.
First) Why do you use commands.Cog()? It should be client.command/commands.command. The commands just inherite from commands.Cog
Second) The reply method is a bit deprecated. Instead we use await ctx.send()
Now, to include an error code you have to include your own handler for it.
Have a look at the following code:
#commands.Cog.listener
async def on_command_error(self, ctx, error):
try:
if isinstance(error, commands.CommandNotFound):
pass
elif isinstance(error, commands.MissingRequiredArgument):
await ctx.send(str(error))
except Exception as error:
pass
This error handler informs you about the required arguments and mentions them.
I read the answer here and tried to adapt it for my own usage:
How to add a function to discord.py event loop?
My situation is as follows:
I'm developing a discord bot in which I have some commands users can run. These commands have a possibility of erroring, in which case I do client.loop.create_task(errorReportingFunction()) which reports the error to me by messaging me. I also have a command which uses asyncio.create_task() instead.
However I'm having memory leaks which vary from mild to crashing the bot after prolonged usage which leads me to think that I'm not using the tasks system correctly. Should I be cleaning up after the tasks I create and deleting them somehow after I'm done using them? Or is there a system that does that automatically.
I'm also not sure how asyncio.create_task() and client.loop.create_task() differ so I'd appreciate some advice on which to use when, or if they're basically the same.
I believe that what you want is to do something (for example sending a message) when an error happens. If so, there are better ways to handle this in discord.py instead of creating tasks.
In the case that you want to control this only for a certain function, you could create an error handler to track the error raised from that function and send you a message whenever one happens:
#bot.command()
async def info(ctx, *, member: discord.Member):
"""Tells you some info about the member."""
fmt = '{0} joined on {0.joined_at} and has {1} roles.'
await ctx.send(fmt.format(member, len(member.roles)))
#info.error
async def info_error(ctx, error):
if isinstance(error, commands.BadArgument):
await ctx.send('I could not find that member...')
# Send a message to the bot owner about the error
await self.bot.get_user(self.bot.owner_id).send('There has been an error in the command')
Although a common practice in discord bots is to have an error handling cog, which would allow you to centralize all the error handling in a single function. It could be something like this:
class ErrorCog(commands.Cog, name='Error'):
'''Cog in charge of the error handling functions.'''
def __init__(self, bot):
self.bot = bot
#commands.Cog.listener()
async def on_command_error(self, ctx, error):
'''Event that takes place when there is an error in a command.
Keyword arguments:
error -- error message '''
error = getattr(error, 'original', error)
# Wrong command
if isinstance(error, commands.CommandNotFound):
message = 'This is not a valid command'
return await ctx.send(message)
# Command is on cooldown
elif isinstance(error, commands.CommandOnCooldown):
if ctx.author.id is self.bot.owner_id:
ctx.command.reset_cooldown(ctx)
return await ctx.command.reinvoke(ctx)
cooldowns = {
commands.BucketType.default: f'for the whole bot.',
commands.BucketType.user: f'for you.',
commands.BucketType.guild: f'for this server.',
commands.BucketType.channel: f'for this channel.',
commands.BucketType.member: f'cooldown for you.',
commands.BucketType.category: f'for this channel category.',
commands.BucketType.role: f'for your role.'
}
return await ctx.send(f'The command `{ctx.command}` is on cooldown {cooldowns[error.cooldown.type]} ')
# Bot lacks permissions.
elif isinstance(error, commands.BotMissingPermissions):
permissions = '\n'.join([f'> {permission}' for permission in error.missing_perms])
message = f'I am missing the following permissions required to run the command `{ctx.command}`.\n{permissions}'
try:
return await ctx.send(message)
except discord.Forbidden:
try:
return await ctx.author.send(message)
except discord.Forbidden:
return
# Here you need to add more instances
# of errors according to your needs
def setup(bot):
bot.add_cog(ErrorCog(bot))
I am working on a discord bot for basic moderation which does kick, ban and mute for now at least. But the problem is other members can use it too. I want only a few specified role who can use it.
Don't want to work on it depending on the #role either because the name of the roles across different servers aren't the same. Also wanting to keep the bot as simple as possible.
Now, I started out as this:
#client.command(name='ban')
async def mod_ban(member: discord.User):
try:
await client.ban(member, delete_message_days=0)
await client.say('**{0}** has been banned.'.format(str(member)))
except Exception as error:
await client.say(error)
But any member can use the commands then. So, tried follow this one = Permission System for Discord.py Bot and ended up with this:
#client.command(name='ban')
async def mod_ban(context, member: discord.User):
if context.message.author.server_premission.administrator:
try:
await client.ban(member, delete_message_days=0)
await client.say('**{0}** has been banned.'.format(str(member)))
except Exception as error:
await client.say(error)
else:
await client.say('Looks like you don\'t have the perm.')
Which lands me with this error: ;-;
raise MissingRequiredArgument('{0.name} is a required argument that is missing.'.format(param))
discord.ext.commands.errors.MissingRequiredArgument: member is a required argument that is missing.
Also, besides context.message.author.server_premission.administrator I don't only want the roles with Admin perm to use this command. I also want a few other roles with have few perms like manage message, manage roles etc. to use to command too.
Thanks in advance for the help! Also, sorry if I've missed anything stupid or silly ;-;
You aren't passing the context into the coroutine in your second example (and as #Andrei suggests, you can only ban members):
#client.command(name='ban', pass_context=True)
async def mod_ban(context, member: discord.Member):
...
Also, I should probably update my answer to that question. In the context of commands, you can use the very powerful checks built into discord.ext.commands to do a lot of this for you. has_permissions does exactly what you're looking for, validating that the user has any of the necessary permissions.
from discord.ext.commands import has_permissions, CheckFailure
#client.command(name='ban', pass_context=true)
#has_permissions(administrator=True, manage_messages=True, manage_roles=True)
async def mod_ban(ctx, member: discord.Member):
await client.ban(member, delete_message_days=0)
await client.say('**{0}** has been banned.'.format(str(member)))
#mod_ban.error
async def mod_ban_error(error, ctx):
if isinstance(error, CheckFailure):
await client.send_message(ctx.message.channel, "Looks like you don't have the perm.")
As far as I can see in the discord.py documentation discord.User is not the same as discord.Member.
Try to change
async def mod_ban(context, member: discord.User):
to
async def mod_ban(context, member: discord.Member):
If you are using discord.py rewrite, you can use checks (Discord.py rewrite checks)
Which (obviusly) checks for certain things suchs as roles or permissions on the command invoker
You can use both of this decorators, below your first decorator
#commands.has_role("rolename"/roleid)
#commands.has_any_role("rolename"/roleid,"rolename"/roleid,"rolename"/roleid ...)
Where rolename is a string containing the EXACT name of the role, letter by letter and space by space, and roleid is the id of the role, which, in case it's mentionable, you can get it by typing #rolename on any of your server chatrooms
Note how you must use the second decorator if you want to pass more than one role to check