rewrite: Initial rewrite skeleton.
Remove modules that will no longer be required. Move pending modules to pending-rewrite folders.
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
from .base import * # noqa
|
||||
from .setting_types import * # noqa
|
||||
|
||||
from .user_settings import UserSettings, UserSetting # noqa
|
||||
from .guild_settings import GuildSettings, GuildSetting # noqa
|
||||
from .app_settings import AppSettings
|
||||
@@ -1,5 +0,0 @@
|
||||
import settings
|
||||
from utils.lib import DotDict
|
||||
|
||||
class AppSettings(settings.ObjectSettings):
|
||||
settings = DotDict()
|
||||
@@ -1,514 +0,0 @@
|
||||
import json
|
||||
import discord
|
||||
from cmdClient.cmdClient import cmdClient
|
||||
from cmdClient.lib import SafeCancellation
|
||||
from cmdClient.Check import Check
|
||||
|
||||
from utils.lib import prop_tabulate, DotDict
|
||||
|
||||
from LionContext import LionContext as Context
|
||||
|
||||
from meta import client
|
||||
from data import Table, RowTable
|
||||
|
||||
|
||||
class Setting:
|
||||
"""
|
||||
Abstract base class describing a stored configuration setting.
|
||||
A setting consists of logic to load the setting from storage,
|
||||
present it in a readable form, understand user entered values,
|
||||
and write it again in storage.
|
||||
Additionally, the setting has attributes attached describing
|
||||
the setting in a user-friendly manner for display purposes.
|
||||
"""
|
||||
attr_name: str = None # Internal attribute name for the setting
|
||||
_default: ... = None # Default data value for the setting.. this may be None if the setting overrides 'default'.
|
||||
|
||||
write_ward: Check = None # Check that must be passed to write the setting. Not implemented internally.
|
||||
|
||||
# Configuration interface descriptions
|
||||
display_name: str = None # User readable name of the setting
|
||||
desc: str = None # User readable brief description of the setting
|
||||
long_desc: str = None # User readable long description of the setting
|
||||
accepts: str = None # User readable description of the acceptable values
|
||||
|
||||
def __init__(self, id, data: ..., **kwargs):
|
||||
self.client: cmdClient = client
|
||||
self.id = id
|
||||
self._data = data
|
||||
|
||||
# Configuration embeds
|
||||
@property
|
||||
def embed(self):
|
||||
"""
|
||||
Discord Embed showing an information summary about the setting.
|
||||
"""
|
||||
embed = discord.Embed(
|
||||
title="Configuration options for `{}`".format(self.display_name),
|
||||
)
|
||||
fields = ("Current value", "Default value", "Accepted input")
|
||||
values = (self.formatted or "Not Set",
|
||||
self._format_data(self.id, self.default) or "None",
|
||||
self.accepts)
|
||||
table = prop_tabulate(fields, values)
|
||||
embed.description = "{}\n{}".format(self.long_desc.format(self=self, client=self.client), table)
|
||||
return embed
|
||||
|
||||
async def widget(self, ctx: Context, **kwargs):
|
||||
"""
|
||||
Show the setting widget for this setting.
|
||||
By default this displays the setting embed.
|
||||
Settings may override this if they need more complex widget context or logic.
|
||||
"""
|
||||
return await ctx.reply(embed=self.embed)
|
||||
|
||||
@property
|
||||
def summary(self):
|
||||
"""
|
||||
Formatted summary of the data.
|
||||
May be implemented in `_format_data(..., summary=True, ...)` or overidden.
|
||||
"""
|
||||
return self._format_data(self.id, self.data, summary=True)
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
"""
|
||||
Response message sent when the setting has successfully been updated.
|
||||
"""
|
||||
return "Setting Updated!"
|
||||
|
||||
# Instance generation
|
||||
@classmethod
|
||||
def get(cls, id: int, **kwargs):
|
||||
"""
|
||||
Return a setting instance initialised from the stored value.
|
||||
"""
|
||||
data = cls._reader(id, **kwargs)
|
||||
return cls(id, data, **kwargs)
|
||||
|
||||
@classmethod
|
||||
async def parse(cls, id: int, ctx: Context, userstr: str, **kwargs):
|
||||
"""
|
||||
Return a setting instance initialised from a parsed user string.
|
||||
"""
|
||||
data = await cls._parse_userstr(ctx, id, userstr, **kwargs)
|
||||
return cls(id, data, **kwargs)
|
||||
|
||||
# Main interface
|
||||
@property
|
||||
def data(self):
|
||||
"""
|
||||
Retrieves the current internal setting data if it is set, otherwise the default data
|
||||
"""
|
||||
return self._data if self._data is not None else self.default
|
||||
|
||||
@data.setter
|
||||
def data(self, new_data):
|
||||
"""
|
||||
Sets the internal setting data and writes the changes.
|
||||
"""
|
||||
self._data = new_data
|
||||
self.write()
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
"""
|
||||
Retrieves the default value for this setting.
|
||||
Settings should override this if the default depends on the object id.
|
||||
"""
|
||||
return self._default
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Discord-aware object or objects associated with the setting.
|
||||
"""
|
||||
return self._data_to_value(self.id, self.data)
|
||||
|
||||
@value.setter
|
||||
def value(self, new_value):
|
||||
"""
|
||||
Setter which reads the discord-aware object, converts it to data, and writes it.
|
||||
"""
|
||||
self._data = self._data_from_value(self.id, new_value)
|
||||
self.write()
|
||||
|
||||
@property
|
||||
def formatted(self):
|
||||
"""
|
||||
User-readable form of the setting.
|
||||
"""
|
||||
return self._format_data(self.id, self.data)
|
||||
|
||||
def write(self, **kwargs):
|
||||
"""
|
||||
Write value to the database.
|
||||
For settings which override this,
|
||||
ensure you handle deletion of values when internal data is None.
|
||||
"""
|
||||
self._writer(self.id, self._data, **kwargs)
|
||||
|
||||
# Raw converters
|
||||
@classmethod
|
||||
def _data_from_value(cls, id: int, value, **kwargs):
|
||||
"""
|
||||
Convert a high-level setting value to internal data.
|
||||
Must be overriden by the setting.
|
||||
Be aware of None values, these should always pass through as None
|
||||
to provide an unsetting interface.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def _data_to_value(cls, id: int, data: ..., **kwargs):
|
||||
"""
|
||||
Convert internal data to high-level setting value.
|
||||
Must be overriden by the setting.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
async def _parse_userstr(cls, ctx: Context, id: int, userstr: str, **kwargs):
|
||||
"""
|
||||
Parse user provided input into internal data.
|
||||
Must be overriden by the setting if the setting is user-configurable.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def _format_data(cls, id: int, data: ..., **kwargs):
|
||||
"""
|
||||
Convert internal data into a formatted user-readable string.
|
||||
Must be overriden by the setting if the setting is user-viewable.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
# Database access classmethods
|
||||
@classmethod
|
||||
def _reader(cls, id: int, **kwargs):
|
||||
"""
|
||||
Read a setting from storage and return setting data or None.
|
||||
Must be overriden by the setting.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def _writer(cls, id: int, data: ..., **kwargs):
|
||||
"""
|
||||
Write provided setting data to storage.
|
||||
Must be overriden by the setting unless the `write` method is overidden.
|
||||
If the data is None, the setting is empty and should be unset.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
async def command(cls, ctx, id, flags=()):
|
||||
"""
|
||||
Standardised command viewing/setting interface for the setting.
|
||||
"""
|
||||
if not ctx.args and not ctx.msg.attachments:
|
||||
# View config embed for provided cls
|
||||
await cls.get(id).widget(ctx, flags=flags)
|
||||
else:
|
||||
# Check the write ward
|
||||
if cls.write_ward and not await cls.write_ward.run(ctx):
|
||||
await ctx.error_reply(cls.write_ward.msg)
|
||||
else:
|
||||
# Attempt to set config cls
|
||||
try:
|
||||
cls = await cls.parse(id, ctx, ctx.args)
|
||||
except UserInputError as e:
|
||||
await ctx.reply(embed=discord.Embed(
|
||||
description="{} {}".format('❌', e.msg),
|
||||
Colour=discord.Colour.red()
|
||||
))
|
||||
else:
|
||||
cls.write()
|
||||
await ctx.reply(embed=discord.Embed(
|
||||
description="{} {}".format('✅', cls.success_response),
|
||||
Colour=discord.Colour.green()
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def init_task(self, client):
|
||||
"""
|
||||
Initialisation task to be excuted during client initialisation.
|
||||
May be used for e.g. populating a cache or required client setup.
|
||||
|
||||
Main application must execute the initialisation task before the setting is used.
|
||||
Further, the task must always be executable, if the setting is loaded.
|
||||
Conditional initalisation should go in the relevant module's init tasks.
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
class ObjectSettings:
|
||||
"""
|
||||
Abstract class representing a linked collection of settings for a single object.
|
||||
Initialised settings are provided as instance attributes in the form of properties.
|
||||
"""
|
||||
__slots__ = ('id', 'params')
|
||||
|
||||
settings: DotDict = None
|
||||
|
||||
def __init__(self, id, **kwargs):
|
||||
self.id = id
|
||||
self.params = tuple(kwargs.items())
|
||||
|
||||
@classmethod
|
||||
def _setting_property(cls, setting):
|
||||
def wrapped_setting(self):
|
||||
return setting.get(self.id, **dict(self.params))
|
||||
return wrapped_setting
|
||||
|
||||
@classmethod
|
||||
def attach_setting(cls, setting: Setting):
|
||||
name = setting.attr_name or setting.__name__
|
||||
setattr(cls, name, property(cls._setting_property(setting)))
|
||||
cls.settings[name] = setting
|
||||
return setting
|
||||
|
||||
def tabulated(self):
|
||||
"""
|
||||
Convenience method to provide a complete setting property-table.
|
||||
"""
|
||||
formatted = {
|
||||
setting.display_name: setting.get(self.id, **dict(self.params)).formatted
|
||||
for name, setting in self.settings.items()
|
||||
}
|
||||
return prop_tabulate(*zip(*formatted.items()))
|
||||
|
||||
|
||||
class ColumnData:
|
||||
"""
|
||||
Mixin for settings stored in a single row and column of a Table.
|
||||
Intended to be used with tables where the only primary key is the object id.
|
||||
"""
|
||||
# Table storing the desired data
|
||||
_table_interface: Table = None
|
||||
|
||||
# Name of the column storing the setting object id
|
||||
_id_column: str = None
|
||||
|
||||
# Name of the column with the desired data
|
||||
_data_column: str = None
|
||||
|
||||
# Whether to use create a row if not found (only applies to TableRow)
|
||||
_create_row = False
|
||||
|
||||
# Whether to upsert or update for updates
|
||||
_upsert: bool = True
|
||||
|
||||
# High level data cache to use, set to None to disable cache.
|
||||
_cache = None # Map[id -> value]
|
||||
|
||||
@classmethod
|
||||
def _reader(cls, id: int, use_cache=True, **kwargs):
|
||||
"""
|
||||
Read in the requested entry associated to the id.
|
||||
Supports reading cached values from a `RowTable`.
|
||||
"""
|
||||
if cls._cache is not None and id in cls._cache and use_cache:
|
||||
return cls._cache[id]
|
||||
|
||||
table = cls._table_interface
|
||||
if isinstance(table, RowTable) and cls._id_column == table.id_col:
|
||||
if cls._create_row:
|
||||
row = table.fetch_or_create(id)
|
||||
else:
|
||||
row = table.fetch(id)
|
||||
data = row.data[cls._data_column] if row else None
|
||||
else:
|
||||
params = {
|
||||
"select_columns": (cls._data_column,),
|
||||
cls._id_column: id
|
||||
}
|
||||
row = table.select_one_where(**params)
|
||||
data = row[cls._data_column] if row else None
|
||||
|
||||
if cls._cache is not None:
|
||||
cls._cache[id] = data
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _writer(cls, id: int, data: ..., **kwargs):
|
||||
"""
|
||||
Write the provided entry to the table, allowing replacements.
|
||||
"""
|
||||
table = cls._table_interface
|
||||
params = {
|
||||
cls._id_column: id
|
||||
}
|
||||
values = {
|
||||
cls._data_column: data
|
||||
}
|
||||
|
||||
# Update data
|
||||
if cls._upsert:
|
||||
# Upsert data
|
||||
table.upsert(
|
||||
constraint=cls._id_column,
|
||||
**params,
|
||||
**values
|
||||
)
|
||||
else:
|
||||
# Update data
|
||||
table.update_where(values, **params)
|
||||
|
||||
if cls._cache is not None:
|
||||
cls._cache[id] = data
|
||||
|
||||
|
||||
class ListData:
|
||||
"""
|
||||
Mixin for list types implemented on a Table.
|
||||
Implements a reader and writer.
|
||||
This assumes the list is the only data stored in the table,
|
||||
and removes list entries by deleting rows.
|
||||
"""
|
||||
# Table storing the setting data
|
||||
_table_interface: Table = None
|
||||
|
||||
# Name of the column storing the id
|
||||
_id_column: str = None
|
||||
|
||||
# Name of the column storing the data to read
|
||||
_data_column: str = None
|
||||
|
||||
# Name of column storing the order index to use, if any. Assumed to be Serial on writing.
|
||||
_order_column: str = None
|
||||
_order_type: str = "ASC"
|
||||
|
||||
# High level data cache to use, set to None to disable cache.
|
||||
_cache = None # Map[id -> value]
|
||||
|
||||
@classmethod
|
||||
def _reader(cls, id: int, use_cache=True, **kwargs):
|
||||
"""
|
||||
Read in all entries associated to the given id.
|
||||
"""
|
||||
if cls._cache is not None and id in cls._cache and use_cache:
|
||||
return cls._cache[id]
|
||||
|
||||
table = cls._table_interface # type: Table
|
||||
params = {
|
||||
"select_columns": [cls._data_column],
|
||||
cls._id_column: id
|
||||
}
|
||||
if cls._order_column:
|
||||
params['_extra'] = "ORDER BY {} {}".format(cls._order_column, cls._order_type)
|
||||
|
||||
rows = table.select_where(**params)
|
||||
data = [row[cls._data_column] for row in rows]
|
||||
|
||||
if cls._cache is not None:
|
||||
cls._cache[id] = data
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _writer(cls, id: int, data: ..., add_only=False, remove_only=False, **kwargs):
|
||||
"""
|
||||
Write the provided list to storage.
|
||||
"""
|
||||
# TODO: Transaction lock on the table so this is atomic
|
||||
# Or just use the connection context manager
|
||||
|
||||
table = cls._table_interface # type: Table
|
||||
|
||||
# Handle None input as an empty list
|
||||
if data is None:
|
||||
data = []
|
||||
|
||||
current = cls._reader(id, **kwargs)
|
||||
if not cls._order_column and (add_only or remove_only):
|
||||
to_insert = [item for item in data if item not in current] if not remove_only else []
|
||||
to_remove = data if remove_only else (
|
||||
[item for item in current if item not in data] if not add_only else []
|
||||
)
|
||||
|
||||
# Handle required deletions
|
||||
if to_remove:
|
||||
params = {
|
||||
cls._id_column: id,
|
||||
cls._data_column: to_remove
|
||||
}
|
||||
table.delete_where(**params)
|
||||
|
||||
# Handle required insertions
|
||||
if to_insert:
|
||||
columns = (cls._id_column, cls._data_column)
|
||||
values = [(id, value) for value in to_insert]
|
||||
table.insert_many(*values, insert_keys=columns)
|
||||
|
||||
if cls._cache is not None:
|
||||
new_current = [item for item in current + to_insert if item not in to_remove]
|
||||
cls._cache[id] = new_current
|
||||
else:
|
||||
# Remove all and add all to preserve order
|
||||
# TODO: This really really should be atomic if anything else reads this
|
||||
delete_params = {cls._id_column: id}
|
||||
table.delete_where(**delete_params)
|
||||
|
||||
if data:
|
||||
columns = (cls._id_column, cls._data_column)
|
||||
values = [(id, value) for value in data]
|
||||
table.insert_many(*values, insert_keys=columns)
|
||||
|
||||
if cls._cache is not None:
|
||||
cls._cache[id] = data
|
||||
|
||||
|
||||
class KeyValueData:
|
||||
"""
|
||||
Mixin for settings implemented in a Key-Value table.
|
||||
The underlying table should have a Unique constraint on the `(_id_column, _key_column)` pair.
|
||||
"""
|
||||
_table_interface: Table = None
|
||||
|
||||
_id_column: str = None
|
||||
|
||||
_key_column: str = None
|
||||
|
||||
_value_column: str = None
|
||||
|
||||
_key: str = None
|
||||
|
||||
@classmethod
|
||||
def _reader(cls, id: ..., **kwargs):
|
||||
params = {
|
||||
"select_columns": (cls._value_column, ),
|
||||
cls._id_column: id,
|
||||
cls._key_column: cls._key
|
||||
}
|
||||
|
||||
row = cls._table_interface.select_one_where(**params)
|
||||
data = row[cls._value_column] if row else None
|
||||
|
||||
if data is not None:
|
||||
data = json.loads(data)
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _writer(cls, id: ..., data: ..., **kwargs):
|
||||
params = {
|
||||
cls._id_column: id,
|
||||
cls._key_column: cls._key
|
||||
}
|
||||
if data is not None:
|
||||
values = {
|
||||
cls._value_column: json.dumps(data)
|
||||
}
|
||||
cls._table_interface.upsert(
|
||||
constraint=f"{cls._id_column}, {cls._key_column}",
|
||||
**params,
|
||||
**values
|
||||
)
|
||||
else:
|
||||
cls._table_interface.delete_where(**params)
|
||||
|
||||
|
||||
class UserInputError(SafeCancellation):
|
||||
pass
|
||||
@@ -1,197 +0,0 @@
|
||||
import datetime
|
||||
import asyncio
|
||||
import discord
|
||||
|
||||
import settings
|
||||
from utils.lib import DotDict
|
||||
from utils import seekers # noqa
|
||||
|
||||
from wards import guild_admin
|
||||
from data import tables as tb
|
||||
|
||||
|
||||
class GuildSettings(settings.ObjectSettings):
|
||||
settings = DotDict()
|
||||
|
||||
|
||||
class GuildSetting(settings.ColumnData, settings.Setting):
|
||||
_table_interface = tb.guild_config
|
||||
_id_column = 'guildid'
|
||||
_create_row = True
|
||||
|
||||
category = None
|
||||
|
||||
write_ward = guild_admin
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
class event_log(settings.Channel, GuildSetting):
|
||||
category = "Meta"
|
||||
|
||||
attr_name = 'event_log'
|
||||
_data_column = 'event_log_channel'
|
||||
|
||||
display_name = "event_log"
|
||||
desc = "Bot event logging channel."
|
||||
|
||||
long_desc = (
|
||||
"Channel to post 'events', such as workouts completing or members renting a room."
|
||||
)
|
||||
|
||||
_chan_type = discord.ChannelType.text
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return "The event log is now {}.".format(self.formatted)
|
||||
else:
|
||||
return "The event log has been unset."
|
||||
|
||||
def log(self, description="", colour=discord.Color.orange(), **kwargs):
|
||||
channel = self.value
|
||||
if channel:
|
||||
embed = discord.Embed(
|
||||
description=description,
|
||||
colour=colour,
|
||||
timestamp=datetime.datetime.utcnow(),
|
||||
**kwargs
|
||||
)
|
||||
asyncio.create_task(channel.send(embed=embed))
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
class admin_role(settings.Role, GuildSetting):
|
||||
category = "Guild Roles"
|
||||
|
||||
attr_name = 'admin_role'
|
||||
_data_column = 'admin_role'
|
||||
|
||||
display_name = "admin_role"
|
||||
desc = "Server administrator role."
|
||||
|
||||
long_desc = (
|
||||
"Server administrator role.\n"
|
||||
"Allows usage of the administrative commands, such as `config`.\n"
|
||||
"These commands may also be used by anyone with the discord adminitrator permission."
|
||||
)
|
||||
# TODO Expand on what these are.
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return "The administrator role is now {}.".format(self.formatted)
|
||||
else:
|
||||
return "The administrator role has been unset."
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
class mod_role(settings.Role, GuildSetting):
|
||||
category = "Guild Roles"
|
||||
|
||||
attr_name = 'mod_role'
|
||||
_data_column = 'mod_role'
|
||||
|
||||
display_name = "mod_role"
|
||||
desc = "Server moderator role."
|
||||
|
||||
long_desc = (
|
||||
"Server moderator role.\n"
|
||||
"Allows usage of the modistrative commands."
|
||||
)
|
||||
# TODO Expand on what these are.
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return "The moderator role is now {}.".format(self.formatted)
|
||||
else:
|
||||
return "The moderator role has been unset."
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
class unranked_roles(settings.RoleList, settings.ListData, settings.Setting):
|
||||
category = "Guild Roles"
|
||||
|
||||
attr_name = 'unranked_roles'
|
||||
|
||||
_table_interface = tb.unranked_roles
|
||||
_id_column = 'guildid'
|
||||
_data_column = 'roleid'
|
||||
|
||||
write_ward = guild_admin
|
||||
display_name = "unranked_roles"
|
||||
desc = "Roles to exclude from the leaderboards."
|
||||
|
||||
_force_unique = True
|
||||
|
||||
long_desc = (
|
||||
"Roles to be excluded from the `top` and `topcoins` leaderboards."
|
||||
)
|
||||
|
||||
# Flat cache, no need to expire objects
|
||||
_cache = {}
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return "The following roles will be excluded from the leaderboard:\n{}".format(self.formatted)
|
||||
else:
|
||||
return "The excluded roles have been removed."
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
class donator_roles(settings.RoleList, settings.ListData, settings.Setting):
|
||||
category = "Hidden"
|
||||
|
||||
attr_name = 'donator_roles'
|
||||
|
||||
_table_interface = tb.donator_roles
|
||||
_id_column = 'guildid'
|
||||
_data_column = 'roleid'
|
||||
|
||||
write_ward = guild_admin
|
||||
display_name = "donator_roles"
|
||||
desc = "Donator badge roles."
|
||||
|
||||
_force_unique = True
|
||||
|
||||
long_desc = (
|
||||
"Members with these roles will be considered donators and have access to premium features."
|
||||
)
|
||||
|
||||
# Flat cache, no need to expire objects
|
||||
_cache = {}
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return "The donator badges are now:\n{}".format(self.formatted)
|
||||
else:
|
||||
return "The donator badges have been removed."
|
||||
|
||||
|
||||
@GuildSettings.attach_setting
|
||||
class alert_channel(settings.Channel, GuildSetting):
|
||||
category = "Meta"
|
||||
|
||||
attr_name = 'alert_channel'
|
||||
_data_column = 'alert_channel'
|
||||
|
||||
display_name = "alert_channel"
|
||||
desc = "Channel to display global user alerts."
|
||||
|
||||
long_desc = (
|
||||
"This channel will be used for group notifications, "
|
||||
"for example group timers and anti-cheat messages, "
|
||||
"as well as for critical alerts to users that have their direct messages disapbled.\n"
|
||||
"It should be visible to all members."
|
||||
)
|
||||
|
||||
_chan_type = discord.ChannelType.text
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return "The alert channel is now {}.".format(self.formatted)
|
||||
else:
|
||||
return "The alert channel has been unset."
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,42 +0,0 @@
|
||||
import datetime
|
||||
|
||||
import settings
|
||||
from utils.lib import DotDict
|
||||
|
||||
from data import tables as tb
|
||||
|
||||
|
||||
class UserSettings(settings.ObjectSettings):
|
||||
settings = DotDict()
|
||||
|
||||
|
||||
class UserSetting(settings.ColumnData, settings.Setting):
|
||||
_table_interface = tb.user_config
|
||||
_id_column = 'userid'
|
||||
_create_row = True
|
||||
|
||||
write_ward = None
|
||||
|
||||
|
||||
@UserSettings.attach_setting
|
||||
class timezone(settings.Timezone, UserSetting):
|
||||
attr_name = 'timezone'
|
||||
_data_column = 'timezone'
|
||||
|
||||
_default = 'UTC'
|
||||
|
||||
display_name = 'timezone'
|
||||
desc = "Timezone to display prompts in."
|
||||
long_desc = (
|
||||
"Timezone used for displaying certain prompts (e.g. selecting an accountability room)."
|
||||
)
|
||||
|
||||
@property
|
||||
def success_response(self):
|
||||
if self.value:
|
||||
return (
|
||||
"Your personal timezone is now {}.\n"
|
||||
"Your current time is **{}**."
|
||||
).format(self.formatted, datetime.datetime.now(tz=self.value).strftime("%H:%M"))
|
||||
else:
|
||||
return "Your personal timezone has been unset."
|
||||
Reference in New Issue
Block a user