In discord.py is there some option how i can get access to msg.content (or ctx.content how everyone uses it) under a command ? Below you can see 2 examples of what i mean. The first one is event and i simply copy the message and let the bot to send it back. The second is command but there the msg.content doesnt work. My problem is that i dont want to use events so much and have everything under a command.
#bot.event
async def on_message(msg):
chat = bot.get_channel(797224597443051611)
if msg.channel.id != channel:
return
if msg.content.startswith("!rps"):
message = str(msg.content)
await chat.send(message)
Someone types !rps hello. Outpup in discord is !rps hello
#bot.command()
async def rps(msg):
if msg.channel.id != channel:
return
message = str(msg.content)
await msg.send(message)
Someone types !rps hello (my prefix is !). Output is error in console:
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'Context' object has no attribute 'content'
Commands always take commands.Context as the first argument, also you should call it ctx instead of msg, to access the message content you can use ctx.message.content
#bot.command()
async def rps(ctx):
if ctx.channel.id != channel:
return
message = str(ctx.message.content)
await ctx.send(message)
Take a look at the commands introduction
In order to get the rest of the message of a command, you need to pass another argument. This argument will include all the messages that's sent after !rps. Also, using ctx instead of msg is better in commands.
#bot.command()
async def rps(ctx, *, args):
if ctx.channel.id != channel:
return
await ctx.send(args)
In this code, args argument includes all the messages after the !rps.
Related
Ok so I've tried everything for my command but nothing worked for my !say command to work and not tag #everyone. I tried doing it myself but it still doesn't want to. I'm a beginner so i'm bad but where is the problem and what do i do?
here is my code:
async def say(ctx, msg=None):
if msg is not None:
await ctx.send(msg)
await ctx.message.delete()
if message == "#everyone" or "#here":
break:
Avoid specific mention when sending a message
discord.py already has a feature built-in, to allow or not specific type of mention.
Combining discord.AllowedMentions and discord.abc.Messageable.send, as the following example.
async def avoid_everyone(ctx: commands.Context, *message: str)
# Disallow everyone when sending message
allowed = discord.AllowedMentions(everyone=False)
# Raise an exception if #everyone in message
await ctx.send(message, allowed_mentions=allowed)
so i am trying to make a discord bot with discord,.py and i want the bot to say something when someone sends a message that contains a certain string. this is what i want to happen
#client.event
async def on_message(msg):
if 'i' in msg:
channel = client.get_channel("742261026686500896")
await channel.send("i")
i get this error
TypeError: argument of type 'Message' is not iterable
how would i go about doing this?
#client.event
async def on_message(msg):
if 'i' in msg:
channel = client.get_channel("742261026686500896")
await channel.send("i")
The discord Message is an uniterable object (the reason you are getting the error). You are not accessing the information properly. The Message object contains information on the other, channel, etc.
You can check the docs on this topic, here.
To access the content of the message, you should use msg.content.
Then, you can run the test on that. Example:
#client.event
async def on_message(msg):
if 'i' in msg.conent:
channel = client.get_channel("742261026686500896")
await channel.send("i")
msg is an object with several attributes. The text portion is found in msg.content. Try this:
#client.event
async def on_message(msg):
if 'i' in msg.content:
channel = client.get_channel("742261026686500896")
await channel.send("i")
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'm very confused at how this error is occurring, it is only occurring when I use the kick/ban command in my bot and no other commands, I looked in the on_message function and it doesn't cause an error when I message anything else except from the kick/ban command and I'm not sure why. I am passing in guild correctly and there is an id attribute because the code is still working but it comes up with this anyway.
Here's the error:
Ignoring exception in on_message
Traceback (most recent call last):
File ".local/lib/python3.7/site-packages/discord/client.py", line 333, in _run_event
await coro(*args, **kwargs)
File "file path", line 1937, in on_message
await open_automod(guild)
File "file path", line 1920, in open_automod
if str(guild.id) in data:
AttributeError: 'NoneType' object has no attribute 'id'
Here's an example of a bot command that causes this error:
#bot.command(name='testlog')
#commands.has_permissions(kick_members=True)
async def kick (ctx, member:discord.Member, *reason):
moderator = ctx.message.author.id
server = ctx.guild.id
if member == None or member == moderator:
await ctx.channel.send("You cannot kick yourself")
return
if reason == None:
reason = "no reason stated"
reason = " ".join(reason)
message = f"You have been banned from {ctx.guild.name}. Reason: {reason}"
await member.send(message)
await ctx.channel.send(f"{member} has been kicked. Reason: {reason}")
await send_log('test', moderator, member.id, reason, server)
Here's the open_automod function:
async def open_automod(guild):
data = await get_automod_data()
if str(guild.id) in data:
return False
else:
data[str(guild.id)] = {}
data[str(guild.id)]["automodenabled"] = f'off'
data[str(guild.id)]["automodlogging"] = f'false'
data[str(guild.id)]["automodchannel"] = 0
with open("automoderator.json","w") as f:
json.dump(data,f)
return True
Here's the section of the code that is passing in 'guild':
#bot.event
async def on_message(message):
guild = message.guild
await open_automod(guild)
What I don't get is why this error only shows up on the message where im using kicks or bans or in the above case a test kick. Could anyone explain why i'm getting this error and why it only happens on this message? An example of the message would be 'h/testlog #user reason'
Looks like on_message is being run instead of kick as the error shows. From your questions it looks like you are expecting kick to be run. This is because of overriding on_message. See discord.py documentation for Why does on_message make my commands stop working?:
Overriding the default provided on_message forbids any extra commands from running. To fix this, add a bot.process_commands(message) line at the end of your on_message.
Alternatively, you can place your on_message logic into a listener [with #bot.listen('on_message')]
From the error message it's quite clear that guild is None. Discord.py docs do show that Message.guild is optional. So you should guard against that:
#bot.event
async def on_message(message):
if (guild := message.guild):
await open_automod(guild)
However, it again looks like you're expecting guild to have a value in both kick and on_message, so you should figure out why it doesn't. My guess is you're sending our testlog message/command directly to the bot and not in a guild channel - therefore the bot doesn't know what guild to target.
If the kick is meant to only be called within a channel, you should make sure to check that ctx.guild and ctx.channel are not None before attempting to use them and instead reply with instructions how to use the bot correctly.
If kick command is meant to be sent directly to the bot and not in a channel, you should figure out another way of getting the guild and channel (e.g. storing it, or passing as part of the command).
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))