Discord.py adding modifiers to commands - python-3.x

So I've seen other bots where you can do a ban command, for example !ban 383777390851260426 -r you crossed the line -t 7d -Nodm
Im curious how you get the modifiers on such as -r.
Any help is appreciated

These are called arguments or parameter. One way of parsing through them is by looking at all the text you got from the method, and look for each option or parameter you have.
Here is an example of parsing the arguments you get after mentionning the member, feel free to modify this code to connect it with other commands (such as parsing the time_left variable into a Datetime, setting a timer for unbanning, etc)
Please note that this code only really uses the reason, and not the time or the -nodm since this require more complex implementation. This is just an example to show you how you could get this started
#command()
async def ban(ctx: Context, member: Member, *args):
reason_index = args.index("-r") # Returns -1 if not found, else the position of the "-r"
reason = args[reason_index+1] if reason_index != -1 else None
time_index = args.index("-t")
time_left = args[time_index +1] if time_index != -1 else None
nodm = True if "-Nodm" in args else False
await member.ban(reason=reason)
await ctx.send(f"I banned {member.mention} with reason {reason} for {time_left}")

Do you mean like this?
#bot.command() #ban command
#commands.has_permissions(ban_members=True)
async def ban(ctx, member:discord.Member=None, *, reason=None): # the * makes that reason argument takes all text even if it has spaces
if member == ctx.message.author:
await ctx.channel.send("You cannot ban yourself")
if member == None:
await ctx.channel.send("Who are you going to ban?")
if reason == None:
reason = "No reason specified"
await member.ban(reason=reason)
await ctx.send(f"{member.mention} has been banned because of {reason}")
I have no idea if this is not what you ment, also no idea what you ment with -t 7d etc

Related

Get Discord username and discriminator through a mention

I have tried reading the docs, but I don't understand what is going on here and how to fix it. I am trying to map a mention to its proper Name#NNNN form, but alas, it is proving to be a fruitless endeavor for me.
import discord
from discord.ext import commands
from collections import defaultdict
client = commands.Bot(command_prefix=">")
#client.event
async def on_ready():
print('Ready!')
jobz = {}
'''PART 1 v v v'''
#client.event
if message.content.startswith('>jobsched'):
author = message.author
jobz[author].append(...)
await channel.send(jobz[author])
'''PART 2 v v v'''
if message.content.startswith('>when '):
channel = message.channel
worker = list(filter(None, message.content[6:].split(' ')))[0]
uname = message.mentions[0].mention
await channel.send(jobz[uname])
PART 1:
I run this first, the send works as expected as seen below:
>jobsched 'a'
>jobsched 'b'
As seen in the last line, this spits out ['1a', '2b']
PART 2:
Here is where I have my issue.
>when #Name
I expected this to spit out ['1a', '2b'] because I expected it to look up or translate the mentioned name, find its respective name and discriminator. I thought this should happen since, in the above piece, that is how the name gets written into the dictionary is i.e. Name#1234: ['1a','2b']
Printing out .keys() shows that the key has the name and discriminator i.e. Name#1234 in the 'jobz' dictionary.
However, I can't seem to get the mention to give me the Name and Discriminator. I have tried doing mentions[0].mention from what I have seen here on stackoverflow, but it doesn't result in a Member class for me, just a string, presumably just '#Name'. If I leave it alone, as shown in my 'worker' variable, it passes an empty list. It should pull the list because when I override it to jobz['Name#1234'] it gives me the list I expect.
Can anyone please help?
just cast the member object to string to get the name and discriminator as it says in the discord.py docs. To mention someone, put the internal representation like this: f'<#{member.id}>'. To stop problems like this, use client.command() it's way easier to put in parameters, and easier to access info. So, here would be the code:
#client.command()
async def when(ctx, member: discord.Member):
await ctx.send(jobz[str(member)])
Also, if your worker variable is returning None, you're not passing a parameter at all
mentions is a list of Member objects, so when you do mentions[0] you are referencing a Member. Thus, mentions[0].mention is the formatted mention string for the first-mentioned (element 0) Member.
You probably want mentions[0].name and mentions[0].discriminator
See: https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.mentions

How do I fix a counting program where the current number is incremented when a duplicate author is detected?

I'm having a bit of trouble: I made a counting channel and this part of my code:
recent_author = (await message.channel.history(limit=2).flatten())[1].author
if message.author == recent_author:
await message.author.send(f"You can't hog this channel, let someone else have a turn!")
await message.delete()
prevents users from sending messages more than once; but, when the message is deleted, the variable that holds the current number is incremented. That's not supposed to happen but it does: I've tried doing so many things at this point like moving this piece of code below other pieces of code in the event function or merging the code into an if else statement--nothing seems to solve my problem. I was thinking about subtracting 1 from the current number when a duplicate author is detected; however, that would mean subtracting 1 for anything other than a number.
Here's my full code:
import discord
from discord.ext import commands
import re
class Counting(commands.Cog):
""" Users take turns incrementing a number. """
def __init__(self, client):
self.client = client
self.count_channel = 736241527847911494
self.cum_num = 98
self.num_regex = re.compile(r'(^\d+)')
#commands.Cog.listener()
async def on_message(self, message):
if message.channel.id == self.count_channel:
try:
recent_author = (await message.channel.history(limit=2).flatten())[1].author
if message.author == recent_author:
await message.author.send(f"You can't hog this channel, let someone else have a turn!")
await message.delete()
match_obj = self.num_regex.search(message.content)
if int(match_obj.group(1)) != self.cum_num + 1:
await message.author.send(f'Your message must start with **{self.cum_num + 1}**!')
await message.delete()
else:
self.cum_num += 1
except AttributeError:
await message.delete()
This looks like the if/else logic needs tweaking.
Directly after your check for the duplicate author it initialises and sets match_obj then runs the if statement.
So despite the message being removed from the text channel if the message content was valid then it will still increment the counter because nothing is preventing it from running that logic.
It looks to me like you will want to structure it as follows:
recent_author = (await message.channel.history(limit=2).flatten())[1].author
if message.author == recent_author:
await message.author.send(f"You can't hog this channel, let someone else have a turn!")
await message.delete()
else:
match_obj = self.num_regex.search(message.content)
if int(match_obj.group(1)) != self.cum_num + 1:
await message.author.send(f'Your message must start with **{self.cum_num + 1}**!')
await message.delete()
else:
self.cum_num += 1
This way if the latest author is the duplicate author it won't call the code in the else block. However, if it is not a duplicate author it will run the next stage of the code.
You could try using wait_for and in the check compare user names to the previous message, if they are the same then you can do something if not you can ignore it. You could also try using a database or memory for this.

Giving a role to another user with DiscordPy

I am trying to use a command to give a specific role using DiscordPy, but every search I use brings me to the same answers, that don't help: 1, 2, 3.
Clearly, I'm missing a fundamental part, but i have no idea what. The documentation, covers that there is a add_roles command, but that doesn't give any explanation of how to use it for another user. In fact, trying await add_roles("Team Captain") gives the error NameError: name 'add_roles' is not defined.
What am I missing here? Why does add_roles not exist when it's documented, and how do I use it against a different user.
This is (some of) what I have at the moment, but obviously, doesn't work:
import discord, discord.utils, random, os, re, json
from discord.ext import commands
from discord.utils import get
from dotenv import load_dotenv
client = discord.Client()
load_dotenv()
key = os.getenv('DISCORD_KEY')
#client.event
async def on_message(message):
if message.author == client.user:
return
print('Message from {0.author}: {0.content}'.format(message))
#Want these commands in the right channel
if str(message.channel).lower().startswith("bot"):
if message.content.lower().startswith("$addcaptain"):
if len(message.mentions) == 0:
await message.channel.send("Needs a user to give Team Captain role.")
return
else:
await add_roles("Team Captain") #Doesn't work, and I assume would be against the user who sent the message, not the mentioned one
client.run(key)
key = os.getenv('DISCORD_KEY')
add_roles is a method that belongs to the Member object. This means that you'll need to get the target member, in your case message.mentions[0] as message.mentions returns a list of Members, and then stick .add_roles(..) on the end of it.
Additionally, when adding a role, it accepts Role objects, not just a name or ID. This means you'll need to fetch the role first, which can be done in a multitude of ways, but the one I'll use is utils.get() (other methods such as Guild.get_role() are also available)
This brings us to your code:
#client.event
async def on_message(message):
# code
if str(message.channel).lower().startswith("bot"):
if message.content.lower().startswith("$addcaptain"):
if len(message.mentions) == 0:
await message.channel.send("Needs a user to give Team Captain role.")
return
else:
role = discord.utils.get(message.guild.roles, name="Team Captain")
await message.mentions[0].add_roles(role)
References:
utils.get()
Message.mentions - message.mentions[0] gets the first element of the list, which is the discord.Member that was mentioned.
Member.add_roles()
Guild.get_role() - If you want to use this, you'll do:
role = message.guild.get_role(112233445566778899)
where 112233445566778899 is the Team Captain's role ID.

Python Compare lists

So I have this script,
good_users=[]
async def callposts(ctx):
g0=str(ctx.guild)
g=g0.replace(' ','_')
sqluse(g)
x="SELECT authorid, COUNT(*) FROM posts GROUP BY authorid"
mycursor.execute(x)
k=mycursor.fetchall()
for user in k:
if user[1] > 7:
good_users.append(user)
k.remove(user)
print(user)
return k
async def kick(ctx, uid):
await ctx.guild.kick(ctx.guild.get_member(int(uid)))
#client.command(pass_context = True)
async def post_check(ctx):
ausers=list(ctx.guild.members)
lowposters= await callposts(ctx)
for user in ausers:
if user == client.user:
print(user.name+" is a bot!")
ausers.remove(user)
elif user.id in exempt:
print(user.name+" is exempt!")
ausers.remove(user)
elif user.id in good_users:
print(user.name+" is a good user!")
ausers.remove(user)
else:
await kick(ctx,user.id)
print(ausers)
What I am trying to do here is remove inactive users. So I have 2 lists that I want to compare the memberlist to, exempt and good_users. I also am checking to make sure it isn't the bot. So this script removes the bot from the list but it doesn't remove the user that's in exempt or good users. So in turn it tries to kick everyone that's not a bot. They are trying to take over!
I'm looking this over but right now I'm sick so not 100%.
The prints are just for troubleshooting purposes however, all but the firt print in the callpost function print nothing and that one for some reason only prints the user, now it isn't printing the bot so the bot may not be in the list to get removed.
Any thoughts?
You're never appending anything to exempt_users and because of the scope of good_users it is only filled with users within callposts() because you're not returning it when calling it in post_check().
Changing the following should fix your problem:
Return good_users from callposts()
Add a new variable to where you call callposts() in post_check() like
lowposters, good_users = await callposts(ctx)
So I finally figured this out, the script was looking at user.id as an attribute of user object. To fix that I had to use
elif str(user.id) in exempt): and elif str(user.id) in good_users:

Display all emotes in server but with spaces

Seems quite easy but I just can't figure it out. I basically have a script that makes the bot list all the emotes of the server, but the problem is it doesn't make spaces which means it won't preview the emotes and it will all be just text and boring as i have alot of emotes, lets use actual emojis as an example ":laughing:" shows 😂. ":laughing::laughing:" should show 😂😂 but just shows ':laughing::laughing:' as it is instead. This is because there isn't space, how can i fix that in my script? In this image you can see what i mean Now And then this
#client.command(pass_context=True)
async def emotes(ctx, msg: str = None):
"""List all emotes in this server."""
if msg:
server, found = client.find_server(msg)
if not found:
return await client.send(server)
else:
server = ctx.message.server
emojis = [str(x) for x in server.emojis]
await client.say("".join(emojis))
I suppose the problem is in absence of a space in the separator string "", which you're using for joining emojis.
Replace this
await client.say("".join(emojis))
By this
await client.say(" ".join(emojis))
#client.command(pass_context=True)
async def emotes(ctx, msg: str = None):
"""List all emotes in this server."""
if msg:
server, found = client.find_server(msg)
if not found:
return await client.send(server)
else:
server = ctx.message.server
emojis = [str(x) for x in server.emojis]
await client.say(" ".join(emojis))
different account, turns out it wasn't the spaces as it still didn't work with them, it's because I was using Async, rewrite works well and shows the emotes without having to put spaces

Resources