mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-12-07 09:52:30 -05:00
Add support for set api Modals (#5637)
* Add support for set api Modals Co-authored-by: TrustyJAID <TrustyJAID@gmail.com> * Blaacckkkk! * Swap locations of interaction and button. * Clarified template tokens * Update docs and some string * More docs * Rework the client Co-authored-by: TrustyJAID <TrustyJAID@gmail.com> * Goddamned black! * Missed a few arguments * Black... Again * Update redbot/core/utils/views.py Co-authored-by: TrustyJAID <TrustyJAID@gmail.com> * Update redbot/core/core_commands.py Co-authored-by: TrustyJAID <TrustyJAID@gmail.com> Co-authored-by: TrustyJAID <TrustyJAID@gmail.com>
This commit is contained in:
176
redbot/core/utils/views.py
Normal file
176
redbot/core/utils/views.py
Normal file
@@ -0,0 +1,176 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import discord
|
||||
|
||||
from discord.ext.commands import BadArgument
|
||||
from typing import List, Dict, Union, Optional
|
||||
from redbot.core.commands.converter import get_dict_converter
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
_ = Translator("UtilsViews", __file__)
|
||||
|
||||
|
||||
class SetApiModal(discord.ui.Modal):
|
||||
"""
|
||||
A secure ``discord.ui.Modal`` used to set API keys.
|
||||
|
||||
This Modal can either be used standalone with its own ``discord.ui.View``
|
||||
for custom implementations, or created via ``SetApiView``
|
||||
to have an easy to implemement secure way of setting API keys.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
default_service: Optional[str]
|
||||
The service to add the API keys to.
|
||||
If this is omitted the bot owner is allowed to set his own service.
|
||||
Defaults to ``None``.
|
||||
default_keys: Optional[Dict[str, str]]
|
||||
The API keys the service is expecting.
|
||||
This will only allow the bot owner to set keys the Modal is expecting.
|
||||
Defaults to ``None``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
default_service: Optional[str] = None,
|
||||
default_keys: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
self.default_service = default_service
|
||||
self.default_keys: List[str] = []
|
||||
if default_keys is not None:
|
||||
self.default_keys = list(default_keys.keys())
|
||||
self.default_keys_fmt = self._format_keys(default_keys)
|
||||
|
||||
_placeholder_service = "service"
|
||||
if self.default_service is not None:
|
||||
_placeholder_service = self.default_service
|
||||
_placeholder_token = "client_id YOUR_CLIENT_ID\nclient_secret YOUR_CLIENT_SECRET"
|
||||
if self.default_keys_fmt is not None:
|
||||
_placeholder_token = self.default_keys_fmt
|
||||
|
||||
self.title = _("Set API Keys")
|
||||
self.keys_label = _("Keys and tokens")
|
||||
if self.default_service is not None:
|
||||
self.title = _("Set API Keys for {service}").format(service=self.default_service)
|
||||
self.keys_label = _("Keys and tokens for {service}").format(
|
||||
service=self.default_service
|
||||
)
|
||||
self.default_service = self.default_service.lower()
|
||||
# Lower here to prevent someone from capitalizing a service name for the sake of UX.
|
||||
|
||||
super().__init__(title=self.title)
|
||||
|
||||
self.service_input = discord.ui.TextInput(
|
||||
label=_("Service"),
|
||||
required=True,
|
||||
placeholder=_placeholder_service,
|
||||
default=self.default_service,
|
||||
)
|
||||
|
||||
self.token_input = discord.ui.TextInput(
|
||||
label=self.keys_label,
|
||||
style=discord.TextStyle.long,
|
||||
required=True,
|
||||
placeholder=_placeholder_token,
|
||||
default=self.default_keys_fmt,
|
||||
)
|
||||
|
||||
if self.default_service is None:
|
||||
self.add_item(self.service_input)
|
||||
self.add_item(self.token_input)
|
||||
|
||||
@staticmethod
|
||||
def _format_keys(keys: Optional[Dict[str, str]]) -> Optional[str]:
|
||||
"""Format the keys to be used on a long discord.TextInput format"""
|
||||
if keys is not None:
|
||||
ret = ""
|
||||
for k, v in keys.items():
|
||||
if v:
|
||||
ret += f"{k} {v}\n"
|
||||
else:
|
||||
ret += f"{k} YOUR_{k.upper()}\n"
|
||||
return ret
|
||||
else:
|
||||
return None
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
if not await interaction.client.is_owner(
|
||||
interaction.user
|
||||
): # Prevent non-bot owners from somehow aquiring and saving the modal.
|
||||
return await interaction.response.send_message(
|
||||
_("This modal is for bot owners only. Whoops!"), ephemeral=True
|
||||
)
|
||||
|
||||
if self.default_keys is not None:
|
||||
converter = get_dict_converter(*self.default_keys, delims=[";", ",", " "])
|
||||
else:
|
||||
converter = get_dict_converter(delims=[";", ",", " "])
|
||||
tokens = " ".join(self.token_input.value.split("\n")).rstrip()
|
||||
|
||||
try:
|
||||
tokens = await converter().convert(None, tokens)
|
||||
except BadArgument as exc:
|
||||
return await interaction.response.send_message(
|
||||
_("{error_message}\nPlease try again.").format(error_message=str(exc)),
|
||||
ephemeral=True,
|
||||
)
|
||||
|
||||
if self.default_service is not None: # Check is there is a service set.
|
||||
await interaction.client.set_shared_api_tokens(self.default_service, **tokens)
|
||||
return await interaction.response.send_message(
|
||||
_("`{service}` API tokens have been set.").format(service=self.default_service),
|
||||
ephemeral=True,
|
||||
)
|
||||
else:
|
||||
service = self.service_input.value.lower()
|
||||
await interaction.client.set_shared_api_tokens(service, **tokens)
|
||||
return await interaction.response.send_message(
|
||||
_("`{service}` API tokens have been set.").format(service=service),
|
||||
ephemeral=True,
|
||||
)
|
||||
|
||||
|
||||
class SetApiView(discord.ui.View):
|
||||
"""
|
||||
A secure ``discord.ui.View`` used to set API keys.
|
||||
|
||||
This view is an standalone, easy to implement ``discord.ui.View``
|
||||
to allow an bot owner to securely set API keys in a public environment.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
default_service: Optional[str]
|
||||
The service to add the API keys to.
|
||||
If this is omitted the bot owner is allowed to set his own service.
|
||||
Defaults to ``None``.
|
||||
default_keys: Optional[Dict[str, str]]
|
||||
The API keys the service is expecting.
|
||||
This will only allow the bot owner to set keys the Modal is expecting.
|
||||
Defaults to ``None``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
default_service: Optional[str] = None,
|
||||
default_keys: Optional[Dict[str, str]] = None,
|
||||
):
|
||||
self.default_service = default_service
|
||||
self.default_keys = default_keys
|
||||
super().__init__()
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
if not await interaction.client.is_owner(interaction.user):
|
||||
await interaction.response.send_message(
|
||||
_("This button is for bot owners only, oh well."), ephemeral=True
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
@discord.ui.button(
|
||||
label=_("Set API token"),
|
||||
style=discord.ButtonStyle.grey,
|
||||
)
|
||||
async def auth_button(self, interaction: discord.Interaction, button: discord.Button):
|
||||
return await interaction.response.send_modal(
|
||||
SetApiModal(self.default_service, self.default_keys)
|
||||
)
|
||||
Reference in New Issue
Block a user