mirror of
https://github.com/Paillat-dev/discord-progress-bar.git
synced 2026-01-02 09:06:21 +00:00
First Commit
This commit is contained in:
13
src/discord_progress_bar/__init__.py
Normal file
13
src/discord_progress_bar/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright (c) Paillat-dev
|
||||
# SPDX-License-Identifier: MIT
|
||||
from .progress_bar import ProgressBar, ProgressBarManager
|
||||
from .types import ProgressBarEmojiMapping, ProgressBarPart, ProgressBarPathMapping, ProgressBarUrlMapping
|
||||
|
||||
__all__ = (
|
||||
"ProgressBar",
|
||||
"ProgressBarEmojiMapping",
|
||||
"ProgressBarManager",
|
||||
"ProgressBarPart",
|
||||
"ProgressBarPathMapping",
|
||||
"ProgressBarUrlMapping",
|
||||
)
|
||||
16
src/discord_progress_bar/default_bars.py
Normal file
16
src/discord_progress_bar/default_bars.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) Paillat-dev
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from .types import ProgressBarPart, ProgressBarUrlMapping
|
||||
|
||||
DEFAULT_PROGRESS_BARS: dict[str, ProgressBarUrlMapping] = {
|
||||
"green": {
|
||||
# From https://emoji.gg/user/ravenastar
|
||||
ProgressBarPart.LEFT_EMPTY: "https://cdn3.emoji.gg/emojis/5499-lb2-g.png",
|
||||
ProgressBarPart.LEFT_FILLED: "https://cdn3.emoji.gg/emojis/5988-lb-g.png",
|
||||
ProgressBarPart.MIDDLE_EMPTY: "https://cdn3.emoji.gg/emojis/2827-l2-g.png",
|
||||
ProgressBarPart.MIDDLE_FILLED: "https://cdn3.emoji.gg/emojis/3451-l-g.png",
|
||||
ProgressBarPart.RIGHT_EMPTY: "https://cdn3.emoji.gg/emojis/2881-lb3-g.png",
|
||||
ProgressBarPart.RIGHT_FILLED: "https://cdn3.emoji.gg/emojis/3166-lb4-g.png",
|
||||
}
|
||||
}
|
||||
115
src/discord_progress_bar/progress_bar.py
Normal file
115
src/discord_progress_bar/progress_bar.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# Copyright (c) Paillat-dev
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from collections import defaultdict
|
||||
from typing import Self, cast
|
||||
|
||||
import aiofile
|
||||
import aiohttp
|
||||
import discord
|
||||
|
||||
from .default_bars import DEFAULT_PROGRESS_BARS
|
||||
from .types import ProgressBarEmojiMapping, ProgressBarPart, ProgressBarPathMapping, ProgressBarUrlMapping
|
||||
|
||||
|
||||
class ProgressBar:
|
||||
def __init__(self, mapping: ProgressBarEmojiMapping, length: int = 5) -> None:
|
||||
if length < 2:
|
||||
raise ValueError("Length must be at least 2.")
|
||||
self._mapping: ProgressBarEmojiMapping = mapping
|
||||
self._length: int = length
|
||||
|
||||
def full(self) -> str:
|
||||
return "".join(
|
||||
[
|
||||
str(self._mapping[ProgressBarPart.LEFT_FILLED])
|
||||
+ str(self._mapping[ProgressBarPart.MIDDLE_FILLED]) * (self._length - 2)
|
||||
+ str(self._mapping[ProgressBarPart.RIGHT_FILLED])
|
||||
]
|
||||
)
|
||||
|
||||
def empty(self) -> str:
|
||||
return "".join(
|
||||
[
|
||||
str(self._mapping[ProgressBarPart.LEFT_EMPTY])
|
||||
+ str(self._mapping[ProgressBarPart.MIDDLE_EMPTY]) * (self._length - 2)
|
||||
+ str(self._mapping[ProgressBarPart.RIGHT_EMPTY])
|
||||
]
|
||||
)
|
||||
|
||||
def partial(self, percent: float) -> str:
|
||||
if percent < 0 or percent > 1:
|
||||
raise ValueError("Percent must be between 0 and 1.")
|
||||
filled_length = round(self._length * percent)
|
||||
empty_length = self._length - filled_length
|
||||
|
||||
# Handle edge cases
|
||||
if filled_length == 0:
|
||||
return self.empty()
|
||||
if filled_length == self._length:
|
||||
return self.full()
|
||||
|
||||
# For partial progress, ensure we have the correct number of middle emojis
|
||||
middle_filled_count = max(0, filled_length - 1) # At least one for left filled
|
||||
middle_empty_count = max(0, empty_length - 1) # At least one for right empty
|
||||
|
||||
return "".join(
|
||||
[
|
||||
str(self._mapping[ProgressBarPart.LEFT_FILLED])
|
||||
+ str(self._mapping[ProgressBarPart.MIDDLE_FILLED]) * middle_filled_count
|
||||
+ str(self._mapping[ProgressBarPart.MIDDLE_EMPTY]) * middle_empty_count
|
||||
+ str(self._mapping[ProgressBarPart.RIGHT_EMPTY])
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class ProgressBarManager:
|
||||
def __init__(self, bot: discord.Bot) -> None:
|
||||
self._bot: discord.Bot = bot
|
||||
self._emojis: dict[str, ProgressBarEmojiMapping] = defaultdict(dict)
|
||||
|
||||
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:
|
||||
await self._bot.wait_until_ready()
|
||||
for emoji in self._bot.app_emojis:
|
||||
if emoji.name.startswith("pb_"):
|
||||
key: str = emoji.name.split("_")[1]
|
||||
part: ProgressBarPart = cast("ProgressBarPart", int(emoji.name.split("_")[2]))
|
||||
self._emojis[key][part] = emoji
|
||||
|
||||
async def create_emojis_from_urls(self, name: str, emojis: ProgressBarUrlMapping) -> None:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
for key, url in emojis.items():
|
||||
emoji_name = f"pb_{name}_{key}"
|
||||
response = await session.get(url)
|
||||
response.raise_for_status()
|
||||
image = await response.read()
|
||||
emoji = await self._bot.create_emoji(
|
||||
name=emoji_name,
|
||||
image=image,
|
||||
)
|
||||
self._emojis[name][key] = emoji
|
||||
|
||||
async def create_emojis_from_files(self, name: str, emojis: ProgressBarPathMapping) -> None:
|
||||
for key, path in emojis.items():
|
||||
emoji_name = f"pb_{name}_{key}"
|
||||
async with aiofile.async_open(path, "rb") as f:
|
||||
image = await f.read()
|
||||
emoji = await self._bot.create_emoji(
|
||||
name=emoji_name,
|
||||
image=image,
|
||||
)
|
||||
self._emojis[name][key] = emoji
|
||||
|
||||
async def progress_bar(self, name: str, *, length: int = 5) -> ProgressBar:
|
||||
if name not in self._emojis:
|
||||
if default := DEFAULT_PROGRESS_BARS.get(name):
|
||||
await self.create_emojis_from_urls(name, default)
|
||||
else:
|
||||
raise ValueError(f"Progress bar {name} not found.")
|
||||
return ProgressBar(self._emojis[name], length=length)
|
||||
0
src/discord_progress_bar/py.typed
Normal file
0
src/discord_progress_bar/py.typed
Normal file
27
src/discord_progress_bar/types.py
Normal file
27
src/discord_progress_bar/types.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# ruff: noqa: A005
|
||||
# Copyright (c) Paillat-dev
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import pathlib
|
||||
from enum import IntEnum
|
||||
|
||||
import discord
|
||||
|
||||
|
||||
class ProgressBarPart(IntEnum):
|
||||
"""Enum for the different parts of a progress bar."""
|
||||
|
||||
LEFT_EMPTY = 0
|
||||
LEFT_FILLED = 1
|
||||
MIDDLE_EMPTY = 2
|
||||
MIDDLE_FILLED = 3
|
||||
RIGHT_EMPTY = 4
|
||||
RIGHT_FILLED = 5
|
||||
|
||||
|
||||
type ProgressBarUrlMapping = dict[ProgressBarPart, str]
|
||||
type ProgressBarPathMapping = dict[ProgressBarPart, pathlib.Path]
|
||||
type ProgressBarEmojiMapping = dict[ProgressBarPart, discord.AppEmoji]
|
||||
Reference in New Issue
Block a user