from typing import Optional import discord from meta import LionBot from meta.errors import UserInputError from babel.translator import ctx_translator from settings import ModelData from settings.groups import SettingGroup, ModelConfig, SettingDotDict from settings.setting_types import ( ChannelSetting, RoleSetting, BoolSetting, StringSetting, IntegerSetting, DurationSetting ) from .data import TimerData from . import babel _p = babel._p class TimerConfig(ModelConfig): settings = SettingDotDict() _model_settings = set() model = TimerData.Timer class TimerOptions(SettingGroup): @TimerConfig.register_model_setting class VoiceChannel(ModelData, ChannelSetting): setting_id = 'voice_channel' _display_name = _p('timerset:voice_channel', "channel") _desc = _p( 'timerset:voice_channel|desc', "Channel in which to track timer members and send alerts." ) _model = TimerData.Timer _column = TimerData.Timer.channelid.name @TimerConfig.register_model_setting class NotificationChannel(ModelData, ChannelSetting): setting_id = 'notification_channel' _display_name = _p('timerset:notification_channel', "notification_channel") _desc = _p( 'timerset:notification_channel|desc', "Channel to which to send timer status cards and notifications." ) _model = TimerData.Timer _column = TimerData.Timer.notification_channelid.name @classmethod async def _check_value(cls, parent_id: int, value: Optional[discord.abc.GuildChannel], **kwargs): if value is not None: # TODO: Check we either have or can create a webhook # TODO: Check we can send messages, embeds, and files ... @classmethod def _format_data(cls, parent_id, data, timer=None, **kwargs): actual = timer.notification_channel if timer is not None else None if data is None and actual is not None: t = ctx_translator.get().t formatted = t(_p( 'timerset:notification_channel|format:notset', "Not Set (Using {channel})" )).format(channel=actual.mention) else: formatted = super()._format_data(parent_id, data, **kwargs) return formatted @TimerConfig.register_model_setting class InactivityThreshold(ModelData, IntegerSetting): setting_id = 'inactivity_threshold' _display_name = _p('timerset:inactivity_threshold|inactivity_threshold', "inactivity_threshold") _desc = _p( 'timerset:inactivity_threshold|desc', "Number of inactive focus+break stages before a member is removed from the timer." ) _accepts = _p( 'timerset:inactivity_threshold|desc', "How many timer cycles before kicking inactive members." ) _model = TimerData.Timer _column = TimerData.Timer.inactivity_threshold.name _min = 0 _max = 64 @property def input_formatted(self): return str(self._data) if self._data is not None else '' @TimerConfig.register_model_setting class ManagerRole(ModelData, RoleSetting): setting_id = 'manager_role' _display_name = _p('timerset:manager_role', "manager_role") _desc = _p( 'timerset:manager_role|desc', "Role allowed to start, stop, and edit the focus/break lengths." ) _model = TimerData.Timer _column = TimerData.Timer.manager_roleid.name @classmethod def _format_data(cls, parent_id, data, timer=None, **kwargs): if data is None: t = ctx_translator.get().t formatted = t(_p( 'timerset:manager_role|format:notset', "Not Set (Only Admins may start/stop or edit pattern)" )) else: formatted = super()._format_data(parent_id, data, **kwargs) return formatted @TimerConfig.register_model_setting class VoiceAlerts(ModelData, BoolSetting): setting_id = 'voice_alerts' _display_name = _p('timerset:voice_alerts', "voice_alerts") _desc = _p( 'timerset:voice_alerts|desc', "Whether to join the voice channel and announce focus and break stages." ) _default = True _model = TimerData.Timer _column = TimerData.Timer.voice_alerts.name @TimerConfig.register_model_setting class BaseName(ModelData, StringSetting): setting_id = 'base_name' _display_name = _p('timerset:base_name', "name") _desc = _p( 'timerset:base_name|desc', "Timer name, as shown on the timer card." ) _accepts = _p( 'timerset:base_name|accepts', "Any short name, shown on the timer card." ) # TODO: Consider ways of localising string setting defaults? # Probably using the default property? _default = "Timer" _model = TimerData.Timer _column = TimerData.Timer.pretty_name.name @TimerConfig.register_model_setting class ChannelFormat(ModelData, StringSetting): setting_id = 'channel_name_format' _display_name = _p('timerset:channel_name_format', "channel_name") _desc = _p( 'timerset:channel_name_format|desc', "Auto-updating voice channel name, accepting {remaining}, {name}, {pattern}, and {stage} keys." ) _accepts = _p( 'timerset:channel_name|accepts', "Timer channel name, with keys {remaining}, {name}, {pattern}, and {stage}." ) _default = "{name} {pattern} - {stage}" _model = TimerData.Timer _column = TimerData.Timer.channel_name.name @TimerConfig.register_model_setting class FocusLength(ModelData, DurationSetting): setting_id = 'focus_length' _display_name = _p('timerset:focus_length', "focus_length") _desc = _p( 'timerset:focus_length|desc', "Length of the focus stage of the timer in minutes." ) _virtual = True _accepts = _p( 'timerset:focus_length|accepts', "A positive integer number of minutes." ) _required = True _model = TimerData.Timer _column = TimerData.Timer.focus_length.name _default_multiplier = 60 allow_zero = False _show_days = False @property def input_formatted(self): return str(int(self._data // 60)) if self._data else None @classmethod async def _parse_string(cls, parent_id, string, **kwargs): try: return await super()._parse_string(parent_id, string, **kwargs) except UserInputError: t = ctx_translator.get().t raise UserInputError( t(_p( 'timerset:focus_length|desc', "Please enter a positive number of minutes." )) ) @TimerConfig.register_model_setting class BreakLength(ModelData, DurationSetting): setting_id = 'break_length' _display_name = _p('timerset:break_length', "break_length") _desc = _p( 'timerset:break_length|desc', "Length of the break stage of the timer in minutes." ) _virtual = True _accepts = _p( 'timerset:break_length|accepts', "A positive integer number of minutes." ) _required = True _model = TimerData.Timer _column = TimerData.Timer.break_length.name _default_multiplier = 60 allow_zero = False _show_days = False @property def input_formatted(self): return str(int(self._data // 60)) if self._data else None @classmethod async def _parse_string(cls, parent_id, string, **kwargs): try: return await super()._parse_string(parent_id, string, **kwargs) except UserInputError: t = ctx_translator.get().t raise UserInputError( t(_p( 'timerset:break_length|desc', "Please enter a positive number of minutes." )) )