rewrite: Refactor timezone config.
This commit is contained in:
@@ -49,7 +49,7 @@ class LionUser(Timezoned):
|
||||
|
||||
@property
|
||||
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):
|
||||
"""
|
||||
|
||||
@@ -95,6 +95,12 @@ class GeneralSettingsCog(LionCog):
|
||||
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()
|
||||
@cmds.check(low_management)
|
||||
async def cmd_configure_general(self, ctx: LionContext,
|
||||
@@ -164,42 +170,4 @@ class GeneralSettingsCog(LionCog):
|
||||
)
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@cmd_configure_general.autocomplete('timezone')
|
||||
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
|
||||
cmd_configure_general.autocomplete('timezone')(TimezoneSetting.parse_acmpl)
|
||||
|
||||
@@ -5,6 +5,7 @@ import pytz
|
||||
import discord
|
||||
from discord import app_commands as appcmds
|
||||
from discord.ext import commands as cmds
|
||||
from discord.ui.button import ButtonStyle
|
||||
|
||||
from settings.data import ModelData
|
||||
from settings.groups import SettingGroup
|
||||
@@ -12,6 +13,7 @@ from settings.setting_types import TimezoneSetting
|
||||
|
||||
from meta import LionBot, LionContext, LionCog
|
||||
from meta.errors import UserInputError
|
||||
from utils.ui import AButton, AsComponents
|
||||
from babel.translator import ctx_translator
|
||||
|
||||
from core.data import CoreData
|
||||
@@ -43,7 +45,7 @@ class UserConfigSettings(SettingGroup):
|
||||
"including personal statistics. "
|
||||
"Note that leaderboards will still be shown in the server's own timezone."
|
||||
)
|
||||
_default = 'UTC'
|
||||
_default = None
|
||||
|
||||
_model = CoreData.User
|
||||
_column = CoreData.User.timezone.name
|
||||
@@ -51,10 +53,16 @@ class UserConfigSettings(SettingGroup):
|
||||
@property
|
||||
def update_message(self):
|
||||
t = ctx_translator.get().t
|
||||
if self._data:
|
||||
return t(_p(
|
||||
'userset:timezone|response',
|
||||
'userset:timezone|response:set',
|
||||
"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):
|
||||
@@ -68,115 +76,6 @@ class UserConfigCog(LionCog):
|
||||
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(
|
||||
name=_p('cmd:userconfig', "my"),
|
||||
description=_p('cmd:userconfig|desc', "User configuration commands.")
|
||||
@@ -184,3 +83,59 @@ class UserConfigCog(LionCog):
|
||||
async def userconfig_group(self, ctx: LionContext):
|
||||
# Group base command, no function.
|
||||
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)
|
||||
|
||||
@@ -3,6 +3,8 @@ from enum import Enum
|
||||
|
||||
import pytz
|
||||
import discord
|
||||
import discord.app_commands as appcmds
|
||||
|
||||
import itertools
|
||||
import datetime as dt
|
||||
from discord import ui
|
||||
@@ -737,6 +739,40 @@ class TimezoneSetting(InteractiveSetting[ParentID, str, TZT]):
|
||||
) from None
|
||||
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
|
||||
def _format_data(cls, parent_id: ParentID, data, **kwargs):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user