Refactor async initialization in ProgressBarManager (#16)

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Paillat <me@paillat.dev>
This commit is contained in:
nicebots-xyz-bot
2025-06-05 16:11:42 +02:00
committed by GitHub
parent 6e36674473
commit 26f0f50aab
3 changed files with 60 additions and 27 deletions

View File

@@ -79,24 +79,21 @@ from discord_progress_bar import ProgressBarManager
# Create a Discord bot with emoji caching enabled # Create a Discord bot with emoji caching enabled
bot = discord.Bot(cache_app_emojis=True) bot = discord.Bot(cache_app_emojis=True)
progress_bar_manager = None # Create the progress bar manager (doesn't load emojis yet)
progress_bar = None progress_manager = ProgressBarManager(bot)
@bot.event @bot.event
async def on_ready(): async def on_ready():
"""Initialize the ProgressBarManager when the bot is ready.""" """Load emojis when the bot is ready."""
global progress_bar_manager, progress_bar await progress_manager.load()
print(f"Logged in as {bot.user}")
# Initialize the progress bar manager @bot.command()
progress_bar_manager = await ProgressBarManager(bot)
# Get a progress bar with the "green" style
progress_bar = await progress_bar_manager.progress_bar("green")
@bot.slash_command()
async def show_progress(ctx, percent: float = 0.5): async def show_progress(ctx, percent: float = 0.5):
"""Display a progress bar with the specified percentage.""" """Display a progress bar with the specified percentage."""
await ctx.respond(f"Progress: {progress_bar.partial(percent)}") progress_bar = await progress_manager.progress_bar("green", length=10)
await ctx.send(f"Progress: {progress_bar.partial(percent)}")
# Run your bot # Run your bot
bot.run("YOUR_TOKEN") bot.run("YOUR_TOKEN")
@@ -233,14 +230,22 @@ This is required for the `ProgressBarManager` to properly load and manage emojis
### Progress Bar Manager Initialization ### Progress Bar Manager Initialization
Initialize the `ProgressBarManager` after your bot is ready: Create a `ProgressBarManager` instance when your bot starts, and load emojis in the
`on_ready` event:
```python ```python
@discord.Cog.listener() # Create the manager (doesn't load emojis yet)
async def on_ready(self) -> None: progress_manager = ProgressBarManager(bot)
self.progress_bar_manager = await ProgressBarManager(self.bot)
@bot.event
async def on_ready():
"""Load emojis from the server after the bot is ready."""
await progress_manager.load()
``` ```
This approach ensures that your bot can properly initialize and load emojis once it's
connected to Discord.
### Custom Progress Bar Styles ### Custom Progress Bar Styles
You can create custom progress bar styles by providing your own emoji images: You can create custom progress bar styles by providing your own emoji images:

View File

@@ -17,22 +17,27 @@ bot = discord.Bot(cache_app_emojis=True)
class MyCog(discord.Cog): class MyCog(discord.Cog):
def __init__(self, bot: discord.Bot) -> None: def __init__(self, bot: discord.Bot) -> None:
self.bot: discord.Bot = bot self.bot: discord.Bot = bot
self.progress_bar_manager: ProgressBarManager self.progress_bar_manager: ProgressBarManager = ProgressBarManager(self.bot)
self.progress_bar: ProgressBar self.progress_bar: ProgressBar | None = None
@discord.Cog.listener() @discord.Cog.listener()
async def on_ready(self) -> None: async def on_ready(self) -> None:
print(f"Logged in as {bot.user} (ID: {bot.user.id})") # pyright: ignore [reportOptionalMemberAccess] print(f"Logged in as {bot.user} (ID: {bot.user.id})") # pyright: ignore [reportOptionalMemberAccess]
print("------") print("------")
self.progress_bar_manager = await ProgressBarManager(self.bot) await self.progress_bar_manager.load()
self.progress_bar = await self.progress_bar_manager.progress_bar("green", length=10) self.progress_bar = await self.progress_bar_manager.progress_bar("green", length=10)
print("Progress bar manager loaded.") print("Progress bar manager loaded.")
@discord.slash_command() # pyright: ignore [reportUntypedFunctionDecorator] @discord.slash_command() # pyright: ignore [reportUntypedFunctionDecorator]
async def get_progress_bar(self, ctx: discord.ApplicationContext, percent: float | None = None) -> None: async def get_progress_bar(self, ctx: discord.ApplicationContext, percent: float | None = None) -> None:
"""Send a progress bar message.""" """Send a progress bar message."""
if percent is None: if not self.progress_bar:
await ctx.respond("Progress bar manager is not loaded yet.")
return
if percent is None or percent > 1:
percent = 1 percent = 1
await ctx.respond(f"Progress: {self.progress_bar.partial(percent)}") await ctx.respond(f"Progress: {self.progress_bar.partial(percent)}")

View File

@@ -2,7 +2,7 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
from collections import defaultdict from collections import defaultdict
from typing import Self, cast from typing import cast
import aiofile import aiofile
import aiohttp import aiohttp
@@ -67,20 +67,25 @@ class ProgressBarManager:
def __init__(self, bot: discord.Bot) -> None: def __init__(self, bot: discord.Bot) -> None:
self._bot: discord.Bot = bot self._bot: discord.Bot = bot
self._emojis: dict[str, ProgressBarEmojiMapping] = defaultdict(dict) self._emojis: dict[str, ProgressBarEmojiMapping] = defaultdict(dict)
self._loaded: bool = False
async def __new__(cls, bot: discord.Bot) -> Self:
obj = super().__new__(cls)
obj.__init__(bot)
await obj.load()
return obj
async def load(self) -> None: async def load(self) -> None:
await self._bot.wait_until_ready() """Load emojis from the Discord server.
This method should be called after the bot is ready.
Typically called in your bot's on_ready event.
"""
for emoji in self._bot.app_emojis: for emoji in self._bot.app_emojis:
if emoji.name.startswith("pb_"): if emoji.name.startswith("pb_"):
key: str = emoji.name.split("_")[1] key: str = emoji.name.split("_")[1]
part: ProgressBarPart = cast("ProgressBarPart", int(emoji.name.split("_")[2])) part: ProgressBarPart = cast("ProgressBarPart", int(emoji.name.split("_")[2]))
self._emojis[key][part] = emoji self._emojis[key][part] = emoji
self._loaded = True
@property
def loaded(self) -> bool:
"""Check if the manager has been loaded."""
return self._loaded
async def create_emojis_from_urls(self, name: str, emojis: ProgressBarUrlMapping) -> None: async def create_emojis_from_urls(self, name: str, emojis: ProgressBarUrlMapping) -> None:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
@@ -107,9 +112,27 @@ class ProgressBarManager:
self._emojis[name][key] = emoji self._emojis[name][key] = emoji
async def progress_bar(self, name: str, *, length: int = 5) -> ProgressBar: async def progress_bar(self, name: str, *, length: int = 5) -> ProgressBar:
"""Get a progress bar by name.
Args:
name: The name of the progress bar.
length: The length of the progress bar (default is 5).
Returns:
A ProgressBar instance with the specified emojis.
Raises:
ValueError: If the progress bar is not found
RuntimeError: If the manager is not loaded.
"""
if not self.loaded:
raise RuntimeError("ProgressBarManager has not been loaded yet. Call load() first.")
if name not in self._emojis: if name not in self._emojis:
if default := DEFAULT_PROGRESS_BARS.get(name): if default := DEFAULT_PROGRESS_BARS.get(name):
await self.create_emojis_from_urls(name, default) await self.create_emojis_from_urls(name, default)
else: else:
raise ValueError(f"Progress bar {name} not found.") raise ValueError(f"Progress bar {name} not found.")
return ProgressBar(self._emojis[name], length=length) return ProgressBar(self._emojis[name], length=length)