Spaces:
Sleeping
Sleeping
File size: 7,379 Bytes
494c89b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
"""
Централизованная конфигурация
"""
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
@dataclass
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) для прокси
@dataclass
class RegistrationConfig:
"""Настройки регистрации"""
email_domain: str = ''
email_prefix: str = 'kiro_auto'
default_name: str = 'Kiro User'
auto_inject_to_kiro: bool = True
@dataclass
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
@dataclass
class ImapConfig:
"""Настройки IMAP"""
host: str = 'imap.yandex.ru'
port: int = 993
use_ssl: bool = True
email: str = ''
password: str = ''
@dataclass
class QuotaConfig:
"""Настройки мониторинга квот"""
auto_refresh_tokens: bool = True
refresh_interval_minutes: int = 30
warn_threshold_percent: int = 80
critical_threshold_percent: int = 95
@dataclass
class MachineIdConfig:
"""Настройки Machine ID"""
auto_reset_on_switch: bool = False
backup_before_reset: bool = True
@dataclass
class DebugConfig:
"""Настройки отладки"""
verbose: bool = False
save_html_on_error: bool = False
pause_on_error: bool = False
log_api_responses: bool = False
@dataclass
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)
@classmethod
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))
@classmethod
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)
|