diff --git a/.dockerignore b/.dockerignore index b694934..056c9d4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,3 @@ -.venv \ No newline at end of file +.venv +.github +.gitignore \ No newline at end of file diff --git a/bot.py b/bot.py index a2d129d..e1a43ca 100644 --- a/bot.py +++ b/bot.py @@ -1,18 +1,22 @@ import discord -import colour -import discord.guild import os import sys -import re +from create import color_user +from manage import clean_empty_color_roles + +intents = discord.Intents.default() +intents.members = True guild_ids = sys.argv[1:] -bot = discord.Bot() +bot = discord.Bot(intents=intents) + @bot.event async def on_ready(): print("Palette Bot is up!") print(f"We have logged in as {bot.user}") + @bot.slash_command(description="Give your name a random color", guild_ids=guild_ids) async def colormerandom(ctx: discord.ApplicationContext): await color_user(ctx, ctx.user, os.urandom(3).hex()) @@ -23,61 +27,14 @@ async def colorme(ctx: discord.ApplicationContext, color: str): await color_user(ctx, ctx.user, color) -async def color_user( - ctx: discord.ApplicationContext, user, color: str -): - color = color.strip().replace(" ", "") - if re.search("^[a-f0-9]{3}$|^[a-f0-9]{6}$", color, re.IGNORECASE): - color = "#" + color - - try: - role_color = colour.Color(color) - except ValueError: - await ctx.respond( - f"Didn't recognize color: {color} - try something else, or a specific hex code!" - ) - return - print(f"Coloring {user.name} the color {role_color.web} ({role_color.get_hex()})") - color_roles: list[discord.Role] = list( - filter(lambda role: role.name.startswith("#"), ctx.guild.roles) - ) - current_role = next( - filter(lambda role: role.name == role_color.get_hex(), color_roles), None - ) - if not current_role: - current_role = await new_role(ctx.guild, role_color) - - await user.remove_roles(*color_roles) - await user.add_roles(current_role) - await ctx.respond(f"Colored you {role_color.web}!") - - -async def new_role(guild: discord.Guild, color: colour.Color): - new_role = await guild.create_role( - name=color.get_hex(), - color=discord.Color.from_rgb( - int(color.get_red() * 255), - int(color.get_green() * 255), - int(color.get_blue() * 255), - ), - ) - return await new_role.edit( - position=max(map(lambda rule: rule.position, guild.me.roles)) - 2 - ) - - @bot.slash_command(description="Clean unused colors", guild_ids=guild_ids) async def cleanbrushes(ctx: discord.ApplicationContext): - empty_color_roles = filter( - lambda role: role.name.startswith("#") and len(role.members) == 0, - ctx.guild.roles, - ) - for role in empty_color_roles: - try: - await role.delete() - except: - pass - await ctx.respond("Cleaned up all empty color roles!") + await clean_empty_color_roles(ctx) -bot.run(os.environ.get("DISCORD_TOKEN", "DISCORDTOKENNOTFOUND")) +bot.run( + os.environ.get( + "DISCORD_TOKEN", + "DISCORD_TOKEN_NOT_FOUND", + ) +) diff --git a/create.py b/create.py new file mode 100644 index 0000000..e5674bf --- /dev/null +++ b/create.py @@ -0,0 +1,47 @@ +import discord +import re +import colour +from util import create_new_role, errorAndRespond, logInfo + + +async def color_user( + ctx: discord.ApplicationContext, user: discord.Member | discord.User, color: str +): + try: + assert ( + type(ctx.guild) is discord.Guild + ), "Encountered an issue accessing the Discord guild" + + color = color.strip().replace(" ", "") + if re.search("^[a-f0-9]{3}$|^[a-f0-9]{6}$", color, re.IGNORECASE): + color = "#" + color + + try: + role_color = colour.Color(color) + except ValueError: + return await ctx.respond( + f"Didn't recognize color: {color} - try something else, or a specific hex code!" + ) + + logInfo( + f"Coloring {user.name} the color {role_color.web} ({role_color.get_hex()})" + ) + + all_color_roles: list[discord.Role] = [ + role for role in ctx.guild.roles if role.name.startswith("#") + ] + desired_color_role = next( + (role for role in all_color_roles if role.name == role_color.get_hex()), + None, + ) or await create_new_role(ctx.guild, role_color) + if desired_color_role is None: + return await errorAndRespond( + ctx, f"Failed to create new role for color: {color}" + ) + + await user.remove_roles(*all_color_roles) + await user.add_roles(desired_color_role) + return await ctx.respond(f"Colored you {role_color.web}!") + + except AssertionError as errorMessage: + return await errorAndRespond(ctx, errorMessage) diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..c25d794 --- /dev/null +++ b/manage.py @@ -0,0 +1,24 @@ +import discord +from util import errorAndRespond, logAndRespond, logError + + +async def clean_empty_color_roles(ctx: discord.ApplicationContext): + try: + assert ( + type(ctx.guild) is discord.Guild + ), "Encountered an issue accessing the Discord guild" + empty_color_roles = [ + role + for role in ctx.guild.roles + if role.name.startswith("#") and len(role.members) == 0 + ] + counter = 0 + for role in empty_color_roles: + try: + await role.delete() + counter += 1 + except: + logError(f"Failed to delete role {role.name}") + return await logAndRespond(ctx, f"Cleaned up {counter} empty color roles!") + except AssertionError as errorMessage: + return await errorAndRespond(ctx, errorMessage) diff --git a/modify.py b/modify.py new file mode 100644 index 0000000..e69de29 diff --git a/util.py b/util.py new file mode 100644 index 0000000..9ab31a4 --- /dev/null +++ b/util.py @@ -0,0 +1,46 @@ +import discord +import colour + + +async def create_new_role( + guild: discord.Guild, color: colour.Color +) -> discord.Role | None: + try: + new_role = await guild.create_role( + name=color.get_hex(), + color=discord.Color.from_rgb( + int(color.get_red() * 255), + int(color.get_green() * 255), + int(color.get_blue() * 255), + ), + ) + logInfo(f"Successfully created new role {color.get_hex()}") + except (discord.Forbidden, discord.HTTPException, discord.InvalidArgument) as e: + logError(f"Failed to create role {color.get_hex()} with {e.text}") + return None + + try: + return await new_role.edit( + position=max([role.position for role in guild.me.roles]) - 2 + ) + except (discord.Forbidden, discord.HTTPException, discord.InvalidArgument) as e: + logError(f"Failed to move role {color.get_hex()} with {e.text}") + return new_role + + +async def errorAndRespond(ctx: discord.ApplicationContext, message: str): + logError(message) + return await ctx.respond(f"{message}, please try again in a few seconds") + + +async def logAndRespond(ctx: discord.ApplicationContext, message: str): + logInfo(message) + return await ctx.respond(message) + + +def logError(message: str): + print(f"ERROR: {message}") + + +def logInfo(message: str): + print(f"INFO: {message}")