From 16675b5740637870d087300549190e0e3fbf4a9e Mon Sep 17 00:00:00 2001 From: NimVrod Date: Wed, 16 Apr 2025 18:32:42 +0200 Subject: [PATCH 1/6] Add dropdownRoles.py Allows --- pychan/commands/utilities/__init__.py | 2 + pychan/commands/utilities/dropdownRoles.py | 59 ++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 pychan/commands/utilities/dropdownRoles.py diff --git a/pychan/commands/utilities/__init__.py b/pychan/commands/utilities/__init__.py index 2ce0351..eb087d4 100644 --- a/pychan/commands/utilities/__init__.py +++ b/pychan/commands/utilities/__init__.py @@ -2,6 +2,7 @@ from .aoc import AoC from .prefix import ChangePrefix from .tempVoice import TempVoice +from .dropdownRoles import DropdownRoles class Utilities(commands.Cog): @@ -10,3 +11,4 @@ def __init__(self, bot): self.bot.add_cog(AoC(bot)) self.bot.add_cog(ChangePrefix(bot)) self.bot.add_cog(TempVoice(bot)) + self.bot.add_cog(DropdownRoles(bot)) diff --git a/pychan/commands/utilities/dropdownRoles.py b/pychan/commands/utilities/dropdownRoles.py new file mode 100644 index 0000000..23714a3 --- /dev/null +++ b/pychan/commands/utilities/dropdownRoles.py @@ -0,0 +1,59 @@ +import nextcord +from nextcord.ext import commands +from typing import List + + +class RolesDropdown(nextcord.ui.Select): + def __init__(self, roles: List[nextcord.Role]): + options = [ + nextcord.SelectOption(label=role.name, value=str(role.id)) + for role in roles + ] + super().__init__(placeholder="Wybierz rolę", options=options) + + async def callback(self, interaction: nextcord.Interaction): + selected_role_id = int(self.values[0]) + guild = interaction.guild + member = guild.get_member(interaction.user.id) + selected_role = guild.get_role(selected_role_id) + + if selected_role in member.roles: + await member.remove_roles(selected_role) + await interaction.response.send_message(f"Usunięto {selected_role.name} role.", ephemeral=True) + else: + await member.add_roles(selected_role) + await interaction.response.send_message(f"Dodano {selected_role.name}", ephemeral=True) + + +class RolesDropdownView(nextcord.ui.View): + def __init__(self, roles: List[nextcord.Role]): + super().__init__() + self.add_item(RolesDropdown(roles)) + + + +class DropdownRoles(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.roles = {} + + + #TODO: Add admin check + @nextcord.slash_command(guild_ids=[381092165729910786]) + async def add_roles(self, interaction: nextcord.Interaction, role: nextcord.Role): + if role > interaction.guild.me.top_role or role > interaction.user.top_role: + await interaction.response.send_message("Nie możesz dodać tej roli", ephemeral=True) + return + + # TODO: Add database integration + + if not interaction.guild.id in self.roles: + self.roles[interaction.guild.id] = [role] + else: + self.roles[interaction.guild.id].append(role) + view = RolesDropdownView(self.roles[interaction.guild.id]) + await interaction.send("Dodano rolę do bazy danych", view=view, ephemeral=True) + + @nextcord.slash_command(guild_ids=[381092165729910786]) + async def dropdown_roles(self, interaction: nextcord.Interaction): + await interaction.send("Wybierz rolę z rozwijanej listy", view=RolesDropdownView(self.roles[interaction.guild.id]), ephemeral=True) \ No newline at end of file From bca574adaf515f298390802d89ea5f0ecce0fcfd Mon Sep 17 00:00:00 2001 From: NimVrod Date: Sat, 19 Apr 2025 11:47:18 +0200 Subject: [PATCH 2/6] Add database logic to dropdownRoles.py Add remove_role command --- pychan/commands/utilities/dropdownRoles.py | 103 +++++++++++++++++++-- pychan/database.py | 5 + 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/pychan/commands/utilities/dropdownRoles.py b/pychan/commands/utilities/dropdownRoles.py index 23714a3..9363b60 100644 --- a/pychan/commands/utilities/dropdownRoles.py +++ b/pychan/commands/utilities/dropdownRoles.py @@ -1,6 +1,8 @@ import nextcord from nextcord.ext import commands from typing import List +from sqlalchemy import select +from pychan import database class RolesDropdown(nextcord.ui.Select): @@ -17,6 +19,10 @@ async def callback(self, interaction: nextcord.Interaction): member = guild.get_member(interaction.user.id) selected_role = guild.get_role(selected_role_id) + if not selected_role: + await interaction.response.send_message("Nie można znaleźć tej roli", ephemeral=True) + return + if selected_role in member.roles: await member.remove_roles(selected_role) await interaction.response.send_message(f"Usunięto {selected_role.name} role.", ephemeral=True) @@ -25,35 +31,112 @@ async def callback(self, interaction: nextcord.Interaction): await interaction.response.send_message(f"Dodano {selected_role.name}", ephemeral=True) +class RolesDropdownRemove(RolesDropdown): + async def callback(self, interaction: nextcord.Interaction): + selected_role_id = int(self.values[0]) + guild = interaction.guild + + selected_role = guild.get_role(selected_role_id) + + if not remove_guild_role(selected_role): + await interaction.response.send_message("Nie można usunąć tej roli", ephemeral=True) + return + await interaction.response.send_message(f"Usunięto {selected_role.name} rolę z bazy danych.", ephemeral=True) + class RolesDropdownView(nextcord.ui.View): def __init__(self, roles: List[nextcord.Role]): super().__init__() self.add_item(RolesDropdown(roles)) +class RolesDropdownRemoveView(nextcord.ui.View): + def __init__(self, roles: List[nextcord.Role]): + super().__init__() + self.add_item(RolesDropdownRemove(roles)) + + +def get_guild_roles(guild: nextcord.Guild) -> List[nextcord.Role]: + roles = [] + with database.Session() as session: + result = session.execute(select(database.GuildDropdownRoles).where( + database.GuildDropdownRoles.guild_id == str(guild.id) + )).scalars().all() + + if not result: + return roles + + for role in result: + role_id = int(role.role_id) + discord_role = guild.get_role(role_id) + if discord_role: + roles.append(discord_role) + return roles +def add_guild_role(role: nextcord.Role) -> bool: + with database.Session() as session: + result = session.execute(select(database.GuildDropdownRoles).where( + database.GuildDropdownRoles.guild_id == str(role.guild.id), + database.GuildDropdownRoles.role_id == str(role.id) + )).first() + + if result: + return False + + new_role = database.GuildDropdownRoles(guild_id=str(role.guild.id), role_id=str(role.id)) + session.add(new_role) + session.commit() + return True + +def remove_guild_role(role: nextcord.Role) -> bool: + with database.Session() as session: + result = session.execute( + select(database.GuildDropdownRoles).where( + database.GuildDropdownRoles.guild_id == str(role.guild.id), + database.GuildDropdownRoles.role_id == str(role.id) + ) + ).scalar_one_or_none() + + if not result: + return False + + session.delete(result) + session.commit() + return True class DropdownRoles(commands.Cog): def __init__(self, bot): self.bot = bot - self.roles = {} - #TODO: Add admin check + #TODO: Add admin check and remove guild_id @nextcord.slash_command(guild_ids=[381092165729910786]) async def add_roles(self, interaction: nextcord.Interaction, role: nextcord.Role): if role > interaction.guild.me.top_role or role > interaction.user.top_role: await interaction.response.send_message("Nie możesz dodać tej roli", ephemeral=True) return - # TODO: Add database integration + if not add_guild_role(role): + await interaction.response.send_message(f"Rola {role.name} już istnieje w bazie danych", ephemeral=True) + return + await interaction.send(f"Dodano {role.name} do bazy danych", ephemeral=True) - if not interaction.guild.id in self.roles: - self.roles[interaction.guild.id] = [role] - else: - self.roles[interaction.guild.id].append(role) - view = RolesDropdownView(self.roles[interaction.guild.id]) - await interaction.send("Dodano rolę do bazy danych", view=view, ephemeral=True) + # TODO: Add admin check and remove guild_id + @nextcord.slash_command(guild_ids=[381092165729910786]) + async def remove_role(self, interaction: nextcord.Interaction): + + roles = get_guild_roles(interaction.guild) + + await interaction.send("Wybierz rolę do usunięcia", view=RolesDropdownRemoveView(roles), ephemeral=True) + + + # TODO: remove guild_id @nextcord.slash_command(guild_ids=[381092165729910786]) async def dropdown_roles(self, interaction: nextcord.Interaction): - await interaction.send("Wybierz rolę z rozwijanej listy", view=RolesDropdownView(self.roles[interaction.guild.id]), ephemeral=True) \ No newline at end of file + roles = get_guild_roles(interaction.guild) + + if not roles: + await interaction.response.send_message("Nie znaleziono ról w bazie danych", ephemeral=True) + return + + await interaction.send("Wybierz rolę z rozwijanej listy", view=RolesDropdownView(roles), ephemeral=True) + diff --git a/pychan/database.py b/pychan/database.py index 41be166..f1ee81f 100644 --- a/pychan/database.py +++ b/pychan/database.py @@ -48,6 +48,11 @@ class QuizAnswer(Base): answer = Column(String) correct = Column(Boolean) +class GuildDropdownRoles(Base): + __tablename__ = 'guild_dropdown_roles' + guild_id = Column(String, primary_key=True) + role_id = Column(String, primary_key=True) + def create_database(): Base.metadata.create_all(engine) From 5207bbd11b577f24594692c52a6bd41e69e005d3 Mon Sep 17 00:00:00 2001 From: NimVrod Date: Sat, 19 Apr 2025 11:56:59 +0200 Subject: [PATCH 3/6] Remove unnecessary database calls --- pychan/commands/utilities/dropdownRoles.py | 25 ++++++++-------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/pychan/commands/utilities/dropdownRoles.py b/pychan/commands/utilities/dropdownRoles.py index 9363b60..97c036d 100644 --- a/pychan/commands/utilities/dropdownRoles.py +++ b/pychan/commands/utilities/dropdownRoles.py @@ -73,32 +73,25 @@ def get_guild_roles(guild: nextcord.Guild) -> List[nextcord.Role]: def add_guild_role(role: nextcord.Role) -> bool: with database.Session() as session: - result = session.execute(select(database.GuildDropdownRoles).where( - database.GuildDropdownRoles.guild_id == str(role.guild.id), - database.GuildDropdownRoles.role_id == str(role.id) - )).first() - - if result: + try: + new_role = database.GuildDropdownRoles(guild_id=str(role.guild.id), role_id=str(role.id)) + session.add(new_role) + session.commit() + except Exception: + session.rollback() return False - - new_role = database.GuildDropdownRoles(guild_id=str(role.guild.id), role_id=str(role.id)) - session.add(new_role) - session.commit() return True def remove_guild_role(role: nextcord.Role) -> bool: with database.Session() as session: result = session.execute( - select(database.GuildDropdownRoles).where( + database.GuildDropdownRoles.__table__.delete().where( database.GuildDropdownRoles.guild_id == str(role.guild.id), database.GuildDropdownRoles.role_id == str(role.id) ) - ).scalar_one_or_none() - - if not result: + ) + if result.rowcount == 0: return False - - session.delete(result) session.commit() return True From 4795812f7e85c850c1de5b2d5d7411b5e08ade08 Mon Sep 17 00:00:00 2001 From: NimVrod Date: Wed, 23 Apr 2025 11:49:22 +0200 Subject: [PATCH 4/6] refactor dropdownRoles.py --- pychan/commands/utilities/dropdownRoles.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pychan/commands/utilities/dropdownRoles.py b/pychan/commands/utilities/dropdownRoles.py index 97c036d..196e04c 100644 --- a/pychan/commands/utilities/dropdownRoles.py +++ b/pychan/commands/utilities/dropdownRoles.py @@ -95,11 +95,11 @@ def remove_guild_role(role: nextcord.Role) -> bool: session.commit() return True + class DropdownRoles(commands.Cog): def __init__(self, bot): self.bot = bot - #TODO: Add admin check and remove guild_id @nextcord.slash_command(guild_ids=[381092165729910786]) async def add_roles(self, interaction: nextcord.Interaction, role: nextcord.Role): @@ -115,8 +115,6 @@ async def add_roles(self, interaction: nextcord.Interaction, role: nextcord.Role # TODO: Add admin check and remove guild_id @nextcord.slash_command(guild_ids=[381092165729910786]) async def remove_role(self, interaction: nextcord.Interaction): - - roles = get_guild_roles(interaction.guild) await interaction.send("Wybierz rolę do usunięcia", view=RolesDropdownRemoveView(roles), ephemeral=True) From 0b3fed9ea5b29cab8c924f5c50300ce25a7b16d5 Mon Sep 17 00:00:00 2001 From: NimVrod Date: Wed, 23 Apr 2025 12:13:45 +0200 Subject: [PATCH 5/6] fix database schema for dropdown roles --- pychan/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pychan/database.py b/pychan/database.py index f1ee81f..7dd5bfd 100644 --- a/pychan/database.py +++ b/pychan/database.py @@ -50,7 +50,7 @@ class QuizAnswer(Base): class GuildDropdownRoles(Base): __tablename__ = 'guild_dropdown_roles' - guild_id = Column(String, primary_key=True) + guild_id = Column(String, nullable=False) role_id = Column(String, primary_key=True) From 640310987793ddf135ebe1c27ffee788c5cbc4c2 Mon Sep 17 00:00:00 2001 From: NimVrod Date: Wed, 23 Apr 2025 13:46:16 +0200 Subject: [PATCH 6/6] Add names and descriptions to slash commands in dropdownroles.py --- pychan/commands/utilities/dropdownRoles.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pychan/commands/utilities/dropdownRoles.py b/pychan/commands/utilities/dropdownRoles.py index 196e04c..a38b390 100644 --- a/pychan/commands/utilities/dropdownRoles.py +++ b/pychan/commands/utilities/dropdownRoles.py @@ -101,7 +101,7 @@ def __init__(self, bot): self.bot = bot #TODO: Add admin check and remove guild_id - @nextcord.slash_command(guild_ids=[381092165729910786]) + @nextcord.slash_command(guild_ids=[381092165729910786], name="dodaj_role", description="Tylko administrator! Dodaj rolę do bazy danych aby użytkownicy mogli ją wybierać z listy.") async def add_roles(self, interaction: nextcord.Interaction, role: nextcord.Role): if role > interaction.guild.me.top_role or role > interaction.user.top_role: await interaction.response.send_message("Nie możesz dodać tej roli", ephemeral=True) @@ -113,7 +113,7 @@ async def add_roles(self, interaction: nextcord.Interaction, role: nextcord.Role await interaction.send(f"Dodano {role.name} do bazy danych", ephemeral=True) # TODO: Add admin check and remove guild_id - @nextcord.slash_command(guild_ids=[381092165729910786]) + @nextcord.slash_command(guild_ids=[381092165729910786], name="usun_role", description="Tylko Administrator! Usuń rolę z bazy danych listy rozwijanej.") async def remove_role(self, interaction: nextcord.Interaction): roles = get_guild_roles(interaction.guild) @@ -121,7 +121,7 @@ async def remove_role(self, interaction: nextcord.Interaction): # TODO: remove guild_id - @nextcord.slash_command(guild_ids=[381092165729910786]) + @nextcord.slash_command(guild_ids=[381092165729910786], name="wybierz_role", description="Wybierz rolę z listy rozwijanej") async def dropdown_roles(self, interaction: nextcord.Interaction): roles = get_guild_roles(interaction.guild)