discord.py how to accept optional arguments - python

I have made a system that uses JSON files to create a currency system with a discord bot using discord.py
I want users to be able to do +balance to show them how much they have, or +balance in order to check someone else's balance. I tried doing a try: to grab the members' balance and excepting if there was no argument for the member, but I don't know what kind of error it is for that. How do I make it so that the bot will assume if there is no argument that the member they want is ctx.message.author?
if exists('balances.json'):
with open('balances.json', 'r') as file:
balances = json.load(file)
print(balances)
def save():
with open('balances.json', 'w+') as f:
json.dump(balances, f)
#Allows users to check their balance
#bot.command(pass_context=True)
async def balance(ctx, member: discord.Member):
global balances
if str(member.id) not in balances.keys():
await ctx.channel.send(f'{member.name} not registered')
return
if str(member.id) in balances.keys():
requestedUserBalance = balances[str(member.id)]
await ctx.channel.send(f'{member.name} has {requestedUserBalance} LotusTokens')

To give a function a default behavior when an optional variable is not passed, you can give it a default value of None and a behavior when that variable is None.
#bot.command(pass_context=True)
async def balance(ctx, member: discord.Member=None):
if member is None:
member = ctx.message.author
# do stuff

Related

Converting to "discord.member" failed for parameter "member"

#bot.command()
async def test(ctx,user : discord.member):
if user==None:
user=ctx.author
test=Image.open("test.jpg")
asset=ctx.author.avatar_url_as(size=128)
data = BytesIO(await asset.read())
pfp=Image.open(data)
pfp=pfp.resize((177,177))
test.paste(pfp,(446,339))
test.save("test1.jpg")
await ctx.send(file=discord.file('test1.jpg'))
its not working and i am getting following errors
if i type !test it would show me this error
discord.ext.commands.errors.MissingRequiredArgument: user is a required argument that is missing.
but if use !test #mention.some.username it will show me this error
Converting to "discord.member" failed for parameter "user".
There are two mistakes that you are doing here.
discord.member is a module, You are meant to use discord.Member class for annotation of user.
You are doing:
if user == None:
user = ctx.author
but you haven't set a default value to user so it is a required argument.
The correct way is:
#bot.command()
async def test(ctx, user: discord.Member = None):
if user is None:
user = ctx.author
# rest of code here
We just declared a default value to the user as None and properly annotate user as discord.Member
just remove user : discord.member and it should work fine
#bot.command()
async def test(ctx):
user=ctx.author
test=Image.open("test.jpg")
asset=ctx.author.avatar_url_as(size=128)
data = BytesIO(await asset.read())
pfp=Image.open(data)
pfp=pfp.resize((177,177))
test.paste(pfp,(446,339))
test.save("test1.jpg")
await ctx.channel.send(file=discord.file('test1.jpg'))

Banning user outside + inside server

I'm trying my best to make a command that bans people inside and outside a server. Is this possible?
#client.command(aliases=["banmember", "banuser"])
async def ban(ctx, member: discord.Member, *, reason=None):
await ctx.message.delete()
if reason is None:
reason = ""
if reason is not None:
reason = reason
try:
await member.ban(reason=reason)
except:
user = await commands.converter.UserConverter().convert(ctx, user)
banneduser = await client.fetch_user(user.id)
await ctx.guild.ban(banneduser, reason=reason)
It does not look possible, as if i used UserConverter it wouldn't ban members inside the server, if i used Members it wouldn't ban people outside the server. How can I do both?
You can use use typing-union which allows for the command to take in any of the specific types instead of a singular type..
Here is a simple example to send the name to verify it is working for both.
import typing
#bot.command()
async def union(ctx, member: typing.Union[discord.Member, discord.User]):
await ctx.send(member.name)

Discord.py nickname changin

#client.command(pass_context=True)
async def ani(ctx, member: discord.Member, nick):
if member == discord.Member:
member = ctx.message.author
await member.edit(nick=nick)
else:
member = ctx.message.author
await member.edit(nick=nick)
I have read multiple tutorials and the documentation. I do not understand why my code does not work as expected.
Please help, thank you in advance.
There are a few things wrong with your code, or things that could be done better. I will list them out here.
Originally define your variables as None: If a member isn't mentioned, your bot will throw an error telling you that you missed a variable. The same can be said if you don't include a nickname for nick. To combat this, you can make None the default variable by using =None after each of your variables (excluding ctx, of course). You can handle them later on.
Change if member == discord.Member to just if member: Since you already have member defined as a discord.Member object, this statement would not work. If you previously defined your variables as None, then you can simply use if member to check if this variable is not None. If it is, it will immediately go to the else statement.
Catch other errors with a try-except statement: To avoid errors from flooding in your console, you can instead tell the ctx.author what went wrong. Using try and except, you can catch errors such as Missing Permissions or Invalid Form Body.
Use * before nick: If you do not use an asterisk before your nick variable, you will find that the following will happen:
Here is the above expressed in the code below.
#client.command(pass_context=True)
async def ani(ctx, member: discord.Member=None, *, nick=None):
if nick == None:
nick = "A Cool Nickname"
try:
if member:
name_before = member.display_name
# Why not member.nick? If the member did not have a nickname to begin with,
# member.nick would return None
await member.edit(nick=nick)
else:
member = ctx.message.author
name_before = member.display_name
await member.edit(nick=nick)
await ctx.send(f"Changed {member.mention}'s nickname from {name_before} to {member.display_name}")
except Exception as e:
# 403 Forbidden (error code: 50013): Missing Permissions
# - You are above the bot in the role hierarchy
# - The bot does not have the ability to change nicknames
# 400 Bad Request (error code: 50035): Invalid Form Body
# - In nick: Must be 32 or fewer in length.
await ctx.send(str(e))
If you want to make it a bit more complex, you could include a typing.Optional (import typing), which would allow the ctx.author to change their own nickname without having to mention themselves (and avoiding nasty errors). All you have to do is replace a single line.
async def ani(ctx, member: typing.Optional[discord.Member]=None, *, nick=None):

How do I make mentioning a member optional within a command?

I've created a code that sends the gif of a hug on command and specifies who it's to, however, I also want to make it optional to mention a member.
the current code is:
#client.command()
async def hug(ctx, member):
username = ctx.message.author.display_name
embed = discord.Embed(title = (f'{username} has sent a hug to {member}!'), description = ('warm, fuzzy and comforting <3'), color = 0x83B5E3)
image = random.choice([(url1), (url2),....(url10)])
embed.set_image(url=image)
await ctx.channel.send(embed=embed)
I want to change it so that if the author uses the command and doesn't mention the member, the command still works, and sends one of the gifs instead. Do I have to create an if statement?
Additionally, if it is possible, how do I change it so that the member's display name is used just like how the author's display name is used?
I've tried doing something like this, but it doesn't work:
#client.command()
async def hug(ctx, member):
username = ctx.message.author.display_name
name = member.display_name
embed = discord.Embed(title = (f'{username} has sent a hug to {name}!'), description = ('warm, fuzzy and comforting <3'), color = 0x83B5E3)
image = random.choice([(url1), (url2),...(url10)])
embed.set_image(url=image)
await ctx.channel.send(embed=embed)
Thank you in advance for any help
You can define your member argument to None by default. If you invoke your command without mentionning anyone, member will have None as value and the if member statement won't be triggered.
Also, by defining member as a Member object in the function's arguments, you'll be able to access the mentionned member's informations.
Here's how you use it :
#client.command()
async def hug(ctx, member: discord.Member = None):
if member:
embed = discord.Embed(title=f'{ctx.author} has sent a hug to {member}!',
description='warm, fuzzy and comforting <3',
color=0x83B5E3)
else:
embed = discord.Embed(color=0x83B5E3)
image = random.choice([(url1), (url2),....(url10)])
embed.set_image(url=image)
await ctx.channel.send(embed=embed)

How to move a specific person to a specific VC

I'm trying to get it so when you type a command it will move me instantly, right now you have to #me on discord
#commands.command(name='movejohan', aliases=['mj', 'MJ'])
async def MoveJ(self, message, member: discord.Member = None):
"""Moves Johan to xxxxx"""
channel = bot.get_channel(xxxxxxxxxx)
await member.move_to(channel, reason='Moved By {}'.format(message.author))
This is what I have and I don't know how to make it so it moves me specifically, I thought to change member: discord.Member = None to member: discord.Member = bot.get_user(xxxxxxxx)
but that did not work, any ideas?
There's nothing wrong with how you're using the member: discord.Member = None argument.
I'd suggest something like this:
#commands.command(name='movejohan', aliases=['mj', 'MJ'])
async def MoveJ(self, message, member: discord.Member = None):
"""Moves Johan to xxxxx"""
if not member:
member = discord.utils.get(message.guild.members, id=xxxxxxxx) # put your user ID here!
# As Fin mentioned, you can also use message.guild.get_member(userID)
channel = self.bot.get_channel(xxxxxxxxxx)
await member.move_to(channel, reason='Moved By {}'.format(message.author))
When you're putting in the member argument, you can use any attribute a member has - you can use their user ID, display name, username, you don't need to explicitly mention them.
You want to specify the guild to get the member from, which can be done using context:
#commands.command(name='movejohan', aliases=['mj', 'MJ'])
async def MoveJ(self, message):
"""Moves Johan to xxxxx"""
channel = self.bot.get_channel(xxxxx)
johan = message.guild.get_member(xxxxx)
await johan.move_to(channel, reason='Moved by {}'.format(message.author))

Categories

Resources