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): def __init__(self, parent_id: ParentID, data: Optional[SettingData], **kwargs):
self.parent_id = parent_id self.parent_id = parent_id
self._data = data self._data = data
self.kwargs = kwargs
# Instance generation # Instance generation
@classmethod @classmethod

View File

@@ -67,15 +67,16 @@ class SettingGroup:
self.settings.pop(key, None) self.settings.pop(key, None)
return 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. Convenience method for generating a rendered setting table.
""" """
rows = [] rows = []
for setting in self.settings.values(): for setting in self.settings.values():
set = await setting.get(parent_id) if not setting._virtual:
set = await setting.get(parent_id, **kwargs)
name = set.display_name name = set.display_name
value = set.formatted value = str(set.formatted)
rows.append((name, value, set.hover_desc)) rows.append((name, value, set.hover_desc))
table_rows = tabulate( table_rows = tabulate(
*rows, *rows,

View File

@@ -43,7 +43,7 @@ class StringSetting(InteractiveSetting[ParentID, str, str]):
Default: True Default: True
""" """
accepts = _p('settype:bool|accepts', "Any text") _accepts = _p('settype:string|accepts', "Any text")
_maxlen: int = 4000 _maxlen: int = 4000
_quote: bool = True _quote: bool = True
@@ -83,7 +83,7 @@ class StringSetting(InteractiveSetting[ParentID, str, str]):
if len(string) > cls._maxlen: if len(string) > cls._maxlen:
raise UserInputError( raise UserInputError(
t(_p( t(_p(
'settype:bool|error', 'settype:string|error',
"Provided string is too long! Maximum length: {maxlen} characters." "Provided string is too long! Maximum length: {maxlen} characters."
)).format(maxlen=cls._maxlen) )).format(maxlen=cls._maxlen)
) )
@@ -121,7 +121,7 @@ class ChannelSetting(Generic[ParentID, CT], InteractiveSetting[ParentID, int, CT
List of guild channel types to accept. List of guild channel types to accept.
Default: [] Default: []
""" """
accepts = "Enter a channel name or id" _accepts = _p('settype:channel|accepts', "Enter a channel name or id")
_selector_placeholder = "Select a Channel" _selector_placeholder = "Select a Channel"
channel_types: list[discord.ChannelType] = [] channel_types: list[discord.ChannelType] = []
@@ -248,7 +248,7 @@ class RoleSetting(InteractiveSetting[ParentID, int, Union[discord.Role, discord.
Placeholder to use in the Widget selector. Placeholder to use in the Widget selector.
Default: "Select a Role" 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" _selector_placeholder = "Select a Role"
@@ -365,7 +365,7 @@ class BoolSetting(InteractiveSetting[ParentID, bool, bool]):
Default: {True: "On", False: "Off", None: "Not Set"} 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 # Values that are accepted as truthy and falsey by the parser
_truthy = {"yes", "true", "on", "enable", "enabled"} _truthy = {"yes", "true", "on", "enable", "enabled"}
@@ -470,7 +470,7 @@ class IntegerSetting(InteractiveSetting[ParentID, int, int]):
_min = -2147483647 _min = -2147483647
_max = 2147483647 _max = 2147483647
accepts = "An integer" _accepts = _p('settype:integer|accepts', "An integer")
@property @property
def input_formatted(self) -> str: def input_formatted(self) -> str:
@@ -533,7 +533,7 @@ class EmojiSetting(InteractiveSetting[ParentID, str, discord.PartialEmoji]):
None None
""" """
accepts = "Unicode or custom emoji" _accepts = _p('settype:emoji|desc', "Unicode or custom emoji")
@staticmethod @staticmethod
def _parse_emoji(emojistr): def _parse_emoji(emojistr):
@@ -605,7 +605,7 @@ class GuildIDSetting(InteractiveSetting[ParentID, int, int]):
Options Options
------- -------
""" """
accepts = "Any Snowflake ID" _accepts = _p('settype:guildid|accepts', "Any Snowflake ID")
# TODO: Consider autocomplete for guilds the user is in # TODO: Consider autocomplete for guilds the user is in
@property @property
@@ -672,7 +672,8 @@ class TimezoneSetting(InteractiveSetting[ParentID, str, TZT]):
# Maybe list e.g. Europe (Austria - Iceland) and Europe (Ireland - Ukraine) separately # Maybe list e.g. Europe (Austria - Iceland) and Europe (Ireland - Ukraine) separately
# TODO Definitely need autocomplete here # 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) " "A timezone name from [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) "
"(e.g. `Europe/London`)." "(e.g. `Europe/London`)."
) )
@@ -755,7 +756,10 @@ class TimestampSetting(InteractiveSetting[ParentID, str, dt.datetime]):
Parsing accepts YYYY-MM-DD [HH:MM] [+TZ] Parsing accepts YYYY-MM-DD [HH:MM] [+TZ]
Display uses a discord timestamp. 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 @classmethod
def _data_from_value(cls, parent_id: ParentID, value, **kwargs): def _data_from_value(cls, parent_id: ParentID, value, **kwargs):
@@ -829,7 +833,7 @@ class EnumSetting(InteractiveSetting[ParentID, ET, ET]):
_outputs: dict[ET, str] _outputs: dict[ET, str]
_inputs: dict[str, ET] _inputs: dict[str, ET]
accepts = "A valid option." _accepts = _p('settype:enum|accepts', "A valid option.")
@property @property
def input_formatted(self) -> str: def input_formatted(self) -> str:
@@ -908,7 +912,10 @@ class DurationSetting(InteractiveSetting[ParentID, int, int]):
Default: False 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 # Set an upper limit on the duration
_max = 60 * 60 * 24 * 365 _max = 60 * 60 * 24 * 365
@@ -960,7 +967,10 @@ class DurationSetting(InteractiveSetting[ParentID, int, int]):
if cls._default_multiplier and string.isdigit(): if cls._default_multiplier and string.isdigit():
num = int(string) * cls._default_multiplier num = int(string) * cls._default_multiplier
else: 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: if num == 0 and not cls.allow_zero:
raise UserInputError( raise UserInputError(
@@ -1094,7 +1104,8 @@ class ChannelListSetting(ListSetting, InteractiveSetting):
""" """
List of channels List of channels
""" """
accepts = ( _accepts = _p(
'settype:channel_list|accepts',
"Comma separated list of channel mentions/ids/names. Use `None` to unset. " "Comma separated list of channel mentions/ids/names. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove channels." "Write `--add` or `--remove` to add or remove channels."
) )
@@ -1105,7 +1116,8 @@ class RoleListSetting(ListSetting, InteractiveSetting):
""" """
List of roles List of roles
""" """
accepts = ( _accepts = _p(
'settype:role_list|accepts',
"Comma separated list of role mentions/ids/names. Use `None` to unset. " "Comma separated list of role mentions/ids/names. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove roles." "Write `--add` or `--remove` to add or remove roles."
) )
@@ -1121,7 +1133,8 @@ class StringListSetting(InteractiveSetting, ListSetting):
""" """
List of strings List of strings
""" """
accepts = ( _accepts = _p(
'settype:stringlist|accepts',
"Comma separated list of strings. Use `None` to unset. " "Comma separated list of strings. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove strings." "Write `--add` or `--remove` to add or remove strings."
) )
@@ -1132,7 +1145,8 @@ class GuildIDListSetting(InteractiveSetting, ListSetting):
""" """
List of guildids. List of guildids.
""" """
accepts = ( _accepts = _p(
'settype:guildidlist|accepts',
"Comma separated list of guild ids. Use `None` to unset. " "Comma separated list of guild ids. Use `None` to unset. "
"Write `--add` or `--remove` to add or remove ids. " "Write `--add` or `--remove` to add or remove ids. "
"The provided ids are not verified in any way." "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 _desc: LazyStr # User readable brief description of the setting
_long_desc: LazyStr # User readable long description of the setting _long_desc: LazyStr # User readable long description of the setting
_accepts: LazyStr # User readable description of the acceptable values _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 Widget = SettingWidget
@@ -254,12 +256,17 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
@property @property
def hover_desc(self): def hover_desc(self):
"""
This no longer works since Discord changed the hover rules.
return '\n'.join(( return '\n'.join((
self.display_name, self.display_name,
'=' * len(self.display_name), '=' * len(self.display_name),
self.desc, self.desc,
f"\nAccepts: {self.accepts}" f"\nAccepts: {self.accepts}"
)) ))
"""
return self.desc
async def update_response(self, interaction: discord.Interaction, message: Optional[str] = None, **kwargs): 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.write()
await self.update_response(interaction, **kwargs) 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 @property
def embed_field(self): def embed_field(self):
""" """
@@ -327,7 +340,7 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
label=self.display_name, label=self.display_name,
placeholder=self.accepts, placeholder=self.accepts,
default=self.input_formatted, default=self.input_formatted,
required=False required=self._required
) )
@property @property
@@ -353,7 +366,7 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
Default user-readable form of the setting. Default user-readable form of the setting.
Should be a short single line. 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 @property
def input_formatted(self) -> str: def input_formatted(self) -> str:
@@ -373,7 +386,7 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
Formatted summary of the data. Formatted summary of the data.
May be implemented in `_format_data(..., summary=True, ...)` or overidden. 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 @classmethod
async def from_string(cls, parent_id, userstr: str, **kwargs): async def from_string(cls, parent_id, userstr: str, **kwargs):
@@ -400,6 +413,19 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
""" """
raise NotImplementedError 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? command callback for set command?