# Правила для модулей провайдеров

Эти рекомендации распространяются на все файлы в `ioc_checker/providers`. Они описывают, как добавлять новых провайдеров, какие структуры уже используются и чего следует избегать.

## Общие принципы
- Следуем ООП: каждый провайдер — отдельный класс, наследник `BaseProvider` с определённым `name`, флагом `requires_token` и опциональным `token_name`.
- Вся техническая коммуникация (логи, сообщения, комментарии, строки UI) — **только на русском языке**.
- Минимум защитных проверок типов: опираемся на корректность кода, а не на валидацию входных данных.
- Для HTTP-запросов используем таймауты/прокси из `BaseProvider.build_http_client_kwargs`, контексты через `get_context` и общий HTTP-клиент из провайдера.
- Токены берутся из настроек через `get_provider_token`; сам провайдер не лезет в окружение напрямую.

## Структура нового провайдера
1. Создаём модуль `xxx.py` и класс `XXXProvider(BaseProvider)`.
2. Переопределяем:
   - `get_context` — вернуть асинхронный контекст, если нужен HTTP-клиент или инициализация; иначе используем базовый пустой.
   - `fetch` — только получение «сырого» ответа внешнего сервиса (без построения фич).
   - `build_features` — чистое преобразование ответа `fetch` в словарь фич. Не делаем внешних запросов, только расчёты.
3. Регистрируем провайдер в `PROVIDERS` внутри `providers/__init__.py`.
4. Если провайдер требует токен, задаём `requires_token=True` и `token_name`, чтобы настройки могли его найти.

## Паттерны
- Разделяем ответственность: `fetch` не должен строить фичи или доп. обогащения — всё превращаем в фичи в `build_features` или через общие хелперы (`fetch_provider_enrichment`).
- Для разбора ответов используем утилиты из `providers/utils.py` (`ensure_mapping`, `normalize_text`, `parse_date`, `calculate_days_delta` и т. д.), чтобы избежать дублирования.
- Логируем ключевые шаги (`fetch`, построение фич, ошибки внешних сервисов) через `logging.getLogger(__name__)`.
- Контексты HTTP-клиентов закрываем автоматически (`async with provider.get_context(...)`), не оставляем незакрытых соединений.
- Возвращаем значения в виде обычных словарей и примитивов (`FeatureValue`), избегаем кастомных структур.

## Антипаттерны
- Не смешиваем сетевые вызовы с построением фич: не вызываем внешние API внутри `build_features` и не считаем фичи внутри `fetch`.
- Не добавляем избыточные try/except вокруг каждой операции: ловим только там, где нужно добавить осмысленный лог и продолжить выполнение.
- Не дублируем уже существующие помощники для нормализации/парсинга данных.
- Не подменяем конфигурацию токенов/таймаутов прямыми обращениями к переменным окружения или константам — всё через `settings` и методы базового класса.
- Не оставляем комментарии/логи на английском и не добавляем проверки типов ради «безопасности».
