Delete multiple messages - discord.py

Well im creating a discord bot right now and i wanted to add a feature that it deletes messages.
Basically the bot waits for a message to pop up, if this message contains a given command,
in this case "sudo clean (number)", it deletes (number) messages (so if the number is 5 it deletes 5 messages). Well i mean its supposed to do that but yea its not doing what its supposed to do.
I get discord.errors.NotFound: 404 Not Found (error code: 10008): Unknown Message
Thats my code:
elif message.content.lower().startswith("sudo clean"):
userMessage = message.content
message.delete()
for word in userMessage.split():
if word.isdigit():
textToClean = int(word)
for i in range(0, textToClean):
await message.delete()
Whats the Problem?

here is what I use that's a lot easier if you don't want anything fancy
#client.command(name="clear",
brief="Deletes a certain amount of messages ex. .clear 10")
async def clear(ctx, amount=5):
amount += 1
await ctx.channel.purge(limit=amount)

Here is a pretty simple implementation using slash commands, which i currently use.
#commands.slash_command(description='Delete messages from a channel.')
#commands.has_permissions(manage_messages=True)
async def purge(
self,
inter,
amount: int
):
"""Delete a certain amount of messages in the chat
Parameters
----------
amount: The amount of messages you want to delete, can't be greater than 100
"""
if amount > 100:
await inter.send("Wow, take it easy, no more than 100 messages at a time", ephemeral=True)
return
await inter.channel.purge(limit=amount, check=lambda msg: not msg.pinned)
await inter.send('Messages deleted.', ephemeral=True)
Of course, if you don't use slash commands can be easily turned into a regular prefix command
#commands.command()
#commands.has_permissions(manage_messages=True)
async def purge(
self,
ctx,
amount: int
):
"""Delete a certain amount of messages in the chat
Parameters
----------
amount: The amount of messages you want to delete, can't be greater than 100
"""
if amount > 100:
await ctx.send("Wow, take it easy, no more than 100 messages at a time")
return
await ctx.channel.purge(limit=amount, check=lambda msg: not msg.pinned)
await ctx.send('Messages deleted.')
The check is for avoiding Pinned messages

Related

How to change channel permissions without having a message sent first?

I have a program that changes what channel members can see at certain times of the day. To do this, I can either change the roles that every member has, or change the permissions of each channel. However, I have looked all over the web and all of the ways for either method require a message to be sent so that the data from that message can be read and put into the function, such as:
#client.command()
async def perm(ctx):
await ctx.channel.set_permissions(ctx.guild.default_role, send_messages=False
Change the permissions of a discord text channel with discord.py
or
async def addrole(ctx):
member = ctx.message.author
role = get(member.server.roles, name="Test")
await bot.add_roles(member, role)
Discord.py | add role to someone
My current program looks like this:
import discord
client = discord.Client()
import datetime
async def live_day(schedule):
current_place = "the void"
while True:
current_time = str((datetime.datetime.now() -datetime.timedelta(hours=7)).time())
int_time = int(current_time[:2] + current_time[3:5])
for x in range(len(schedule)):
try:
start_time = schedule[x][1]
end_time = schedule[x + 1][1]
except IndexError:
end_time = 2400
if current_place != schedule[x][0] and int_time >= start_time and int_time < end_time:
current_place = schedule[x][0]
#Change the channel permissions of the current place to allow viewing
#Change the channel permissions of the last channel to disallow viewing
#client.event
async def on_ready():
print("{0.user} has arrived for duty".format(client))
client.loop.create_task(live_day([
("Home", 0),
("Work", 900),
("Home", 1700),
("Nightclub", 2000),
("Home", 2200),
]))
client.run(my_secret)
Never mind the badly written code, how would I do this or where should I go to figure this out? Any help is appreciated. Thanks!
Edit: I could get the channels individually by using this,
channel1=client.get_channel(channelid)
discord.py, send message by channel id without on_message() event?
but then I can't use this for more than one server. How can I get channels by name?
Note on on_ready():
This function is not guaranteed to be the first event called. Likewise, this function is not guaranteed to only be called once. This library implements reconnection logic and thus will end up calling this event whenever a RESUME request fails.
Using this function may crash the bot.
Instead create background tasks and use wait_until_ready().
You can find examples in https://github.com/Rapptz/discord.py/blob/v1.7.3/examples/background_task.py
If you want to change only one channel's permissions (per guild) this might fit your needs:
#changing "Home" channel visibility, every day at 5 pm.
#client.event
async def on_ready():
while True:
await asyncio.sleep(1)
current_time = datetime.datetime.now()
five_pm = current_time.replace(hour=17, minute=0, second=0)
if current_time == five_pm:
for guild in client.guilds: #looping through all the guilds your bot is in
channel = discord.utils.get(client.get_all_channels(), name="Home") #getting channel by name by using "discord.utils"
await channel.set_permissions(guild.default_role, view_channel=True) #changing permissions
By using the on_ready() event you can loop through the time check without sending any message.
Remember to import discord.utils.

Discord bot sends many questions in dm at a time without waiting for the user to respond

I need my bot to send questions to users for them to answer when the user writes a command.
but the bot ended up sending 2 or 3 questions at a time randomly without waiting for the user to answer.
Image that shows the output of the bot.
which makes the value of RP N = "Name:"
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('!add'):
await message.author.send(
"``Fill in the blanks by replying, then the bot will give a confirmation message.``")
for element in report:
await message.author.send("**"+element+"**")
response = await client.wait_for('message')
var(response.content)
report is the list of the questions
var is list.append
Edit: I have tried to add check argument.
def check(m):
return m.content != element in report
And that didn't work, still having the same issue.
This fixed my problem.
def check(m):
return message.author == m.author and isinstance(m.channel, discord.DMChannel)
To delay commands I suggest using time.sleep() TimeSleep if you don't want it to respond so fast you can put a little delay.

Command to spam something discord-bot

so basically I am trying to make a spam command for my discord bot, which takes in a custom message to spam. Here's the code:
#client.command(name='spam')
async def spam(ctx):
global stop
stop = 0
content = ctx.message.content[11:]
if ctx.author.guild_permissions.administrator or ctx.author.id in admins:
if lock == 1:
await ctx.send('Jesus bot is currently locked.')
elif lock == 0:
await ctx.send('Beginning spam..')
while not stop:
await ctx.send(content)
else:
await ctx.send('Sorry, but you do not have admin permissions in this server, or you are not a verified admin.')
For some reason, whenever I try to use this command, the bot doesn't respond. I'm not sure why this happens, and could use some help please.
Picture of bot not responding:
I have a spam command, but I only use it to mess around with my friends. I would not recommend using this as a public command, as you may get rate limited or banned for abuse or something like that. Anyway here is the code I have used for it.
#commands.command()
#commands.is_owner()
# If you want to use admin only, use this below
# #commands.has_permissions(administrator=True)
async def spam(self, ctx, amount, *, word):
int(amount)
await asyncio.sleep(2)
print(f"Starting to spam {word} in {ctx.guild.name}")
await ctx.message.delete()
await ctx.send(f"{ctx.author.mention}\nPlease note that this will clog up the bot's reaction time")
await asyncio.sleep(3)
count = 0
counting=True
while counting:
await ctx.send(word)
count = count + 1
if count == amount:
await asyncio.sleep(2)
await ctx.send("Spam complete")
print(Fore.GREEN + "Spam complete")
counting = False
At the top of your code, make sure you import asyncio as time.sleep will cause the whole bot to pause. Also the Fore.GREEN stuff is just colorama (import colorama).
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. Something like this:
from discord.ext import tasks
#client.command(name='spam')
async def spam(ctx):
#get message and do all the ifs that you have there
spamLoop.start()
#client.command(name='stopSpam')
async def spamStop(ctx):
# stop the loop
spamLoop.cancel()
#tasks.loop(seconds=1)
async def spamLoop():
print("The message")
Actually quite a simple way of adding spam
import asyncio
#bot.command(name='spam', help= "Spam to your heart's delight")
async def spam(ctx, thing, amount):
count = 0
while count < int(amount):
await ctx.send(thing)
count += 1
if count < amount:
await asyncio.sleep(1)

Discord.py file logging, change number of messages

I've got this code that I use for discord channel logging. It basically gets the last 10,000 messages in a channel, and makes a .txt file with those messages in it, with the !log command. However, how would I make it so optionally users can type a number after !log, and it will log that number of previous messages? EG: "!log" on it's own logs the last 10,000 messages, but "!log 100" would log the last 100 messages. But I have no clue how to do that.
Here's my code:
#commands.has_any_role('Logger', 'logger')
#bot.command()
async def log(ctx):
try:
channel = ctx.message.channel
await ctx.message.delete()
await channel.send(ctx.message.author.mention+" Creating a log now. This may take up to 5 minutes, please be patient.")
messages = await ctx.channel.history(limit=10000).flatten()
numbers = "\n".join([f"{message.author}: {message.clean_content}" for message in messages])
f = BytesIO(bytes(numbers, encoding="utf-8"))
file = discord.File(fp=f, filename='Log.txt')
await channel.send(ctx.message.author.mention+" Message logging has completed.")
await channel.send(file=file)
except:
embed = discord.Embed(title="Error creating normal log:", description="The bot doesn't have necessary permissions to make this log type. Please confirm the bot has these permissions for this channel: View Channel, Read and Send Messages, Attatch Files (used for sending the log file), Manage Messages (used for deleting the command the user sends when making a log).", color=0xf44336)
await channel.send(embed=embed)
Any help about how to make it so they can optionally provide a number, which would then log that amount, would help a lot. Thanks!
Take the number of messages to be logged as a parameter
...
#bot.command()
async def log(ctx, limit: int=1000):
...
then use the value of the parameter
...
messages = await ctx.channel.history(limit=limit).flatten()
...
docs: https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html#parameters

get channel name and send a message over at that channel

So I am working on a little project here, and pretty much, I want to have one of those "Please type the name of a channel in this server" feature.
So pretty much, the bot asks for a channel name, and I put in for example "#changelog" - and then it will ask for what it should write in that channel, etc etc.
So need to get the channel id (I am guessing), but I don't want users to write the ID, instead only writing the #server-name. And then whenever I have done that, the bot shall write in that channel.
Here is my current code!
class Changelog(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
print('Changelog is loaded')
#commands.command()
async def clhook(self, ctx):
await ctx.send('Write text-channel: ')
text_channel = await self.client.wait_for("message", check=lambda message: message.author == ctx.author, timeout=300)
clhook = self.client.get_channel(text_channel)
def setup(client):
client.add_cog(Changelog(client))
Edit:
The channel ID shall be saved "forever", meaning that I do not have to re-write the channel name where the message should go!
You can use discord.utils.get() with this example:
text_channel = await self.client.wait_for("message", check=lambda message: message.author == ctx.author, timeout=300)
channel = discord.utils.get(ctx.guild.text_channels, name=text_channel)
await channel.send('Bla Bla')
So when you type (prefix)clhook then only the channel name, for example general, it will send Bla Bla to the channel named general .
There is another way to do this and I think it's simple than the first option, here it is:
#commands.command()
async def clhook(self, ctx, channel: discord.TextChannel):
await channel.send('Bla Bla')
So in this command, usage is changed. You can use that with this: (prefix)clhook #general(mention the channel). I suggest this solution and I think it's more useful.
You can use message.channel_mentions. This will return a list of all channels that were mentioned using the #channel-name notation. That way, you can just use channel.id to get the id of the channel they mentioned.
Don't forget, however, to check if the user did in fact tag a channel (which you can also put in your check). I put it in a separate function to make it a bit more readable for the sake of this reply, but you can fit that in your lambda if you really want to.
Also, make sure to check if it's a Text Channel and not a Voice Channel or Category Channel.
#commands.command()
async def clhook(self, ctx):
def check(self, message):
author_ok = message.author == ctx.author # Sent by the same author
mentioned_channel = len(message.channel_mentions) == 1 and isinstance(message.channel_mentions[0], discord.TextChannel)
return author_ok and mentioned_channel
await ctx.send("Write text-channel: ")
text_channel = await self.client.wait_for("message", check=check)
chlhook = text_channel.channel_mentions[0]
I put two conditions on the mentioned_channel line, because if the first one fails, the second one could cause an IndexError. Alternatively you can also use an if-statement to return sooner at that place to solve the same issue.

Resources