feat: Implement rank refresh.
This commit is contained in:
@@ -2,3 +2,4 @@ from .editor import RankEditor
|
||||
from .preview import RankPreviewUI
|
||||
from .overview import RankOverviewUI
|
||||
from .config import RankConfigUI
|
||||
from .refresh import RankRefreshUI
|
||||
|
||||
@@ -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(
|
||||
|
||||
259
src/modules/ranks/ui/refresh.py
Normal file
259
src/modules/ranks/ui/refresh.py
Normal 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
|
||||
Reference in New Issue
Block a user