rewrite: Refactor timezone config.

This commit is contained in:
2023-06-05 17:25:51 +03:00
parent eb0731e185
commit 809cada228
4 changed files with 113 additions and 154 deletions

View File

@@ -49,7 +49,7 @@ class LionUser(Timezoned):
@property @property
def timezone(self) -> pytz.timezone: def timezone(self) -> pytz.timezone:
return self.config.timezone.value return self.config.timezone.value or pytz.UTC
async def touch_discord_model(self, user: discord.User, seen=True): async def touch_discord_model(self, user: discord.User, seen=True):
""" """

View File

@@ -95,6 +95,12 @@ class GeneralSettingsCog(LionCog):
name=_p('cmd:configure_general', "general"), name=_p('cmd:configure_general', "general"),
description=_p('cmd:configure_general|desc', "General configuration panel") 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.guild_only()
@cmds.check(low_management) @cmds.check(low_management)
async def cmd_configure_general(self, ctx: LionContext, async def cmd_configure_general(self, ctx: LionContext,
@@ -164,42 +170,4 @@ class GeneralSettingsCog(LionCog):
) )
await ctx.reply(embed=embed) await ctx.reply(embed=embed)
@cmd_configure_general.autocomplete('timezone') cmd_configure_general.autocomplete('timezone')(TimezoneSetting.parse_acmpl)
async def cmd_configure_general_acmpl_timezone(self, interaction: discord.Interaction, partial: str):
"""
Autocomplete timezone options.
Each option is formatted as timezone (current time).
Partial text is matched directly by case-insensitive substring.
"""
t = self.bot.translator.t
# TODO: To be refactored to Timezone setting
timezones = pytz.all_timezones
matching = [tz for tz in timezones if partial.strip().lower() in tz.lower()][:25]
if not matching:
choices = [
appcmds.Choice(
name=t(_p(
'cmd:configure_general|acmpl:timezone|no_matching',
"No timezones matching '{input}'!"
)).format(input=partial),
value=partial
)
]
else:
choices = []
for tz in matching:
timezone = pytz.timezone(tz)
now = dt.datetime.now(timezone)
nowstr = now.strftime("%H:%M")
name = t(_p(
'cmd:configure_general|acmpl:timezone|choice',
"{tz} (Currently {now})"
)).format(tz=tz, now=nowstr)
choice = appcmds.Choice(
name=name,
value=tz
)
choices.append(choice)
return choices

View File

@@ -5,6 +5,7 @@ import pytz
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
from discord.ui.button import ButtonStyle
from settings.data import ModelData from settings.data import ModelData
from settings.groups import SettingGroup from settings.groups import SettingGroup
@@ -12,6 +13,7 @@ from settings.setting_types import TimezoneSetting
from meta import LionBot, LionContext, LionCog from meta import LionBot, LionContext, LionCog
from meta.errors import UserInputError from meta.errors import UserInputError
from utils.ui import AButton, AsComponents
from babel.translator import ctx_translator from babel.translator import ctx_translator
from core.data import CoreData from core.data import CoreData
@@ -43,7 +45,7 @@ class UserConfigSettings(SettingGroup):
"including personal statistics. " "including personal statistics. "
"Note that leaderboards will still be shown in the server's own timezone." "Note that leaderboards will still be shown in the server's own timezone."
) )
_default = 'UTC' _default = None
_model = CoreData.User _model = CoreData.User
_column = CoreData.User.timezone.name _column = CoreData.User.timezone.name
@@ -51,10 +53,16 @@ class UserConfigSettings(SettingGroup):
@property @property
def update_message(self): def update_message(self):
t = ctx_translator.get().t t = ctx_translator.get().t
return t(_p( if self._data:
'userset:timezone|response', return t(_p(
"Your personal timezone has been set to `{timezone}`." 'userset:timezone|response:set',
)).format(timezone=self.data) "Your personal timezone has been set to `{timezone}`."
)).format(timezone=self.data)
else:
return t(_p(
'userset:timezone|response:unset',
"You have unset your timezone. Statistics will be displayed in the server timezone."
))
class UserConfigCog(LionCog): class UserConfigCog(LionCog):
@@ -68,115 +76,6 @@ class UserConfigCog(LionCog):
async def cog_unload(self): async def cog_unload(self):
... ...
@cmds.hybrid_command(
name=_p('cmd:set', "set"),
description=_p('cmd:set|desc', "Your personal settings. Configure how I interact with you.")
)
@appcmds.rename(
timezone=UserConfigSettings.Timezone._display_name
)
@appcmds.describe(
timezone=UserConfigSettings.Timezone._desc
)
async def set_cmd(self, ctx: LionContext, timezone: Optional[str] = None):
"""
Configuration interface for the user's timezone.
"""
# TODO: Cohesive configuration panel for set
t = self.bot.translator.t
if not ctx.interaction:
return
await ctx.interaction.response.defer(thinking=True)
updated = []
error_embed = None
if timezone is not None:
# TODO: Add None/unsetting support to timezone
try:
timezone_setting = await self.settings.Timezone.from_string(ctx.author.id, timezone)
updated.append(timezone_setting)
except UserInputError as err:
# Handle UserInputError from timezone parsing
error_embed = discord.Embed(
colour=discord.Colour.brand_red(),
title=t(_p(
'cmd:set|parse_failure:timezone',
"Could not set your timezone!"
)),
description=err.msg
)
if error_embed is not None:
# Could not parse requested configuration update
await ctx.reply(embed=error_embed)
elif updated:
# Update requested configuration
lines = []
for to_update in updated:
await to_update.write()
response = to_update.update_message
lines.append(f"{self.bot.config.emojis.tick} {response}")
success_embed = discord.Embed(
colour=discord.Colour.brand_green(),
description='\n'.join(lines)
)
await ctx.reply(embed=success_embed)
# TODO update listening panel UI
else:
# Show the user's configuration panel
# TODO: Interactive UI panel
panel_embed = discord.Embed(
colour=discord.Colour.orange(),
title=t(_p(
'cmd:set|embed:panel|title',
"Your StudyLion settings"
))
)
panel_embed.add_field(**ctx.luser.config.timezone.embed_field)
await ctx.reply(embed=panel_embed)
@set_cmd.autocomplete('timezone')
async def set_cmd_acmpl_timezone(self, interaction: discord.Interaction, partial: str):
"""
Autocomplete timezone options.
Each option is formatted as timezone (current time).
Partial text is matched directly by case-insensitive substring.
"""
# TODO: To be refactored to Timezone setting
t = self.bot.translator.t
timezones = pytz.all_timezones
matching = [tz for tz in timezones if partial.strip().lower() in tz.lower()][:25]
if not matching:
choices = [
appcmds.Choice(
name=t(_p(
'cmd:set|acmpl:timezone|no_matching',
"No timezones matching '{input}'!"
)).format(input=partial),
value=partial
)
]
else:
choices = []
for tz in matching:
timezone = pytz.timezone(tz)
now = dt.datetime.now(timezone)
nowstr = now.strftime("%H:%M")
name = t(_p(
'cmd:set|acmpl:timezone|choice',
"{tz} (Currently {now})"
)).format(tz=tz, now=nowstr)
choice = appcmds.Choice(
name=name,
value=tz
)
choices.append(choice)
return choices
@cmds.hybrid_group( @cmds.hybrid_group(
name=_p('cmd:userconfig', "my"), name=_p('cmd:userconfig', "my"),
description=_p('cmd:userconfig|desc', "User configuration commands.") description=_p('cmd:userconfig|desc', "User configuration commands.")
@@ -184,3 +83,59 @@ class UserConfigCog(LionCog):
async def userconfig_group(self, ctx: LionContext): async def userconfig_group(self, ctx: LionContext):
# Group base command, no function. # Group base command, no function.
pass pass
@userconfig_group.command(
name=_p('cmd:userconfig_timezone', "timezone"),
description=_p(
'cmd:userconfig_timezone|desc',
"Set your personal timezone, used for displaying stats and setting reminders."
)
)
@appcmds.rename(
timezone=_p('cmd:userconfig_timezone|param:timezone', "timezone")
)
@appcmds.describe(
timezone=_p(
'cmd:userconfig_timezone|param:timezone|desc',
"What timezone are you in? Try typing your country or continent."
)
)
async def userconfig_timezone_cmd(self, ctx: LionContext, timezone: Optional[str] = None):
if not ctx.interaction:
return
t = self.bot.translator.t
setting = ctx.luser.config.get(UserConfigSettings.Timezone.setting_id)
if timezone:
new_data = await setting._parse_string(ctx.author.id, timezone)
await setting.interactive_set(new_data, ctx.interaction, ephemeral=True)
else:
if setting.value:
desc = t(_p(
'cmd:userconfig_timezone|response:set',
"Your timezone is currently set to {timezone}"
)).format(timezone=setting.formatted)
@AButton(
label=t(_p('cmd:userconfig_timezone|button:reset|label', "Reset")),
style=ButtonStyle.red
)
async def reset_button(_press: discord.Interaction, pressed):
await _press.response.defer()
await setting.interactive_set(None, ctx.interaction, view=None)
view = AsComponents(reset_button)
else:
guild_tz = ctx.lguild.config.get('timezone').value if ctx.guild else 'UTC'
desc = t(_p(
'cmd:userconfig_timezone|response:unset',
"Your timezone is not set. Using the server timezone `{timezone}`."
)).format(timezone=guild_tz)
view = None
embed = discord.Embed(
colour=discord.Colour.orange(),
description=desc
)
await ctx.reply(embed=embed, ephemeral=True, view=view)
userconfig_timezone_cmd.autocomplete('timezone')(TimezoneSetting.parse_acmpl)

View File

@@ -3,6 +3,8 @@ from enum import Enum
import pytz import pytz
import discord import discord
import discord.app_commands as appcmds
import itertools import itertools
import datetime as dt import datetime as dt
from discord import ui from discord import ui
@@ -737,6 +739,40 @@ class TimezoneSetting(InteractiveSetting[ParentID, str, TZT]):
) from None ) from None
return str(timezone) return str(timezone)
@classmethod
async def parse_acmpl(cls, interaction: discord.Interaction, partial: str):
bot = interaction.client
t = bot.translator.t
timezones = pytz.all_timezones
matching = [tz for tz in timezones if partial.strip().lower() in tz.lower()][:25]
if not matching:
choices = [
appcmds.Choice(
name=t(_p(
'set_type:timezone|acmpl|no_matching',
"No timezones matching '{input}'!"
)).format(input=partial),
value=partial
)
]
else:
choices = []
for tz in matching:
timezone = pytz.timezone(tz)
now = dt.datetime.now(timezone)
nowstr = now.strftime("%H:%M")
name = t(_p(
'set_type:timezone|acmpl|choice',
"{tz} (Currently {now})"
)).format(tz=tz, now=nowstr)
choice = appcmds.Choice(
name=name,
value=tz
)
choices.append(choice)
return choices
@classmethod @classmethod
def _format_data(cls, parent_id: ParentID, data, **kwargs): def _format_data(cls, parent_id: ParentID, data, **kwargs):
""" """