import os import logging from typing import Any from dotenv import load_dotenv logger = logging.getLogger(__name__) class ConfigError(Exception): """Ошибка валидации конфигурации""" pass class Config: def __init__(self): self._config_data = self._load() logger.info("Конфигурация успешно загружена.") def _load_replies(self, file_path: str = "storage/replies.txt"): script_ad_reply = [ o.strip() for o in str( "Скрипт для авто-раздачи :da: [ad][UNFURL]https://lolz.team/threads/10187517/[/UNFURL][/ad] || Сиськи, сливы 18+ [ad][UNFURL]https://lolz.team/threads/10187517/[/UNFURL][/ad] || Твой помощник для авто-раздачи :animeyessir: [ad][UNFURL]https://lolz.team/threads/10187517/[/UNFURL][/ad]" ).split(" || ") ] default_replies = [["✓"], script_ad_reply] try: reply_lines = [] with open(file_path, "r", encoding="utf-8") as replies: for line in replies: line = line.rstrip("\n") if not line: continue if " || " in line: options = [o.strip() for o in line.split(" || ")] else: options = [line] reply_lines.append(options) reply_lines.append(script_ad_reply) return reply_lines if reply_lines else default_replies except FileNotFoundError: return default_replies def _load_captcha(self, file_path: str = "storage/captcha.txt"): default_captcha = ( f"[CENTER][B]Напишите ответ:[/B]\n" f"сколько будет 20 + 20? [I][COLOR=rgb(80, 80, 78)](Капча меняется раз в месяц)[/COLOR][/I][/CENTER]" ).strip() try: captcha = "" with open(file_path, "r", encoding="utf-8") as replies: captcha = replies.read() return captcha.strip() if captcha else default_captcha except FileNotFoundError: return default_captcha def _load_captcha_answer(self): """Load captcha answer from environment variable (supports live reload)""" load_dotenv(override=True) # Reload .env file return os.getenv(key="CAPTCHA_ANSWER", default=None) def _load(self): load_dotenv() try: # Pyrogram api_id = int(os.getenv(key="API_ID", default=2040)) api_hash = os.getenv( key="API_HASH", default="b18441a1ff607e10a989891a5462e627" ) # Notification bot (aiogram) bot_token = os.getenv(key="BOT_TOKEN", default=None) admin_id_str = os.getenv(key="ADMIN_ID", default=None) admin_id = int(admin_id_str) if admin_id_str else None notify_admin = ( os.getenv(key="NOTIFY_ADMIN", default="true") ).lower() == "true" # Lolz - required fields lolz_token = os.getenv(key="LOLZ_TOKEN", default=None) if not lolz_token: raise ConfigError("LOLZ_TOKEN обязателен, но не установлен") lolz_thread_id_str = os.getenv(key="LOLZ_THREAD_ID", default=None) if not lolz_thread_id_str: raise ConfigError("LOLZ_THREAD_ID обязателен, но не установлен") try: lolz_thread_id = int(lolz_thread_id_str) except (ValueError, TypeError) as e: raise ConfigError( f"LOLZ_THREAD_ID должен быть целым числом: {lolz_thread_id_str}" ) from e # Stars stars_count = int(os.getenv(key="STARS_COUNT", default=1)) # Misc default_choice = int(os.getenv(key="DEFAULT_CHOICE", default=2)) start_page = max(int(os.getenv(key="START_PAGE", default=1)), 1) check_interval = max( float(os.getenv(key="CHECK_INTERVAL", default=30)), 0.2 ) # 300r/60s для GET запросов, 60/300 = минимум api_delay = max( float(os.getenv(key="API_DELAY", default=5)), 2 ) # 30r/60s для non-GET запросов, 60/30 = минимум max_retries = int(os.getenv(key="MAX_RETRIES", default=3)) enable_reply = ( os.getenv(key="ENABLE_REPLY", default="true") ).lower() == "true" skip_commented = ( os.getenv(key="SKIP_COMMENTED", default="true") ).lower() == "true" enable_auto_bump = ( os.getenv(key="ENABLE_AUTO_BUMP", default="true") ).lower() == "true" bump_check_interval = max( float(os.getenv(key="BUMP_CHECK_INTERVAL", default=3600)), 3600 ) # 1 поднятие/3600с (1ч) = минимум enable_captcha = ( os.getenv(key="ENABLE_CAPTCHA", default="true") ).lower() == "true" captcha_answer = os.getenv(key="CAPTCHA_ANSWER", default=None) captcha_check_interval = max( float(os.getenv(key="CAPTCHA_CHECK_INTERVAL", default=1800)), 300 ) # проверка обновления капчи, минимум 300с (5 минут) # Validate captcha configuration if enable_captcha and not captcha_answer: raise ConfigError( "ENABLE_CAPTCHA установлен, но CAPTCHA_ANSWER не задан" ) return { # Pyrogram "api_id": api_id, "api_hash": api_hash, # Notification bot (aiogram) "bot_token": bot_token, "admin_id": admin_id, "notify_admin": notify_admin, # Lolz "lolz_token": lolz_token, "lolz_thread_id": lolz_thread_id, # Stars "stars_count": stars_count, # Posts "start_page": start_page, "enable_reply": enable_reply, "check_interval": check_interval, "skip_commented": skip_commented, # Misc "api_delay": api_delay, "max_retries": max_retries, "default_choice": default_choice, # Bump "enable_auto_bump": enable_auto_bump, "bump_check_interval": bump_check_interval, # Captcha "enable_captcha": enable_captcha, "captcha_answer": captcha_answer, "captcha_check_interval": captcha_check_interval, } except ConfigError: raise except (ValueError, TypeError) as e: logger.error(f"Invalid configuration value: {e}") raise ConfigError(f"Invalid configuration value: {e}") from e except Exception as e: logger.error(f"Unexpected error loading configuration: {e}") raise ConfigError(f"Failed to load configuration: {e}") from e def __getattr__(self, name: str) -> Any: return self._config_data.get( name, True if name == "skip_posts_with_comments" else None )