rewrite (settings): New fields and localisation.

This commit is contained in:
2023-05-19 09:42:22 +03:00
parent 90b967201d
commit 8d5840c696
4 changed files with 68 additions and 26 deletions

View File

@@ -33,6 +33,7 @@ class BaseSetting(Generic[ParentID, SettingData, SettingValue]):
def __init__(self, parent_id: ParentID, data: Optional[SettingData], **kwargs):
self.parent_id = parent_id
self._data = data
self.kwargs = kwargs
# Instance generation
@classmethod

View File

@@ -67,16 +67,17 @@ class SettingGroup:
self.settings.pop(key, None)
return
async def make_setting_table(self, parent_id):
async def make_setting_table(self, parent_id, **kwargs):
"""
Convenience method for generating a rendered setting table.
"""
rows = []
for setting in self.settings.values():
set = await setting.get(parent_id)
name = set.display_name
value = set.formatted
rows.append((name, value, set.hover_desc))
if not setting._virtual:
set = await setting.get(parent_id, **kwargs)
name = set.display_name
value = str(set.formatted)
rows.append((name, value, set.hover_desc))
table_rows = tabulate(
*rows,
row_format="[`{invis}{key:<{pad}}{colon}`](https://lionbot.org \"{field[2]}\")\t{value}"

View File

@@ -43,7 +43,7 @@ class StringSetting(InteractiveSetting[ParentID, str, str]):
Default: True
"""
accepts = _p('settype:bool|accepts', "Any text")
_accepts = _p('settype:string|accepts', "Any text")
_maxlen: int = 4000
_quote: bool = True
@@ -83,7 +83,7 @@ class StringSetting(InteractiveSetting[ParentID, str, str]):
if len(string) > cls._maxlen:
raise UserInputError(
t(_p(
'settype:bool|error',
'settype:string|error',
"Provided string is too long! Maximum length: {maxlen} characters."
)).format(maxlen=cls._maxlen)
)
@@ -121,7 +121,7 @@ class ChannelSetting(Generic[ParentID, CT], InteractiveSetting[ParentID, int, CT
List of guild channel types to accept.
Default: []
"""
accepts = "Enter a channel name or id"
_accepts = _p('settype:channel|accepts', "Enter a channel name or id")
_selector_placeholder = "Select a Channel"
channel_types: list[discord.ChannelType] = []
@@ -145,7 +145,7 @@ class ChannelSetting(Generic[ParentID, CT], InteractiveSetting[ParentID, int, CT
channel = bot.get_channel(data)
if channel is None:
channel = discord.Object(id=data)
return channel
return channel
@classmethod
async def _parse_string(cls, parent_id, string: str, **kwargs):
@@ -248,7 +248,7 @@ class RoleSetting(InteractiveSetting[ParentID, int, Union[discord.Role, discord.
Placeholder to use in the Widget selector.
Default: "Select a Role"
"""
accepts = "Enter a role name or id"
_accepts = _p('settype:role|accepts', "Enter a role name or id")
_selector_placeholder = "Select a Role"
@@ -365,7 +365,7 @@ class BoolSetting(InteractiveSetting[ParentID, bool, bool]):
Default: {True: "On", False: "Off", None: "Not Set"}
"""
accepts = "True/False"
_accepts = _p('settype:bool|accepts', "True/False")
# Values that are accepted as truthy and falsey by the parser
_truthy = {"yes", "true", "on", "enable", "enabled"}
@@ -470,7 +470,7 @@ class IntegerSetting(InteractiveSetting[ParentID, int, int]):
_min = -2147483647
_max = 2147483647
accepts = "An integer"
_accepts = _p('settype:integer|accepts', "An integer")
@property
def input_formatted(self) -> str:
@@ -533,7 +533,7 @@ class EmojiSetting(InteractiveSetting[ParentID, str, discord.PartialEmoji]):
None
"""
accepts = "Unicode or custom emoji"
_accepts = _p('settype:emoji|desc', "Unicode or custom emoji")
@staticmethod
def _parse_emoji(emojistr):
@@ -605,7 +605,7 @@ class GuildIDSetting(InteractiveSetting[ParentID, int, int]):
Options
-------
"""
accepts = "Any Snowflake ID"
_accepts = _p('settype:guildid|accepts', "Any Snowflake ID")
# TODO: Consider autocomplete for guilds the user is in
@property
@@ -672,7 +672,8 @@ class TimezoneSetting(InteractiveSetting[ParentID, str, TZT]):
# Maybe list e.g. Europe (Austria - Iceland) and Europe (Ireland - Ukraine) separately
# TODO Definitely need autocomplete here
_accepts = (
_accepts = _p(
'settype:timezone|accepts',
"A timezone name from [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) "
"(e.g. `Europe/London`)."
)
@@ -755,7 +756,10 @@ class TimestampSetting(InteractiveSetting[ParentID, str, dt.datetime]):
Parsing accepts YYYY-MM-DD [HH:MM] [+TZ]
Display uses a discord timestamp.
"""
_accepts = "A timestamp in the form yyyy-mm-dd HH:MM"
_accepts = _p(
'settype:timestamp|accepts',
"A timestamp in the form yyyy-mm-dd HH:MM"
)
@classmethod
def _data_from_value(cls, parent_id: ParentID, value, **kwargs):
@@ -829,7 +833,7 @@ class EnumSetting(InteractiveSetting[ParentID, ET, ET]):
_outputs: dict[ET, str]
_inputs: dict[str, ET]
accepts = "A valid option."
_accepts = _p('settype:enum|accepts', "A valid option.")
@property
def input_formatted(self) -> str:
@@ -908,7 +912,10 @@ class DurationSetting(InteractiveSetting[ParentID, int, int]):
Default: False
"""
accepts = "A number of days, hours, minutes, and seconds, e.g. `2d 4h 10s`."
_accepts = _p(
'settype:duration|accepts',
"A number of days, hours, minutes, and seconds, e.g. `2d 4h 10s`."
)
# Set an upper limit on the duration
_max = 60 * 60 * 24 * 365
@@ -960,7 +967,10 @@ class DurationSetting(InteractiveSetting[ParentID, int, int]):
if cls._default_multiplier and string.isdigit():
num = int(string) * cls._default_multiplier
else:
num = parse_dur(string)
num = parse_duration(string)
if num is None:
raise UserInputError("Could not parse the provided duration!")
if num == 0 and not cls.allow_zero:
raise UserInputError(
@@ -1094,7 +1104,8 @@ class ChannelListSetting(ListSetting, InteractiveSetting):
"""
List of channels
"""
accepts = (
_accepts = _p(
'settype:channel_list|accepts',
"Comma separated list of channel mentions/ids/names. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove channels."
)
@@ -1105,7 +1116,8 @@ class RoleListSetting(ListSetting, InteractiveSetting):
"""
List of roles
"""
accepts = (
_accepts = _p(
'settype:role_list|accepts',
"Comma separated list of role mentions/ids/names. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove roles."
)
@@ -1121,7 +1133,8 @@ class StringListSetting(InteractiveSetting, ListSetting):
"""
List of strings
"""
accepts = (
_accepts = _p(
'settype:stringlist|accepts',
"Comma separated list of strings. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove strings."
)
@@ -1132,7 +1145,8 @@ class GuildIDListSetting(InteractiveSetting, ListSetting):
"""
List of guildids.
"""
accepts = (
_accepts = _p(
'settype:guildidlist|accepts',
"Comma separated list of guild ids. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove ids. "
"The provided ids are not verified in any way."

View File

@@ -172,6 +172,8 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
_desc: LazyStr # User readable brief description of the setting
_long_desc: LazyStr # User readable long description of the setting
_accepts: LazyStr # User readable description of the acceptable values
_virtual: bool = False # Whether the setting should be hidden from tables and dashboards
_required: bool = False
Widget = SettingWidget
@@ -254,12 +256,17 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
@property
def hover_desc(self):
"""
This no longer works since Discord changed the hover rules.
return '\n'.join((
self.display_name,
'=' * len(self.display_name),
self.desc,
f"\nAccepts: {self.accepts}"
))
"""
return self.desc
async def update_response(self, interaction: discord.Interaction, message: Optional[str] = None, **kwargs):
"""
@@ -281,6 +288,12 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
await self.write()
await self.update_response(interaction, **kwargs)
async def format_in(self, bot, **kwargs):
"""
Formatted version of the setting given an asynchronous context with client.
"""
return self.formatted
@property
def embed_field(self):
"""
@@ -327,7 +340,7 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
label=self.display_name,
placeholder=self.accepts,
default=self.input_formatted,
required=False
required=self._required
)
@property
@@ -353,7 +366,7 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
Default user-readable form of the setting.
Should be a short single line.
"""
return self._format_data(self.parent_id, self.data)
return self._format_data(self.parent_id, self.data, **self.kwargs)
@property
def input_formatted(self) -> str:
@@ -373,7 +386,7 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
Formatted summary of the data.
May be implemented in `_format_data(..., summary=True, ...)` or overidden.
"""
return self._format_data(self.parent_id, self.data, summary=True)
return self._format_data(self.parent_id, self.data, summary=True, **self.kwargs)
@classmethod
async def from_string(cls, parent_id, userstr: str, **kwargs):
@@ -400,6 +413,19 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
"""
raise NotImplementedError
@classmethod
def _check_value(cls, parent_id, value, **kwargs) -> Optional[str]:
"""
Check the provided value is valid.
Many setting update methods now provide Discord objects instead of raw data or user strings.
This method may be used for value-checking such a value.
Returns `None` if there are no issues, otherwise an error message.
Subclasses should override this to implement a value checker.
"""
pass
"""
command callback for set command?