Why is await causing my function to trigger multiple times in python? - python

Im still learining python/programming generally and am working on a discord bot as a school project. The assigment is to create a chatbot and we want to create a bot that can for one, respond to certain keywords and also use openais ChatGPT API to generate responses.
We are using the discord.py library, which´s setup guide showed the message.channel.send() function being used with await in front. I dont really get the hang of the whole async and await thing just yet, so any explanations, aimed at beginners, are welcome, since googling just made me more confused.
Anyway, the problem is, that for some reason, each response gets sent twice.
The part of code thats causing the behaviour is the following:
messages = {
'hello': 'hello',
'capybara': capytime(), # capytime() returns the link of a random capybara gif
}
#client.event
async def on_message(message: discord.Message):
response = ""
if message.author != client.user:
if message.content.startswith('*'):
await openai_respond(message)
return
else:
for m in messages:
if m in message.content.lower():
response = messages[m]
await message.channel.send(response)
I tried getting rid of await since I couldnt find any bugs in the rest of the code and it solved the problem of sending twice.
With await
Without await
But im not quite sure if i can just remove the await and call it a day. Id guess, if it werent important, it wouldnt be there in the first place.
So my questions are as follows:
Why is await causing this unexpected behavior?
Which functionality would I loose if I just got rid of the await?
Is there any way to fix this while keeping the await?

Related

Is there kind of a limit for asyncio in discord.py?

So I‘ve got a python discord bot. The idea is, there is a channel for funny quotes. In order to keep the channel organized, people can as well discuss in it, but after 24h their message should be deleted, if there‘s no %q before it. I‘ve got the following code:
#bot.event
async def on_message(ctx):
await bot.process_commands(ctx)
channel = bot.get_channel(ID_OF_CHANNEL)
if ctx.channel == channel:
if not ctx.content.lower().startswith("%q"):
await asyncio.sleep(86400)
await ctx.delete()
The problem is, it just doesn‘t work. The bot responds to other commands, but doesn‘t delete every single message after 24h. In fact, it doesn‘t delete any messages. I‘ve tried it with only 3h as well, but it didn‘t work too. Passing in only 30 seconds works…
I‘m sorry if my question may be stupid, but I‘m just clueless and I hope anybody can help me :)
May there be a limit of messages the bot can view or a limit of time it can do so?
Btw: I‘m pretty sure the bot was running all the time, so it was not interrupted.
EDIT: Just to make that clear: The idea is to start something like a timer on every single message, which works for 30 seconds, but strangely not for 3h.
Try using tasks instead of asyncio. It is made for such repetetive operations and it is easier and nicer because it is made by discord and is included in discord.ext. The way I would do it is something like this:
from discord.ext import tasks
#bot.command()
async def start(ctx):
deletingLoop.start() #here you start the loop
#tasks.loop(hours=24)
async def deletingLoop():
guild = self.bot.get_guild(guildID) #get the guild
channel = guild.get_channel(channelID) #and the channel
channel.purge(limit=1000) #clear the channel
and if you are afraid that this channel may have more than 1000 messages you can add on_message() event that would count the messages on this channel.
You have to take ctx.message as you want to delete the message of the context. This can take the parameter delay which deletes the selected message after that amount of time.
Code:
#bot.event
async def on_message(ctx):
await bot.process_commands(ctx)
channel = bot.get_channel(ID_OF_CHANNEL)
if ctx.channel == channel:
if not ctx.content.lower().startswith("%q"):
await ctx.message.delete(delay=24h_in_seconds)

How do I get a bot to mention someone?

I've been trying to make my bot mention someone for a command for a while, I was looking around Google and other websites and most of them where specific people like using <#(User ID)> but I want to make it mention whoever, like, when I use a command like "?hit" I would like it to say "(bot pings who used the command) punches (person pinged in command)
(Random message idk)"
I can't find a website or video of how to do it I need a little help.
Every Member object is mentionable (using the mention function), read up on the docs here. Using that, we can create a command which takes in a member to hit. Then, we can ping the ctx.author and member using the function mention. It would look something like:
#client.command()
async def hit(ctx, member: discord.Member):
await ctx.send(f"{ctx.author.mention} punches {member.mention}")
Output:
I don't mean to be rude but this question was easily answered by looking at the discord API docs, as it explains everything in the API and what it does. My solution is below. You didn't provide any code or problem you've had with code so this is all I can do.
#client.command() # here, we make the client command.
async def hit(ctx, member : discord.Member): # we are making the function that actually hits the person here.
await ctx.send(f'{ctx.message.author.mention} hits {member.mention}.') # here, we send the message that pings both the user who triggered the command, and the person who the you want to ping.
If you wanted to have it select from a list, for example, here's how that'd work.
#client.command()
async def hit(ctx, member : discord.Member):
# this is a list of random responses that could happen.
hit_responses = [f'{ctx.message.author.mention} hits {member.mention}.',
f'{ctx.message.author.mention} swings and hits {member.mention}.',
f'{ctx.message.author.mention} slaps {member.mention} across the face.']
hit_message = random.choice(hit_respones) # this chooses a random response.
await ctx.send(hit_message) # this sends the random response
An example output would be as follows:
#goose.mp4 slaps #user across the face.

A good example of PermissionOverwrite in discord.py

I've been trying to use this feature but can't get the hang of it. Here's something I've tried.
# lockdown
#client.command()
#commands.has_permissions(administrator=True)
async def lockdown(ctx, channel: discord.TextChannel=None):
channel = channel or ctx.channel
await ctx.send("This channel has been locked. You will need to change the settings to revert this action.")
if ctx.guild.default_role not in channel.overwrites:
overwrites = {
ctx.guild.default_role: discord.PermmisionOverwrite(send_messages=False)
}
await channel.edit(overwrites=overwrites)
It doesn't seem to work though, and I know why. I don't know how to use this function properly. Seeing the API documentation made me even confused. Can I like, get a good example of this?
Where you put discord.PermmisionOverwrite, you misspelt. You can put discord.PermissionOverwrite instead

Why doesn't python allow me to use 2 arguments in discord.py?

Okay so im making a filter for my discord. and after about 2 hours i figured out how to delete the message.
but i've realised that i cant use 2 arguments.
#bot.event
async def on_message(ctx,message):
if 'cancer' in message.content.lower():
await message.delete()
bad = discord.Embed(title='YOU SAID A NO NO WORD!!!', description ='Dont do that again or you\'ll
have some problems <a:Sippp:668488891820408862>', colour=discord.Color.red())
await ctx.channel.send(embed=bad)
When i do this, i cant use both ctx and message. Could someone help me out?
The reason i reuse this and its similar to another question of mine is because its not explained there, and because its not only handy to know for others, if they arent looking for that precise question.
on_message should take a single argument, the message sent (docs here).
If you need to access the channel the message was sent in, you can use message.channel (see channel attribute in the message section of the docs). So your function becomes:
#bot.event
async def on_message(message):
if 'cancer' in message.content.lower():
await message.delete()
bad = discord.Embed(title='YOU SAID A NO NO WORD!!!', description ='Dont do that again or you\'ll
have some problems <a:Sippp:668488891820408862>', colour=discord.Color.red())
await message.channel.send(embed=bad)

Discord Bot Python Deleting Author's Message

I made a small discord bot for some fun:
I want the author's messages to be deleted when Author run this command. But this does not work.
if message.content.startswith('Delete me.'):
async for msg in client.logs_from(message.channel, message.author):
await client.delete_message(msg)
Looking at their docs, logs_from takes a channel, along with additional arguments allowing you to
filter which messages to retrieve.
So if you just wanted to delete all messages, you can do something like
async def on_message(message):
NONSENSE_LIMIT = 999999999999
if message.content.startswith('!delete'):
async for log in client.logs_from(message.channel, limit=NONSENSE_LIMIT):
if log.author == message.author:
await client.delete_message(log)
Which will delete all messages for a user that types in that command. Your bot must have the manage messages permission for that channel.
Note that the number of messages returned is limited, and here I just write down some arbitrary nonsense limit. If you want to be accurate, you would have to figure out how many messages there are in the channel somehow, and then use that as the limit.
I just looked up discord API in the last 5 minutes so I don't know if there's a more efficient way to do it.
Your problem is in the client.logs_from(message.channel, message.author)statement. logs_from accepts the following arguments: logs_from(channel, limit=100, *, before=None, after=None, around=None, reverse=False). See the API reference. So you have two channel arguments where logs_from only accepts 1. Perhaps you intended client.logs_from(message.author) instead?

Categories

Resources