discordbot/cogs/role_creator_cog.py
2025-06-05 21:31:06 -06:00

253 lines
9.5 KiB
Python

import discord
from discord.ext import commands
import os
from dotenv import load_dotenv
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s:%(levelname)s:%(name)s: %(message)s"
)
logger = logging.getLogger(__name__)
# Load environment variables
load_dotenv()
OWNER_USER_ID = int(
os.getenv("OWNER_USER_ID")
) # Although commands.is_owner() handles this, loading for clarity/potential future use
class RoleCreatorCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(
name="create_roles",
help="Creates predefined roles for reaction roles. Owner only.",
)
@commands.is_owner() # Restricts this command to the bot owner specified during bot setup
async def create_roles(self, ctx):
"""Creates a set of predefined roles typically used with reaction roles."""
guild = ctx.guild
if not guild:
await ctx.send("This command can only be used in a server.")
return
# Check if the bot has permission to manage roles
if not ctx.me.guild_permissions.manage_roles:
await ctx.send("I don't have permission to manage roles.")
logger.warning(
f"Missing 'Manage Roles' permission in guild {guild.id} ({guild.name})."
)
return
# Define color mapping for specific roles
color_map = {
"Red": discord.Color.red(),
"Blue": discord.Color.blue(),
"Green": discord.Color.green(),
"Yellow": discord.Color.gold(),
"Purple": discord.Color.purple(),
"Orange": discord.Color.orange(),
"Pink": discord.Color.fuchsia(),
"Black": discord.Color(
0x010101
), # Near black to avoid blending with themes
"White": discord.Color(0xFEFEFE), # Near white to avoid blending
}
await ctx.send("Starting role creation/update process...")
logger.info(
f"Role creation/update initiated by {ctx.author} in guild {guild.id} ({guild.name})."
)
role_categories = {
"Colors": [
"Red",
"Blue",
"Green",
"Yellow",
"Purple",
"Orange",
"Pink",
"Black",
"White",
],
"Regions": ["NA East", "NA West", "EU", "Asia", "Oceania", "South America"],
"Pronouns": ["He/Him", "She/Her", "They/Them", "Ask Pronouns"],
"Interests": [
"Art",
"Music",
"Movies",
"Books",
"Technology",
"Science",
"History",
"Food",
"Programming",
"Anime",
"Photography",
"Travel",
"Writing",
"Cooking",
"Fitness",
"Nature",
"Gaming",
"Philosophy",
"Psychology",
"Design",
"Machine Learning",
"Cryptocurrency",
"Astronomy",
"Mythology",
"Languages",
"Architecture",
"DIY Projects",
"Hiking",
"Streaming",
"Virtual Reality",
"Coding Challenges",
"Board Games",
"Meditation",
"Urban Exploration",
"Tattoo Art",
"Comics",
"Robotics",
"3D Modeling",
"Podcasts",
],
"Gaming Platforms": [
"PC",
"PlayStation",
"Xbox",
"Nintendo Switch",
"Mobile",
],
"Favorite Vocaloids": [
"Hatsune Miku",
"Kasane Teto",
"Akita Neru",
"Kagamine Rin",
"Kagamine Len",
"Megurine Luka",
"Kaito",
"Meiko",
"Gumi",
"Kaai Yuki",
"Adachi Rei",
],
"Notifications": ["Announcements"],
}
created_count = 0
updated_count = 0 # Renamed from eped_count
skipped_other_count = 0 # For non-color roles that exist
error_count = 0
existing_roles = {
role.name.lower(): role for role in guild.roles
} # Cache existing roles for faster lookup
for category, names in role_categories.items():
logger.info(f"Processing category: {category}")
for name in names:
role_color = color_map.get(name) if category == "Colors" else None
role_exists = name.lower() in existing_roles
try:
if role_exists:
existing_role = existing_roles[name.lower()]
# Only edit if it's a color role and needs a color update (or just ensure color is set)
if category == "Colors" and role_color is not None:
# Check if color needs updating to avoid unnecessary API calls
if existing_role.color != role_color:
await existing_role.edit(color=role_color)
logger.info(
f"Successfully updated color for existing role: {name}"
)
updated_count += 1
else:
logger.info(
f"Role '{name}' already exists with correct color. Skipping update."
)
updated_count += 1 # Count as updated/checked even if no change needed
else:
# Non-color role exists, skip it
logger.info(
f"Non-color role '{name}' already exists. Skipping."
)
skipped_other_count += 1
continue # Move to next role name
# Role does not exist, create it
await guild.create_role(
name=name,
color=role_color
or discord.Color.default(), # Use mapped color or default
permissions=discord.Permissions.none(),
mentionable=False,
)
logger.info(
f"Successfully created role: {name}"
+ (f" with color {role_color}" if role_color else "")
)
created_count += 1
except discord.Forbidden:
logger.error(
f"Forbidden to {'edit' if role_exists else 'create'} role '{name}'. Check bot permissions."
)
await ctx.send(
f"Error: I lack permissions to {'edit' if role_exists else 'create'} the role '{name}'."
)
error_count += 1
# Stop if permission error occurs, as it likely affects subsequent operations
await ctx.send(
f"Stopping role processing due to permission error on role '{name}'."
)
return
except discord.HTTPException as e:
logger.error(
f"Failed to {'edit' if role_exists else 'create'} role '{name}': {e}"
)
await ctx.send(
f"Error {'editing' if role_exists else 'creating'} role '{name}': {e}"
)
error_count += 1
except Exception as e:
logger.exception(
f"An unexpected error occurred while processing role '{name}': {e}"
)
await ctx.send(
f"An unexpected error occurred for role '{name}'. Check logs."
)
error_count += 1
summary_message = (
f"Role creation/update process complete.\n"
f"Created: {created_count}\n"
f"Updated/Checked Colors: {updated_count}\n"
f"Skipped (Other existing): {skipped_other_count}\n"
f"Errors: {error_count}"
)
await ctx.send(summary_message)
logger.info(summary_message)
async def setup(bot):
# Ensure the owner ID is loaded correctly before adding the cog
if not OWNER_USER_ID:
logger.error(
"OWNER_USER_ID not found in .env file. RoleCreatorCog will not be loaded."
)
return
# Check if the bot object has owner_id or owner_ids set, which discord.py uses for is_owner()
if not bot.owner_id and not bot.owner_ids:
logger.warning(
"Bot owner_id or owner_ids not set. The 'is_owner()' check might not function correctly."
)
# Potentially load from OWNER_USER_ID if needed, though discord.py usually handles this
# bot.owner_id = OWNER_USER_ID # Uncomment if necessary and discord.py doesn't auto-load
await bot.add_cog(RoleCreatorCog(bot))
logger.info("RoleCreatorCog loaded successfully.")