diff --git a/notes.txt b/notes.txt index 6509235..aeff8fe 100644 --- a/notes.txt +++ b/notes.txt @@ -37,6 +37,24 @@ https://apidocs.cheapshark.com/ https://www.freetogame.com/api-doc https://www.gamerpower.com/api-read +Ignoring exception in command : +Traceback (most recent call last): + File "/home/container/.local/lib/python3.12/site-packages/nextcord/application_command.py", line 918, in invoke_callback_with_hooks + await self(interaction, *args, **kwargs) + File "/home/container/src/commands/ai/Summarizer.py", line 73, in summarize_message + await interaction.response.send_message(content='Fill out the translation form:', view=view,ephemeral=True) + File "/home/container/.local/lib/python3.12/site-packages/nextcord/interactions.py", line 896, in send_message + await adapter.create_interaction_response( + File "/home/container/.local/lib/python3.12/site-packages/nextcord/webhook/async_.py", line 197, in request + raise HTTPException(response, data) +nextcord.errors.HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body +In data.components.0.components.0: Value of field "type" must be one of (2, 3, 5, 6, 7, 8). + +The above exception was the direct cause of the following exception: + +nextcord.errors.ApplicationInvokeError: Command raised an exception: HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body +In data.components.0.components.0: Value of field "type" must be one of (2, 3, 5, 6, 7, 8). + ✔ Creare un comando per visualizzare le estensioni installate, attive e disattivate - Spiegare il perche' bisogna fare la verifica con il bot... @@ -44,11 +62,8 @@ https://www.gamerpower.com/api-read - ! E' inutile limitare manualmente dove il comando puo' essere usato o da chi..., e' una cosa che esiste gia' all'interno di discord (non in tutti i casi) -- !!! Per il setup delle estensioni utilizzare il comando univoco ma per ogni estensione ritornare all'utente una view o una modal diversa in base al comando triggerato... -- !! Forse sarebbe meglio togliere ephemeral nelle view e negli embed dei setup - e fare un bottone "abort" solo che devo utilizzare view.interaction_check - per controllare chi sta interagendo +✔ !!! Per il setup delle estensioni utilizzare il comando univoco ma per ogni estensione ritornare all'utente una view o una modal diversa in base al comando triggerato... - ! Rendere il datetime format univoco per tutte le voci del database e modificabile tramite config.json - !! Aggiungere un timeout alla generazione dei canali per evitare spam di canali vocali temporanei - Risolvere l'errore che da il database se non e' presente una colonna che dovrebbe essere creata -- !!! Directory refactoring \ No newline at end of file +✔ !!! Directory refactoring \ No newline at end of file diff --git a/src/commands/manager/ExtensionsUi.py b/src/commands/manager/ExtensionsUi.py index 9b4be94..9cf6823 100644 --- a/src/commands/manager/ExtensionsUi.py +++ b/src/commands/manager/ExtensionsUi.py @@ -13,19 +13,28 @@ button \ from nextcord import \ + Forbidden, \ + HTTPException, \ + TextChannel, \ + PermissionOverwrite, \ + Permissions, \ ChannelType, \ SelectOption, \ ButtonStyle, \ Interaction, \ Embed, \ Guild, \ + Role, \ Colour +from nextcord.abc import GuildChannel from nextcord.ext import commands from typing import Callable -from ..verify.VerificationUis import VerificationTypes +from ..verify.VerificationUis import \ + VerificationTypes, \ + StartVerificationUI class ExtensionUi(Embed, View): def __init__(self, bot : commands.Bot, guild : Guild, extension : str, submit_callback : Callable[[Interaction], None], timeout : int = 120): @@ -138,36 +147,103 @@ class VerifyUi(ExtensionUi): def __init__(self, bot : commands.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" : [], 'verified_role' : None} + self.config = { "modes" : [], 'verify_channel' : None, 'verify_message' : None, 'verified_role' : None} self.add_field( - name="1. Verified Role", - value="Choose the role that users who have verified their accounts will receive.", + 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. Allowed verification modes", - value="Select which verification modes you want to make enabled.\n(Each person can choose from one of these modes)", + name="2. Verified Role", + value="Choose the role that will be given to users who verify\n(no choice=create one)", inline=False ) - @role_select(placeholder="1. Verified Role", min_values=1, row=1) + 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="2. Allowed verification modes",min_values=1, max_values=len(modes), options=modes, row=3) + @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 = await role.edit(permissions=Permissions(view_channel=True), 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 self.config.get('verified_role') is not None, "You must choose one verified role!" 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() + await interaction.response.send_message(f"Verification channel {verify_channel.jump_url} and the role {verified_role.mention} created successfully!", ephemeral=True) class StaffUi(ExtensionUi): def __init__(self, bot : commands.Bot, guild : Guild, extension : str): diff --git a/src/commands/verify/VerificationUis.py b/src/commands/verify/VerificationUis.py index fd08c54..63528de 100644 --- a/src/commands/verify/VerificationUis.py +++ b/src/commands/verify/VerificationUis.py @@ -1,5 +1,7 @@ from nextcord.ext import commands from nextcord import \ + WebhookMessage, \ + SelectOption, \ Interaction, \ ButtonStyle, \ TextInputStyle, \ @@ -14,19 +16,24 @@ HTTPException, \ Colour from nextcord.ui import \ + StringSelect, \ Modal, \ View, \ TextInput, \ Button, \ + string_select, \ button from datetime import datetime, timezone from typing import Callable from enum import StrEnum import hashlib +import random from utils.exceptions import ExtensionException +from utils.commons import Extensions from utils.terminal import getlogger from utils.commons import asyncget +from utils.db import Database logger = getlogger() @@ -39,6 +46,72 @@ class VerificationStatus(StrEnum): NOT_VERIFIED = "NOT_VERIFIED" VERIFIED = "VERIFIED" +class StartVerificationUI(Embed, View): + modes = [SelectOption(label=type.value.capitalize(), value=type.value) for type in VerificationTypes] + def __init__(self, bot : commands.Bot): + View.__init__(self, timeout=None) + Embed.__init__(self) + self.description = "Select the verification mode you want to use to verify yourself (optional) and then click **Start Verification** to begin." + self.uis : dict[VerificationTypes, VerificationUI] = { + VerificationTypes.BUTTON : ButtonVerificationUi, + VerificationTypes.QUESTION : QuestionVerificationUi + } + self.colour = Colour.green() + self.db = Database() + self.bot = bot + + self.mode : VerificationTypes | None = None + + self.set_author(name=bot.user.name, icon_url=bot.user.avatar.url) + self.set_footer(text=f"Official verification message sent by {bot.user.name}", icon_url=bot.user.avatar.url) + + @string_select(placeholder="Select verification mode",custom_id='GGsBot:Verify::SelectMode', min_values=0, max_values=1, options=modes) + async def select_mode_callback(self, select : StringSelect, interaction : Interaction): + self.mode = select.values[0] if len(select.values) > 0 else None + + @button(label="Start Verification", style=ButtonStyle.primary, custom_id='GGsBot:Verify::StartVerification') + async def start_verification(self, button : Button, interaction : Interaction): + message = None + try: + await interaction.response.defer(ephemeral=True) + + async with self.db: + config, enabled = await self.db.getExtensionConfig(interaction.guild, Extensions.VERIFY) + assert enabled, "Verification is not enabled for this server" + + if not self.mode: + self.mode = random.choice(config['modes']) + else: + assert self.mode in config['modes'], f"This server does not allow verification with this method!" + + ui_type = self.uis.get(VerificationTypes(self.mode)) + + if ui_type is not None: + verified_role_id = config['verified_role'] + + verified_role = interaction.guild.get_role(verified_role_id) + + ui : VerificationUI = ui_type(self.bot, verified_role) + await ui.async_init() + + message = await interaction.followup.send(embed=ui, view=ui, wait=True, ephemeral=True) + assert not await ui.wait(), f'The verification process has expired' + + except AssertionError as e: + if message: await message.delete() + await interaction.followup.send(e, ephemeral=True) + except ExtensionException as e: + if message: await message.delete() + await interaction.followup.send(embed=e.asEmbed(), ephemeral=True) + else: + if ui.status == VerificationStatus.ALREADY_VERIFIED: + await message.edit('You have already been verified!', view=None, embed=None, delete_after=5) + elif ui.status == VerificationStatus.NOT_VERIFIED: + await message.edit('Verification failed!', view=None, embed=None, delete_after=5) + else: + await message.edit(f'Verification completed successfully!', view=None, embed=None, delete_after=5) + + class VerificationUI(Embed, View): def __init__(self, bot : commands.Bot, @@ -58,6 +131,8 @@ def status(self): return self._status @status.setter def status(self, value : VerificationStatus): self._status = value + async def async_init(self): pass + class ButtonVerificationUi(VerificationUI): def __init__(self, bot : commands.Bot, diff --git a/src/commands/verify/Verify.py b/src/commands/verify/Verify.py index d5f3087..38bef29 100644 --- a/src/commands/verify/Verify.py +++ b/src/commands/verify/Verify.py @@ -1,4 +1,4 @@ -from nextcord.ext import commands +from nextcord.ext.commands import Bot, Cog from nextcord import \ WebhookMessage, \ slash_command, \ @@ -14,73 +14,25 @@ from utils.db import Database from utils.commons import Extensions from utils.exceptions import ExtensionException -from .VerificationUis import * +from .VerificationUis import StartVerificationUI permissions = Permissions( administrator=True ) -class Verify(commands.Cog): - def __init__(self, bot : commands.Bot): - commands.Cog.__init__(self) +class Verify(Cog): + def __init__(self, bot : Bot): + Cog.__init__(self) self.db = Database() self.bot = bot + self.persistent_views_added = False - self.modes : dict[VerificationTypes, VerificationUI] = { - VerificationTypes.BUTTON : ButtonVerificationUi, - VerificationTypes.QUESTION : QuestionVerificationUi - } + @Cog.listener() + async def on_ready(self): + if not self.persistent_views_added: + view = StartVerificationUI(self.bot) + self.bot.add_view(view) + self.persistent_views_added = True - @slash_command(name="verify", description="Set of verifications commands", dm_permission=False) - async def verify(self, - interaction : Interaction, - mode : str | None = SlashOption(description="Choose verification mode", choices=VerificationTypes, required=False, default=None) - ): - try: - message : WebhookMessage = None - await interaction.response.defer(ephemeral=True) - - async with self.db: - config, enabled = await self.db.getExtensionConfig(interaction.guild, Extensions.VERIFY) - assert enabled, "Extension is not enabled" - - verified_role = interaction.guild.get_role(config['verified_role']) - - if mode: - assert mode in config['modes'], f'Mode {mode} is not a valid verification mode for this server' - else: - mode = random.choice(config['modes']) - - ui_type : VerificationUI = self.modes.get(VerificationTypes(mode), None) - - ui : VerificationUI = ui_type( - bot=self.bot, - verified=verified_role, - ) - - if ui_type == QuestionVerificationUi: - await ui.async_init() - - message = await interaction.followup.send(embed=ui, view=ui, wait=True) - assert not await ui.wait(), f'The verification process has expired' - - except AssertionError as e: - if message: - await message.edit(e, view=None, embed=None) - else: - await interaction.followup.send(e, ephemeral=True) - except ExtensionException as e: - if message: - await message.edit(content=None, embed=e.asEmbed(), view=None) - else: - await interaction.followup.send(embed=e.asEmbed(), ephemeral=True) - else: - if ui.status == VerificationStatus.ALREADY_VERIFIED: - await message.edit('You have already been verified!', view=None, embed=None, delete_after=5) - elif ui.status == VerificationStatus.NOT_VERIFIED: - await message.edit('Verification failed!', view=None, embed=None, delete_after=5) - else: - await message.edit(f'Verification completed successfully!', view=None, embed=None, delete_after=5) - -def setup(bot : commands.Bot): +def setup(bot : Bot): bot.add_cog(Verify(bot)) \ No newline at end of file diff --git a/src/commands/web/HTTPServer.py b/src/commands/web/HTTPServer.py index 6a94f83..11d81d7 100644 --- a/src/commands/web/HTTPServer.py +++ b/src/commands/web/HTTPServer.py @@ -21,6 +21,11 @@ def __init__(self, bot : Bot): self.app.router.add_get('/', self.index) + async def index(self, request : Request): + return Response(text="Hello World!", content_type="text/plain") + + + @Cog.listener() async def on_ready(self): runner = AppRunner(self.app) @@ -30,10 +35,6 @@ async def on_ready(self): logger.info(f"HTTP Server started on {self.protocol}://{self.address}:{self.port}") - async def index(self, request : Request): - return Response(text="Hello World!", content_type="text/plain") - - def setup(bot : Bot): bot.add_cog(HTTPServer(bot)) \ No newline at end of file