""" AMO CRM API Client Simple client for fetching data from AMO CRM API """ import httpx import asyncio from typing import Dict, Any, List, Optional from utils.config import settings class AmoCRMClient: def __init__(self, domain: Optional[str] = None, access_token: Optional[str] = None): self.domain = domain or settings.AMO_CRM_DOMAIN self.access_token = access_token or settings.AMO_CRM_ACCESS_TOKEN self.base_url = f"https://{self.domain}/api/v4" self.headers = { "Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json", } async def _make_request(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: """Make HTTP request to AMO CRM API""" async with httpx.AsyncClient() as client: url = f"{self.base_url}/{endpoint}" response = await client.get(url, headers=self.headers, params=params or {}) response.raise_for_status() return response.json() async def get_users(self, limit: int = 250) -> Dict[str, Any]: """Get users (пользователи)""" return await self._make_request("users", {"limit": limit}) async def get_pipelines(self) -> Dict[str, Any]: """Get pipelines (воронки)""" return await self._make_request("leads/pipelines") async def get_companies(self, limit: int = 250, page: int = 1) -> Dict[str, Any]: """Get companies (компании)""" params = {"limit": limit, "page": page, "with": "custom_fields_values"} return await self._make_request("companies", params) async def get_contacts(self, limit: int = 250, page: int = 1) -> Dict[str, Any]: """Get contacts (контакты)""" params = {"limit": limit, "page": page, "with": "custom_fields_values"} return await self._make_request("contacts", params) async def get_deals(self, limit: int = 250, page: int = 1) -> Dict[str, Any]: """Get deals/leads (сделки)""" params = {"limit": limit, "page": page, "with": "contacts,companies,custom_fields_values"} return await self._make_request("leads", params) async def get_events(self, limit: int = 250, page: int = 1, event_types: Optional[List[str]] = None) -> Dict[str, Any]: """Get events (события)""" params: Dict[str, Any] = {"limit": limit, "page": page} # Filter by event types if event_types: params["filter[type]"] = ",".join(event_types) else: # Default to our supported event types params["filter[type]"] = ",".join(["incoming_call", "outgoing_call", "lead_status_changed"]) return await self._make_request("events", params) async def get_custom_fields(self, entity_type: str = "leads") -> Dict[str, Any]: """Get custom fields metadata for entity type""" return await self._make_request(f"{entity_type}/custom_fields") async def fetch_entity_data( self, entity_type: str, limit: Optional[int] = None, page: Optional[int] = None, updated_at: Optional[int] = None, **kwargs ) -> List[Dict[str, Any]]: """ Generic method to fetch any entity type from AMO CRM. Args: entity_type: Type of entity (deals, contacts, companies, users, pipelines, events) limit: Maximum number of records to fetch page: Page number for pagination updated_at: Unix timestamp for incremental updates **kwargs: Additional query parameters Returns: List of entity records from AMO CRM """ # Map entity types to their API endpoints and response keys entity_config = { "deals": {"endpoint": "leads", "key": "leads", "with": "contacts,companies,custom_fields_values"}, "contacts": {"endpoint": "contacts", "key": "contacts", "with": "custom_fields_values"}, "companies": {"endpoint": "companies", "key": "companies", "with": "custom_fields_values"}, "users": {"endpoint": "users", "key": "users", "with": None}, "pipelines": {"endpoint": "leads/pipelines", "key": "pipelines", "with": None}, "events": {"endpoint": "events", "key": "events", "with": None}, } if entity_type not in entity_config: raise ValueError(f"Unsupported entity type: {entity_type}. Must be one of {list(entity_config.keys())}") config = entity_config[entity_type] params: Dict[str, Any] = {} # Add pagination parameters if limit: params["limit"] = limit if page: params["page"] = page # Add incremental update filter if updated_at: params["filter[updated_at][from]"] = updated_at # Add entity-specific 'with' parameter if config["with"]: params["with"] = config["with"] # Add any additional parameters params.update(kwargs) # Special handling for events - add default type filter if entity_type == "events" and "filter[type]" not in params: params["filter[type]"] = ",".join(["incoming_call", "outgoing_call", "lead_status_changed"]) # Fetch data from API response = await self._make_request(config["endpoint"], params) # Extract entities from response embedded = response.get("_embedded", {}) entities = embedded.get(config["key"], []) return entities async def fetch_all_data(self) -> Dict[str, Any]: """Fetch all data from AMO CRM for testing""" print("Fetching AMO CRM data...") results = {} try: print("- Fetching users...") results["users"] = await self.get_users() print(f" Found {len(results['users'].get('_embedded', {}).get('users', []))} users") print("- Fetching pipelines...") results["pipelines"] = await self.get_pipelines() print(f" Found {len(results['pipelines'].get('_embedded', {}).get('pipelines', []))} pipelines") print("- Fetching companies...") results["companies"] = await self.get_companies(limit=10) # Limit for testing print(f" Found {len(results['companies'].get('_embedded', {}).get('companies', []))} companies") print("- Fetching contacts...") results["contacts"] = await self.get_contacts(limit=10) # Limit for testing print(f" Found {len(results['contacts'].get('_embedded', {}).get('contacts', []))} contacts") print("- Fetching deals...") results["deals"] = await self.get_deals(limit=10) # Limit for testing print(f" Found {len(results['deals'].get('_embedded', {}).get('leads', []))} deals") print("- Fetching events...") results["events"] = await self.get_events(limit=10) # Limit for testing print(f" Found {len(results['events'].get('_embedded', {}).get('events', []))} events") print("- Fetching custom fields...") results["custom_fields_deals"] = await self.get_custom_fields("leads") results["custom_fields_contacts"] = await self.get_custom_fields("contacts") results["custom_fields_companies"] = await self.get_custom_fields("companies") print("✅ All data fetched successfully!") except Exception as e: print(f"❌ Error fetching data: {e}") raise return results # Convenience function for testing async def fetch_amocrm_data(): """Fetch AMO CRM data for testing""" client = AmoCRMClient() return await client.fetch_all_data() if __name__ == "__main__": # Test the client asyncio.run(fetch_amocrm_data())