rewrite: New Pomodoro Timer system.

This commit is contained in:
2023-05-19 09:45:06 +03:00
parent 8d5840c696
commit 4aa2587c45
29 changed files with 2860 additions and 12 deletions

View File

@@ -0,0 +1,315 @@
from typing import Optional, TYPE_CHECKING
import asyncio
import discord
from discord.ui.button import button, Button, ButtonStyle
from discord.ui.select import select, Select, RoleSelect, ChannelSelect
from meta import LionBot, conf
from utils.lib import utc_now, MessageArgs, error_embed
from utils.ui import MessageUI
from babel.translator import ctx_locale
from .. import babel
from ..lib import TimerRole
from ..options import TimerOptions
from .edit import TimerEditor
if TYPE_CHECKING:
from ..timer import Timer
_p = babel._p
class TimerOptionsUI(MessageUI):
"""
View options for and reconfigure a single timer.
"""
def __init__(self, bot: LionBot, timer: 'Timer', role: TimerRole, **kwargs):
self.locale = timer.locale.value
ctx_locale.set(self.locale)
super().__init__(**kwargs)
self.bot = bot
self.timer = timer
self.role = role
@button(label="EDIT_PLACEHOLDER", style=ButtonStyle.blurple)
async def edit_button(self, press: discord.Interaction, pressed: Button):
"""
Open the edit modal to modify focus/break/threshold/name/format_string
"""
modal = await TimerEditor.open_editor(self.bot, press, self.timer, press.user, callback=self.refresh)
await modal.wait()
async def refresh_edit_button(self):
self.edit_button.label = self.bot.translator.t(_p(
'ui:timer_options|button:edit|label',
"Edit"
))
@button(label="VOICE_ALERT_PLACEHOLDER", style=ButtonStyle.green)
async def voice_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
value = not self.timer.voice_alerts
setting = self.timer.config.get('voice_alerts')
setting.value = value
await setting.write()
await self.refresh(thinking=press)
async def refresh_voice_button(self):
button = self.voice_button
button.label = self.bot.translator.t(_p(
'ui:timer_options|button:voice_alerts|label',
"Voice Alerts"
))
if self.timer.voice_alerts:
button.style = ButtonStyle.green
else:
button.style = ButtonStyle.grey
@button(label="DELETE_PLACEHOLDER", style=ButtonStyle.red)
async def delete_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True)
channelid = self.timer.data.channelid
# Destroy through cog to maintain cache
cog = self.bot.get_cog('TimerCog')
await cog.destroy_timer(self.timer, reason="Manually destroyed through OptionUI")
await self.quit()
t = self.bot.translator.t
embed = discord.Embed(
colour=discord.Colour.brand_red(),
title=t(_p(
'ui:timer_options|button:delete|success|title',
"Timer Deleted"
)),
description=t(_p(
'ui:timer_options|button:delete|success|description',
"The timer in {channel} has been removed."
)).format(channel=f"<#{channelid}>")
)
await press.edit_original_response(embed=embed)
async def refresh_delete_button(self):
self.delete_button.label = self.bot.translator.t(_p(
'ui:timer_options|button:delete|label',
"Delete"
))
@button(emoji=conf.emojis.cancel, style=ButtonStyle.red)
async def close_button(self, press: discord.Interaction, pressed: Button):
print("HERE")
await press.response.defer()
await self.quit()
async def refresh_close_button(self):
pass
@select(cls=ChannelSelect, placeholder="VOICE_CHANNEL_PLACEHOLDER", channel_types=[discord.ChannelType.voice])
async def voice_menu(self, selection: discord.Interaction, selected):
...
async def refresh_voice_menu(self):
self.voice_menu.placeholder = self.bot.translator.t(_p(
'ui:timer_options|menu:voice_channel|placeholder',
"Set Voice Channel"
))
@select(cls=ChannelSelect,
placeholder="NOTIFICATION_PLACEHOLDER",
channel_types=[discord.ChannelType.text, discord.ChannelType.voice],
min_values=0, max_values=1)
async def notification_menu(self, selection: discord.Interaction, selected):
await selection.response.defer(thinking=True, ephemeral=True)
value = selected.values[0] if selected.values else None
setting = self.timer.config.get('notification_channel')
if issue := await setting._check_value(self.timer.data.channelid, value):
await selection.edit_original_response(embed=error_embed(issue))
else:
setting.value = value
await setting.write()
await self.timer.send_status()
await self.refresh(thinking=selection)
async def refresh_notification_menu(self):
self.notification_menu.placeholder = self.bot.translator.t(_p(
'ui:timer_options|menu:notification_channel|placeholder',
"Set Notification Channel"
))
@select(cls=RoleSelect, placeholder="ROLE_PLACEHOLDER", min_values=0, max_values=1)
async def manage_role_menu(self, selection: discord.Interaction, selected):
await selection.response.defer(thinking=True, ephemeral=True)
value = selected.values[0] if selected.values else None
setting = self.timer.config.get('manager_role')
setting.value = value
await setting.write()
await self.refresh(thinking=selection)
async def refresh_manage_role_menu(self):
self.manage_role_menu.placeholder = self.bot.translator.t(_p(
'ui:timer_options|menu:manager_role|placeholder',
"Set Manager Role"
))
# ----- UI FLow -----
async def make_message(self) -> MessageArgs:
t = self.bot.translator.t
title = t(_p(
'ui:timer_options|embed|title',
"Timer Control Panel for {channel}"
)).format(channel=f"<#{self.timer.data.channelid}>")
table = await TimerOptions().make_setting_table(self.timer.data.channelid, timer=self.timer)
footer = t(_p(
'ui:timer_options|embed|footer',
"Hover over the option names to view descriptions."
))
embed = discord.Embed(
colour=discord.Colour.orange(),
title=title,
description=table
)
embed.set_footer(text=footer)
# Add pattern field
embed.add_field(
name=t(_p('ui:timer_options|embed|field:pattern|name', "Pattern")),
value=t(_p(
'ui:timer_options|embed|field:pattern|value',
"**`{focus_len} minutes`** focus\n**`{break_len} minutes`** break"
)).format(
focus_len=self.timer.data.focus_length // 60,
break_len=self.timer.data.break_length // 60
)
)
# Add channel name field
embed.add_field(
name=t(_p(
'ui:timer_options|embed|field:channel_name|name',
"Channel Name Preview"
)),
value=t(_p(
'ui:timer_options|embed|field:channel_name|value',
"**`{name}`**\n(The actual channel name may not match due to ratelimits.)"
)).format(name=self.timer.channel_name)
)
# Add issue field (if there are any permission issues).
issues = await self._get_issues()
if issues:
embed.add_field(
name=t(_p(
'ui:timer_options|embed|field:issues|name',
"Issues"
)),
value='\n'.join(f"{conf.emojis.warning} {issue}" for issue in issues),
inline=False
)
return MessageArgs(embed=embed)
async def _get_issues(self):
"""
Report any issues with the timer setup, particularly with permissions.
"""
t = self.bot.translator.t
issues = []
if self.timer.channel is None:
issues.append(
t(_p(
'ui:timer_options|issue:no_voice_channel',
"The configured voice channel does not exist! Please update it below."
))
)
else:
channel = self.timer.channel
# Check join and speak permissions
my_voice_permissions = channel.permissions_for(channel.guild.me)
if self.timer.voice_alerts and not (my_voice_permissions.connect and my_voice_permissions.speak):
issues.append(
t(_p(
'ui:timer_options|issue:cannot_speak',
"Voice alerts are on, but I don't have speaking permissions in {channel}"
)).format(channel=channel.mention)
)
if not my_voice_permissions.manage_channels:
issues.append(
t(_p(
'ui:timer_options|issue:cannot_change_name',
"I cannot update the name of {channel}! (Needs `MANAGE_CHANNELS` permission)"
)).format(channel=channel.mention)
)
notif_channelid = self.timer.data.notification_channelid
if notif_channelid:
channel = self.bot.get_channel(notif_channelid)
if channel is None:
issues.append(
t(_p(
'ui:timer_options|issue:notif_channel_dne',
"Configured notification channel does not exist!"
))
)
else:
perms = channel.permissions_for(channel.guild.me)
if not (perms.embed_links and perms.attach_files):
issues.append(
t(_p(
'ui:timer_options|issue:notif_channel_write',
"I cannot attach files (`ATTACH_FILES`) or send embeds (`EMBED_LINKS`) in {channel}"
)).format(channel=channel.mention)
)
if not (perms.manage_webhooks):
issues.append(
t(_p(
'ui:timer_options|issues:cannot_make_webhooks',
"I cannot create the notification webhook (`MANAGE_WEBHOOKS`) in {channel}"
)).format(channel=channel.mention)
)
return issues
async def refresh_layout(self):
if self.timer.owned:
# Owned timers cannot change their voice channel, text channel, or manager role.
await asyncio.gather(
self.refresh_edit_button(),
self.refresh_close_button(),
self.refresh_voice_button(),
self.refresh_delete_button(),
)
self.set_layout(
(self.edit_button, self.voice_button, self.close_button)
)
else:
await asyncio.gather(
self.refresh_edit_button(),
self.refresh_close_button(),
self.refresh_voice_button(),
self.refresh_delete_button(),
# self.refresh_voice_menu(),
self.refresh_manage_role_menu(),
self.refresh_notification_menu()
)
self.set_layout(
# (self.voice_menu,),
(self.notification_menu,),
(self.manage_role_menu,),
(self.edit_button, self.voice_button, self.delete_button, self.close_button)
)
async def reload(self):
"""
Nothing to reload.
"""
pass