rewrite (settings): New fields and localisation.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
Reference in New Issue
Block a user