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,30 +0,0 @@
|
|||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
class Channel(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@commands.slash_command(name="clear_channel",
|
|
||||||
description="Permanently delete all messages in a channel",
|
|
||||||
guild_ids=[681414775468589180])
|
|
||||||
@commands.has_permissions(manage_messages=True)
|
|
||||||
async def clear_channel(self, ctx: discord.ApplicationContext):
|
|
||||||
progress = await ctx.respond("Deleting all messages in this channel...", ephemeral=True)
|
|
||||||
channel = ctx.channel
|
|
||||||
|
|
||||||
# Fetch all messages in the channel
|
|
||||||
async for message in channel.history(limit=None):
|
|
||||||
try:
|
|
||||||
await message.delete()
|
|
||||||
except discord.Forbidden:
|
|
||||||
await ctx.send("I do not have permission to delete messages.")
|
|
||||||
return
|
|
||||||
except discord.HTTPException:
|
|
||||||
await ctx.send("Failed to delete a message.")
|
|
||||||
return
|
|
||||||
|
|
||||||
await progress.edit(content="✔ All messages deleted successfully! ✔")
|
|
||||||
|
|
||||||
def setup(bot):
|
|
||||||
bot.add_cog(Channel(bot))
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
class Hello(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@commands.slash_command(name="hello",
|
|
||||||
description="Say hi to the bot!",
|
|
||||||
guild_ids=[681414775468589180])
|
|
||||||
async def hello(self, ctx: discord.ApplicationContext):
|
|
||||||
await ctx.respond("Hello there!")
|
|
||||||
|
|
||||||
def setup(bot):
|
|
||||||
bot.add_cog(Hello(bot))
|
|
||||||
13
cogs/ping.py
13
cogs/ping.py
@@ -1,13 +0,0 @@
|
|||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
class Ping(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@commands.slash_command(name="ping", description="Ping the bot")
|
|
||||||
async def ping(self, ctx: discord.ApplicationContext):
|
|
||||||
await ctx.respond("🏓 Pong!")
|
|
||||||
|
|
||||||
def setup(bot):
|
|
||||||
bot.add_cog(Ping(bot))
|
|
||||||
@@ -1,132 +1,88 @@
|
|||||||
import os
|
import os
|
||||||
|
import uuid
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
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
|
from util.console import console, panel, track_iterable as track
|
||||||
|
|
||||||
GUILD_IDS = [int(os.getenv("DEV_GUILD_ID"))]
|
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):
|
async def check_server_instantiated(ctx: discord.ApplicationContext):
|
||||||
# Generate role list
|
"""
|
||||||
roles = react_roles.get_react_roles_by_category(category_name)
|
Check if the server has been instantiated.
|
||||||
role_list = []
|
"""
|
||||||
for role in roles:
|
if not db_react_roles.guild_exists(ctx.guild.id):
|
||||||
emoji = role[2]
|
await ctx.send("Server not instantiated. Please run the `/instantiate_server` command first.")
|
||||||
description = role[3]
|
return False
|
||||||
role_list.append(f"{emoji} - {description}")
|
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):
|
class ReactRoles(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
# Slash command to add a new reaction role Category / Message
|
### COMMANDS ###
|
||||||
@commands.slash_command(name="new_react_role_category",
|
# Command to create a new reaction role message
|
||||||
description="Create a new category of react roles",
|
@commands.slash_command(name="new_reaction_message", description="Create a new reaction message", guild_ids=GUILD_IDS)
|
||||||
guild_ids=GUILD_IDS)
|
|
||||||
@commands.has_permissions(manage_roles=True)
|
@commands.has_permissions(manage_roles=True)
|
||||||
async def new_react_role_category(self, ctx: discord.ApplicationContext, category_name: str):
|
async def new_reaction_message(self, ctx: discord.ApplicationContext, description: str, thumbnail_url: str = None):
|
||||||
# Verify the category is unique
|
# Check if server has been instantiated
|
||||||
existing_categories = react_roles.get_react_roles_categories()
|
await check_server_instantiated(ctx)
|
||||||
if category_name.lower() in [cat[1] for cat in existing_categories]:
|
|
||||||
await ctx.respond(f"Category '{category_name}' already exists.", ephemeral=True)
|
# 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
|
return
|
||||||
|
await ctx.respond(f"Reaction message created successfully!", ephemeral=True)
|
||||||
|
|
||||||
# Send a new Embed message
|
@commands.slash_command(name="delete_reaction_message", description="Delete a reaction message", guild_ids=GUILD_IDS)
|
||||||
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.has_permissions(manage_roles=True)
|
@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):
|
async def delete_reaction_message(self, ctx: discord.ApplicationContext,
|
||||||
# Verify the category exists
|
reaction_role_id: str):
|
||||||
existing_categories = react_roles.get_react_roles_categories()
|
# Check if server has been instantiated
|
||||||
if category_name.lower() not in [cat[1] for cat in existing_categories]:
|
await check_server_instantiated(ctx)
|
||||||
await ctx.respond(f"Category '{category_name}' does not exist.", ephemeral=True)
|
|
||||||
|
# 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
|
return
|
||||||
|
|
||||||
# Get the message ID of the category
|
if message[2] != ctx.guild.id:
|
||||||
message_id = next(cat[0] for cat in existing_categories if cat[1] == category_name.lower())
|
await ctx.respond("That Reaction Role ID does not exist in this server", ephemeral=True)
|
||||||
|
|
||||||
# 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:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ge all react roles for the message_id that was reacted to
|
# Delete the message from the channel
|
||||||
message_id = payload.message_id
|
channel = message[3]
|
||||||
emoji = str(payload.emoji)
|
message = await self.bot.get_channel(channel).fetch_message(int(message_id))
|
||||||
react_roles_list = react_roles.get_react_roles_by_message_id(message_id)
|
await message.delete()
|
||||||
|
|
||||||
if react_roles_list:
|
# Delete the message from the database
|
||||||
for role in react_roles_list:
|
db_react_roles.delete_reaction_message_from_db(int(message_id))
|
||||||
if role[2] == emoji:
|
|
||||||
guild = self.bot.get_guild(payload.guild_id)
|
|
||||||
member = payload.member
|
|
||||||
role_obj = guild.get_role(role[1])
|
|
||||||
|
|
||||||
# Add the role to the member
|
# Send confirmation message
|
||||||
if role_obj not in member.roles:
|
await ctx.respond(f"Reaction message deleted successfully!", ephemeral=True)
|
||||||
await member.add_roles(role_obj)
|
|
||||||
console.log (f"[green]✔ Added role:[/] {emoji} {role_obj.name} to {member.name}")
|
|
||||||
|
|
||||||
# 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):
|
def setup(bot):
|
||||||
bot.add_cog(ReactRoles(bot))
|
bot.add_cog(ReactRoles(bot))
|
||||||
30
cogs/setup.py
Normal file
30
cogs/setup.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
from data import react_roles
|
||||||
|
|
||||||
|
from util.console import console, panel, track_iterable as track
|
||||||
|
|
||||||
|
GUILD_IDS = [int(os.getenv("DEV_GUILD_ID"))]
|
||||||
|
|
||||||
|
class Setup(commands.Cog):
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
### COMMANDS ###
|
||||||
|
@commands.slash_command(name="instantiate_server", description="Instantiate the server.", guild_ids=GUILD_IDS)
|
||||||
|
@commands.has_permissions(manage_guild=True)
|
||||||
|
async def instantiate_server(self, ctx: discord.ApplicationContext):
|
||||||
|
"""
|
||||||
|
Command to instantiate the server.
|
||||||
|
This command creates the necessary database tables and initializes the server.
|
||||||
|
"""
|
||||||
|
# Add guild to the database
|
||||||
|
react_roles.check_and_add_guild(ctx.guild_id, ctx.guild.name)
|
||||||
|
console.log(f"[green]Guild - {ctx.guild.name} - added to the database.[/green]")
|
||||||
|
await ctx.respond(f"Server '{ctx.guild.name}' instantiated successfully!", ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Setup(bot))
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
from util.console import console, panel, track_iterable as track
|
||||||
|
|
||||||
DB_PATH = 'data.db'
|
DB_PATH = 'data.db'
|
||||||
|
|
||||||
|
### INITIALIZATION ###
|
||||||
|
|
||||||
def init_db():
|
def init_db():
|
||||||
"""
|
"""
|
||||||
Initialize the database and create necessary tables.
|
Initialize the database and create necessary tables.
|
||||||
@@ -9,136 +13,295 @@ def init_db():
|
|||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# Create the react_roles table
|
# Create guilds table
|
||||||
|
# This table stores the guild IDs and their respective names.
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS react_roles (
|
CREATE TABLE IF NOT EXISTS guilds (
|
||||||
message_id INTEGER,
|
id BIGINT PRIMARY KEY,
|
||||||
role_id INTEGER,
|
name TEXT
|
||||||
emoji TEXT,
|
)
|
||||||
description TEXT
|
''')
|
||||||
|
|
||||||
|
# Create the reaction_messages table
|
||||||
|
# This table stores the messages that will be used for reaction roles.
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS reaction_messages (
|
||||||
|
uid TEXT PRIMARY KEY,
|
||||||
|
message_id BIGINT,
|
||||||
|
guild_id BIGINT REFERENCES guilds(id),
|
||||||
|
channel_id BIGINT,
|
||||||
|
description TEXT,
|
||||||
|
thumbnail TEXT DEFAULT NULL,
|
||||||
|
UNIQUE(guild_id, channel_id, id),
|
||||||
|
UNIQUE(description)
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
# Create the react_role_categories table
|
# Create the react_role_categories table
|
||||||
|
# This table stores the emoji-role mappings for each message.
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS react_role_categories (
|
CREATE TABLE IF NOT EXISTS reaction_roles (
|
||||||
message_id INTEGER,
|
id SERIAL PRIMARY KEY,
|
||||||
category_name TEXT
|
message_id BIGINT REFERENCES reaction_messages(id) ON DELETE CASCADE,
|
||||||
|
emoji TEXT,
|
||||||
|
role_id [BIGINT],
|
||||||
|
description TEXT,
|
||||||
|
forbidden_roles [BIGINT] DEFAULT NULL,
|
||||||
|
required_roles [BIGINT] DEFAULT NULL,
|
||||||
|
UNIQUE(message_id, emoji)
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def add_react_role_category_to_db(message_id: int, category_name: str):
|
### DATABASE FUNCTIONS ###
|
||||||
|
### GUILD TABLE FUNCTIONS ###
|
||||||
|
|
||||||
|
### CHECK EXISTANCE ###
|
||||||
|
def guild_exists(guild_id: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Add a new react role category to the database.
|
Check if a guild exists in the database.
|
||||||
:param message_id: The ID of the message to which the category is associated.
|
:param guild_id: The ID of the guild.
|
||||||
:param category_name: The name of the category (e.g., "Category1").
|
:return: True if the guild exists, False otherwise.
|
||||||
"""
|
"""
|
||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# Create the table if it doesn't exist
|
# Check if the guild exists in the database
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS react_role_categories (
|
SELECT EXISTS(SELECT 1 FROM guilds WHERE id=?)
|
||||||
message_id INTEGER,
|
''', (guild_id,))
|
||||||
category_name TEXT
|
exists = cursor.fetchone()[0]
|
||||||
)
|
|
||||||
''')
|
|
||||||
|
|
||||||
# Insert the new react-role category into the database
|
conn.close()
|
||||||
|
return exists
|
||||||
|
|
||||||
|
### ADD ROW ###
|
||||||
|
def add_guild_to_db(guild_id: int, guild_name: str):
|
||||||
|
"""
|
||||||
|
Add a new guild to the database.
|
||||||
|
:param guild_id: The ID of the guild.
|
||||||
|
:param guild_name: The name of the guild.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Insert the new guild into the database
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
INSERT INTO react_role_categories (message_id, category_name)
|
INSERT INTO guilds (id, name)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?)
|
||||||
''', (message_id, category_name.lower()))
|
''', (guild_id, guild_name))
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def add_react_role_to_db(message_id: int, role_id: int, emoji: str, description: str):
|
### CHECK THEN ADD ROW ###
|
||||||
|
def check_and_add_guild(guild_id: int, guild_name: str):
|
||||||
"""
|
"""
|
||||||
Add a new react role to the database.
|
Check if a guild exists in the database, and if not, add it.
|
||||||
:param message_id: The message ID of the react-role message.
|
:param guild_id: The ID of the guild.
|
||||||
:param role_id: The ID of the role to assign.
|
:param guild_name: The name of the guild.
|
||||||
:param emoji: The emoji to react with.
|
"""
|
||||||
|
if not guild_exists(guild_id):
|
||||||
|
add_guild_to_db(guild_id, guild_name)
|
||||||
|
console.log(f"[green]✔ DB Added Row to table guilds:[/] {guild_name} ({guild_id})")
|
||||||
|
|
||||||
|
|
||||||
|
### REACTION MESSAGES TABLE FUNCTIONS ###
|
||||||
|
|
||||||
|
### CHECK EXISTENCE ###
|
||||||
|
def reaction_message_exists(guild_id: int, channel_id: int, message_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a reaction message exists in the database.
|
||||||
|
:param guild_id: The ID of the guild.
|
||||||
|
:param channel_id: The ID of the channel.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
:return: True if the reaction message exists, False otherwise.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Check if the reaction message exists in the database
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT EXISTS(SELECT 1 FROM reaction_messages WHERE guild_id=? AND channel_id=? AND id=?)
|
||||||
|
''', (guild_id, channel_id, message_id))
|
||||||
|
exists = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return exists
|
||||||
|
|
||||||
|
### ADD ROW ###
|
||||||
|
def add_reaction_message_to_db(uuid: str, guild_id: int, channel_id: int, message_id: int, description: str, thumbnail: str = None):
|
||||||
|
"""
|
||||||
|
Add a new reaction message to the database.
|
||||||
|
:param uuid: Unique identifier for the message.
|
||||||
|
:param description: The name of the reaction message. IE "Gaming Roles"
|
||||||
|
:param guild_id: The ID of the guild.
|
||||||
|
:param channel_id: The ID of the channel.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
:param thumbnail: The URL of the thumbnail image.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Insert the new reaction message into the database
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT INTO reaction_messages (uuid, id, guild_id, channel_id, description, thumbnail)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
''', (uuid, message_id, guild_id, channel_id, description, thumbnail))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
### DELETE ROW ###
|
||||||
|
def delete_reaction_message_from_db(message_id: int):
|
||||||
|
"""
|
||||||
|
Delete a reaction message from the database.
|
||||||
|
:param message_id: The ID of the message to delete.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Delete the reaction message from the database
|
||||||
|
cursor.execute('''
|
||||||
|
DELETE FROM reaction_messages WHERE id=?
|
||||||
|
''', (message_id,))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
### FETCH MESSAGE BY ID ###
|
||||||
|
def get_reaction_message_by_id(message_id: int):
|
||||||
|
"""
|
||||||
|
Fetch a reaction message by its ID.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
:return: The reaction message if found, None otherwise.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Fetch the reaction message from the database
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT * FROM reaction_messages WHERE id=?
|
||||||
|
''', (message_id,))
|
||||||
|
message = cursor.fetchone()
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return message
|
||||||
|
|
||||||
|
### FETCH MESSAGE BY UUID ###
|
||||||
|
def get_reaction_message_by_uuid(uuid: str):
|
||||||
|
"""
|
||||||
|
Fetch a reaction message by its UUID.
|
||||||
|
:param uuid: The UUID of the message.
|
||||||
|
:return: The reaction message if found, None otherwise.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Fetch the reaction message from the database
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT * FROM reaction_messages WHERE uuid=?
|
||||||
|
''', (uuid,))
|
||||||
|
message = cursor.fetchone()
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return message
|
||||||
|
|
||||||
|
### FETCH ALL REACTION MESSAGES ID:DESCRIPTION PAIRS ###
|
||||||
|
def get_all_reaction_messages_id_pairs() -> dict:
|
||||||
|
"""
|
||||||
|
Fetch all reaction messages from the database.
|
||||||
|
:return: A dictionary of message IDs and their descriptions.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Fetch all reaction messages from the database
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT id, description FROM reaction_messages
|
||||||
|
''')
|
||||||
|
messages = cursor.fetchall()
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return {message[1]: message[0] for message in messages}
|
||||||
|
|
||||||
|
### CHECK THEN ADD ROW ###
|
||||||
|
def check_and_add_reaction_message(uuid: str, guild_id: int, channel_id: int, message_id: int, description: str, thumbnail: str = None):
|
||||||
|
"""
|
||||||
|
Check if a reaction message exists in the database, and if not, add it.
|
||||||
|
:param uuid:
|
||||||
|
:param thumbnail:
|
||||||
|
:param description:
|
||||||
|
:param guild_id: The ID of the guild.
|
||||||
|
:param channel_id: The ID of the channel.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
"""
|
||||||
|
if not reaction_message_exists(guild_id, channel_id, message_id):
|
||||||
|
console.log(f"[green]✔ DB Added Row to table reaction_messages:[/] {description}")
|
||||||
|
add_reaction_message_to_db(uuid, guild_id, channel_id, message_id, description, thumbnail)
|
||||||
|
else:
|
||||||
|
console.log(f"[yellow]⚠️ DB Adding Row to table reaction_messages failed:[/] {description} already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
### REACTION ROLES TABLE FUNCTIONS ###
|
||||||
|
|
||||||
|
### CHECK EXISTENCE ###
|
||||||
|
def reaction_role_exists(message_id: int, emoji: str) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a reaction role exists in the database.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
:param emoji: The emoji associated with the role.
|
||||||
|
:return: True if the reaction role exists, False otherwise.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Check if the reaction role exists in the database
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT EXISTS(SELECT 1 FROM reaction_roles WHERE message_id=? AND emoji=?)
|
||||||
|
''', (message_id, emoji))
|
||||||
|
exists = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return exists
|
||||||
|
|
||||||
|
### ADD ROW ###
|
||||||
|
def add_reaction_role_to_db(message_id: int, emoji: str, role_id: [int], description: str, forbidden_roles: [int] = None, required_roles: [int] = None):
|
||||||
|
"""
|
||||||
|
Add a new reaction role to the database.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
:param emoji: The emoji associated with the role.
|
||||||
|
:param role_id: A list of role IDs to assign.
|
||||||
|
:param description: The description of the role.
|
||||||
|
:param forbidden_roles: A list of role IDs that are forbidden.
|
||||||
|
:param required_roles: A list of role IDs that are required.
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Insert the new reaction role into the database
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT INTO reaction_roles (message_id, emoji, role_id, description, forbidden_roles, required_roles)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
''', (message_id, emoji, role_id, description, forbidden_roles, required_roles))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
### CHECK THEN ADD ROW ###
|
||||||
|
def check_and_add_reaction_role(message_id: int, emoji: str, role_id: int, description: str):
|
||||||
|
"""
|
||||||
|
Check if a reaction role exists in the database, and if not, add it.
|
||||||
|
:param message_id: The ID of the message.
|
||||||
|
:param emoji: The emoji associated with the role.
|
||||||
|
:param role_id: The ID of the role.
|
||||||
:param description: The description of the role.
|
:param description: The description of the role.
|
||||||
"""
|
"""
|
||||||
conn = sqlite3.connect(DB_PATH)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Create the table if it doesn't exist
|
if not reaction_role_exists(message_id, emoji):
|
||||||
cursor.execute('''
|
console.log(f"[green]✔ DB Adding Row to table reaction_roles:[/] {message_id} ({emoji})")
|
||||||
CREATE TABLE IF NOT EXISTS react_roles (
|
add_reaction_role_to_db(message_id, emoji, role_id, description)
|
||||||
message_id INTEGER,
|
else:
|
||||||
role_id INTEGER,
|
console.log(f"[yellow]⚠️ DB Adding Row to table reaction_roles failed:[/] {message_id} ({emoji}) already exists.")
|
||||||
emoji TEXT,
|
|
||||||
description TEXT
|
|
||||||
)
|
|
||||||
''')
|
|
||||||
|
|
||||||
# Insert the new react-role into the database
|
|
||||||
cursor.execute('''
|
|
||||||
INSERT INTO react_roles (message_id, role_id, emoji, description)
|
|
||||||
VALUES (?, ?, ?, ?)
|
|
||||||
''', (message_id, role_id, emoji, description))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def get_react_roles_categories():
|
|
||||||
"""
|
|
||||||
Get all react role categories from the database.
|
|
||||||
:return: A list of tuples containing message_id and category_name.
|
|
||||||
"""
|
|
||||||
conn = sqlite3.connect(DB_PATH)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Fetch all react-role categories from the database
|
|
||||||
cursor.execute('''
|
|
||||||
SELECT * FROM react_role_categories
|
|
||||||
''')
|
|
||||||
categories = cursor.fetchall()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return categories
|
|
||||||
|
|
||||||
def get_react_roles_by_category(category_name: str):
|
|
||||||
"""
|
|
||||||
Get all react roles for a specific category from the database.
|
|
||||||
:param category_name: The name of the category (e.g., "Category1").
|
|
||||||
:return: A list of tuples containing message_id, role_id, emoji, and description.
|
|
||||||
"""
|
|
||||||
# Get the message id of the category
|
|
||||||
categories = get_react_roles_categories()
|
|
||||||
message_id = next((cat[0] for cat in categories if cat[1] == category_name.lower()), None)
|
|
||||||
|
|
||||||
conn = sqlite3.connect(DB_PATH)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Fetch all react-roles for the specified category from the database
|
|
||||||
cursor.execute('''
|
|
||||||
SELECT * FROM react_roles WHERE message_id = ?
|
|
||||||
''', (message_id,))
|
|
||||||
roles = cursor.fetchall()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return roles
|
|
||||||
|
|
||||||
def get_react_roles_by_message_id(message_id: int):
|
|
||||||
"""
|
|
||||||
Get all react roles for a specific message ID from the database.
|
|
||||||
:param message_id: The ID of the message to which the roles are associated.
|
|
||||||
:return: A list of tuples containing message_id, role_id, emoji, and description.
|
|
||||||
"""
|
|
||||||
conn = sqlite3.connect(DB_PATH)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Fetch all react-roles for the specified message ID from the database
|
|
||||||
cursor.execute('''
|
|
||||||
SELECT * FROM react_roles WHERE message_id = ?
|
|
||||||
''', (message_id,))
|
|
||||||
roles = cursor.fetchall()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return roles
|
|
||||||
|
|||||||
20
main.py
20
main.py
@@ -27,35 +27,33 @@ COG_PATH = Path(__file__).resolve().parent / "cogs"
|
|||||||
cogs_available = {}
|
cogs_available = {}
|
||||||
loaded_cog_modules = {}
|
loaded_cog_modules = {}
|
||||||
|
|
||||||
# Arguments used in cog loading
|
# Add available cogs to the list
|
||||||
COG_CONFIG = {
|
|
||||||
"react_roles": {
|
|
||||||
"guild_ids": [DEV_GUILD_ID],
|
|
||||||
"react_channel_id": REACT_ROLE_CHANNEL_ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for file in COG_PATH.iterdir():
|
for file in COG_PATH.iterdir():
|
||||||
if file.suffix == ".py" and not file.name.startswith("__"):
|
if file.suffix == ".py" and not file.name.startswith("__"):
|
||||||
cog_name = file.stem
|
cog_name = file.stem
|
||||||
cogs_available[cog_name] = file
|
cogs_available[cog_name] = file
|
||||||
|
|
||||||
|
############################
|
||||||
|
### COGS LOADING HANDLER ###
|
||||||
|
############################
|
||||||
|
|
||||||
async def load_cogs():
|
async def load_cogs():
|
||||||
|
# Check if there are any cogs to load
|
||||||
if not cogs_available:
|
if not cogs_available:
|
||||||
console.log("[yellow]⚙️ No cogs available to load...[/yellow]")
|
console.log("[yellow]⚙️ No cogs available to load...[/yellow]")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if the cogs directory exists
|
||||||
if not COG_PATH.exists():
|
if not COG_PATH.exists():
|
||||||
console.log(f"[red]❌ Cogs directory not found at {COG_PATH}[/red]")
|
console.log(f"[red]❌ Cogs directory not found at {COG_PATH}[/red]")
|
||||||
return
|
return
|
||||||
|
|
||||||
console.print(panel(content="🔌 [bold]Loading Cogs[/bold]", style="cyan"))
|
console.print(panel(content="🔌 [bold]Loading Cogs[/bold]", style="cyan"))
|
||||||
|
|
||||||
|
# Load each cog
|
||||||
for cog in track(cogs_available, description="Loading Cogs..."):
|
for cog in track(cogs_available, description="Loading Cogs..."):
|
||||||
module_path = f"cogs.{cog}"
|
module_path = f"cogs.{cog}"
|
||||||
|
|
||||||
config = COG_CONFIG.get(cog, {})
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bot.load_extension(module_path)
|
bot.load_extension(module_path)
|
||||||
loaded_cog_modules[cog] = module_path
|
loaded_cog_modules[cog] = module_path
|
||||||
@@ -71,10 +69,10 @@ async def shutdown():
|
|||||||
await bot.close()
|
await bot.close()
|
||||||
|
|
||||||
def handle_signals():
|
def handle_signals():
|
||||||
|
# Handle shutdown signals
|
||||||
for sig in (signal.SIGINT, signal.SIGTERM):
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
||||||
signal.signal(sig, lambda s, f: asyncio.create_task(shutdown()))
|
signal.signal(sig, lambda s, f: asyncio.create_task(shutdown()))
|
||||||
|
|
||||||
|
|
||||||
# Bot Event Handlers
|
# Bot Event Handlers
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
|
|||||||
@@ -1,17 +1,31 @@
|
|||||||
import discord
|
import discord
|
||||||
from data import react_roles
|
from data import react_roles as db_react_roles
|
||||||
|
|
||||||
def update_react_role_embed(category_name):
|
from util.console import console, panel, track_iterable as track
|
||||||
# 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}")
|
|
||||||
|
|
||||||
|
### EMBED CREATION ###
|
||||||
|
def generate_reaction_message_embed(description: str, thumbnail: str, uuid: str):
|
||||||
|
"""
|
||||||
|
Generate an embed message for the reaction role message.
|
||||||
|
"""
|
||||||
# Create an embed for the react-role category
|
# Create an embed for the react-role category
|
||||||
embed = discord.Embed(title=f"**{category_name.capitalize()} Roles**",
|
embed = discord.Embed(title=f"**{description.capitalize()} Roles**",
|
||||||
description="React to this message to gain access to the relevant text and voice channels.\n\n" + "\n".join(role_list),
|
thumbnail=f"{thumbnail}",
|
||||||
|
description="Use the reactions below to be granted access to the relevant text and "
|
||||||
|
"voice channels. Remove your reaction to remove access. Simples!!! \n\n",
|
||||||
color=discord.Color.dark_orange())
|
color=discord.Color.dark_orange())
|
||||||
|
embed.set_footer(text=f"Reaction Role ID: {uuid}")
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
def required_emojis_for_reaction_message(message_id):
|
||||||
|
"""
|
||||||
|
Get the required emojis for a reaction message.
|
||||||
|
"""
|
||||||
|
# Get the emojis for the reaction message
|
||||||
|
message_db_data = db_react_roles.get_reaction_message_by_id(message_id)
|
||||||
|
emojis = []
|
||||||
|
for message in message_db_data:
|
||||||
|
emojis.append(message[2])
|
||||||
|
if not emojis:
|
||||||
|
return None
|
||||||
|
return emojis
|
||||||
|
|||||||
Reference in New Issue
Block a user