File size: 11,263 Bytes
ab02ac4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#!/usr/bin/env python3
"""
HuggingFace Space Crypto Resources API Client Service
سرویس کلاینت برای API منابع کریپتو در HuggingFace Space

This service provides access to the external HF Space Crypto Resources API:
https://really-amin-crypto-api-clean.hf.space

Features:
- Market data (top coins, trending)
- Global market overview
- Fear & Greed Index
- Resource database (281 resources in 12 categories)
"""

import httpx
import asyncio
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime, timezone
from functools import lru_cache

logger = logging.getLogger(__name__)

# Base URL for the HF Space API
HF_CRYPTO_API_BASE_URL = "https://really-amin-crypto-api-clean.hf.space"

# Cache for resources (they don't change frequently)
_resources_cache: Dict[str, Any] = {}
_cache_timestamp: Optional[datetime] = None
CACHE_TTL_SECONDS = 300  # 5 minutes


class HFSpaceCryptoService:
    """
    Service for accessing HuggingFace Space Crypto Resources API
    Follows project patterns with proper error handling and logging
    """
    
    def __init__(self, timeout: int = 15):
        self.base_url = HF_CRYPTO_API_BASE_URL
        self.timeout = timeout
        self._client: Optional[httpx.AsyncClient] = None
    
    async def _get_client(self) -> httpx.AsyncClient:
        """Get or create async HTTP client"""
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(timeout=self.timeout)
        return self._client
    
    async def close(self):
        """Close the HTTP client"""
        if self._client and not self._client.is_closed:
            await self._client.aclose()
    
    async def _request(self, endpoint: str, params: Dict = None) -> Dict[str, Any]:
        """
        Make async request to HF Space API with proper error handling
        
        Returns standardized response format matching project patterns
        """
        provider = "HFSpaceCryptoAPI"
        start_time = datetime.now(timezone.utc)
        
        try:
            client = await self._get_client()
            url = f"{self.base_url}{endpoint}"
            
            response = await client.get(url, params=params)
            response.raise_for_status()
            
            data = response.json()
            response_time_ms = (datetime.now(timezone.utc) - start_time).total_seconds() * 1000
            
            logger.info(f"✅ {provider} - {endpoint} - {response_time_ms:.0f}ms")
            
            return {
                "provider": provider,
                "endpoint": endpoint,
                "data": data,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "response_time_ms": response_time_ms,
                "success": True,
                "error": None
            }
            
        except httpx.HTTPStatusError as e:
            logger.error(f"❌ {provider} - {endpoint} - HTTP {e.response.status_code}")
            return {
                "provider": provider,
                "endpoint": endpoint,
                "data": None,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "success": False,
                "error": f"HTTP {e.response.status_code}",
                "error_type": "http_error"
            }
        except httpx.TimeoutException:
            logger.error(f"❌ {provider} - {endpoint} - Timeout")
            return {
                "provider": provider,
                "endpoint": endpoint,
                "data": None,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "success": False,
                "error": "Request timeout",
                "error_type": "timeout"
            }
        except Exception as e:
            logger.error(f"❌ {provider} - {endpoint} - {str(e)}")
            return {
                "provider": provider,
                "endpoint": endpoint,
                "data": None,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "success": False,
                "error": str(e),
                "error_type": "exception"
            }
    
    # ===== MARKET DATA =====
    
    async def get_top_coins(self, limit: int = 50) -> Dict[str, Any]:
        """
        Get top coins by market cap
        دریافت برترین ارزها بر اساس مارکت کپ
        """
        return await self._request("/api/coins/top", params={"limit": limit})
    
    async def get_trending(self) -> Dict[str, Any]:
        """
        Get trending coins
        دریافت ارزهای ترند
        """
        return await self._request("/api/trending")
    
    async def get_market_overview(self) -> Dict[str, Any]:
        """
        Get global market overview
        خلاصه کلی بازار
        """
        return await self._request("/api/market")
    
    # ===== SENTIMENT =====
    
    async def get_global_sentiment(self, timeframe: str = "1D") -> Dict[str, Any]:
        """
        Get Fear & Greed Index
        شاخص ترس و طمع
        """
        return await self._request("/api/sentiment/global", params={"timeframe": timeframe})
    
    async def get_asset_sentiment(self, symbol: str) -> Dict[str, Any]:
        """
        Get sentiment for specific asset
        احساسات یک ارز خاص
        """
        return await self._request(f"/api/sentiment/asset/{symbol}")
    
    # ===== RESOURCES DATABASE =====
    
    async def get_resources_stats(self) -> Dict[str, Any]:
        """
        Get resources database statistics
        آمار منابع
        """
        return await self._request("/api/resources/stats")
    
    async def get_categories(self) -> Dict[str, Any]:
        """
        Get list of resource categories
        لیست دسته‌بندی‌ها
        """
        return await self._request("/api/categories")
    
    async def get_resources_by_category(self, category: str) -> Dict[str, Any]:
        """
        Get resources for a specific category
        منابع یک دسته خاص
        
        Categories: rpc_nodes, block_explorers, market_data_apis, news_apis,
                   sentiment_apis, onchain_analytics_apis, whale_tracking_apis,
                   hf_resources, free_http_endpoints, cors_proxies
        """
        return await self._request(f"/api/resources/category/{category}")
    
    async def get_all_resources(self) -> Dict[str, Any]:
        """
        Get all resources list
        لیست همه منابع
        """
        return await self._request("/api/resources/list")
    
    # ===== SYSTEM STATUS =====
    
    async def health_check(self) -> Dict[str, Any]:
        """Check API health status"""
        return await self._request("/health")
    
    async def get_providers_status(self) -> Dict[str, Any]:
        """Get data providers status"""
        return await self._request("/api/providers")
    
    async def get_system_status(self) -> Dict[str, Any]:
        """Get system status"""
        return await self._request("/api/status")
    
    # ===== CONVENIENCE METHODS =====
    
    async def get_fear_greed_index(self) -> int:
        """
        Get current Fear & Greed Index value
        
        Returns:
            int: Fear & Greed Index (0-100)
        """
        result = await self.get_global_sentiment()
        if result["success"] and result["data"]:
            return result["data"].get("fear_greed_index", 50)
        return 50  # Default neutral
    
    async def get_btc_price(self) -> float:
        """
        Get current Bitcoin price
        
        Returns:
            float: BTC price in USD
        """
        result = await self.get_top_coins(limit=1)
        if result["success"] and result["data"]:
            coins = result["data"].get("coins", [])
            if coins:
                return coins[0].get("current_price", 0)
        return 0
    
    async def get_total_market_cap(self) -> float:
        """
        Get total crypto market cap
        
        Returns:
            float: Total market cap in USD
        """
        result = await self.get_market_overview()
        if result["success"] and result["data"]:
            return result["data"].get("total_market_cap", 0)
        return 0


# ===== SINGLETON INSTANCE =====

_service_instance: Optional[HFSpaceCryptoService] = None


def get_hf_space_crypto_service() -> HFSpaceCryptoService:
    """Get singleton instance of HF Space Crypto Service"""
    global _service_instance
    if _service_instance is None:
        _service_instance = HFSpaceCryptoService()
    return _service_instance


# ===== STANDALONE FUNCTIONS (for collectors compatibility) =====

async def fetch_hf_space_top_coins(limit: int = 50) -> Dict[str, Any]:
    """Fetch top coins from HF Space API"""
    service = get_hf_space_crypto_service()
    return await service.get_top_coins(limit)


async def fetch_hf_space_trending() -> Dict[str, Any]:
    """Fetch trending coins from HF Space API"""
    service = get_hf_space_crypto_service()
    return await service.get_trending()


async def fetch_hf_space_market_overview() -> Dict[str, Any]:
    """Fetch market overview from HF Space API"""
    service = get_hf_space_crypto_service()
    return await service.get_market_overview()


async def fetch_hf_space_sentiment() -> Dict[str, Any]:
    """Fetch global sentiment from HF Space API"""
    service = get_hf_space_crypto_service()
    return await service.get_global_sentiment()


async def fetch_hf_space_resources(category: Optional[str] = None) -> Dict[str, Any]:
    """Fetch resources from HF Space API"""
    service = get_hf_space_crypto_service()
    if category:
        return await service.get_resources_by_category(category)
    return await service.get_resources_stats()


# ===== TEST =====

if __name__ == "__main__":
    async def main():
        service = get_hf_space_crypto_service()
        
        print("=" * 60)
        print("Testing HF Space Crypto Service")
        print("=" * 60)
        
        # Health check
        print("\n1. Health Check:")
        result = await service.health_check()
        print(f"   Success: {result['success']}")
        if result['success']:
            print(f"   Status: {result['data'].get('status')}")
        
        # Top coins
        print("\n2. Top 3 Coins:")
        result = await service.get_top_coins(limit=3)
        if result['success']:
            for coin in result['data'].get('coins', []):
                print(f"   {coin['name']}: ${coin['current_price']:,.2f}")
        
        # Sentiment
        print("\n3. Fear & Greed Index:")
        fgi = await service.get_fear_greed_index()
        print(f"   Index: {fgi}")
        
        # Resources
        print("\n4. Resources Stats:")
        result = await service.get_resources_stats()
        if result['success']:
            print(f"   Total: {result['data'].get('total_resources')}")
            print(f"   Categories: {result['data'].get('total_categories')}")
        
        await service.close()
        print("\n" + "=" * 60)
        print("Tests completed!")
    
    asyncio.run(main())