From 0779902274c871e7bcc9cbb3ab435a8efd06971f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:22:07 +0000 Subject: [PATCH 01/82] Bump spotipy from 2.24.0 to 2.25.0 Bumps [spotipy](https://github.com/plamere/spotipy) from 2.24.0 to 2.25.0. - [Release notes](https://github.com/plamere/spotipy/releases) - [Changelog](https://github.com/spotipy-dev/spotipy/blob/master/CHANGELOG.md) - [Commits](https://github.com/plamere/spotipy/compare/2.24.0...2.25.0) --- updated-dependencies: - dependency-name: spotipy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77eb17d..7643657 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,7 +44,7 @@ redis==5.2.1 requests==2.32.3 setuptools==75.2.0 sniffio==1.3.1 -spotipy==2.24.0 +spotipy==2.25.0 typing_extensions==4.12.2 urllib3==2.2.3 valo_api==2.0.8 From 7365bd944ff87094acc244f2908842c6dae416be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:22:13 +0000 Subject: [PATCH 02/82] Bump numpy from 2.1.2 to 2.2.1 Bumps [numpy](https://github.com/numpy/numpy) from 2.1.2 to 2.2.1. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst) - [Commits](https://github.com/numpy/numpy/compare/v2.1.2...v2.2.1) --- updated-dependencies: - dependency-name: numpy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77eb17d..06774c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ msgspec==0.18.6 multidict==6.1.0 mutagen==1.47.0 nextcord==3.0.1 -numpy==2.1.2 +numpy==2.2.1 pillow==10.4.0 propcache==0.2.0 pycparser==2.22 From 50d01159e15404f293f88c5a4b657aebe34b7148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:22:17 +0000 Subject: [PATCH 03/82] Bump cloudflare from 3.1.0 to 3.1.1 Bumps [cloudflare](https://github.com/cloudflare/cloudflare-python) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/cloudflare/cloudflare-python/releases) - [Changelog](https://github.com/cloudflare/cloudflare-python/blob/main/CHANGELOG.md) - [Commits](https://github.com/cloudflare/cloudflare-python/commits) --- updated-dependencies: - dependency-name: cloudflare dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77eb17d..fcdf36d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ certifi==2024.8.30 cffi==1.17.1 charset-normalizer==3.4.0 click==8.1.8 -cloudflare==3.1.0 +cloudflare==3.1.1 colorama==0.4.6 distro==1.9.0 Flask==3.1.0 From ca23d6a0b259b709d61570405fb04fa9c31f2b58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:22:20 +0000 Subject: [PATCH 04/82] Bump anyio from 4.6.2.post1 to 4.8.0 Bumps [anyio](https://github.com/agronholm/anyio) from 4.6.2.post1 to 4.8.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/4.6.2.post1...4.8.0) --- updated-dependencies: - dependency-name: anyio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77eb17d..78e6270 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ aiohttp==3.10.11 aiosignal==1.3.1 aiosqlite==0.20.0 annotated-types==0.7.0 -anyio==4.6.2.post1 +anyio==4.8.0 asyncio==3.4.3 attrs==24.2.0 blinker==1.9.0 From 8f804610ae8fdff2b431cc6050ab7ce517a2ee92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:22:35 +0000 Subject: [PATCH 05/82] Bump yarl from 1.16.0 to 1.18.3 --- updated-dependencies: - dependency-name: yarl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77eb17d..2844469 100644 --- a/requirements.txt +++ b/requirements.txt @@ -50,5 +50,5 @@ urllib3==2.2.3 valo_api==2.0.8 websockets==13.1 Werkzeug==3.1.3 -yarl==1.16.0 +yarl==1.18.3 yt-dlp==2024.10.22 From fe825ea099f9eeeea2c68db14b022f84526a7bc5 Mon Sep 17 00:00:00 2001 From: GitGinocchio Date: Mon, 6 Jan 2025 23:07:17 +0100 Subject: [PATCH 06/82] Update abc.py --- src/utils/abc.py | 118 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 21 deletions(-) diff --git a/src/utils/abc.py b/src/utils/abc.py index c7f1e75..0bdeb8f 100644 --- a/src/utils/abc.py +++ b/src/utils/abc.py @@ -1,16 +1,20 @@ from nextcord.ext.commands import Bot from nextcord import Embed, Interaction, Guild, Colour, ButtonStyle -from nextcord.ui import View, Item, Button +from nextcord.ui import View, Item, Button, button from typing import Callable import inspect +from .terminal import getlogger +logger = getlogger() + +""" class SetupUI(Embed, View): def __init__(self, bot : Bot, guild : Guild, title : str, - submit_callback : Callable[[Interaction], None], + submit_callback : Callable[[Interaction], None], submit_title : str = "Submit", timeout : int = 120 ): @@ -54,19 +58,90 @@ async def __setup(self, interaction : Interaction): await self._submit_callback(interaction) except Exception as e: raise e - -class GGsBotPage:#(Embed, View): - def __init__(self, ui : 'GGsBotUI'): - #Embed.__init__(self) - #View.__init__(self) +""" + +class BasePage(Embed, View): + def __init__(self, ui : 'UI', timeout : int = 180): + Embed.__init__(self) + View.__init__(self, timeout=timeout) self.ui = ui + async def interaction_check(self, interaction: Interaction) -> bool: + logger.debug(f"Checking interaction for {self.ui}") + return await super().interaction_check(interaction) + async def on_error(self, item : Item, interaction : Interaction): + await interaction.followup.delete_message(interaction.message.id) + logger.error(f"An error occurred in {self.ui} in item {item}") + self.stop() -class GGsBotUI: - def __init__(self): - self.pages = [cls(self) for _, cls in inspect.getmembers(MyUI, lambda x: inspect.isclass(x) and issubclass(x, GGsBotPage))] - print(self.pages) + async def on_timeout(self): + logger.error(f"{self.ui} timed out") + +class Page(BasePage): + def __init__(self, ui : 'UI'): + BasePage.__init__(self, ui) + + @button(label="Next", style=ButtonStyle.primary, row=4) + async def next_page(self, interaction: Interaction): + self.ui.current_page += 1 + + next_page = self.ui.pages[self.ui.current_page] + + await interaction.message.edit(embed=next_page, view=next_page) + + @button(label="Back", style=ButtonStyle.secondary, row=4) + async def prev_page(self, interaction: Interaction): + self.ui.current_page -= 1 + + next_page = self.ui.pages[self.ui.current_page] + + await interaction.message.edit(embed=next_page, view=next_page) + +class SubmitPage(BasePage): + def __init__(self, + ui : 'UI', + submit_title : str = "Submit", + submit_callback : Callable[[Interaction], None] = None + ): + BasePage.__init__(self, ui) + self.submit_title = submit_title + self._submit_callback = submit_callback + + self.button = Button(label=submit_title, style=ButtonStyle.success, row=4) + self.button.callback = self.submit + self.add_item(self.button) + + async def submit(self, interaction : Interaction): + try: + if self._submit_callback: + await self._submit_callback(interaction) + else: + self.stop() + except Exception as e: + raise e + + + +class UI(object): + def __init__(self, + bot : Bot, + guild : Guild + ): + self.pages : list[Page] = [cls(self) for _, cls in inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, Page))] + + submit_pages = inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, SubmitPage)) + if (len_submit_pages:=len(submit_pages)) == 1: + self.pages.append(submit_pages[0][1](self)) + elif len_submit_pages > 1: + raise ValueError("There should be only one SubmitPage class in the UI") + else: + self.pages.append(SubmitPage(self)) + + self.current_page = 0 + self._config = {} + self._guild = guild + self._bot = bot @property def bot(self): return self._bot @@ -81,17 +156,18 @@ def config(self): return self._config def config(self, config : dict): self._config = config - -class MyUI(GGsBotUI): +class MyUI(UI): def __init__(self): - GGsBotUI.__init__(self) + UI.__init__(self) - class MyFirstPage(GGsBotPage): - def __init__(self, ui : GGsBotUI): - GGsBotPage.__init__(self, ui) + class MyFirstPage(Page): + def __init__(self, ui : UI): + Page.__init__(self, ui) - class MySecondPage(GGsBotPage): - def __init__(self, ui : GGsBotUI): - GGsBotPage.__init__(self, ui) + class MySecondPage(Page): + def __init__(self, ui : UI): + Page.__init__(self, ui) -MyUI() \ No newline at end of file + class MySubmitPage(SubmitPage): + def __init__(self, ui : UI): + SubmitPage.__init__(self, ui) From 7a62daf1f6c18ccbc16590eb1e29994285d73307 Mon Sep 17 00:00:00 2001 From: GitGinocchio Date: Mon, 6 Jan 2025 23:26:13 +0100 Subject: [PATCH 07/82] Update abc.py --- src/utils/abc.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/utils/abc.py b/src/utils/abc.py index 0bdeb8f..1389467 100644 --- a/src/utils/abc.py +++ b/src/utils/abc.py @@ -79,8 +79,8 @@ async def on_timeout(self): logger.error(f"{self.ui} timed out") class Page(BasePage): - def __init__(self, ui : 'UI'): - BasePage.__init__(self, ui) + def __init__(self, ui : 'UI', timeout : int = 180): + BasePage.__init__(self, ui, timeout) @button(label="Next", style=ButtonStyle.primary, row=4) async def next_page(self, interaction: Interaction): @@ -102,9 +102,10 @@ class SubmitPage(BasePage): def __init__(self, ui : 'UI', submit_title : str = "Submit", - submit_callback : Callable[[Interaction], None] = None + submit_callback : Callable[[Interaction], None] = None, + timeout : int = 180 ): - BasePage.__init__(self, ui) + BasePage.__init__(self, ui, timeout) self.submit_title = submit_title self._submit_callback = submit_callback From af030f1fafd95d01efe02579b2ef5355dd37128d Mon Sep 17 00:00:00 2001 From: GitGinocchio Date: Tue, 7 Jan 2025 00:31:57 +0100 Subject: [PATCH 08/82] New BasePage, Page and UI classes --- config/config.jsonc | 2 +- src/commands/manager/CommandsManager.py | 27 ++- src/commands/manager/ExtensionsUi.py | 251 +++------------------- src/commands/manager/OldExtensionsUi.py | 266 ++++++++++++++++++++++++ src/utils/abc.py | 83 ++++---- 5 files changed, 348 insertions(+), 281 deletions(-) create mode 100644 src/commands/manager/OldExtensionsUi.py diff --git a/config/config.jsonc b/config/config.jsonc index d2576de..a0b53e2 100644 --- a/config/config.jsonc +++ b/config/config.jsonc @@ -21,7 +21,7 @@ // Logger settings "logger" : { - "level" : "INFO", + "level" : "DEBUG", "dir" : "./logs", "tofile" : true, "datefmt" : "%Y-%m-%d, %H:%M:%S", diff --git a/src/commands/manager/CommandsManager.py b/src/commands/manager/CommandsManager.py index d084a58..08be7da 100644 --- a/src/commands/manager/CommandsManager.py +++ b/src/commands/manager/CommandsManager.py @@ -25,7 +25,9 @@ GUILD_INTEGRATION, \ USER_INTEGRATION -from .ExtensionsUi import * +from .ExtensionsUi import \ + SetupUI, \ + AiChatBotSetupUI logger = getlogger() @@ -38,13 +40,13 @@ def __init__(self, bot : commands.Bot): commands.Cog.__init__(self) self.db = Database() self.bot = bot - self.setup_dict : dict[Extensions, ExtensionUI] = { - Extensions.AICHATBOT : AiChatBotUi, - Extensions.GREETINGS : GreetingsUi, - Extensions.CHEAPGAMES : CheapGamesUi, - Extensions.VERIFY : VerifyUi, - Extensions.STAFF : StaffUi, - Extensions.TEMPVC : TempVCUi + self.setup_dict : dict[Extensions, SetupUI] = { + Extensions.AICHATBOT : AiChatBotSetupUI, + Extensions.GREETINGS : SetupUI, + Extensions.CHEAPGAMES : SetupUI, + Extensions.VERIFY : SetupUI, + Extensions.STAFF : SetupUI, + Extensions.TEMPVC : SetupUI } @slash_command(name='ext', description='Set of commands to manage bot extensions',default_member_permissions=permissions,integration_types=GUILD_INTEGRATION) @@ -114,12 +116,15 @@ async def setup(self, if ui_type is not None: ui = ui_type(self.bot, interaction.guild, extension) else: - ui = ExtensionUI(self.bot, interaction.guild, extension, lambda _:None) + ui = SetupUI(self.bot, interaction.guild, extension) + + page = ui.get_current_page() + submit_page = ui.get_submit_page() print('pre-interaction') - message = await interaction.followup.send(embed=ui,view=ui, wait=True) + message = await interaction.followup.send(embed=page,view=page, wait=True) - assert not await ui.wait(), f'The configuration process has expired' + assert not await submit_page.wait(), f'The configuration process has expired' print('post-interaction') config = dict(ui.config) diff --git a/src/commands/manager/ExtensionsUi.py b/src/commands/manager/ExtensionsUi.py index 891f624..08022b3 100644 --- a/src/commands/manager/ExtensionsUi.py +++ b/src/commands/manager/ExtensionsUi.py @@ -26,239 +26,36 @@ VerificationTypes, \ StartVerificationUI -from utils.abc import SetupUI +from utils.abc import UI, Page, SubmitPage -class ExtensionUI(SetupUI): - def __init__(self, bot : Bot, guild : Guild, extension : str, submit_callback : Callable[[Interaction], None], timeout : int = 120): - SetupUI.__init__(self, bot, guild, f'{extension.capitalize()} extension setup', submit_callback, "Setup", timeout) - self.description = f"Setup process for {extension.capitalize()} extension" - self.colour = Colour.green() - -class AiChatBotUi(ExtensionUI): - delays = [SelectOption(label=f'{n} Seconds', value=str(n), default=(True if n == 5 else False)) for n in range(1, 26,1)] - def __init__(self, bot : Bot, guild : Guild, extension : str): - ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) - self.description = f"{super().description}, this extension allows you to have a chatbot Ai within your discord server" - self.config = { 'chat-delay' : 0, 'threads' : {} } - - self.add_field( - name="2. Chat Delay", - value="Select the delay of the chat with GGsBot Ai. (no choice=5 Seconds)" - ) - - @string_select(placeholder="2. Chat Delay", options=delays, min_values=1,row=1) - async def select_delay(self, select: StringSelect, interaction : Interaction): - self.config['chat-delay'] = (select.values[0] if len(select.values) != 0 else None) - - async def on_submit(self, interaction : Interaction): - self.stop() - -class GreetingsUi(ExtensionUI): - def __init__(self, bot : Bot, guild : Guild, extension : str): - ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) - self.description = f"{super().description}, This extension allows you to generate messages whenever a user joins the server" - self.config = {'welcome_channel':None,'goodbye_channel':None} - - self.add_field( - name="1. Welcome Channel", - value="Choose the text channel where welcome messages will be sent\n(no choice=no channel)", - inline=False - ) - - self.add_field( - name="2. Goodbye Channel", - value="Choose the text channel where goodbye messages will be sent\n(no choice=no channel)" - ) - - @channel_select(placeholder="1. Welcome Channel",channel_types=[ChannelType.text], min_values=0, row=1) - async def welcome_channel(self, select: ChannelSelect, interaction : Interaction): - self.config['welcome_channel'] = (select.values[0].id if len(select.values) > 0 else None) - - @channel_select(placeholder="2. Goodbye Channel",channel_types=[ChannelType.text], min_values=0, row=2) - async def goodbye_channel(self, select: ChannelSelect, interaction : Interaction): - self.config['goodbye_channel'] = (select.values[0].id if len(select.values) > 0 else None) - - async def on_submit(self, interaction : Interaction): - try: - assert self.config.get('welcome_channel') is not None or self.config.get('goodbye_channel') is not None, "You must choose at least one welcome channel or one goodbye channel!" - except AssertionError as e: - await interaction.response.send_message(e, ephemeral=True, delete_after=5) - except Exception as e: - print(e) - else: - self.stop() - -class TempVCUi(ExtensionUI): +class SetupUI(UI): def __init__(self, bot : Bot, guild : Guild, extension : str): - ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) - self.description = f"{super().description}, This extension allows you to transform voice channels into \"generators\" of temporary voice channels" - self.config = {'listeners' : {}, 'channels' : {}} + UI.__init__(self, bot, guild, extension) - def on_submit(self, interaction : Interaction): - self.stop() + class SetupSubmitPage(SubmitPage): + def __init__(self, ui : 'SetupUI', extension : str): + SubmitPage.__init__(self, ui, extension, submit_title="Setup") + self.description = f"Clicking Setup will confirm all saved settings so far and install the {self.extension.capitalize()} extension on the server, do you want to continue?" + self.colour = Colour.green() -class VerifyUi(ExtensionUI): - modes = [SelectOption(label=type.value.capitalize(), value=type.value) for type in VerificationTypes] +class AiChatBotSetupUI(SetupUI): def __init__(self, bot : Bot, guild : Guild, extension : str): - ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) - self.description = f'{super().description}, this extension allows you to add a verification level of the account of users who enter within this server' - self.config = { "modes" : [], 'verify_channel' : None, 'verify_message' : None, 'verified_role' : None} - - self.add_field( - name="1. Verification Channel", - value="Choose the text channel where verification messages will be sent\n(no choice=create one)", - inline=False - ) - - self.add_field( - name="2. Verified Role", - value="Choose the role that will be given to users who verify\n(no choice=create one)", - inline=False - ) - - self.add_field( - name="3. Allowed verification modes", - value="Select which verification modes you want to enable.\n(Each member can choose from one of these modes to verify)", - inline=False - ) - - @channel_select(placeholder="1. Verification Channel",channel_types=[ChannelType.text], min_values=0, row=1) - async def verify_channel(self, select: ChannelSelect, interaction : Interaction): - self.config['verify_channel'] = (select.values[0].id if len(select.values) > 0 else None) + SetupUI.__init__(self, bot, guild, extension) - @role_select(placeholder="2. Verified Role", min_values=0, row=2) - async def verified_role(self, select: RoleSelect, interaction : Interaction): - self.config['verified_role'] = (select.values[0].id if len(select.values) > 0 else None) - - @string_select(placeholder="3. Allowed verification modes",min_values=1, max_values=len(modes), options=modes, row=3) - async def verification_modes(self, select: StringSelect, interaction : Interaction): - self.config['modes'] = select.values - - async def setup_verified_role(self, role : Role | None = None): - if role == None: - role = await self.guild.create_role( - name=f"Verified by {self.bot.user.name}", - colour=Colour.green(), - permissions=Permissions(view_channel=True), - reason="GGsBot:Verify" - ) - else: - role.permissions.update(view_channel=True) - role = await role.edit(permissions=role.permissions, reason="GGsBot:Verify") - return role - - async def setup_verify_channel(self, verified_role : Role, verify_channel : TextChannel | None = None): - overwrites = { - self.guild.default_role: PermissionOverwrite(view_channel=True,read_message_history=True), - verified_role: PermissionOverwrite(view_channel=False) - } - - if verify_channel == None: - channel = await self.guild.create_text_channel( - name=f"verify", - reason="GGsBot:Verify", - position=0, - overwrites=overwrites + class AiChatBotPage(Page): + delays = [SelectOption(label=f'{n} Seconds', value=str(n), default=(True if n == 5 else False)) for n in range(1, 26,1)] + def __init__(self, ui : UI, extension : str): + Page.__init__(self, ui, extension) + self.description = f"This extension allows you to have a chatbot Ai within your discord server" + self.ui.config = { 'chat-delay' : 5, 'threads' : {} } + self.colour = Colour.green() + + self.add_field( + name="2. Chat Delay", + value="Select the delay of the chat with GGsBot Ai. (no choice=5 Seconds)" ) - else: - channel = await verify_channel.edit(overwrites=overwrites, reason="GGsBot:Verify") - return channel - - async def ensure_everyone_permissions(self): - await self.guild.default_role.edit( - reason="GGsBot:Verify", - permissions=Permissions.none() - ) - - async def send_verification_message(self, channel : TextChannel): - ui = StartVerificationUI(self.bot) - message = await channel.send(embed=ui, view=ui) - self.config['verify_message'] = message.id - - async def on_submit(self, interaction : Interaction): - try: - assert len(self.config.get('modes')), "You must choose at least one verification mode!" - - await self.ensure_everyone_permissions() - - verified_role = (self.guild.get_role(verified_role_id) if (verified_role_id:=self.config.get('verified_role')) else None) - verify_channel = (self.guild.get_channel(verify_channel_id) if (verify_channel_id:=self.config.get('verify_channel')) else None) - - verified_role = await self.setup_verified_role(verified_role) - verify_channel = await self.setup_verify_channel(verified_role, verify_channel) - - self.config['verify_channel'] = verify_channel.id - self.config['verified_role'] = verified_role.id - - await self.send_verification_message(verify_channel) - except AssertionError as e: - await interaction.response.send_message(e, ephemeral=True, delete_after=5) - except Forbidden as e: - await interaction.response.send_message(e, ephemeral=True, delete_after=5) - except Exception as e: - print(e) - else: - self.stop() - -class StaffUi(ExtensionUI): - def __init__(self, bot : Bot, guild : Guild, extension : str): - ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) - self.description = f"{super().description}, This extension allows you to assign staff members a role when they are inactive and why" - self.config = { 'staff_role': None, 'inactive_role': None, 'inactive': {}} - - self.add_field( - name="1. Staff Role", - value="The role assigned to each staff member", - inline=False - ) - - self.add_field( - name="2. Inactive Role", - value="The role assigned to each inactive staff member" - ) - - @role_select(placeholder="1. Staff Role",min_values=1, row=1) - async def staff_role(self, select: RoleSelect, interaction : Interaction): - self.config['staff_role'] = (select.values[0].id if len(select.values) > 0 else None) - - @role_select(placeholder="2. Inactive Role",min_values=1, row=2) - async def inactive_role(self, select: RoleSelect, interaction : Interaction): - self.config['inactive_role'] = (select.values[0].id if len(select.values) > 0 else None) - - async def on_submit(self, interaction : Interaction): - try: - assert self.config.get('staff_role') is not None and self.config.get('inactive_role') is not None, "You must choose at least one staff role and one inactive role!" - assert self.config.get('staff_role') != self.config.get('inactive_role'), "The staff role and inactive role cannot be the same!" - except AssertionError as e: - await interaction.response.send_message(e, ephemeral=True, delete_after=5) - except Exception as e: - print(e) - else: - self.stop() - -class CheapGamesUi(ExtensionUI): - def __init__(self, bot : Bot, guild : Guild, extension : str): - ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) - self.description = f'{super().description}, This extension allows you to add commands and updates for free or discounted games for different types and platforms' - self.config = { 'updates' : {} } - - self.add_field( - name="Updates", - value="This extension allows you to create more game updates (also by channel) based on different conditions and types such as deals and giveaways", - inline=False - ) - - self.add_field( - name="Conditions", - value="You can define conditions such as price range, platform (such as Steam or Epic Games), and other criteria to determine which updates are considered valid for an extension to be used by different users", - inline=False - ) - self.add_field( - name="Update interval", - value="Updates will be sent every hour", - inline=False - ) + @string_select(placeholder="2. Chat Delay", options=delays, min_values=1,row=1) + async def select_delay(self, select: StringSelect, interaction : Interaction): + self.ui.config['chat-delay'] = (select.values[0] if len(select.values) != 0 else None) - async def on_submit(self, interaction : Interaction): - self.stop() \ No newline at end of file diff --git a/src/commands/manager/OldExtensionsUi.py b/src/commands/manager/OldExtensionsUi.py new file mode 100644 index 0000000..7418b5d --- /dev/null +++ b/src/commands/manager/OldExtensionsUi.py @@ -0,0 +1,266 @@ +from nextcord.ui import \ + RoleSelect, \ + StringSelect, \ + ChannelSelect, \ + channel_select, \ + string_select, \ + role_select \ + +from nextcord import \ + Forbidden, \ + TextChannel, \ + PermissionOverwrite, \ + Permissions, \ + ChannelType, \ + SelectOption, \ + Interaction, \ + Guild, \ + Role, \ + Colour + +from nextcord.ext.commands import Bot + +from typing import Callable + +from ..verify.VerificationUis import \ + VerificationTypes, \ + StartVerificationUI + +""" +from utils.abc import SetupUI + +class ExtensionUI(SetupUI): + def __init__(self, bot : Bot, guild : Guild, extension : str, submit_callback : Callable[[Interaction], None], timeout : int = 120): + SetupUI.__init__(self, bot, guild, f'{extension.capitalize()} extension setup', submit_callback, "Setup", timeout) + self.description = f"Setup process for {extension.capitalize()} extension" + self.colour = Colour.green() + +class AiChatBotUi(ExtensionUI): + delays = [SelectOption(label=f'{n} Seconds', value=str(n), default=(True if n == 5 else False)) for n in range(1, 26,1)] + def __init__(self, bot : Bot, guild : Guild, extension : str): + ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) + self.description = f"{super().description}, this extension allows you to have a chatbot Ai within your discord server" + self.config = { 'chat-delay' : 0, 'threads' : {} } + + self.add_field( + name="2. Chat Delay", + value="Select the delay of the chat with GGsBot Ai. (no choice=5 Seconds)" + ) + + @string_select(placeholder="2. Chat Delay", options=delays, min_values=1,row=1) + async def select_delay(self, select: StringSelect, interaction : Interaction): + self.config['chat-delay'] = (select.values[0] if len(select.values) != 0 else None) + + async def on_submit(self, interaction : Interaction): + self.stop() + +class GreetingsUi(ExtensionUI): + def __init__(self, bot : Bot, guild : Guild, extension : str): + ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) + self.description = f"{super().description}, This extension allows you to generate messages whenever a user joins the server" + self.config = {'welcome_channel':None,'goodbye_channel':None} + + self.add_field( + name="1. Welcome Channel", + value="Choose the text channel where welcome messages will be sent\n(no choice=no channel)", + inline=False + ) + + self.add_field( + name="2. Goodbye Channel", + value="Choose the text channel where goodbye messages will be sent\n(no choice=no channel)" + ) + + @channel_select(placeholder="1. Welcome Channel",channel_types=[ChannelType.text], min_values=0, row=1) + async def welcome_channel(self, select: ChannelSelect, interaction : Interaction): + self.config['welcome_channel'] = (select.values[0].id if len(select.values) > 0 else None) + + @channel_select(placeholder="2. Goodbye Channel",channel_types=[ChannelType.text], min_values=0, row=2) + async def goodbye_channel(self, select: ChannelSelect, interaction : Interaction): + self.config['goodbye_channel'] = (select.values[0].id if len(select.values) > 0 else None) + + async def on_submit(self, interaction : Interaction): + try: + assert self.config.get('welcome_channel') is not None or self.config.get('goodbye_channel') is not None, "You must choose at least one welcome channel or one goodbye channel!" + except AssertionError as e: + await interaction.response.send_message(e, ephemeral=True, delete_after=5) + except Exception as e: + print(e) + else: + self.stop() + +class TempVCUi(ExtensionUI): + def __init__(self, bot : Bot, guild : Guild, extension : str): + ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) + self.description = f"{super().description}, This extension allows you to transform voice channels into \"generators\" of temporary voice channels" + self.config = {'listeners' : {}, 'channels' : {}} + + def on_submit(self, interaction : Interaction): + self.stop() + +class VerifyUi(ExtensionUI): + modes = [SelectOption(label=type.value.capitalize(), value=type.value) for type in VerificationTypes] + def __init__(self, bot : Bot, guild : Guild, extension : str): + ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) + self.description = f'{super().description}, this extension allows you to add a verification level of the account of users who enter within this server' + self.config = { "modes" : [], 'verify_channel' : None, 'verify_message' : None, 'verified_role' : None} + + self.add_field( + name="1. Verification Channel", + value="Choose the text channel where verification messages will be sent\n(no choice=create one)", + inline=False + ) + + self.add_field( + name="2. Verified Role", + value="Choose the role that will be given to users who verify\n(no choice=create one)", + inline=False + ) + + self.add_field( + name="3. Allowed verification modes", + value="Select which verification modes you want to enable.\n(Each member can choose from one of these modes to verify)", + inline=False + ) + + @channel_select(placeholder="1. Verification Channel",channel_types=[ChannelType.text], min_values=0, row=1) + async def verify_channel(self, select: ChannelSelect, interaction : Interaction): + self.config['verify_channel'] = (select.values[0].id if len(select.values) > 0 else None) + + @role_select(placeholder="2. Verified Role", min_values=0, row=2) + async def verified_role(self, select: RoleSelect, interaction : Interaction): + self.config['verified_role'] = (select.values[0].id if len(select.values) > 0 else None) + + @string_select(placeholder="3. Allowed verification modes",min_values=1, max_values=len(modes), options=modes, row=3) + async def verification_modes(self, select: StringSelect, interaction : Interaction): + self.config['modes'] = select.values + + async def setup_verified_role(self, role : Role | None = None): + if role == None: + role = await self.guild.create_role( + name=f"Verified by {self.bot.user.name}", + colour=Colour.green(), + permissions=Permissions(view_channel=True), + reason="GGsBot:Verify" + ) + else: + role.permissions.update(view_channel=True) + role = await role.edit(permissions=role.permissions, reason="GGsBot:Verify") + return role + + async def setup_verify_channel(self, verified_role : Role, verify_channel : TextChannel | None = None): + overwrites = { + self.guild.default_role: PermissionOverwrite(view_channel=True,read_message_history=True), + verified_role: PermissionOverwrite(view_channel=False) + } + + if verify_channel == None: + channel = await self.guild.create_text_channel( + name=f"verify", + reason="GGsBot:Verify", + position=0, + overwrites=overwrites + ) + else: + channel = await verify_channel.edit(overwrites=overwrites, reason="GGsBot:Verify") + return channel + + async def ensure_everyone_permissions(self): + await self.guild.default_role.edit( + reason="GGsBot:Verify", + permissions=Permissions.none() + ) + + async def send_verification_message(self, channel : TextChannel): + ui = StartVerificationUI(self.bot) + message = await channel.send(embed=ui, view=ui) + self.config['verify_message'] = message.id + + async def on_submit(self, interaction : Interaction): + try: + assert len(self.config.get('modes')), "You must choose at least one verification mode!" + + await self.ensure_everyone_permissions() + + verified_role = (self.guild.get_role(verified_role_id) if (verified_role_id:=self.config.get('verified_role')) else None) + verify_channel = (self.guild.get_channel(verify_channel_id) if (verify_channel_id:=self.config.get('verify_channel')) else None) + + verified_role = await self.setup_verified_role(verified_role) + verify_channel = await self.setup_verify_channel(verified_role, verify_channel) + + self.config['verify_channel'] = verify_channel.id + self.config['verified_role'] = verified_role.id + + await self.send_verification_message(verify_channel) + except AssertionError as e: + await interaction.response.send_message(e, ephemeral=True, delete_after=5) + except Forbidden as e: + await interaction.response.send_message(e, ephemeral=True, delete_after=5) + except Exception as e: + print(e) + else: + self.stop() + +class StaffUi(ExtensionUI): + def __init__(self, bot : Bot, guild : Guild, extension : str): + ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) + self.description = f"{super().description}, This extension allows you to assign staff members a role when they are inactive and why" + self.config = { 'staff_role': None, 'inactive_role': None, 'inactive': {}} + + self.add_field( + name="1. Staff Role", + value="The role assigned to each staff member", + inline=False + ) + + self.add_field( + name="2. Inactive Role", + value="The role assigned to each inactive staff member" + ) + + @role_select(placeholder="1. Staff Role",min_values=1, row=1) + async def staff_role(self, select: RoleSelect, interaction : Interaction): + self.config['staff_role'] = (select.values[0].id if len(select.values) > 0 else None) + + @role_select(placeholder="2. Inactive Role",min_values=1, row=2) + async def inactive_role(self, select: RoleSelect, interaction : Interaction): + self.config['inactive_role'] = (select.values[0].id if len(select.values) > 0 else None) + + async def on_submit(self, interaction : Interaction): + try: + assert self.config.get('staff_role') is not None and self.config.get('inactive_role') is not None, "You must choose at least one staff role and one inactive role!" + assert self.config.get('staff_role') != self.config.get('inactive_role'), "The staff role and inactive role cannot be the same!" + except AssertionError as e: + await interaction.response.send_message(e, ephemeral=True, delete_after=5) + except Exception as e: + print(e) + else: + self.stop() + +class CheapGamesUi(ExtensionUI): + def __init__(self, bot : Bot, guild : Guild, extension : str): + ExtensionUI.__init__(self, bot, guild, extension, self.on_submit) + self.description = f'{super().description}, This extension allows you to add commands and updates for free or discounted games for different types and platforms' + self.config = { 'updates' : {} } + + self.add_field( + name="Updates", + value="This extension allows you to create more game updates (also by channel) based on different conditions and types such as deals and giveaways", + inline=False + ) + + self.add_field( + name="Conditions", + value="You can define conditions such as price range, platform (such as Steam or Epic Games), and other criteria to determine which updates are considered valid for an extension to be used by different users", + inline=False + ) + + self.add_field( + name="Update interval", + value="Updates will be sent every hour", + inline=False + ) + + async def on_submit(self, interaction : Interaction): + self.stop() +""" \ No newline at end of file diff --git a/src/utils/abc.py b/src/utils/abc.py index 1389467..fcbb0b8 100644 --- a/src/utils/abc.py +++ b/src/utils/abc.py @@ -1,6 +1,7 @@ from nextcord.ext.commands import Bot from nextcord import Embed, Interaction, Guild, Colour, ButtonStyle from nextcord.ui import View, Item, Button, button +from datetime import datetime, timezone from typing import Callable import inspect @@ -61,51 +62,59 @@ async def __setup(self, interaction : Interaction): """ class BasePage(Embed, View): - def __init__(self, ui : 'UI', timeout : int = 180): - Embed.__init__(self) + def __init__(self, ui : 'UI', extension : str, timeout : int = 180): + Embed.__init__(self, timestamp=datetime.now(timezone.utc)) View.__init__(self, timeout=timeout) + self.extension = extension self.ui = ui + self.set_author(name=self.ui.bot.user.name, icon_url=self.ui.bot.user.avatar.url) + async def interaction_check(self, interaction: Interaction) -> bool: - logger.debug(f"Checking interaction for {self.ui}") + logger.debug(f"Checking interaction for extension {self.extension} ({self.ui})") return await super().interaction_check(interaction) async def on_error(self, item : Item, interaction : Interaction): await interaction.followup.delete_message(interaction.message.id) - logger.error(f"An error occurred in {self.ui} in item {item}") + logger.error(f"An error occurred in extension {self.extension} ({self.ui}) in item {item}") self.stop() async def on_timeout(self): - logger.error(f"{self.ui} timed out") + logger.error(f"Extension {self.extension} ({self.ui}) timed out") class Page(BasePage): - def __init__(self, ui : 'UI', timeout : int = 180): - BasePage.__init__(self, ui, timeout) + def __init__(self, ui : 'UI', extension : str, timeout : int = 180): + BasePage.__init__(self, ui, extension, timeout) - @button(label="Next", style=ButtonStyle.primary, row=4) - async def next_page(self, interaction: Interaction): - self.ui.current_page += 1 + @button(label="Back", style=ButtonStyle.secondary, row=4) + async def prev_page(self, button: Button, interaction: Interaction): + if self.ui.current_page <= 0: return + + self.ui.current_page -= 1 - next_page = self.ui.pages[self.ui.current_page] + back_page = self.ui.pages[self.ui.current_page] - await interaction.message.edit(embed=next_page, view=next_page) + await interaction.response.edit_message(embed=back_page, view=back_page) - @button(label="Back", style=ButtonStyle.secondary, row=4) - async def prev_page(self, interaction: Interaction): - self.ui.current_page -= 1 + @button(label="Next", style=ButtonStyle.primary, row=4) + async def next_page(self, button: Button, interaction: Interaction): + if self.ui.current_page >= len(self.ui.pages): return + + self.ui.current_page += 1 next_page = self.ui.pages[self.ui.current_page] - await interaction.message.edit(embed=next_page, view=next_page) + await interaction.response.edit_message(embed=next_page, view=next_page) class SubmitPage(BasePage): def __init__(self, ui : 'UI', + extension : str, submit_title : str = "Submit", submit_callback : Callable[[Interaction], None] = None, timeout : int = 180 ): - BasePage.__init__(self, ui, timeout) + BasePage.__init__(self, ui, extension, timeout) self.submit_title = submit_title self._submit_callback = submit_callback @@ -127,22 +136,23 @@ async def submit(self, interaction : Interaction): class UI(object): def __init__(self, bot : Bot, - guild : Guild + guild : Guild, + extension : str, ): - self.pages : list[Page] = [cls(self) for _, cls in inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, Page))] + self.current_page = 0 + self._config = {} + self._guild = guild + self._bot = bot + + self.pages : list[Page | SubmitPage] = [cls(self, extension) for _, cls in inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, Page))] submit_pages = inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, SubmitPage)) if (len_submit_pages:=len(submit_pages)) == 1: - self.pages.append(submit_pages[0][1](self)) + self.pages.append(submit_pages[0][1](self, extension)) elif len_submit_pages > 1: raise ValueError("There should be only one SubmitPage class in the UI") else: - self.pages.append(SubmitPage(self)) - - self.current_page = 0 - self._config = {} - self._guild = guild - self._bot = bot + self.pages.append(SubmitPage(self, extension)) @property def bot(self): return self._bot @@ -156,19 +166,8 @@ def config(self): return self._config @config.setter def config(self, config : dict): self._config = config - -class MyUI(UI): - def __init__(self): - UI.__init__(self) - - class MyFirstPage(Page): - def __init__(self, ui : UI): - Page.__init__(self, ui) - - class MySecondPage(Page): - def __init__(self, ui : UI): - Page.__init__(self, ui) - - class MySubmitPage(SubmitPage): - def __init__(self, ui : UI): - SubmitPage.__init__(self, ui) + def get_current_page(self) -> Page: + return self.pages[self.current_page] + + def get_submit_page(self) -> SubmitPage: + return self.pages[-1] From a1b491468bab21903b7c4f2287dd5d11e0103739 Mon Sep 17 00:00:00 2001 From: GitGinocchio Date: Tue, 7 Jan 2025 21:06:35 +0100 Subject: [PATCH 09/82] Create error.txt --- .dev/error.txt | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .dev/error.txt diff --git a/.dev/error.txt b/.dev/error.txt new file mode 100644 index 0000000..029db04 --- /dev/null +++ b/.dev/error.txt @@ -0,0 +1,102 @@ +Ignoring exception in view for item