rewrite: Localisation support.
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
from babel.translator import LocalBabel
|
||||
babel = LocalBabel('settings_base')
|
||||
|
||||
from .data import ModelData
|
||||
from .base import BaseSetting
|
||||
from .ui import SettingWidget, InteractiveSetting
|
||||
from .groups import SettingDotDict, SettingGroup, ModelSettings, ModelSetting
|
||||
|
||||
@@ -26,6 +26,8 @@ class BaseSetting(Generic[ParentID, SettingData, SettingValue]):
|
||||
Additionally, the setting has attributes attached describing
|
||||
the setting in a user-friendly manner for display purposes.
|
||||
"""
|
||||
setting_id: str # Unique source identifier for the setting
|
||||
|
||||
_default: Optional[SettingData] = None # Default data value for the setting
|
||||
|
||||
def __init__(self, parent_id: ParentID, data: Optional[SettingData], **kwargs):
|
||||
|
||||
@@ -23,6 +23,15 @@ class ModelData:
|
||||
# High level data cache to use, leave as None to disable cache.
|
||||
_cache = None # Map[id -> value]
|
||||
|
||||
@classmethod
|
||||
def _read_from_row(cls, parent_id, row, **kwargs):
|
||||
data = row[cls._column]
|
||||
|
||||
if cls._cache is not None:
|
||||
cls._cache[parent_id] = data
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
async def _reader(cls, parent_id, use_cache=True, **kwargs):
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
from typing import Generic, Type, TypeVar, Optional
|
||||
from typing import Generic, Type, TypeVar, Optional, overload
|
||||
|
||||
from data import RowModel
|
||||
|
||||
from .data import ModelData
|
||||
from .ui import InteractiveSetting
|
||||
from .base import BaseSetting
|
||||
|
||||
from utils.lib import tabulate
|
||||
|
||||
@@ -47,7 +52,7 @@ class SettingGroup:
|
||||
self.settings: SettingDotDict[InteractiveSetting] = self.__init_settings__()
|
||||
|
||||
def attach(self, cls: Type[T], name: Optional[str] = None):
|
||||
name = name or cls.__name__
|
||||
name = name or cls.setting_id
|
||||
self.settings[name] = cls
|
||||
return cls
|
||||
|
||||
@@ -77,3 +82,58 @@ class SettingGroup:
|
||||
row_format="[`{invis}{key:<{pad}}{colon}`](https://lionbot.org \"{field[2]}\")\t{value}"
|
||||
)
|
||||
return '\n'.join(table_rows)
|
||||
|
||||
|
||||
class ModelSetting(ModelData, BaseSetting):
|
||||
...
|
||||
|
||||
|
||||
class ModelSettings:
|
||||
"""
|
||||
A ModelSettings instance aggregates multiple `ModelSetting` instances
|
||||
bound to the same parent id on a single Model.
|
||||
|
||||
This enables a single point of access
|
||||
for settings of a given Model,
|
||||
with support for caching or deriving as needed.
|
||||
|
||||
This is an abstract base class,
|
||||
and should be subclassed to define the contained settings.
|
||||
"""
|
||||
_settings: SettingDotDict = SettingDotDict()
|
||||
model: Type[RowModel]
|
||||
|
||||
def __init__(self, parent_id, row, **kwargs):
|
||||
self.parent_id = parent_id
|
||||
self.row = row
|
||||
self.kwargs = kwargs
|
||||
|
||||
@classmethod
|
||||
async def fetch(cls, *parent_id, **kwargs):
|
||||
"""
|
||||
Load an instance of this ModelSetting with the given parent_id
|
||||
and setting keyword arguments.
|
||||
"""
|
||||
row = await cls.model.fetch_or_create(*parent_id)
|
||||
return cls(parent_id, row, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def attach(self, setting_cls):
|
||||
"""
|
||||
Decorator to attach the given setting class to this modelsetting.
|
||||
"""
|
||||
# This violates the interface principle, use structured typing instead?
|
||||
if not (issubclass(setting_cls, BaseSetting) and issubclass(setting_cls, ModelData)):
|
||||
raise ValueError(
|
||||
f"The provided setting class must be `ModelSetting`, not {setting_cls.__class__.__name__}."
|
||||
)
|
||||
self._settings[setting_cls.setting_id] = setting_cls
|
||||
return setting_cls
|
||||
|
||||
def get(self, setting_id):
|
||||
setting_cls = self._settings.get(setting_id)
|
||||
data = setting_cls._read_from_row(self.parent_id, self.row, **self.kwargs)
|
||||
return setting_cls(self.parent_id, data, **self.kwargs)
|
||||
|
||||
def __getitem__(self, setting_id):
|
||||
return self.get(setting_id)
|
||||
|
||||
@@ -8,17 +8,24 @@ from discord import ui
|
||||
from discord.ui.button import button, Button, ButtonStyle
|
||||
|
||||
from meta.context import context
|
||||
from utils.lib import strfdur, parse_dur
|
||||
from meta.errors import UserInputError
|
||||
from utils.lib import strfdur, parse_dur
|
||||
from babel import ctx_translator
|
||||
|
||||
from .base import ParentID
|
||||
from .ui import InteractiveSetting, SettingWidget
|
||||
from . import babel
|
||||
|
||||
_, _p = babel._, babel._p
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from discord.guild import GuildChannel
|
||||
|
||||
|
||||
# TODO: Localise this file
|
||||
|
||||
|
||||
class StringSetting(InteractiveSetting[ParentID, str, str]):
|
||||
"""
|
||||
Setting type mixin describing an arbitrary string type.
|
||||
@@ -34,7 +41,7 @@ class StringSetting(InteractiveSetting[ParentID, str, str]):
|
||||
Default: True
|
||||
"""
|
||||
|
||||
accepts = "Any text"
|
||||
accepts = _p('settype:bool|accepts', "Any text")
|
||||
|
||||
_maxlen: int = 4000
|
||||
_quote: bool = True
|
||||
@@ -70,8 +77,14 @@ class StringSetting(InteractiveSetting[ParentID, str, str]):
|
||||
Provides some minor input validation.
|
||||
Treats an empty string as a `None` value.
|
||||
"""
|
||||
t = ctx_translator.get().t
|
||||
if len(string) > cls._maxlen:
|
||||
raise UserInputError("Provided string is too long! Maximum length: {} characters.".format(cls._maxlen))
|
||||
raise UserInputError(
|
||||
t(_p(
|
||||
'settype:bool|error',
|
||||
"Provided string is too long! Maximum length: {maxlen} characters."
|
||||
)).format(maxlen=cls._maxlen)
|
||||
)
|
||||
elif len(string) == 0:
|
||||
return None
|
||||
else:
|
||||
|
||||
@@ -223,14 +223,14 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
|
||||
f"\nAccepts: {self.accepts}"
|
||||
))
|
||||
|
||||
async def update_response(self, interaction: discord.Interaction, **kwargs):
|
||||
async def update_response(self, interaction: discord.Interaction, message: Optional[str] = None, **kwargs):
|
||||
"""
|
||||
Respond to an interaction which triggered a setting update.
|
||||
Usually just wraps `update_message` in an embed and sends it back.
|
||||
Passes any extra `kwargs` to the message creation method.
|
||||
"""
|
||||
embed = discord.Embed(
|
||||
description=f"{str(conf.emojis.tick)} {self.update_message}",
|
||||
description=f"{str(conf.emojis.tick)} {message or self.update_message}",
|
||||
colour=discord.Color.green()
|
||||
)
|
||||
if interaction.response.is_done():
|
||||
|
||||
Reference in New Issue
Block a user