Discord py mute command specifying reason without time - python-3.x

So I made a mute command and converted it to an optional tempmute yesterday, it worked fine except for when I specified a reason and no time. Obviously that would create a problem but sadly I could not figure out how to fix it. i.e: N?mute user time reason works but N?mute user reason doesn't. If I don't specify the reason nor time it mutes them for 2 years. I want to be able to perm mute people with a reason and also temp mute people with a reason (already works). Hereby the code:
#commands.command()
#commands.has_permissions(manage_roles=True)
async def mute(self, ctx, member: discord.Member, time="670d", *, reason="no reason provided"):
role = discord.utils.get(ctx.guild.roles, name="Muted")
admin = discord.utils.get(ctx.guild.roles, name="Admin")
mod = discord.utils.get(ctx.guild.roles, name="Moderator")
guild = ctx.guild
pfp = member.avatar_url
time_convert = {"s":1, "m":60, "h":3600,"d":86400}
tempmute= int(time[0]) * time_convert[time[-1]]
if admin in member.roles or mod in member.roles:
embed = discord.Embed(title="Mute failed", description="Staff cannot be muted.", color=0xff4654)
await ctx.channel.send(embed=embed)
return
if role in member.roles:
embed = discord.Embed(title="Mute failed", description="**{0}** is already muted!".format(member), color=0xff4654)
await ctx.channel.send(embed=embed)
if role not in guild.roles:
perms = discord.Permissions(send_messages=False, speak=False)
await guild.create_role(name="Muted", permissions=perms)
await member.add_roles(role, reason=reason) #User muted
embed = discord.Embed(title="User muted!", description="**{0}** was succesfully muted by **{1}** for **{2}**.".format(member, ctx.message.author, reason), color=0xff4654)
await ctx.channel.send(embed=embed)
channel = discord.utils.get(guild.channels, name="logs")
embed = discord.Embed(title=" ", description="**{0}** was muted by **{1}**. Reason: **{2}**".format(member, ctx.message.author, reason), color=0xff4654)
embed.set_author(name="{0}".format(member), icon_url=pfp)
await channel.send(embed=embed) #Log
await asyncio.sleep(tempmute)
await member.remove_roles(role)
else:
await member.add_roles(role, reason=reason) #User muted
embed = discord.Embed(title="User muted!", description="**{0}** was succesfully muted by **{1}** for **{2}**.".format(member, ctx.message.author, reason), color=0xff4654)
await ctx.channel.send(embed=embed)
channel = discord.utils.get(guild.text_channels, name="logs")
embed = discord.Embed(title=" ", description="<#{0}> was muted by <#{1}>. Reason: **{2}**".format(member.id, ctx.message.author.id, reason), color=0xff4654)
embed.set_author(name="{0}".format(member), icon_url=pfp)
await channel.send(embed=embed) #Log
await asyncio.sleep(tempmute)
await member.remove_roles(role)
I tried doing time: Optional[int] as someone else suggested but alas it broke it entirely.

To answer bluntly, there isn't a nice way of doing this. What I recommend is that you split the command into two separate ones, mute and tempmute.
If you really do want it as one function, you could do something like this:
import re
async def mute(self, ctx, member: discord.Member, *options):
if len(options) == 2: # User specified date and reason
expiry, reason = options
if len(options) == 1: # User specified one of the above so we need to find out which
if re.match(r"\d+\D", options[0]):
# Regex to check for one or more digit then a single non-digit
expiry = options[0]
reason = None
else:
expiry = None
reason = options[0]
On a slightly unrelated note, you should not do await asyncio.sleep(tempmute). Long sleeps are innacurate and if the bot restarts, the person will never be unmuted. You should create a database with datetimes of when the user should be unmuted and have a looping task which queries the database and unmutes users when their time is up.

Related

Discord.py bot sending messages multiple times

I'm new to coding in python, and I'm having trouble with this code.
#client.event
async def on_message(message):
if message.author == client.user:
return
if 'choose my pick' in message.content:
channel = message.channel
await channel.send('Que role tu ta?')
def check(m):
return m.content == 'top' or 'jg' or 'mid' or 'adc' or 'sup' and m.channel == channel
role = await client.wait_for('message', check=check)
sentence_start = random.choice(inu_exp)
iten = random.choice(itens)
if 'top' in role.content:
champion = random.choice(top_champs)
await channel.send(f'{sentence_start} {champion} de {iten}')
await client.process_commands(message)
It works as I want to, when someone type 'choose my pick' it asks 'Que role tu ta', but it asks many times, and it also sends the answer await channel.send(f'{sentence_start} {champion} de {iten}') many times.
Here's the output: https://prnt.sc/y1cucv
I'm using python 3.8.6
I have tested your code onto another bot and it only sent the message once! Make sure that there aren't multiple clients running at the same time! Another thing to make sure of is your internet ping as this could cause lag and result in having multiple messages! (Past Experience)
In addition, if your still having problems please send the entire code (without the token of course) because many variables are missing and I can't do anything without those variables. For example the inu_exp!

Error await keeps creating multiple channels instead of checking

Hi I'm having an issue where my code is creating more than one channels. The function of the code should:
Check to see if the channel exists
If Channel does exist don't create a new one.
Otherwise, if the channel does not exist create one.
In a nutshell, how it supposed to work, The on_message event checks for a response from a user in a direct message sent to the bot, their message is then relayed to a guild channel that either already exists otherwise a new one is created before the message is sent.
In this is the issue is that I can't get around it with a check to see if the channel exists, it sure does but it just duplicates everytime a message is sent:
I have tried both these methods to check:
#Check 1
for channel in guild.text_channels:
if channel.name == f"{message.author.name.lower()}{message.author.discriminator}" and channel.name is not None:
await channel.send(embed=embed)
else:
channel_non = await guild.create_text_channel(f'{message.author.name}{message.author.discriminator}', overwrites=overwrites, category=self.bot.get_channel(744944688271720518))
await channel_non.send(embed=embed)
#Check 2
channel_present = False
for channel in guild.text_channels:
if channel.name == f"{message.author.name.lower()}{message.author.discriminator}":
await channel.send(embed=embed)
channel_present = True
if channel_present:
channel_non = await guild.create_text_channel(f'{message.author.name}{message.author.discriminator}', overwrites=overwrites, category=self.bot.get_channel(744944688271720518))
await channel_non.send(embed=embed)
The check 2 code causes the on_message event to do nothing. Help would be much appreciated I don't know where I'm going wrong here.
Honestly, I would just do it by a command.
#client.command()
async def command(ctx):
author = ctx.message.author
guild = ctx.guild
for channel in guild.text_channels:
if channel.name == ctx.message.author:
await channel.send(embed = embed)
return
channel = await guild.create_text_channel(f"{ctx.message.author}", overwrites = overwrites, category = self.bot.get_channel(744944688271720518))
await channel.send(embed = embed)

discord.py my mute command blocks other commands

I'm making a timed mute command for my Discord server but when I use .mute command, the bot doesn't respond for a while, then it works. But while the member is muted, other commands are not working and when mute time ends, the commands that i wrote in the mute time are working. Here is the command:
#commands.command()
async def mute(self, ctx, member: discord.Member, time1, reason):
time1 = int(time1) * 60
guild = ctx.guild
var1 = 0
for role in guild.roles:
if role.name == "Muted":
var2 = var1
continue
else:
var1 += 1
for channel in guild.channels:
await channel.set_permissions(guild.roles[var2], send_messages=False)
for rol in guild.roles:
if rol.name == "Muted":
await member.add_roles(rol)
await ctx.send(f"{member.mention} muted by {ctx.author.mention}, reason : {reason}")
time.sleep(time1)
await member.remove_roles(rol)
await ctx.send(f"{member.mention} can type now.")
And when member is unmuted automatically, "on_ready" event works again with no reason.
Nothing else in your code will run when someone is mutted because you use time.sleep. Using time.sleep will freeze your entire code.
To solve your problem, you can use the asyncio library, especially, asyncio.sleep.

How do I make and assign roles using discord.py?

I'm trying to make a completly new role in discord (with discord.py) but everything I try, doesn't work. I allready tried
await guild.create_role(name("R3KT")) #NameError: name 'guild' not defined
and
author = ctx.message.author
await client.create_role(author.server, name="role name") #NameError: name 'ctx' not defined
I've tried changing ctx and guild to 'client' but it still didn't work. if needed I'll send the whole code (without the discord name and bot key)
here's the entire code:
import discord
token = "bot-key"
client = discord.Client()
#client.event
async def on_ready():
print("Bot is online and connected to Discord")
#client.event
async def on_message(message):
message.content = message.content.upper()
if message.author == client.user:
return
if message.content.startswith("AS HELLO"):
await message.channel.send("works")
elif message.content.startswith("AS CREATEROLE"):
if str(message.author) == "myDiscName#6969":
author = message.author
await discord.create_role(author.server, name="R3KT")
elif message.content.startswith("AS GIVEROLE"):
if str(message.author) == "myDiscName#6969":
print("give role")
client.run(token)
In the second example you provided, it looks like you're using a mish-mash between the old d.py (0.16.x) docs and d.py rewrite (1.x).
Make sure you have the most up-to-date version (rewrite) installed, as async is no longer being maintained.
Here's an example with the command decorator (usage: !newrole Member)
#client.command()
async def newrole(ctx, *, rolename=None):
if not rolename:
await ctx.send("You forgot to provide a name!")
else:
role = await ctx.guild.create_role(name=rolename, mentionable=True)
await ctx.author.add_roles(role)
await ctx.send(f"Successfully created and assigned {role.mention}!")
The mentionable kwarg isn't compulsory - it defaults to False if not specified - I just set it to True for the example. You're also able to write your own permissions for the role.
And another example using on_message - recommended to use command decorator instead, as args are easier to deal with
async def on_message(message):
args = message.content.split(" ")[2:] # 2: because prefix contains space
if message.content.lower().startswith("as createrole"):
role = await message.guild.create_role(name=" ".join(args))
await message.author.add_roles(role)
await message.channel.send(f"Successfully created and assigned {role.mention}!")
References:
discord.py Rewrite docs
Guild.create_role()
discord.Permissions
Member.add_roles()
discord.on_message()
You don't want to check by name and number (as you can change that if you have nitro), you should check by ID which is unique to every user and immutable.
Also note that server is referred to as guild in discord.py.
elif message.content.startswith("AS CREATEROLE"):
if message.author.id == YOUR_ID_GOES_HERE: # find your id by right clicking on yourself and selecting "copy id" at the bottom of the menu of options
author = message.author
# create a role named "R3KT" that has the permissions "Administrator" that has the color "white" that is not displayed separately and is not mentionable by everyone
await message.guild.create_role(name="R3KT", permissions=discord.Permissions(8), colour=discord.Colour.from_rgb(255, 255, 255), hoist=False, mentionable=False)
Permissions integer calculator
discord.Guild.create_role
With giving yourself a role:
elif message.content.startswith("AS GIVEROLE"):
if message.author.id == YOUR_ID_GOES_HERE:
user = message.author
await user.add_roles(message.guild.get_role(ROLE_ID_GOES_HERE), reason='Someone did a giverole bot command') # left click on the role and click "copy id"
discord.Member.add_roles
discord.Guild.get_role

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

Resources