Python Compare lists - python-3.x

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:

Related

How do I make Discord.py bot remove roles sooner?

I have made a bot which automates role adding and removal. It all works, but the adding works in 15/20 seconds after running the command. The removal takes up to 15 minutes.
What am I doing wrong? I have started a few months ago with programming and started with Python, so any hint is usefull.
The guildid, channelid and roleid are numbers now, I put placeholders in.
#tasks.loop(seconds=10)
async def autoderolergame():
tasks = []
guild = client.get_guild(guildid)
channel = guild.get_channel(channelid)
role = guild.get_role(roleid)
for member in guild.members:
if member not in channel.members:
try:
await member.remove_roles(role, reason='Leaving Voice channel')
except:
pass
if str(member.status) == 'offline':
try:
await member.remove_roles(role, reason='Going offline, so no more VIP rain')
except:
pass
I also try to subtitute 'for member in guild.members' with
'async for member in guild.fetch_members():'
Sadly this made no difference.
I am thinking about saving a list of users in the channel and updating that every 10 seconds and then look at the list and select all users which aren't in the channel anymore, remove the role and then remove them from the list, would that be a better solution?
I rather don't because I would like to use the built in functions of Discord.py.
Thanks for your help!
You can use an asynchronous approach to make it faster
import asyncio
remove_roles_semaphore = asyncio.Semaphore(20)
async def remove_role(member, role, reason):
async with remove_roles_semaphore:
try:
await member.remove_roles(role, reason=reason)
except:
pass
#tasks.loop(seconds=10)
async def autoderolergame():
guild = client.get_guild(guildid)
channel = guild.get_channel(channelid)
role = guild.get_role(roleid)
tasks = []
for member in guild.members:
if member not in channel.members:
tasks.append(remove_role(member, role, 'Leaving Voice channel')
if str(member.status) == 'offline':
tasks.append(remove_role(member, role, 'Going offline, so no more VIP rain')
await asyncio.gather(*tasks)
Though keep in mind that you can get easily ratelimited for this, if you hit the global ratelimit (50 requests per second) you can get banned. Use this method at your own risk (you might want to enable logging to see any ratelimit warnings)
I used asyncio.Semaphore to limit the amount of requests made concurrently, feel free to change the value, but remember -- the higher the value the higher the chances to get banned (do not exceed 50).
Sadly I cannot really test the code so I can't promise that it's going to be faster.
I found a way to solve the problem.
I was using the code to look at all the guild members.
Since I want roles to be removed upon leaving the channel, I only needed too look at the roles a user has and then check if they are still in the room.
So this is my new code:
#tasks.loop(seconds=10)
async def autoderolergame():
tasks = []
guild = client.get_guild(guildid)
channel = guild.get_channel(channelid)
role = guild.get_role(roleid)
for member in role.members: (<-- only changed this from guild to role)
if member not in channel.members:
try:
await member.remove_roles(role, reason='Leaving Voice channel')
except:
pass
if str(member.status) == 'offline':
try:
await member.remove_roles(role, reason='Going offline, so no more VIP rain')
except:
pass
I also changed to seconds to a longer interval because of the tip Łukasz Kwieciński gave me regarding the rate limit. It's not an issue now since it works like 1000x times faster.

Discord.py adding modifiers to commands

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

Why does it show me a sum of 0?

I just wrote a discord bot and used a function to show me the members of each guild the bot is in. That worked all the time but now i wanted to use a other server so i copied my files and dragged them on the other server. But for some reason now it says the sum of all the member = 0. Can someone help me?
Thx in advance ^^
async def member():
while True:
for guild in my_guild_id: # list of guild id's
guild = bot.get_guild(guild)
member = sum(discord.member and not member.bot for member in guild.members)
for channel in members_channel_id: # list of channel id's
check = str(guild.get_channel(channel)).encode('ascii', 'ignore')
if check != none: # none = b'None' cause there were some bugs
channel = bot.get_channel(channel)
await channel.edit(name=f"👥Servermember: {Member}")
await asyncio.sleep(20)
Your function has the same name as a variable in the function, you should be using a different name for it.
Also, none takes a capitalized N: None.
Finally, you're using the variable Member when editing the channel, but this variable isn't defined, python is case-sensitive.
I don't think you just "copied and dragged" your files, and that couldn't work on another server...

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.

Gathering reactions from a private DM but ignoring bot reactions

I'm making a command for my bot where it sends you this message:
The two reactions (tick and cross) become pre-reacted by the bot to show the user what to do. I wan't my bot to wait a set period of time and check for what the user has reacted with, at the moment my bot counts its own reactions so the count is false.
How would I go about doing this, everything I have tried has failed and I can no longer Ctrl+Z to the point where I have a vaguely working code.
Thanks.
EDIT: I actually fixed my problem about half an hour ago after many attempts, here's what I used:
res1 = await client.wait_for_reaction(emoji="✅", message=message_1, timeout=10)
res2 = await client.wait_for_reaction(emoji="❌", message=message_1, timeout=10)
if not res1.user == None or not res2.user == None:
if not res1.user.id == client.user or res2.user.id == client.user:
The best way to do this is to add a check to you wait_for_reaction that just checks the reacting user against client.user. This has the benefit of also working in scenarios where you want to accept exactly one reaction, and can't do another loop if you accidentally see a bot reaction
def not_bot_reaction(reaction, user):
return user != client.user
...
res1 = await client.wait_for_reaction(emoji="✅", message=message_1, timeout=10, check=not_bot_reaction)
res2 = await client.wait_for_reaction(emoji="❌", message=message_1, timeout=10, check=not_bot_reaction)
if res1 or res2: # If the wait_for times out, it will return None instead of the namedtuple
...

Resources