py-discord-role-selector/bot.py

149 lines
4.6 KiB
Python

import re
import json
import os
import asyncio
import shutil
import discord
intents = discord.Intents(messages=True, guilds=True, message_content=True, reactions=True, members=True)
client = discord.Client(intents=intents)
settings = {}
@client.event
async def on_ready():
print("READY.")
@client.event
async def on_message(msg):
chl = await client.fetch_channel(msg.channel.id)
msg = await chl.fetch_message(msg.id)
guild = await client.fetch_guild(chl.guild.id)
member = await guild.fetch_member(msg.author.id)
bot = await guild.fetch_member(client.user.id)
arg_str = msg.content
arg_str = re.sub(r"\s{1,}", " ", arg_str)
args = arg_str.split(" ")
cmd = args.pop(0)
arg_str = arg_str.lstrip(cmd).strip()
# Allow creating quick roles:
if cmd == ".new":
if len(arg_str) > 0:
if member.guild_permissions.administrator:
new_role = await guild.create_role(name=arg_str)
await new_role.edit(position=bot.top_role.position - 1)
await msg.delete()
return
# Allow users to change their own color:
if cmd == ".color":
if len(args) > 0:
color = args[0].lower()
print(color)
existing_role = None
color_regex = r"^#[A-Fa-f0-9]{6}$"
# Discord treats all zeroes as transparent:
if color == "#000000":
color = "#010101"
if not re.search(color_regex, color):
await msg.add_reaction("")
await asyncio.sleep(10)
await msg.delete()
return
# Remove any previously assigned color roles:
for role in member.roles:
if re.search(color_regex, role.name):
await member.remove_roles(role)
# Cache the members early:
members = [member async for member in guild.fetch_members(limit=None)]
# Iterate through all guild roles, find existing role, and delete those without members:
for role in guild.roles:
if not re.match(color_regex, role.name):
continue
if role.name.lower() == color:
existing_role = role
break
# Build a list of role members by iterating through members:
role_members = [m for m in members if role in m.roles]
if len(role_members) < 1:
try:
await role.delete(reason="Role contains no more members.")
except:
pass
if existing_role is None:
color_value = discord.Color(int(color.strip("#"), 16))
existing_role = await guild.create_role(name=color, color=color_value)
await existing_role.edit(position=bot.top_role.position - 1)
await member.add_roles(existing_role)
await msg.delete()
# Handle both reaction events:
async def on_raw_reaction_add_or_remove(rxn, add=True):
guild_id = rxn.guild_id
channel_id = rxn.channel_id
message_id = rxn.message_id
guild = await client.fetch_guild(guild_id)
chl = await client.fetch_channel(rxn.channel_id)
msg = await chl.fetch_message(rxn.message_id)
author_member = await guild.fetch_member(msg.author.id)
user_member = await guild.fetch_member(rxn.user_id)
# Exit early if:
if not author_member.guild_permissions.administrator: return
if user_member.bot: return
if client.user in msg.mentions:
lines = msg.content.split("\n")
found_line = None
for line in lines:
if rxn.emoji.name in line:
found_line = line
break
role_id = re.search(r"<@&([0-9]{1,})>", found_line)
role_id = int(role_id.group(1)) if role_id is not None else None
if role_id is None:
return
role = await guild.fetch_role(role_id)
if add:
await msg.add_reaction(rxn.emoji.name)
await user_member.add_roles(role)
else:
await user_member.remove_roles(role)
@client.event
async def on_raw_reaction_add(rxn):
await on_raw_reaction_add_or_remove(rxn, True)
@client.event
async def on_raw_reaction_remove(rxn):
await on_raw_reaction_add_or_remove(rxn, False)
def main():
token = None
with open("token.txt") as f:
token = f.read()
client.run(token)
if __name__ == "__main__":
main()