feat(eventlog): Add eventlog setting.

Also refactors the GeneralSettings to use the new style.
This commit is contained in:
2023-10-10 16:05:57 +03:00
parent 4e8eb366f3
commit fe81945391
7 changed files with 339 additions and 106 deletions

View File

@@ -93,3 +93,12 @@ class LionGuild(Timezoned):
"""
if self.data.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

@@ -6,8 +6,6 @@ babel = LocalBabel('config')
async def setup(bot):
from .general import GeneralSettingsCog
from .cog import DashCog
from .cog import GuildConfigCog
await bot.add_cog(GeneralSettingsCog(bot))
await bot.add_cog(DashCog(bot))
await bot.add_cog(GuildConfigCog(bot))

View File

@@ -3,22 +3,31 @@ from discord import app_commands as appcmds
from discord.ext import commands as cmds
from meta import LionBot, LionContext, LionCog
from wards import low_management_ward
from . import babel
from .dashboard import GuildDashboard
from .settings import GeneralSettings
from .settingui import GeneralSettingUI
_p = babel._p
class DashCog(LionCog):
class GuildConfigCog(LionCog):
depends = {'CoreCog'}
def __init__(self, bot: LionBot):
self.bot = bot
self.settings = GeneralSettings()
async def cog_load(self):
...
self.bot.core.guild_config.register_model_setting(GeneralSettings.Timezone)
self.bot.core.guild_config.register_model_setting(GeneralSettings.Eventlog)
async def cog_unload(self):
...
configcog = self.bot.get_cog('ConfigCog')
if configcog is None:
raise ValueError("Cannot load GuildConfigCog without ConfigCog")
self.crossload_group(self.configure_group, configcog.configure_group)
@cmds.hybrid_command(
name="dashboard",
@@ -30,3 +39,72 @@ class DashCog(LionCog):
ui = GuildDashboard(self.bot, ctx.guild, ctx.author.id, ctx.channel.id)
await ui.run(ctx.interaction)
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 -----
@LionCog.placeholder_group
@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)
# TODO

View File

@@ -26,48 +26,6 @@ from . import babel
_p = babel._p
class GeneralSettings(SettingGroup):
class Timezone(ModelData, TimezoneSetting):
"""
Guild timezone configuration.
Exposed via `/configure general timezone:`, and the standard interface.
The `timezone` setting acts as the default timezone for all members,
and the timezone used to display guild-wide statistics.
"""
setting_id = 'timezone'
_event = 'guild_setting_update_timezone'
_display_name = _p('guildset:timezone', "timezone")
_desc = _p(
'guildset:timezone|desc',
"Guild timezone for statistics display."
)
_long_desc = _p(
'guildset:timezone|long_desc',
"Guild-wide timezone. "
"Used to determine start of the day for the leaderboards, "
"and as the default statistics timezone for members who have not set one."
)
_default = 'UTC'
_model = CoreData.Guild
_column = CoreData.Guild.timezone.name
@property
def update_message(self):
t = ctx_translator.get().t
return t(_p(
'guildset:timezone|response',
"The guild timezone has been set to `{timezone}`."
)).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 GeneralSettingsCog(LionCog):
depends = {'CoreCog'}
@@ -87,68 +45,72 @@ class GeneralSettingsCog(LionCog):
@LionCog.placeholder_group
@cmds.hybrid_group("configure", with_app_command=False)
async def configure_group(self, ctx: LionContext):
# Placeholder configure group command.
...
# 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
)
@appcmds.describe(
timezone=GeneralSettings.Timezone._desc
)
@appcmds.guild_only()
@appcmds.default_permissions(manage_guild=True)
@low_management_ward
async def cmd_configure_general(self, ctx: LionContext,
timezone: Optional[str] = None):
t = self.bot.translator.t
@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)
# 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)
updated = [] # Possibly empty list of setting instances which were updated, with new data stored
error_embed = None
updated = [] # Possibly empty list of setting instances which were updated, with new data stored
error_embed = None
if timezone is not None:
try:
timezone_setting = await self.settings.Timezone.from_string(ctx.guild.id, timezone)
updated.append(timezone_setting)
except UserInputError as err:
error_embed = discord.Embed(
colour=discord.Colour.brand_red(),
if timezone is not None:
try:
timezone_setting = await self.settings.Timezone.from_string(ctx.guild.id, timezone)
updated.append(timezone_setting)
except UserInputError as err:
error_embed = discord.Embed(
colour=discord.Colour.brand_red(),
title=t(_p(
'cmd:configure_general|parse_failure:timezone',
"Could not set the timezone!"
)),
description=err.msg
)
if error_embed is not None:
# User requested configuration updated, but we couldn't parse input
await ctx.reply(embed=error_embed)
elif updated:
# Save requested configuration updates
results = [] # List of "success" update responses for each updated setting
for to_update in updated:
# TODO: Again need a better way of batch writing
# Especially since most of these are on one model...
await to_update.write()
results.append(to_update.update_message)
# Post aggregated success message
success_embed = discord.Embed(
colour=discord.Colour.brand_green(),
title=t(_p(
'cmd:configure_general|parse_failure:timezone',
"Could not set the timezone!"
'cmd:configure_general|success',
"Settings Updated!"
)),
description=err.msg
)
if error_embed is not None:
# User requested configuration updated, but we couldn't parse input
await ctx.reply(embed=error_embed)
elif updated:
# Save requested configuration updates
results = [] # List of "success" update responses for each updated setting
for to_update in updated:
# TODO: Again need a better way of batch writing
# Especially since most of these are on one model...
await to_update.write()
results.append(to_update.update_message)
# Post aggregated success message
success_embed = discord.Embed(
colour=discord.Colour.brand_green(),
title=t(_p(
'cmd:configure_general|success',
"Settings Updated!"
)),
description='\n'.join(
f"{self.bot.config.emojis.tick} {line}" for line in results
)

View File

@@ -0,0 +1,79 @@
from settings import ModelData
from settings.setting_types import TimezoneSetting, ChannelSetting
from settings.groups import SettingGroup
from core.data import CoreData
from babel.translator import ctx_translator
from . import babel
_p = babel._p
class GeneralSettings(SettingGroup):
class Timezone(ModelData, TimezoneSetting):
"""
Guild timezone configuration.
Exposed via `/configure general timezone:`, and the standard interface.
The `timezone` setting acts as the default timezone for all members,
and the timezone used to display guild-wide statistics.
"""
setting_id = 'timezone'
_event = 'guild_setting_update_timezone'
_display_name = _p('guildset:timezone', "timezone")
_desc = _p(
'guildset:timezone|desc',
"Guild timezone for statistics display."
)
_long_desc = _p(
'guildset:timezone|long_desc',
"Guild-wide timezone. "
"Used to determine start of the day for the leaderboards, "
"and as the default statistics timezone for members who have not set one."
)
_default = 'UTC'
_model = CoreData.Guild
_column = CoreData.Guild.timezone.name
@property
def update_message(self):
t = ctx_translator.get().t
return t(_p(
'guildset:timezone|response',
"The guild timezone has been set to `{timezone}`."
)).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):
"""
Guild event log channel.
"""
setting_id = 'eventlog'
_event = 'guildset_eventlog'
_display_name = _p('guildset:eventlog', "event_log")
_desc = _p(
'guildset:eventlog|desc',
"Channel to which to log server events, such as voice sessions and equipped roles."
)
# TODO: Reword
_long_desc = _p(
'guildset:eventlog|long_desc',
"An audit log for my own systems, "
"I will send most significant actions and events that occur through my interface "
"to this channel. For example, this includes:\n"
"- Member voice activity\n"
"- Roles equipped and expiring from rolemenus\n"
"- Privated rooms rented and expiring\n"
"- Activity ranks earned\n"
"I must have the 'Manage Webhooks' permission in this channel."
)
# TODO: Updatestr

View File

View File

@@ -0,0 +1,107 @@
import asyncio
import discord
from discord.ui.select import select, ChannelSelect
from meta import LionBot
from utils.ui import ConfigUI, DashboardSection
from utils.lib import MessageArgs
from . import babel
from .settings import GeneralSettings
_p = babel._p
class GeneralSettingUI(ConfigUI):
setting_classes = (
GeneralSettings.Timezone,
GeneralSettings.Eventlog,
)
def __init__(self, bot: LionBot, guildid: int, channelid: int, **kwargs):
self.settings = bot.get_cog('GeneralSettingsCog').settings
super().__init__(bot, guildid, channelid, **kwargs)
# ----- UI Components -----
# Event log
@select(
cls=ChannelSelect,
channel_types=[discord.ChannelType.text, discord.ChannelType.voice],
placeholder='EVENT_LOG_PLACEHOLDER',
min_values=0, max_values=1,
)
async def eventlog_menu(self, selection: discord.Interaction, selected: ChannelSelect):
"""
Single channel selector for the event log.
"""
await selection.response.defer(thinking=True, ephemeral=True)
setting = self.get_instance(GeneralSettings.Eventlog)
value = selected.values[0] if selected.values else None
if issue := (await setting.check_value(value)):
raise UserInputError(issue)
setting.value = value
await setting.write()
await selection.delete_original_response()
async def eventlog_menu_refresh(self):
menu = self.eventlog_menu
t = self.bot.translator.t
menu.placeholder = t(_p(
'ui:general_config|menu:event_log|placeholder',
"Select Event Log"
))
# ----- UI Flow -----
async def make_message(self) -> MessageArgs:
t = self.bot.translator.t
title = t(_p(
'ui:general_config|embed:title',
"General Configuration"
))
embed = discord.Embed(
title=title,
colour=discord.Colour.orange()
)
for setting in self.instances:
embed.add_field(**setting.embed_field, inline=False)
return MessageArgs(embed=embed)
async def reload(self):
self.instances = [
await setting.get(self.guildid)
for setting in self.setting_classes
]
async def refresh_components(self):
to_refresh = (
self.edit_button_refresh(),
self.close_button_refresh(),
self.reset_button_refresh(),
self.eventlog_menu_refresh(),
)
await asyncio.gather(*to_refresh)
self.set_layout(
(self.eventlog_menu,),
(self.edit_button, self.reset_button, self.close_button,),
)
class GeneralDashboard(DashboardSection):
section_name = _p(
"dash:general|title",
"General Dashboard Settings ({commands[configure general]})"
)
_option_name = _p(
"dash:general|option|name",
"General Configuration Panel"
)
configui = GeneralSettingsUI
setting_classes = configui.setting_classes