Clear by user command does not clear all messages by user - python-3.x

I was trying to make a command that clears specified number of messages by a particular user, I am new to discord.py, my problem is that the command doesn't clear messages if any other user has sent a message/interrupted messages of the specified user.
Eg:
User 1: A
User 2: B
User 1: A
User 1: A
I try to clear 3 messages by user 1, but it clears only the last 2 and not the one that comes before User 2 sent a message. Here's my code:
#bot.slash_command(description='Clears messages by a specified user.')
async def user_clear(ctx,amount:int, member:discord.Member):
def check(message):
return message.author==member
await ctx.channel.purge(check=check,limit=amount)
await ctx.respond(f"Cleared {amount} messages by {member}!")
I'd appreciate any help!

Here's one which works:
#bot.command()
async def user_clear(ctx: commands.Context, amount: int, member: discord.Member = None):
counter = 0
async for message in ctx.channel.history(limit=100, before=ctx.message):
if counter < amount and message.author == member:
await message.delete()
counter += 1
await ctx.channel.send(f"Cleared {amount} messages by {member}!")

Related

#client.event returning an output from an if condition that is false

What's The Problem?
In the following code
#client.event
async def on_message(messag):
if cur == ":gem:": #checks if the currency (defined previously in the code) is gems
if "gem" in messag.content.lower():
await ctx.send("noticed bid of g.e.m.s.") #to prevent the bot from detecting its own message i used g.e.m.s for testing purposes
elif "gems" in messag.content.lower():
await ctx.send("noticed bid of g.e.m.s.")
elif ":gem:" in messag.content.lower():
await ctx.send("noticed bid of g.e.m.s.")
else:
pass
if "gem" not in messag.content.lower():
if "gems" not in messag.content.lower():
await messag.author.send("This auction only accepts bids in :gem: gems. Please be more attentive before bidding")
else:
pass
when the string "gem" or "gems" is NOT in the message sent by a user, it sends a DM to them as intended, however the bot also sends the message "noticed bid of g.e.m.s." in the channel where the command was invoked (not in the DM to that person). This event code is after the bot sends an embed when a user runs the command. I have not included the embed's code for the sake of simplicity.
The solution I want
The bot only does the things under the if conditions that are True

discord.py prevent bot from reading message as a command

The bot asks the user to select a type. I have used the client.wait_for() function to get user input. The command prefix is '.' In case the user types a message starting with '.' , I do not want the bot to read it as a command and execute that command. How do I do that?
This is the code:
#client.command()
async def search(ctx):
try:
await ctx.send("Enter '"+selected_type[0]+"' or '"+selected_type[1]+"' to search for required type")
msg = await client.wait_for('message', timeout=10, check=lambda message: message.author == ctx.author)
selection = msg.content.title()
except asyncio.TimeoutError as e: #if user does not give input in 10 sec, this exception occurs
await ctx.send("Too slow")
except:
await ctx.send("Search failed.")```
Will something like this work for you?
if not msg.content.startswith('.'): selection = msg.content.title()

Reaction Handling in Discord.py Rewrite Commands

Is there a way to capture a reaction from a command. I have made a command that deletes a channel but I would like to ask the user if they are sure using reactions. I would like to prevent others from reacting to this message (Only the Context Author should React).
So far what I have found is just to use the on_reaction_add() but this can not detect the user who sent the command. I would like to only update the command if the message author is the one who reacted to the message, anyone else, ignore it.
Update: I found that wait_for() does exactly what I want but the issue now is how do I check for if the wrong reaction is set? (i.e if I press the second reaction, delete the message)
if is_admin:
msg = await ctx.send('Clear: Are you sure you would like to purge this entire channel?')
emoji1 = u"\u2705"
emoji2 = u"\u274E"
await msg.add_reaction(emoji=emoji1)
await msg.add_reaction(emoji=emoji2)
def check(reaction, user):
return user == ctx.message.author and reaction.emoji == u"\u2705"
try:
reaction, user = await self.client.wait_for('reaction_add', timeout=10.0, check=check)
except asyncio.TimeoutError:
return await msg.delete()
else:
channel = ctx.message.channel
new_channel = await channel.clone(name=channel.name, reason=f'Clear Channel ran by {ctx.message.author.name}')
await new_channel.edit(position=channel.position)
await channel.delete(reason=f'Clear Channel ran by {ctx.message.author.name}')
await new_channel.send('Clear: Channel has now been cleared.', delete_after=7)
else:
await ctx.send(f"Sorry, you do not have access to this command.", delete_after=5)
Here's a function I use for generating check functions for wait_for:
from collections.abc import Sequence
def make_sequence(seq):
if seq is None:
return ()
if isinstance(seq, Sequence) and not isinstance(seq, str):
return seq
else:
return (seq,)
def reaction_check(message=None, emoji=None, author=None, ignore_bot=True):
message = make_sequence(message)
message = tuple(m.id for m in message)
emoji = make_sequence(emoji)
author = make_sequence(author)
def check(reaction, user):
if ignore_bot and user.bot:
return False
if message and reaction.message.id not in message:
return False
if emoji and reaction.emoji not in emoji:
return False
if author and user not in author:
return False
return True
return check
We can pass the message(s), user(s), and emoji(s) that we want to wait for, and it will automatically ignore everything else.
check = reaction_check(message=msg, author=ctx.author, emoji=(emoji1, emoji2))
try:
reaction, user = await self.client.wait_for('reaction_add', timeout=10.0, check=check)
if reaction.emoji == emoji1:
# emoji1 logic
elif reaction.emoji == emoji2:
# emoji2 logic
except TimeoutError:
# timeout logic

Trying to get my bot to kick members that spam and have more than 3 violations

I can get the bot to delete spam messages perfectly, I cannot get it to kick members who's violation count is over 3.
if message.author.name in logs:
if message.author.name in users is True:
return
else:
delta = message.created_at-logs[message.author.name].lastMessage
if(delta.seconds < timeout):
logs[message.author.name].violations += 1
await message.delete()
print("Spam Detected!")
print("In Channel:", message.channel)
print("Spammer:", message.author.name)
print("Message: " + message.clean_content)
print("Time Deleted:", str(datetime.datetime.now()), "\n")
name = message.author.name
if name in logs:
log = logs[name]
if log.violations > 3:
await discord.Member.kick(reason=None)
logs[message.author.name].lastMessage = message.created_at
else:
logs[message.author.name] = Log(message.created_at)
The await discord.Member.kick(reason=None) pulls an error of TypeError: kick() missing 1 required positional argument: 'self'.
I've also tried using await discord.Guild.kick(user=user, reason=None) with the same error.
Try doing await message.guild.kick(message.author).
(Note that gives an error if the message is not sent in a guild)
Basically it fetches the guild that the message was sent in with message.Guild, and kicks the person who sent the message (message.author).
The reason why discord.Member.kick(reason=None) did not work was because discord.Member was a type, not an object.
Doing message.author should have been the correct way.
(It would also make more sense since its saying "fetch the author of this message", rather than saying "fetch the member of this discord", given the fact that there are a lot of members on discord)
The following reason is the same for discord.Guild.kick(user=user, reason=None) not working.

Delete all the bot's messages in a channel

I am trying to create a command in my bot that will delete all the bot's messages only. Is there a way to iterate through all the channel's messages and if it is a message the bot sent, the bot would delete it? I've understood delete_message but I can't figure out how to iterate through all the channel's messages, if that is even possible.
The following code wouldn't iterate through all the channel's messages, but it would delete the message if the author ID is 383804325077581834:
#bot.event
async def on_message(message):
if message.author.id == '383804325077581834':
await bot.delete_message(message)
383804325077581834 is my bot's ID. So I would like to know how I can iterate through all channel messages and delete those that were sent by my bot. Thank you so much!
EDIT: Tried doing this:
#bot.command(pass_context=True)
async def delete(ctx, number):
msgs = []
number = int(number)
async for msg in bot.logs_from(ctx.message.channel, limit=number):
if msg.author.id == '383804325077581834':
msgs.append(msg)
await bot.delete_messages(msgs)
But I get the error discord.ext.commands.errors.MissingRequiredArgument: number is a required argument that is missing.
#bot.command(pass_context=True)
async def delete(ctx):
msgs = []
async for msg in bot.logs_from(ctx.message.channel):
if msg.author.id == '383804325077581834':
msgs.append(msg)
await bot.delete_messages(msgs)
The command you tried using needed a number parameter which would delete that number of messages from the channel.
The logs_from function will delete all messages from that channel if the limit is not passed into it.
EDIT: Forgot to remove number, whoops.

Resources