fix(config): Fix general settings UI.

This commit is contained in:
2023-10-11 07:28:53 +03:00
parent fe81945391
commit 66e7c2f2e4
9 changed files with 178 additions and 152 deletions

View File

@@ -80,7 +80,7 @@ class LionGuild(Timezoned):
return GuildMode.StudyGuild return GuildMode.StudyGuild
@property @property
def timezone(self) -> pytz.timezone: def timezone(self) -> str:
return self.config.timezone.value return self.config.timezone.value
@property @property
@@ -94,11 +94,3 @@ class LionGuild(Timezoned):
if self.data.name != guild.name: if self.data.name != guild.name:
await self.data.update(name=guild.name) await self.data.update(name=guild.name)
async def _event_log(self, ...):
...
def event_log(self, **kwargs):
asyncio.create_task(self._event_log(**kwargs), name='event-log')
def error_log(self, ...):
...

View File

@@ -1,3 +1,5 @@
from typing import Optional
import discord import discord
from discord import app_commands as appcmds from discord import app_commands as appcmds
from discord.ext import commands as cmds from discord.ext import commands as cmds
@@ -22,7 +24,7 @@ class GuildConfigCog(LionCog):
async def cog_load(self): async def cog_load(self):
self.bot.core.guild_config.register_model_setting(GeneralSettings.Timezone) self.bot.core.guild_config.register_model_setting(GeneralSettings.Timezone)
self.bot.core.guild_config.register_model_setting(GeneralSettings.Eventlog) self.bot.core.guild_config.register_model_setting(GeneralSettings.EventLog)
configcog = self.bot.get_cog('ConfigCog') configcog = self.bot.get_cog('ConfigCog')
if configcog is None: if configcog is None:
@@ -36,43 +38,13 @@ class GuildConfigCog(LionCog):
@appcmds.guild_only @appcmds.guild_only
@appcmds.default_permissions(manage_guild=True) @appcmds.default_permissions(manage_guild=True)
async def dashboard_cmd(self, ctx: LionContext): async def dashboard_cmd(self, ctx: LionContext):
if not ctx.guild or not ctx.interaction:
return
ui = GuildDashboard(self.bot, ctx.guild, ctx.author.id, ctx.channel.id) ui = GuildDashboard(self.bot, ctx.guild, ctx.author.id, ctx.channel.id)
await ui.run(ctx.interaction) await ui.run(ctx.interaction)
await ui.wait() await ui.wait()
@cmds.hybrid_group("configure", with_app_command=False)
async def configure_group(self, ctx: LionContext):
# Placeholder configure group command.
...
@configure_group.command(
name=_p('cmd:configure_general', "general"),
description=_p('cmd:configure_general|desc', "General configuration panel")
)
@appcmds.rename(
timezone=GeneralSettings.Timezone._display_name,
event_log=GeneralSettings.EventLog._display_name,
)
@appcmds.describe(
timezone=GeneralSettings.Timezone._desc,
event_log=GeneralSettings.EventLog._display_name,
)
@appcmds.guild_only()
@appcmds.default_permissions(manage_guild=True)
@low_management_ward
async def cmd_configure_general(self, ctx: LionContext,
timezone: Optional[str] = None,
event_log: Optional[discord.TextChannel] = None,
):
t = self.bot.translator.t
# Typechecker guards because they don't understand the check ward
if not ctx.guild:
return
if not ctx.interaction:
return
await ctx.interaction.response.defer(thinking=True)
# ----- Configuration ----- # ----- Configuration -----
@LionCog.placeholder_group @LionCog.placeholder_group
@cmds.hybrid_group("configure", with_app_command=False) @cmds.hybrid_group("configure", with_app_command=False)
@@ -90,7 +62,7 @@ class GuildConfigCog(LionCog):
) )
@appcmds.describe( @appcmds.describe(
timezone=GeneralSettings.Timezone._desc, timezone=GeneralSettings.Timezone._desc,
event_log=GeneralSettings.EventLog._display_name, event_log=GeneralSettings.EventLog._desc,
) )
@appcmds.guild_only() @appcmds.guild_only()
@appcmds.default_permissions(manage_guild=True) @appcmds.default_permissions(manage_guild=True)
@@ -107,4 +79,34 @@ class GuildConfigCog(LionCog):
if not ctx.interaction: if not ctx.interaction:
return return
await ctx.interaction.response.defer(thinking=True) await ctx.interaction.response.defer(thinking=True)
# TODO
modified = []
if timezone is not None:
setting = self.settings.Timezone
instance = await setting.from_string(ctx.guild.id, timezone)
modified.append(instance)
if event_log is not None:
setting = self.settings.EventLog
instance = await setting.from_value(ctx.guild.id, event_log)
modified.append(instance)
if modified:
ack_lines = []
for instance in modified:
await instance.write()
ack_lines.append(instance.update_message)
tick = self.bot.config.emojis.tick
embed = discord.Embed(
colour=discord.Colour.brand_green(),
description='\n'.join(f"{tick} {line}" for line in ack_lines)
)
await ctx.reply(embed=embed)
if ctx.channel.id not in GeneralSettingUI._listening or not modified:
ui = GeneralSettingUI(self.bot, ctx.guild.id, ctx.channel.id)
await ui.run(ctx.interaction)
await ui.wait()

View File

@@ -1,7 +1,12 @@
from typing import Optional
import discord
from settings import ModelData from settings import ModelData
from settings.setting_types import TimezoneSetting, ChannelSetting from settings.setting_types import TimezoneSetting, ChannelSetting
from settings.groups import SettingGroup from settings.groups import SettingGroup
from meta.context import ctx_bot
from meta.errors import UserInputError
from core.data import CoreData from core.data import CoreData
from babel.translator import ctx_translator from babel.translator import ctx_translator
@@ -20,7 +25,8 @@ class GeneralSettings(SettingGroup):
and the timezone used to display guild-wide statistics. and the timezone used to display guild-wide statistics.
""" """
setting_id = 'timezone' setting_id = 'timezone'
_event = 'guild_setting_update_timezone' _event = 'guildset_timezone'
_set_cmd = 'configure general'
_display_name = _p('guildset:timezone', "timezone") _display_name = _p('guildset:timezone', "timezone")
_desc = _p( _desc = _p(
@@ -46,29 +52,24 @@ class GeneralSettings(SettingGroup):
"The guild timezone has been set to `{timezone}`." "The guild timezone has been set to `{timezone}`."
)).format(timezone=self.data) )).format(timezone=self.data)
@property
def set_str(self):
bot = ctx_bot.get()
return bot.core.mention_cmd('configure general') if bot else None
class EventLog(ModelData, ChannelSetting): class EventLog(ModelData, ChannelSetting):
""" """
Guild event log channel. Guild event log channel.
""" """
setting_id = 'eventlog' setting_id = 'eventlog'
_event = 'guildset_eventlog' _event = 'guildset_eventlog'
_set_cmd = 'configure general'
_display_name = _p('guildset:eventlog', "event_log") _display_name = _p('guildset:eventlog', "event_log")
_desc = _p( _desc = _p(
'guildset:eventlog|desc', 'guildset:eventlog|desc',
"Channel to which to log server events, such as voice sessions and equipped roles." "My audit log channel where I send server actions and events (e.g. rankgs and expiring roles)."
) )
# TODO: Reword
_long_desc = _p( _long_desc = _p(
'guildset:eventlog|long_desc', 'guildset:eventlog|long_desc',
"An audit log for my own systems, " "If configured, I will log most significant actions taken "
"I will send most significant actions and events that occur through my interface " "or events which occur through my interface, into this channel. "
"to this channel. For example, this includes:\n" "Logged events include, for example:\n"
"- Member voice activity\n" "- Member voice activity\n"
"- Roles equipped and expiring from rolemenus\n" "- Roles equipped and expiring from rolemenus\n"
"- Privated rooms rented and expiring\n" "- Privated rooms rented and expiring\n"
@@ -76,4 +77,34 @@ class GeneralSettings(SettingGroup):
"I must have the 'Manage Webhooks' permission in this channel." "I must have the 'Manage Webhooks' permission in this channel."
) )
# TODO: Updatestr _model = CoreData.Guild
_column = CoreData.Guild.event_log_channel.name
@classmethod
async def _check_value(cls, parent_id: int, value: Optional[discord.abc.GuildChannel], **kwargs):
if value is not None:
t = ctx_translator.get().t
if not value.permissions_for(value.guild.me).manage_webhooks:
raise UserInputError(
t(_p(
'guildset:eventlog|check_value|error:perms|perm:manage_webhooks',
"Cannot set {channel} as an event log! I lack the 'Manage Webhooks' permission there."
)).format(channel=value)
)
@property
def update_message(self):
t = ctx_translator.get().t
channel = self.value
if channel is not None:
response = t(_p(
'guildset:eventlog|response|set',
"Events will now be logged to {channel}"
)).format(channel=channel.mention)
else:
response = t(_p(
'guildset:eventlog|response|unset',
"Guild events will no longer be logged."
))
return response

View File

@@ -4,6 +4,7 @@ import discord
from discord.ui.select import select, ChannelSelect from discord.ui.select import select, ChannelSelect
from meta import LionBot from meta import LionBot
from meta.errors import UserInputError
from utils.ui import ConfigUI, DashboardSection from utils.ui import ConfigUI, DashboardSection
from utils.lib import MessageArgs from utils.lib import MessageArgs
@@ -18,11 +19,11 @@ _p = babel._p
class GeneralSettingUI(ConfigUI): class GeneralSettingUI(ConfigUI):
setting_classes = ( setting_classes = (
GeneralSettings.Timezone, GeneralSettings.Timezone,
GeneralSettings.Eventlog, GeneralSettings.EventLog,
) )
def __init__(self, bot: LionBot, guildid: int, channelid: int, **kwargs): def __init__(self, bot: LionBot, guildid: int, channelid: int, **kwargs):
self.settings = bot.get_cog('GeneralSettingsCog').settings self.settings = bot.get_cog('GuildConfigCog').settings
super().__init__(bot, guildid, channelid, **kwargs) super().__init__(bot, guildid, channelid, **kwargs)
# ----- UI Components ----- # ----- UI Components -----
@@ -39,13 +40,10 @@ class GeneralSettingUI(ConfigUI):
""" """
await selection.response.defer(thinking=True, ephemeral=True) await selection.response.defer(thinking=True, ephemeral=True)
setting = self.get_instance(GeneralSettings.Eventlog) setting = self.get_instance(GeneralSettings.EventLog)
value = selected.values[0] if selected.values else None value = selected.values[0].resolve() if selected.values else None
if issue := (await setting.check_value(value)): setting = await setting.from_value(self.guildid, value)
raise UserInputError(issue)
setting.value = value
await setting.write() await setting.write()
await selection.delete_original_response() await selection.delete_original_response()
@@ -103,5 +101,5 @@ class GeneralDashboard(DashboardSection):
"dash:general|option|name", "dash:general|option|name",
"General Configuration Panel" "General Configuration Panel"
) )
configui = GeneralSettingsUI configui = GeneralSettingUI
setting_classes = configui.setting_classes setting_classes = configui.setting_classes

View File

@@ -57,7 +57,7 @@ class TimerOptions(SettingGroup):
_allow_object = False _allow_object = False
@classmethod @classmethod
async def _check_value(cls, parent_id: int, value: Optional[discord.abc.GuildChannel], **kwargs): async def _check_value(cls, parent_id: int, value, **kwargs):
if value is not None: if value is not None:
# TODO: Check we either have or can create a webhook # TODO: Check we either have or can create a webhook
# TODO: Check we can send messages, embeds, and files # TODO: Check we can send messages, embeds, and files

View File

@@ -145,9 +145,7 @@ class TimerOptionsUI(MessageUI):
value = selected.values[0] if selected.values else None value = selected.values[0] if selected.values else None
setting = self.timer.config.get('notification_channel') setting = self.timer.config.get('notification_channel')
if issue := await setting._check_value(self.timer.data.channelid, value): await setting._check_value(self.timer.data.channelid, value)
await selection.edit_original_response(embed=error_embed(issue))
else:
setting.value = value setting.value = value
await setting.write() await setting.write()
await self.timer.send_status() await self.timer.send_status()

View File

@@ -453,6 +453,12 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
data = await cls._parse_string(parent_id, userstr, **kwargs) data = await cls._parse_string(parent_id, userstr, **kwargs)
return cls(parent_id, data, **kwargs) return cls(parent_id, data, **kwargs)
@classmethod
async def from_value(cls, parent_id, value, **kwargs):
await cls._check_value(parent_id, value, **kwargs)
data = cls._data_from_value(parent_id, value, **kwargs)
return cls(parent_id, data, **kwargs)
@classmethod @classmethod
async def _parse_string(cls, parent_id, string: str, **kwargs) -> Optional[SettingData]: async def _parse_string(cls, parent_id, string: str, **kwargs) -> Optional[SettingData]:
""" """
@@ -471,15 +477,14 @@ class InteractiveSetting(BaseSetting[ParentID, SettingData, SettingValue]):
raise NotImplementedError raise NotImplementedError
@classmethod @classmethod
async def _check_value(cls, parent_id, value, **kwargs) -> Optional[str]: async def _check_value(cls, parent_id, value, **kwargs):
""" """
Check the provided value is valid. Check the provided value is valid.
Many setting update methods now provide Discord objects instead of raw data or user strings. 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. This method may be used for value-checking such a value.
Returns `None` if there are no issues, otherwise an error message. Raises UserInputError if the value fails validation.
Subclasses should override this to implement a value checker.
""" """
pass pass