feat: Implement rank refresh.

This commit is contained in:
2023-08-24 14:30:03 +03:00
parent df9b835cd5
commit 9b2af56d64
6 changed files with 571 additions and 3 deletions

View File

@@ -2,3 +2,4 @@ from .editor import RankEditor
from .preview import RankPreviewUI
from .overview import RankOverviewUI
from .config import RankConfigUI
from .refresh import RankRefreshUI

View File

@@ -98,7 +98,8 @@ class RankOverviewUI(MessageUI):
Refresh the current ranks,
ensuring that all members have the correct rank.
"""
await press.response.send_message("Not Implemented Yet")
cog = self.bot.get_cog('RankCog')
await cog.interactive_rank_refresh(press, self.guild)
async def refresh_button_refresh(self):
self.refresh_button.label = self.bot.translator.t(_p(

View File

@@ -0,0 +1,259 @@
from typing import Optional
import asyncio
import discord
from discord.ui.select import select, Select, SelectOption, RoleSelect
from discord.ui.button import button, Button, ButtonStyle
from meta import conf, LionBot
from meta.logger import log_wrap
from core.data import RankType
from data import ORDER
from utils.ui import MessageUI
from utils.lib import MessageArgs, utc_now
from babel.translator import ctx_translator
from .. import babel, logger
from ..data import AnyRankData
from ..utils import rank_model_from_type, format_stat_range, stat_data_to_value
from .editor import RankEditor
from .preview import RankPreviewUI
_p = babel._p
class RankRefreshUI(MessageUI):
def __init__(self, bot: LionBot, guild: discord.Guild, **kwargs):
super().__init__(**kwargs)
self.bot = bot
self.guild = guild
self.stage_ranks = None
self.stage_members = None
self.stage_roles = None
self.stage_compute = None
self.to_remove = 0
self.to_add = 0
self.removed = 0
self.added = 0
self.error: Optional[str] = None
self.done = False
self.errors: list[str] = []
self._loop_task: Optional[asyncio.Task] = None
self._wakeup = asyncio.Event()
# ----- API -----
async def set_error(self, error: str):
"""
Set the given error, refresh, and stop.
"""
self.error = error
await self.refresh()
await self.close()
async def set_done(self):
self.done = True
await self.refresh()
await self.close()
def poke(self):
self._wakeup.set()
async def run(self, *args, **kwargs):
await super().run(*args, **kwargs)
self._loop_task = asyncio.create_task(self._refresh_loop(), name='refresh ui loop')
async def cleanup(self):
if self._loop_task and not self._loop_task.done():
self._loop_task.cancel()
await super().cleanup()
def progress_bar(self, value, minimum, maximum, width=10) -> str:
"""
Build a text progress bar representing `value` between `minimum` and `maximum`.
"""
emojis = self.bot.config.emojis
proportion = (value - minimum) / (maximum - minimum)
sections = min(max(int(proportion * width), 0), width)
bar = []
# Starting segment
bar.append(str(emojis.progress_left_empty) if sections == 0 else str(emojis.progress_left_full))
# Full segments up to transition or end
if sections >= 2:
bar.append(str(emojis.progress_middle_full) * (sections - 2))
# Transition, if required
if 1 < sections < width:
bar.append(str(emojis.progress_middle_transition))
# Empty sections up to end
if sections < width:
bar.append(str(emojis.progress_middle_empty) * (width - max(sections, 1) - 1))
# End section
bar.append(str(emojis.progress_right_empty) if sections < width else str(emojis.progress_right_full))
# Join all the sections together and return
return ''.join(bar)
@log_wrap('refresh ui loop')
async def _refresh_loop(self):
while True:
try:
await asyncio.sleep(1)
await self._wakeup.wait()
await self.refresh()
except asyncio.CancelledError:
break
# ----- UI Flow -----
async def make_message(self) -> MessageArgs:
t = self.bot.translator.t
errored = bool(self.error)
if errored:
waiting_emoji = self.bot.config.emojis.cancel
title = t(_p(
'ui:refresh_ranks|embed|title:errored',
"Could not refresh the server ranks!"
))
colour = discord.Colour.brand_red()
else:
waiting_emoji = self.bot.config.emojis.loading
if self.done:
title = t(_p(
'ui:refresh_ranks|embed|title:done',
"Rank refresh complete!"
))
colour = discord.Colour.brand_green()
else:
title = t(_p(
'ui:refresh_ranks|embed|title:working',
"Refreshing your server ranks, please wait."
))
colour = discord.Colour.orange()
embed = discord.Embed(
colour=colour,
title=title,
timestamp=utc_now()
)
lines = []
stop_here = False
if not stop_here:
stage = self.stage_ranks
emoji = self.bot.config.emojis.tick if stage else waiting_emoji
text = t(_p(
'ui:refresh_ranks|embed|line:ranks',
"**Loading server ranks:** {emoji}"
)).format(emoji=emoji)
lines.append(text)
stop_here = not bool(stage)
if not stop_here:
stage = self.stage_members
emoji = self.bot.config.emojis.tick if stage else waiting_emoji
text = t(_p(
'ui:refresh_ranks|embed|line:members',
"**Loading server members:** {emoji}"
)).format(emoji=emoji)
lines.append(text)
stop_here = not bool(stage)
if not stop_here:
stage = self.stage_roles
emoji = self.bot.config.emojis.tick if stage else waiting_emoji
text = t(_p(
'ui:refresh_ranks|embed|line:roles',
"**Loading rank roles:** {emoji}"
)).format(emoji=emoji)
lines.append(text)
stop_here = not bool(stage)
if not stop_here:
stage = self.stage_compute
emoji = self.bot.config.emojis.tick if stage else waiting_emoji
text = t(_p(
'ui:refresh_ranks|embed|line:compute',
"**Computing correct ranks:** {emoji}"
)).format(emoji=emoji)
lines.append(text)
stop_here = not bool(stage)
if not stop_here:
lines.append("")
if self.to_remove > self.removed and not errored:
# Still have members to remove, show loading bar
name = t(_p(
'ui:refresh_ranks|embed|field:remove|name',
"Removing invalid rank roles from members"
))
value = t(_p(
'ui:refresh_ranks|embed|field:remove|value',
"0 {progress} {total}"
)).format(
progress=self.progress_bar(self.removed, 0, self.to_remove),
total=self.to_remove,
)
embed.add_field(name=name, value=value, inline=False)
else:
emoji = self.bot.config.emojis.tick
text = t(_p(
'ui:refresh_ranks|embed|line:remove',
"**Removed invalid ranks:** {done}/{target}"
)).format(done=self.removed, target=self.to_remove)
lines.append(text)
if self.to_add > self.added and not errored:
# Still have members to add, show loading bar
name = t(_p(
'ui:refresh_ranks|embed|field:add|name',
"Giving members their rank roles"
))
value = t(_p(
'ui:refresh_ranks|embed|field:add|value',
"0 {progress} {total}"
)).format(
progress=self.progress_bar(self.added, 0, self.to_add),
total=self.to_add,
)
embed.add_field(name=name, value=value, inline=False)
else:
emoji = self.bot.config.emojis.tick
text = t(_p(
'ui:refresh_ranks|embed|line:add',
"**Updated member ranks:** {done}/{target}"
)).format(done=self.added, target=self.to_add)
lines.append(text)
embed.description = '\n'.join(lines)
if self.errors:
name = (
'ui:refresh_ranks|embed|field:errors|title',
"Issues"
)
value = '\n'.join(self.errors)
embed.add_field(name=name, value=value, inline=False)
if self.error:
name = (
'ui:refresh_ranks|embed|field:critical|title',
"Critical Error! Cannot complete refresh"
)
embed.add_field(name=name, value=self.error, inline=False)
return MessageArgs(embed=embed)
async def refresh_layout(self):
pass
async def reload(self):
pass