Reinforced reaction_message management and interaction, better split responsibilty of tasks across files and functions.
UUID is now used as primary key for reaction_messages.
This commit is contained in:
@@ -1,132 +1,88 @@
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from data import react_roles
|
||||
from data import react_roles as db_react_roles
|
||||
from react_roles import react_roles as rr
|
||||
|
||||
from util.console import console, panel, track_iterable as track
|
||||
|
||||
GUILD_IDS = [int(os.getenv("DEV_GUILD_ID"))]
|
||||
REACTION_ROLE_CHANNEL_ID = int(os.getenv("REACT_ROLE_CHANNEL_ID"))
|
||||
|
||||
# Description of the reaction message cache
|
||||
reaction_message_descriptions_cache = {}
|
||||
|
||||
def update_react_role_embed(category_name):
|
||||
# Generate role list
|
||||
roles = react_roles.get_react_roles_by_category(category_name)
|
||||
role_list = []
|
||||
for role in roles:
|
||||
emoji = role[2]
|
||||
description = role[3]
|
||||
role_list.append(f"{emoji} - {description}")
|
||||
async def check_server_instantiated(ctx: discord.ApplicationContext):
|
||||
"""
|
||||
Check if the server has been instantiated.
|
||||
"""
|
||||
if not db_react_roles.guild_exists(ctx.guild.id):
|
||||
await ctx.send("Server not instantiated. Please run the `/instantiate_server` command first.")
|
||||
return False
|
||||
return True
|
||||
|
||||
# Create an embed for the react-role category
|
||||
embed = discord.Embed(title=f"**{category_name.capitalize()} Roles**",
|
||||
description="React to this message to gain access to the relevant text and voice channels.\n\n" + "\n".join(role_list),
|
||||
color=discord.Color.dark_orange())
|
||||
return embed
|
||||
|
||||
class ReactRoles(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
# Slash command to add a new reaction role Category / Message
|
||||
@commands.slash_command(name="new_react_role_category",
|
||||
description="Create a new category of react roles",
|
||||
guild_ids=GUILD_IDS)
|
||||
### COMMANDS ###
|
||||
# Command to create a new reaction role message
|
||||
@commands.slash_command(name="new_reaction_message", description="Create a new reaction message", guild_ids=GUILD_IDS)
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def new_react_role_category(self, ctx: discord.ApplicationContext, category_name: str):
|
||||
# Verify the category is unique
|
||||
existing_categories = react_roles.get_react_roles_categories()
|
||||
if category_name.lower() in [cat[1] for cat in existing_categories]:
|
||||
await ctx.respond(f"Category '{category_name}' already exists.", ephemeral=True)
|
||||
async def new_reaction_message(self, ctx: discord.ApplicationContext, description: str, thumbnail_url: str = None):
|
||||
# Check if server has been instantiated
|
||||
await check_server_instantiated(ctx)
|
||||
|
||||
# Since we can't get the message ID until we send the message, but need to display in the message itself
|
||||
# some kind of unique identifier, we will create a UUID to display in the message, send the message, and then
|
||||
# add it all to the database.
|
||||
|
||||
# Send Message to Channel - Must happen first so that we can get the message ID for the database
|
||||
unique_id = str(uuid.uuid4())
|
||||
embed = rr.generate_reaction_message_embed(description, thumbnail_url, unique_id)
|
||||
message = await ctx.channel.send(embed=embed)
|
||||
|
||||
# Add the reaction_message to the database
|
||||
db_react_roles.check_and_add_reaction_message(unique_id, ctx.guild.id, ctx.channel.id, message.id, description, thumbnail_url)
|
||||
|
||||
# Ensure the message was put in the database correctly, otherwise delete the message
|
||||
if not db_react_roles.reaction_message_exists(ctx.guild.id, ctx.channel.id, message.id):
|
||||
await message.delete()
|
||||
await ctx.respond("Failed to create the reaction message. Please try again.", ephemeral=True)
|
||||
return
|
||||
await ctx.respond(f"Reaction message created successfully!", ephemeral=True)
|
||||
|
||||
# Send a new Embed message
|
||||
embed = discord.Embed(title=f"**{category_name.capitalize()} Roles**",
|
||||
description="React to this message to gain access to the relevant text and voice channels.",
|
||||
color=discord.Color.dark_orange())
|
||||
message = await ctx.guild.get_channel(REACTION_ROLE_CHANNEL_ID).send(embed=embed)
|
||||
|
||||
# Create the category in the database
|
||||
react_roles.add_react_role_category_to_db(message.id, category_name)
|
||||
|
||||
# Send a confirmation message
|
||||
await ctx.respond(f"React role category '{category_name}' created successfully!", ephemeral=True)
|
||||
|
||||
#Slash command to add a new reaction role to an existing category
|
||||
@commands.slash_command(name="new_react_role",
|
||||
description="Create a new react role",
|
||||
guild_ids=GUILD_IDS)
|
||||
@commands.slash_command(name="delete_reaction_message", description="Delete a reaction message", guild_ids=GUILD_IDS)
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def new_react_role(self, ctx: discord.ApplicationContext, category_name: str, role: discord.Role, emoji: str, description: str):
|
||||
# Verify the category exists
|
||||
existing_categories = react_roles.get_react_roles_categories()
|
||||
if category_name.lower() not in [cat[1] for cat in existing_categories]:
|
||||
await ctx.respond(f"Category '{category_name}' does not exist.", ephemeral=True)
|
||||
async def delete_reaction_message(self, ctx: discord.ApplicationContext,
|
||||
reaction_role_id: str):
|
||||
# Check if server has been instantiated
|
||||
await check_server_instantiated(ctx)
|
||||
|
||||
# Check if the message ID is in the database
|
||||
message = db_react_roles.get_reaction_message_by_uuid(reaction_role_id)
|
||||
if not message:
|
||||
await ctx.respond("That Reaction Role ID does not exist", ephemeral=True)
|
||||
return
|
||||
|
||||
# Get the message ID of the category
|
||||
message_id = next(cat[0] for cat in existing_categories if cat[1] == category_name.lower())
|
||||
|
||||
# Add the react-role to the database
|
||||
react_roles.add_react_role_to_db(message_id, role.id, emoji, description)
|
||||
message = await self.bot.get_channel(REACTION_ROLE_CHANNEL_ID).fetch_message(message_id)
|
||||
await message.edit(embed=update_react_role_embed(category_name))
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
# Send a confirmation message
|
||||
await ctx.respond(f"React role '{emoji}' - '{role.name}' added to category '{category_name}'.", ephemeral=True)
|
||||
|
||||
|
||||
# Event listener for reaction add
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
|
||||
# Check if the reaction is in the correct channel and not from the bot itself
|
||||
if payload.channel_id != REACTION_ROLE_CHANNEL_ID or payload.user_id == self.bot.user.id:
|
||||
if message[2] != ctx.guild.id:
|
||||
await ctx.respond("That Reaction Role ID does not exist in this server", ephemeral=True)
|
||||
return
|
||||
|
||||
# Ge all react roles for the message_id that was reacted to
|
||||
message_id = payload.message_id
|
||||
emoji = str(payload.emoji)
|
||||
react_roles_list = react_roles.get_react_roles_by_message_id(message_id)
|
||||
# Delete the message from the channel
|
||||
channel = message[3]
|
||||
message = await self.bot.get_channel(channel).fetch_message(int(message_id))
|
||||
await message.delete()
|
||||
|
||||
if react_roles_list:
|
||||
for role in react_roles_list:
|
||||
if role[2] == emoji:
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
member = payload.member
|
||||
role_obj = guild.get_role(role[1])
|
||||
# Delete the message from the database
|
||||
db_react_roles.delete_reaction_message_from_db(int(message_id))
|
||||
|
||||
# Add the role to the member
|
||||
if role_obj not in member.roles:
|
||||
await member.add_roles(role_obj)
|
||||
console.log (f"[green]✔ Added role:[/] {emoji} {role_obj.name} to {member.name}")
|
||||
# Send confirmation message
|
||||
await ctx.respond(f"Reaction message deleted successfully!", ephemeral=True)
|
||||
|
||||
# Event Listener for reaction remove
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent):
|
||||
# Check if the reaction is in the correct channel and not from the bot itself
|
||||
if payload.channel_id != REACTION_ROLE_CHANNEL_ID or payload.user_id == self.bot.user.id:
|
||||
return
|
||||
|
||||
# Ge all react roles for the message_id that was reacted to
|
||||
message_id = payload.message_id
|
||||
emoji = str(payload.emoji)
|
||||
react_roles_list = react_roles.get_react_roles_by_message_id(message_id)
|
||||
|
||||
if react_roles_list:
|
||||
for role in react_roles_list:
|
||||
if role[2] == emoji:
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
|
||||
# Member object is not available in the payload, so we need to fetch it...
|
||||
member = await guild.fetch_member(payload.user_id)
|
||||
role_obj = guild.get_role(role[1])
|
||||
|
||||
# Add the role to the member
|
||||
if role_obj in member.roles:
|
||||
await member.remove_roles(role_obj)
|
||||
console.log (f"[red]✖ Removed role:[/] {emoji} {role_obj.name} from {member.name}")
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(ReactRoles(bot))
|
||||
Reference in New Issue
Block a user