Files
echoes-of-the-ash/bot/utils.py
Joan 861f3b8a36 Add visual progress bars and refactor handler modules
- Implement visual HP/Stamina/XP bars using Unicode characters (██░)
- Refactor handlers.py (1308 → 377 lines) into specialized modules:
  * action_handlers.py - World interaction and status display
  * inventory_handlers.py - Inventory management
  * combat_handlers.py - Combat actions
  * profile_handlers.py - Character stats with visual bars
  * corpse_handlers.py - Looting system
  * pickup_handlers.py - Item collection
- Add utility functions: create_progress_bar(), format_stat_bar()
- Organize all documentation into docs/ structure
- Create comprehensive documentation index with navigation
- Add UI examples showing before/after visual improvements
2025-10-19 00:23:44 +02:00

119 lines
3.7 KiB
Python

"""
Utility functions and decorators for the bot.
"""
import os
import functools
import logging
from telegram import Update
from telegram.ext import ContextTypes
logger = logging.getLogger(__name__)
def create_progress_bar(current: int, maximum: int, length: int = 10, filled_char: str = "", empty_char: str = "") -> str:
"""
Create a visual progress bar.
Args:
current: Current value
maximum: Maximum value
length: Length of the bar in characters (default 10)
filled_char: Character for filled portion (default █)
empty_char: Character for empty portion (default ░)
Returns:
String representation of progress bar
Examples:
>>> create_progress_bar(75, 100)
"███████░░░"
>>> create_progress_bar(0, 100)
"░░░░░░░░░░"
>>> create_progress_bar(100, 100)
"██████████"
"""
if maximum <= 0:
return empty_char * length
percentage = min(1.0, max(0.0, current / maximum))
filled_length = int(length * percentage)
empty_length = length - filled_length
return filled_char * filled_length + empty_char * empty_length
def format_stat_bar(label: str, emoji: str, current: int, maximum: int, bar_length: int = 10) -> str:
"""
Format a stat (HP, Stamina, etc.) with visual progress bar.
Args:
label: Stat label (e.g., "HP", "Stamina")
emoji: Emoji to display (e.g., "❤️", "")
current: Current value
maximum: Maximum value
bar_length: Length of the progress bar
Returns:
Formatted string with bar and percentage
Examples:
>>> format_stat_bar("HP", "❤️", 75, 100)
"❤️ HP: ███████░░░ 75% (75/100)"
>>> format_stat_bar("Stamina", "", 50, 100)
"⚡ Stamina: █████░░░░░ 50% (50/100)"
"""
bar = create_progress_bar(current, maximum, bar_length)
percentage = int((current / maximum * 100)) if maximum > 0 else 0
return f"{emoji} {label}: {bar} {percentage}% ({current}/{maximum})"
def get_admin_ids():
"""Get the list of admin user IDs from environment variable."""
admin_ids_str = os.getenv("ADMIN_IDS", "")
if not admin_ids_str:
logger.warning("ADMIN_IDS not set in .env file. No admins configured.")
return set()
try:
# Parse comma-separated list of IDs
admin_ids = set(int(id.strip()) for id in admin_ids_str.split(",") if id.strip())
return admin_ids
except ValueError as e:
logger.error(f"Error parsing ADMIN_IDS: {e}")
return set()
def admin_only(func):
"""
Decorator that restricts command to admin users only.
Usage:
@admin_only
async def my_admin_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
...
"""
@functools.wraps(func)
async def wrapper(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs):
user_id = update.effective_user.id
admin_ids = get_admin_ids()
if user_id not in admin_ids:
await update.message.reply_html(
"🚫 <b>Access Denied</b>\n\n"
"This command is restricted to administrators only."
)
logger.warning(f"User {user_id} attempted to use admin command: {func.__name__}")
return
# User is admin, execute the command
return await func(update, context, *args, **kwargs)
return wrapper
def is_admin(user_id: int) -> bool:
"""Check if a user ID is an admin."""
admin_ids = get_admin_ids()
return user_id in admin_ids