Spaces:
Sleeping
Sleeping
| """ | |
| Централизованная конфигурация | |
| """ | |
| import json | |
| import os | |
| from pathlib import Path | |
| from typing import Any, Optional | |
| from dataclasses import dataclass, field, asdict | |
| from dotenv import load_dotenv | |
| # Load .env from autoreg directory (without override - VS Code extension env vars take priority) | |
| _autoreg_dir = Path(__file__).parent.parent | |
| _env_file = _autoreg_dir / '.env' | |
| if _env_file.exists(): | |
| load_dotenv(_env_file, override=False) | |
| else: | |
| load_dotenv(override=False) # Fallback to current directory | |
| class BrowserConfig: | |
| """Настройки браузера""" | |
| headless: bool = False | |
| incognito: bool = True | |
| slow_mo: int = 0 | |
| devtools: bool = False | |
| screenshots_on_error: bool = True | |
| realistic_typing: bool = True # Реалистичный ввод для обхода FWCIM | |
| human_delays: bool = True # Человеческие задержки между шагами регистрации | |
| delay_multiplier: float = 1.0 # Множитель задержек (0.5 = быстрее, 2.0 = медленнее) | |
| proxy: Optional[str] = None # Прокси в формате "http://host:port" или "socks5://host:port" | |
| proxy_auth: Optional[tuple[str, str]] = None # (username, password) для прокси | |
| class RegistrationConfig: | |
| """Настройки регистрации""" | |
| email_domain: str = '' | |
| email_prefix: str = 'kiro_auto' | |
| default_name: str = 'Kiro User' | |
| auto_inject_to_kiro: bool = True | |
| class TimeoutsConfig: | |
| """Таймауты (оптимизированы для скорости)""" | |
| page_load: int = 2 # Уменьшено с 3 | |
| element_wait: int = 1 # Уменьшено с 2 | |
| verification_code: int = 60 # Уменьшено с 90 | |
| oauth_callback: int = 20 # Уменьшено с 30 | |
| between_accounts: int = 1 # Уменьшено с 2 | |
| imap_poll_interval: int = 1 # Уменьшено с 2 | |
| api_request: int = 20 # Уменьшено с 30 | |
| # Новые таймауты для умных ожиданий | |
| element_poll_interval: float = 0.1 # Интервал проверки элементов | |
| page_transition: int = 3 # Ожидание перехода между страницами | |
| # AWS redirect таймауты (AWS может долго проверять) | |
| password_redirect: int = 60 # Ожидание редиректа после ввода пароля (AWS проверяет fingerprint) | |
| allow_access_wait: int = 90 # Ожидание страницы Allow access | |
| class ImapConfig: | |
| """Настройки IMAP""" | |
| host: str = 'imap.yandex.ru' | |
| port: int = 993 | |
| use_ssl: bool = True | |
| email: str = '' | |
| password: str = '' | |
| class QuotaConfig: | |
| """Настройки мониторинга квот""" | |
| auto_refresh_tokens: bool = True | |
| refresh_interval_minutes: int = 30 | |
| warn_threshold_percent: int = 80 | |
| critical_threshold_percent: int = 95 | |
| class MachineIdConfig: | |
| """Настройки Machine ID""" | |
| auto_reset_on_switch: bool = False | |
| backup_before_reset: bool = True | |
| class DebugConfig: | |
| """Настройки отладки""" | |
| verbose: bool = False | |
| save_html_on_error: bool = False | |
| pause_on_error: bool = False | |
| log_api_responses: bool = False | |
| class Config: | |
| """Главный конфиг""" | |
| browser: BrowserConfig = field(default_factory=BrowserConfig) | |
| registration: RegistrationConfig = field(default_factory=RegistrationConfig) | |
| timeouts: TimeoutsConfig = field(default_factory=TimeoutsConfig) | |
| imap: ImapConfig = field(default_factory=ImapConfig) | |
| quota: QuotaConfig = field(default_factory=QuotaConfig) | |
| machine_id: MachineIdConfig = field(default_factory=MachineIdConfig) | |
| debug: DebugConfig = field(default_factory=DebugConfig) | |
| def to_dict(self) -> dict: | |
| """Конвертирует в словарь""" | |
| return asdict(self) | |
| def from_dict(cls, data: dict) -> 'Config': | |
| """Создаёт из словаря""" | |
| return cls( | |
| browser=BrowserConfig(**data.get('browser', {})), | |
| registration=RegistrationConfig(**data.get('registration', {})), | |
| timeouts=TimeoutsConfig(**data.get('timeouts', {})), | |
| imap=ImapConfig(**data.get('imap', {})), | |
| quota=QuotaConfig(**data.get('quota', {})), | |
| machine_id=MachineIdConfig(**data.get('machine_id', {})), | |
| debug=DebugConfig(**data.get('debug', {})) | |
| ) | |
| def save(self, path: Path): | |
| """Сохраняет конфиг в файл""" | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False)) | |
| def load(cls, path: Path) -> 'Config': | |
| """Загружает конфиг из файла""" | |
| if not path.exists(): | |
| return cls() | |
| try: | |
| data = json.loads(path.read_text()) | |
| return cls.from_dict(data) | |
| except Exception: | |
| return cls() | |
| def get(self, path: str, default: Any = None) -> Any: | |
| """ | |
| Получает значение по пути (dot notation) | |
| Пример: config.get('browser.headless') | |
| """ | |
| keys = path.split('.') | |
| value = self.to_dict() | |
| for key in keys: | |
| if isinstance(value, dict) and key in value: | |
| value = value[key] | |
| else: | |
| return default | |
| return value | |
| def set(self, path: str, value: Any): | |
| """ | |
| Устанавливает значение по пути (dot notation) | |
| Пример: config.set('browser.headless', True) | |
| """ | |
| keys = path.split('.') | |
| # Получаем нужный объект | |
| obj = self | |
| for key in keys[:-1]: | |
| obj = getattr(obj, key) | |
| setattr(obj, keys[-1], value) | |
| # ============================================================================ | |
| # Singleton | |
| # ============================================================================ | |
| _config: Optional[Config] = None | |
| _config_path: Optional[Path] = None | |
| def get_config() -> Config: | |
| """Получить singleton instance Config""" | |
| global _config, _config_path | |
| if _config is None: | |
| from .paths import get_paths | |
| paths = get_paths() | |
| _config_path = paths.settings_file | |
| _config = Config.load(_config_path) | |
| # Загружаем IMAP из env | |
| _config.imap.email = os.getenv('IMAP_USER', os.getenv('IMAP_EMAIL', '')) | |
| _config.imap.password = os.getenv('IMAP_PASSWORD', '') | |
| _config.imap.host = os.getenv('IMAP_SERVER', os.getenv('IMAP_HOST', 'imap.yandex.ru')) | |
| return _config | |
| def save_config(): | |
| """Сохранить текущий конфиг""" | |
| global _config, _config_path | |
| if _config and _config_path: | |
| _config.save(_config_path) | |
| def reset_config(): | |
| """Сбросить конфиг к дефолтным значениям""" | |
| global _config, _config_path | |
| _config = Config() | |
| if _config_path: | |
| _config.save(_config_path) | |