# -*- coding: utf-8 -*- """ 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( "🚫 Access Denied\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