Instantly delete discord messages using python discord.py - python

Hello I'm trying to make this code unsend my messages without having the permission or role to delete/manage messages in a server
I have tried using "delete_after" argument but most of the times messages doesn't get deleted and I'm not sure why
This is my code:
#client.command(pass_context=True)
async def hello(ctx):
while True:
time.sleep(int(timer))
await ctx.send("HELLO", delete_after=1)

The deletions can fail sometimes if you hit ratelimits, the message falls out of cache, etc.
See the docs. If the deletion fails, it will be silently ignored.
If you want to be completely sure they are deleted, you need to save the messages and delete them manually. If this fails, then you will get an error rather than nothing.
while True:
await asyncio.sleep(int(timer-DELETE_AFTER_DURATION))
message = await ctx.send("HELLO")
await asyncio.sleep(DELETE_AFTER_DURATION)
await message.delete()
Sidenote: use asyncio.sleep, which won't block the rest of your code, rather than time.sleep, anywhere in a coroutine.

Related

Doing something on discord.py ratelimit

I have a Discord bot running on the cloud, and when my shared IP hits the rate limit of 50 requests per second, my bot gets blocked for a while (I know, it's a terrible system but it's where I'm at). In the event that this happens, I want it to send me a message on another site. I have a way to send the message (with the Tweepy module for Twitter) that works, but nothing happens when I use the following code:
#client.event
async def on_error(ctx, error):
if isinstance(error, discord.errors.HTTPException):
#do something
Am I doing something wrong?
Since your ratelimit can only be triggered by certain commands and you only get the message in the console afterwards it makes sense to use the on_command_error event.
Look at the following code:
#client.event
async def on_command_error(ctx, error):
if isinstance(error, discord.HTTPException):
await ctx.send("You are ratelimited") # Send whatever you want.

How to make discord.py bot send to the server it's called in?

had a question regarding discord.py.
I run two separate servers that my bot is on, my test server and my main server.
The problem is when I send a message in the test server, the bot then sends its message to the main server, and never sends it back to the server the command is being called in (only in functions).
For example:
if message.content == '!Hello':
await message.channel.send('Hello there!')
If I type the above in the test server, my bot will respond with "Hello there!" in the test server like it should. However, if I tried putting this code in a function and called it:
if message.content == "!Hello":
await hellomessage()
async def hellomessage():
channel = client.get_channel('Channel ID Here')
await channel.send('Hello there!')
The channel ID is obviously set to a specific server. So say I have ID '1234' as my main server and ID '1111' as my test server, regardless whether I call it in my test server or main server, that message is going to send to the main server because the ID is no different. My question is how do I make sure the "channel" attribute changes depending on the server it was called in. I want it so if I say !Hello in my test server, it doesn't send to the main server and only the test server.
Seems like a very trivial answer but I'm just struggling to find it, any help is appreciated!
You can check what guild a message has been sent from using the .guild attribute of a message.
Example:
# You can also have a separate coroutine for your main server
async def hello_test_message():
test_guild_channel = client.get_channel(ID_HERE)
await test_guild_channel.send("Hello there!")
#client.event
async def on_message(message):
if client.user == message.author:
return
if message.guild.id == TEST_GUILD_ID_HERE:
if message.content.lower() == "!hello": # .lower() for case insensitivity
await hello_test_message()
# Another condition for your main server + calling the other coroutine
This being said, I'm assuming that you're not hard-coding values in values for every possible channel etc. and if that is the case, and you just want the bot to respond in the original message's channel, you can use message.channel.send(....
The current method of doing things will result in quite a bit of duplicate code.
I'd also recommend having a look into the commands extension of discord instead of using on_message events for them.
References:
Bot example using command decorators
Message.guild
Guild.id
commands.command()
Message.channel
TextChannel.send()
Messageable.send() - Anything that inherits from abc.Messageable is able to have a message sent to it! So this includes stuff like users, text channels, context etc.
commands.Context - You'll use this a lot when dealing with commands!

How to purge messages for specific users in Discord.py

Hey so I was trying to make a command in discord.py rewrite version was doing something like >purgemessage 5 would purge 5 messages of the user who sent that message but for some reason, it won't work. I am using this in a reply so it won't show the error;-; but here is what I have done so far to try to make it work!
# purges author's messages
#commands.command(name='purgemessage', aliases=['pm'])
#commands.cooldown(1, 300)
#commands.has_any_role(OwnerRole, GuildMasterRole, AdminRole, ExclusiveMemberRole)
async def purgemessage(self, ctx, amount:int, *, arg:str=None):
msg = []
channel = ctx.message.channel.history(limit=None)
async for message in channel:
if message.author == ctx.message.author:
msg.append(message)
await ctx.message.channel.delete_messages(msg)
await asyncio.sleep(.5)
botMessage = await ctx.send(f'**{amount}** messages were successfully deleted!')
await asyncio.sleep(.5)
await botMessage.delete()
I haven't done thorough tests, but the first error I am getting (after rewriting to work outside a cog so I could quickly test), is
discord.errors.HTTPException: 400 Bad Request (error code: 50034): You can only bulk delete messages that are under 14 days old.
This error might be caused by your code not containing anything to stop adding messages after reaching amount, and continuing backward for a user's entire history in a channel.
This could very well not be the error you're getting, depending on the channel you're testing in. I would highly recommend you start working on an environment that will show you errors so we can help you better.
I don't know enough to debug the code you've given, but here's something that does what you want in less code and I know works:
channel = ctx.channel
await channel.purge(limit=amount, check=lambda message: message.author == ctx.author)
ctx.channel is the channel the command was sent in, ctx.author is who sent it.
channel.purge will purge limit amount of messages that meet the check function, which in this case is a lambda (a function defined in one line, looks like this: lambda argument: expression. The lambda will implictly return the expression).
The check keyword will pass message as an argument, so I've named by lambda argument appropriately. The expression checks that the message we're checking (message.author) was written by the invoker of the command (ctx.author).
This code works, even for messages over 14 days old, but it does take some time for larger amounts of messages.

Discord.py commands run twice

I only recently encountered an issue when using discord.py and the discord.ext commands extension to make a bot
When I call a command it runs twice, causing many bugs and errors. It only started happening after adding this command
#commands.command()
async def afk(self, ctx, userMessage):
#if user is already afk, remove them from the afk dict, if not add them to it
if ctx.message.author in self.afkUsers:
self.afkUsers.pop(ctx.message.author)
else:
self.afkUsers[ctx.message.author] = userMessage
however removing this command doesn't solve the issue. I was hosting on heroku but stopped that and ran it on my own pc to test but the problem still persisted . I used print functions in the commands to test if they were running twice and the strings inside them got outputted twice
I also have an on_message event
#commands.Cog.listener()
async def on_message(self, message):
#if a member is mentioned but the member is afk, a message is sent
textChannel = message.channel
afkChannel = self.client.get_channel(690550327975346176)
for member in message.mentions:
if member in self.afkUsers:
await textChannel.send(f"user is afk- {self.afkUsers[member]}")
elif member in afkChannel.members:
await textChannel.send("user is afk")
#allows commands to work with on_message event
await self.client.process_commands(message)
Edit: this also happens on some commands in my main file, but the strange thing is only some of them are affected
You're calling process_commands twice for the same message. This is because the default on_message listener already calls process_commands. So the on_message listener from your cog calls it a second time. You should remove the process_commands call from your cog on_message.

How to have the bot show the content of the message it deleted

#client.command()
async def clear(ctx, amount=2):
await ctx.channel.purge(limit=amount)
await ctx.send(f'Note:I have cleared the previous two messages\nDeleted messages:{(ctx)}')
I am trying to make the bot show which messages it has deleted, but I can't figure out how to do so.
For this use, channel.history. This is better because while channel.purge deletes at once, channel.history go to each and every message before deleting which means you can get the content.
#client.command()
async def clear(ctx, amount:int=2):
messagesSaved = [] # Used to save the messages that are being deleted to be shown later after deleting everything.
async for msg in ctx.channel.history(limit=amount, before=ctx.message): # Before makes it that it deletes everything before the command therfore not deleting the command itself.
await msg.delete() # Delets the messages
messagesSaved.append(msg.content) # Put the message in the list
await ctx.send(f'Note: I have cleared the previous {amount} messages.\nDeleted messages:\n'+'\n'.join(str(message) for message in messagesSaved))
Saving the message in the list instead of saying it after deleting is good so we can send it all at once instead of deleteing and sending the message right after deleting as this could cause many notifications especially if deleleting a lot of message.

Categories