""" AnyAPIClient - Cliente centralizado para a API AnyAPI (anyapi.io) Gerencia autenticação, rotação de contas e chamadas HTTP. """ import requests import logging from typing import Any, Dict, Optional from datetime import datetime logger = logging.getLogger(__name__) _BASE_URL = "https://api.anyapi.io/v1" class AnyAPIClient: """Cliente HTTP para AnyAPI com rotação automática de chaves.""" _instance: Optional["AnyAPIClient"] = None def __new__(cls) -> "AnyAPIClient": if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self) -> None: if self._initialized: return self._initialized = True self._rotation = None self._session = requests.Session() self._session.headers.update({ "Content-Type": "application/json", "User-Agent": "AKIRA-SOFTEDGE/21" }) logger.info("AnyAPIClient inicializado") @property def rotation(self): if self._rotation is None: from modules.anyapi_rotation import get_anyapi_rotation self._rotation = get_anyapi_rotation() return self._rotation def get_raw(self, endpoint: str, params: Optional[Dict[str, Any]] = None, timeout: int = 10) -> Dict[str, Any]: """ Realiza GET na AnyAPI e retorna conteúdo binário (imagem, PDF, etc). Args: endpoint: caminho relativo (ex: '/v1/screenshot') params: query params timeout: timeout em segundos Returns: dict com 'sucesso', 'dados' (bytes do conteúdo binário) e metadados """ api_key = self.rotation.get_current_api_key() if not api_key: return self._error_response("Nenhuma chave AnyAPI disponível") url = f"{_BASE_URL}{endpoint}" query = params or {} query["apikey"] = api_key try: resp = self._session.get(url, params=query, timeout=timeout) if resp.status_code == 429: rotated = self.rotation.handle_rate_limit_error() if rotated: return self.get_raw(endpoint, params, timeout) return self._error_response("Rate limit em todas as contas AnyAPI") resp.raise_for_status() return { "sucesso": True, "dados": resp.content, "content_type": resp.headers.get("Content-Type", "application/octet-stream"), "provider": "anyapi", "account": self.rotation.get_current_account_name(), "timestamp": datetime.now().isoformat() } except requests.exceptions.Timeout: logger.warning(f"AnyAPI timeout: {endpoint}") return self._error_response(f"Timeout ao chamar {endpoint}") except requests.exceptions.ConnectionError: logger.error(f"AnyAPI connection error: {endpoint}") return self._error_response("Erro de conexão com AnyAPI") except requests.exceptions.HTTPError as e: logger.error(f"AnyAPI HTTP error: {e}") return self._error_response(f"Erro HTTP: {e.response.status_code}") except Exception as e: logger.error(f"AnyAPI erro inesperado: {type(e).__name__}: {e}") return self._error_response(str(e)) def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None, timeout: int = 10) -> Dict[str, Any]: """ Realiza GET na AnyAPI. Args: endpoint: caminho relativo (ex: '/v1/email') params: query params timeout: timeout em segundos Returns: dict com 'sucesso', 'dados' e metadados """ api_key = self.rotation.get_current_api_key() if not api_key: return self._error_response("Nenhuma chave AnyAPI disponível") url = f"{_BASE_URL}{endpoint}" query = params or {} query["apikey"] = api_key try: resp = self._session.get(url, params=query, timeout=timeout) if resp.status_code == 429: rotated = self.rotation.handle_rate_limit_error() if rotated: return self.get(endpoint, params, timeout) return self._error_response("Rate limit em todas as contas AnyAPI") resp.raise_for_status() data = resp.json() return { "sucesso": True, "dados": data, "provider": "anyapi", "account": self.rotation.get_current_account_name(), "timestamp": datetime.now().isoformat() } except requests.exceptions.Timeout: logger.warning(f"AnyAPI timeout: {endpoint}") return self._error_response(f"Timeout ao chamar {endpoint}") except requests.exceptions.ConnectionError: logger.error(f"AnyAPI connection error: {endpoint}") return self._error_response("Erro de conexão com AnyAPI") except requests.exceptions.HTTPError as e: logger.error(f"AnyAPI HTTP error: {e}") return self._error_response(f"Erro HTTP: {e.response.status_code}") except Exception as e: logger.error(f"AnyAPI erro inesperado: {type(e).__name__}: {e}") return self._error_response(str(e)) def _error_response(self, message: str) -> Dict[str, Any]: return { "sucesso": False, "erro": message, "provider": "anyapi", "account": self.rotation.get_current_account_name() if self.rotation else "nenhuma", "timestamp": datetime.now().isoformat() }