|
|
|
|
|
""" |
|
|
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__) |
|
|
|
|
|
|
|
|
HF_CRYPTO_API_BASE_URL = "https://really-amin-crypto-api-clean.hf.space" |
|
|
|
|
|
|
|
|
_resources_cache: Dict[str, Any] = {} |
|
|
_cache_timestamp: Optional[datetime] = None |
|
|
CACHE_TTL_SECONDS = 300 |
|
|
|
|
|
|
|
|
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" |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
async def main(): |
|
|
service = get_hf_space_crypto_service() |
|
|
|
|
|
print("=" * 60) |
|
|
print("Testing HF Space Crypto Service") |
|
|
print("=" * 60) |
|
|
|
|
|
|
|
|
print("\n1. Health Check:") |
|
|
result = await service.health_check() |
|
|
print(f" Success: {result['success']}") |
|
|
if result['success']: |
|
|
print(f" Status: {result['data'].get('status')}") |
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
|
print("\n3. Fear & Greed Index:") |
|
|
fgi = await service.get_fear_greed_index() |
|
|
print(f" Index: {fgi}") |
|
|
|
|
|
|
|
|
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()) |
|
|
|