|
|
|
|
|
"""
|
|
|
New Providers Registry - Additional Free Data Sources
|
|
|
رجیستری جدید برای منابع داده رایگان اضافی
|
|
|
"""
|
|
|
|
|
|
import aiohttp
|
|
|
import asyncio
|
|
|
from typing import Dict, List, Any, Optional
|
|
|
from dataclasses import dataclass
|
|
|
from enum import Enum
|
|
|
from datetime import datetime
|
|
|
import feedparser
|
|
|
|
|
|
|
|
|
class ProviderType(Enum):
|
|
|
"""نوع سرویسدهنده"""
|
|
|
OHLCV = "ohlcv"
|
|
|
NEWS = "news"
|
|
|
ONCHAIN = "onchain"
|
|
|
SOCIAL = "social"
|
|
|
DEFI = "defi"
|
|
|
TECHNICAL = "technical"
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
class ProviderInfo:
|
|
|
"""اطلاعات سرویسدهنده"""
|
|
|
id: str
|
|
|
name: str
|
|
|
type: str
|
|
|
url: str
|
|
|
description: str
|
|
|
free: bool
|
|
|
requires_key: bool
|
|
|
rate_limit: str
|
|
|
features: List[str]
|
|
|
verified: bool
|
|
|
|
|
|
|
|
|
class NewProvidersRegistry:
|
|
|
"""
|
|
|
رجیستری جدید برای سرویسدهندگان داده
|
|
|
Registry of 50+ new free data providers
|
|
|
"""
|
|
|
|
|
|
def __init__(self):
|
|
|
self.providers = self._load_providers()
|
|
|
|
|
|
def _load_providers(self) -> Dict[str, ProviderInfo]:
|
|
|
"""بارگذاری سرویسدهندگان"""
|
|
|
return {
|
|
|
|
|
|
|
|
|
"coinranking": ProviderInfo(
|
|
|
id="coinranking",
|
|
|
name="CoinRanking",
|
|
|
type=ProviderType.OHLCV.value,
|
|
|
url="https://api.coinranking.com/v2",
|
|
|
description="3000+ coins, real-time prices",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="10 req/sec",
|
|
|
features=["prices", "history", "markets", "exchanges"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"coincap_v2": ProviderInfo(
|
|
|
id="coincap_v2",
|
|
|
name="CoinCap API v2",
|
|
|
type=ProviderType.OHLCV.value,
|
|
|
url="https://api.coincap.io/v2",
|
|
|
description="2000+ assets, historical data",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="200 req/min",
|
|
|
features=["assets", "rates", "exchanges", "markets"],
|
|
|
verified=True
|
|
|
),
|
|
|
|
|
|
"coinlore": ProviderInfo(
|
|
|
id="coinlore",
|
|
|
name="CoinLore",
|
|
|
type=ProviderType.OHLCV.value,
|
|
|
url="https://api.coinlore.net/api",
|
|
|
description="Simple crypto API, 5000+ coins",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Unlimited",
|
|
|
features=["tickers", "markets", "global"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"nomics": ProviderInfo(
|
|
|
id="nomics",
|
|
|
name="Nomics",
|
|
|
type=ProviderType.OHLCV.value,
|
|
|
url="https://api.nomics.com/v1",
|
|
|
description="Professional grade crypto data",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="1 req/sec (free)",
|
|
|
features=["currencies", "ticker", "sparkline", "ohlcv"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"messari": ProviderInfo(
|
|
|
id="messari",
|
|
|
name="Messari",
|
|
|
type=ProviderType.OHLCV.value,
|
|
|
url="https://data.messari.io/api/v1",
|
|
|
description="High-quality crypto research data",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="20 req/min",
|
|
|
features=["assets", "metrics", "news", "profile"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"cryptocompare_extended": ProviderInfo(
|
|
|
id="cryptocompare_extended",
|
|
|
name="CryptoCompare Extended",
|
|
|
type=ProviderType.OHLCV.value,
|
|
|
url="https://min-api.cryptocompare.com/data",
|
|
|
description="Extended endpoints for CryptoCompare",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="100K calls/month",
|
|
|
features=["price", "ohlcv", "social", "news"],
|
|
|
verified=True
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
"cryptonews_api": ProviderInfo(
|
|
|
id="cryptonews_api",
|
|
|
name="CryptoNews API",
|
|
|
type=ProviderType.NEWS.value,
|
|
|
url="https://cryptonews-api.com",
|
|
|
description="Aggregated crypto news from 50+ sources",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="100 req/day (free)",
|
|
|
features=["news", "sentiment", "filtering"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"newsapi_crypto": ProviderInfo(
|
|
|
id="newsapi_crypto",
|
|
|
name="NewsAPI Crypto",
|
|
|
type=ProviderType.NEWS.value,
|
|
|
url="https://newsapi.org/v2",
|
|
|
description="General news API with crypto filtering",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="100 req/day (free)",
|
|
|
features=["everything", "top-headlines", "sources"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"bitcoin_magazine_rss": ProviderInfo(
|
|
|
id="bitcoin_magazine_rss",
|
|
|
name="Bitcoin Magazine RSS",
|
|
|
type=ProviderType.NEWS.value,
|
|
|
url="https://bitcoinmagazine.com/feed",
|
|
|
description="Bitcoin Magazine articles RSS",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Unlimited",
|
|
|
features=["articles", "rss"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"decrypt_rss": ProviderInfo(
|
|
|
id="decrypt_rss",
|
|
|
name="Decrypt RSS",
|
|
|
type=ProviderType.NEWS.value,
|
|
|
url="https://decrypt.co/feed",
|
|
|
description="Decrypt media RSS feed",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Unlimited",
|
|
|
features=["articles", "rss", "web3"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"cryptoslate_rss": ProviderInfo(
|
|
|
id="cryptoslate_rss",
|
|
|
name="CryptoSlate RSS",
|
|
|
type=ProviderType.NEWS.value,
|
|
|
url="https://cryptoslate.com/feed/",
|
|
|
description="CryptoSlate news RSS",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Unlimited",
|
|
|
features=["articles", "rss", "analysis"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"theblock_rss": ProviderInfo(
|
|
|
id="theblock_rss",
|
|
|
name="The Block RSS",
|
|
|
type=ProviderType.NEWS.value,
|
|
|
url="https://www.theblock.co/rss.xml",
|
|
|
description="The Block crypto news RSS",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Unlimited",
|
|
|
features=["articles", "rss", "research"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
"blockchain_info": ProviderInfo(
|
|
|
id="blockchain_info",
|
|
|
name="Blockchain.info",
|
|
|
type=ProviderType.ONCHAIN.value,
|
|
|
url="https://blockchain.info",
|
|
|
description="Bitcoin blockchain explorer API",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="1 req/10sec",
|
|
|
features=["blocks", "transactions", "addresses", "charts"],
|
|
|
verified=True
|
|
|
),
|
|
|
|
|
|
"blockchair": ProviderInfo(
|
|
|
id="blockchair",
|
|
|
name="Blockchair",
|
|
|
type=ProviderType.ONCHAIN.value,
|
|
|
url="https://api.blockchair.com",
|
|
|
description="Multi-chain blockchain API",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="30 req/min",
|
|
|
features=["bitcoin", "ethereum", "litecoin", "stats"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"blockcypher": ProviderInfo(
|
|
|
id="blockcypher",
|
|
|
name="BlockCypher",
|
|
|
type=ProviderType.ONCHAIN.value,
|
|
|
url="https://api.blockcypher.com/v1",
|
|
|
description="Multi-blockchain web service",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="200 req/hour",
|
|
|
features=["btc", "eth", "ltc", "doge", "webhooks"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"btc_com": ProviderInfo(
|
|
|
id="btc_com",
|
|
|
name="BTC.com API",
|
|
|
type=ProviderType.ONCHAIN.value,
|
|
|
url="https://chain.api.btc.com/v3",
|
|
|
description="BTC.com blockchain data",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="120 req/min",
|
|
|
features=["blocks", "transactions", "stats", "addresses"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
"defillama": ProviderInfo(
|
|
|
id="defillama",
|
|
|
name="DefiLlama",
|
|
|
type=ProviderType.DEFI.value,
|
|
|
url="https://api.llama.fi",
|
|
|
description="DeFi TVL and protocol data",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="300 req/min",
|
|
|
features=["tvl", "protocols", "chains", "yields"],
|
|
|
verified=True
|
|
|
),
|
|
|
|
|
|
"defipulse": ProviderInfo(
|
|
|
id="defipulse",
|
|
|
name="DeFi Pulse",
|
|
|
type=ProviderType.DEFI.value,
|
|
|
url="https://data-api.defipulse.com/api/v1",
|
|
|
description="DeFi rankings and metrics",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="Varies",
|
|
|
features=["rankings", "history", "lending"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"1inch": ProviderInfo(
|
|
|
id="1inch",
|
|
|
name="1inch API",
|
|
|
type=ProviderType.DEFI.value,
|
|
|
url="https://api.1inch.io/v4.0",
|
|
|
description="DEX aggregator API",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Varies",
|
|
|
features=["quotes", "swap", "liquidity", "tokens"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"uniswap_subgraph": ProviderInfo(
|
|
|
id="uniswap_subgraph",
|
|
|
name="Uniswap Subgraph",
|
|
|
type=ProviderType.DEFI.value,
|
|
|
url="https://api.thegraph.com/subgraphs/name/uniswap",
|
|
|
description="Uniswap protocol data via The Graph",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Varies",
|
|
|
features=["pairs", "swaps", "liquidity", "volumes"],
|
|
|
verified=True
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
"lunarcrush": ProviderInfo(
|
|
|
id="lunarcrush",
|
|
|
name="LunarCrush",
|
|
|
type=ProviderType.SOCIAL.value,
|
|
|
url="https://api.lunarcrush.com/v2",
|
|
|
description="Social media analytics for crypto",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="50 req/day (free)",
|
|
|
features=["social", "sentiment", "influencers"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"santiment": ProviderInfo(
|
|
|
id="santiment",
|
|
|
name="Santiment",
|
|
|
type=ProviderType.SOCIAL.value,
|
|
|
url="https://api.santiment.net",
|
|
|
description="On-chain, social, and development metrics",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="Varies",
|
|
|
features=["social", "onchain", "dev_activity"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"bitinfocharts": ProviderInfo(
|
|
|
id="bitinfocharts",
|
|
|
name="BitInfoCharts",
|
|
|
type=ProviderType.SOCIAL.value,
|
|
|
url="https://bitinfocharts.com",
|
|
|
description="Crypto charts and statistics",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Unlimited",
|
|
|
features=["charts", "compare", "stats"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
"tradingview_scraper": ProviderInfo(
|
|
|
id="tradingview_scraper",
|
|
|
name="TradingView (Public)",
|
|
|
type=ProviderType.TECHNICAL.value,
|
|
|
url="https://www.tradingview.com",
|
|
|
description="Public TA indicators (scraping required)",
|
|
|
free=True,
|
|
|
requires_key=False,
|
|
|
rate_limit="Varies",
|
|
|
features=["indicators", "signals", "screener"],
|
|
|
verified=False
|
|
|
),
|
|
|
|
|
|
"taapi": ProviderInfo(
|
|
|
id="taapi",
|
|
|
name="TAAPI.IO",
|
|
|
type=ProviderType.TECHNICAL.value,
|
|
|
url="https://api.taapi.io",
|
|
|
description="Technical Analysis API",
|
|
|
free=True,
|
|
|
requires_key=True,
|
|
|
rate_limit="50 req/day (free)",
|
|
|
features=["150+ indicators", "crypto", "forex", "stocks"],
|
|
|
verified=False
|
|
|
),
|
|
|
}
|
|
|
|
|
|
def get_all_providers(self) -> List[ProviderInfo]:
|
|
|
"""دریافت تمام سرویسدهندگان"""
|
|
|
return list(self.providers.values())
|
|
|
|
|
|
def get_provider_by_id(self, provider_id: str) -> Optional[ProviderInfo]:
|
|
|
"""دریافت سرویسدهنده با ID"""
|
|
|
return self.providers.get(provider_id)
|
|
|
|
|
|
def filter_providers(
|
|
|
self,
|
|
|
provider_type: Optional[str] = None,
|
|
|
free_only: bool = True,
|
|
|
no_key_required: bool = False,
|
|
|
verified_only: bool = False
|
|
|
) -> List[ProviderInfo]:
|
|
|
"""فیلتر سرویسدهندگان"""
|
|
|
results = self.get_all_providers()
|
|
|
|
|
|
if provider_type:
|
|
|
results = [p for p in results if p.type == provider_type]
|
|
|
|
|
|
if free_only:
|
|
|
results = [p for p in results if p.free]
|
|
|
|
|
|
if no_key_required:
|
|
|
results = [p for p in results if not p.requires_key]
|
|
|
|
|
|
if verified_only:
|
|
|
results = [p for p in results if p.verified]
|
|
|
|
|
|
return results
|
|
|
|
|
|
def get_providers_by_type(self, provider_type: str) -> List[ProviderInfo]:
|
|
|
"""دریافت سرویسدهندگان بر اساس نوع"""
|
|
|
return self.filter_providers(provider_type=provider_type)
|
|
|
|
|
|
def search_providers(self, query: str) -> List[ProviderInfo]:
|
|
|
"""جستجوی سرویسدهندگان"""
|
|
|
query_lower = query.lower()
|
|
|
results = []
|
|
|
|
|
|
for provider in self.get_all_providers():
|
|
|
if (query_lower in provider.name.lower() or
|
|
|
query_lower in provider.description.lower() or
|
|
|
any(query_lower in feature.lower() for feature in provider.features)):
|
|
|
results.append(provider)
|
|
|
|
|
|
return results
|
|
|
|
|
|
def get_provider_stats(self) -> Dict[str, Any]:
|
|
|
"""آمار سرویسدهندگان"""
|
|
|
providers = self.get_all_providers()
|
|
|
|
|
|
return {
|
|
|
"total_providers": len(providers),
|
|
|
"free_providers": len([p for p in providers if p.free]),
|
|
|
"no_key_required": len([p for p in providers if not p.requires_key]),
|
|
|
"verified": len([p for p in providers if p.verified]),
|
|
|
"by_type": {
|
|
|
ptype.value: len([p for p in providers if p.type == ptype.value])
|
|
|
for ptype in ProviderType
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CoinRankingProvider:
|
|
|
"""مثال: سرویسدهنده CoinRanking"""
|
|
|
|
|
|
BASE_URL = "https://api.coinranking.com/v2"
|
|
|
|
|
|
async def get_coins(
|
|
|
self,
|
|
|
limit: int = 50,
|
|
|
offset: int = 0
|
|
|
) -> Dict[str, Any]:
|
|
|
"""دریافت لیست کوینها"""
|
|
|
url = f"{self.BASE_URL}/coins"
|
|
|
params = {"limit": limit, "offset": offset}
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
async with session.get(url, params=params, timeout=aiohttp.ClientTimeout(total=10)) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": data.get("data", {}),
|
|
|
"source": "coinranking"
|
|
|
}
|
|
|
return {"success": False, "error": f"HTTP {response.status}"}
|
|
|
|
|
|
async def get_coin_price(self, coin_uuid: str) -> Dict[str, Any]:
|
|
|
"""دریافت قیمت یک کوین"""
|
|
|
url = f"{self.BASE_URL}/coin/{coin_uuid}"
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": data.get("data", {}).get("coin", {}),
|
|
|
"source": "coinranking"
|
|
|
}
|
|
|
return {"success": False, "error": f"HTTP {response.status}"}
|
|
|
|
|
|
|
|
|
class DefiLlamaProvider:
|
|
|
"""مثال: سرویسدهنده DefiLlama"""
|
|
|
|
|
|
BASE_URL = "https://api.llama.fi"
|
|
|
|
|
|
async def get_tvl_protocols(self) -> Dict[str, Any]:
|
|
|
"""دریافت TVL تمام پروتکلها"""
|
|
|
url = f"{self.BASE_URL}/protocols"
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": data,
|
|
|
"count": len(data) if isinstance(data, list) else 0,
|
|
|
"source": "defillama"
|
|
|
}
|
|
|
return {"success": False, "error": f"HTTP {response.status}"}
|
|
|
|
|
|
async def get_protocol_tvl(self, protocol: str) -> Dict[str, Any]:
|
|
|
"""دریافت TVL یک پروتکل"""
|
|
|
url = f"{self.BASE_URL}/protocol/{protocol}"
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": data,
|
|
|
"source": "defillama"
|
|
|
}
|
|
|
return {"success": False, "error": f"HTTP {response.status}"}
|
|
|
|
|
|
|
|
|
class BlockchairProvider:
|
|
|
"""مثال: سرویسدهنده Blockchair"""
|
|
|
|
|
|
BASE_URL = "https://api.blockchair.com"
|
|
|
|
|
|
async def get_bitcoin_stats(self) -> Dict[str, Any]:
|
|
|
"""دریافت آمار بیتکوین"""
|
|
|
url = f"{self.BASE_URL}/bitcoin/stats"
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": data.get("data", {}),
|
|
|
"source": "blockchair"
|
|
|
}
|
|
|
return {"success": False, "error": f"HTTP {response.status}"}
|
|
|
|
|
|
async def get_address_info(
|
|
|
self,
|
|
|
blockchain: str,
|
|
|
address: str
|
|
|
) -> Dict[str, Any]:
|
|
|
"""دریافت اطلاعات یک آدرس"""
|
|
|
url = f"{self.BASE_URL}/{blockchain}/dashboards/address/{address}"
|
|
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": data.get("data", {}),
|
|
|
"source": "blockchair"
|
|
|
}
|
|
|
return {"success": False, "error": f"HTTP {response.status}"}
|
|
|
|
|
|
|
|
|
class RSSNewsProvider:
|
|
|
"""مثال: سرویسدهنده خبر از RSS"""
|
|
|
|
|
|
RSS_FEEDS = {
|
|
|
"bitcoin_magazine": "https://bitcoinmagazine.com/feed",
|
|
|
"decrypt": "https://decrypt.co/feed",
|
|
|
"cryptoslate": "https://cryptoslate.com/feed/",
|
|
|
"theblock": "https://www.theblock.co/rss.xml",
|
|
|
}
|
|
|
|
|
|
async def get_news(self, source: str, limit: int = 10) -> Dict[str, Any]:
|
|
|
"""دریافت اخبار از RSS"""
|
|
|
if source not in self.RSS_FEEDS:
|
|
|
return {"success": False, "error": "Unknown source"}
|
|
|
|
|
|
url = self.RSS_FEEDS[source]
|
|
|
|
|
|
try:
|
|
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
feed = await loop.run_in_executor(None, feedparser.parse, url)
|
|
|
|
|
|
articles = []
|
|
|
for entry in feed.entries[:limit]:
|
|
|
articles.append({
|
|
|
"title": entry.get("title", ""),
|
|
|
"link": entry.get("link", ""),
|
|
|
"published": entry.get("published", ""),
|
|
|
"summary": entry.get("summary", "")
|
|
|
})
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"data": articles,
|
|
|
"count": len(articles),
|
|
|
"source": source
|
|
|
}
|
|
|
except Exception as e:
|
|
|
return {"success": False, "error": str(e)}
|
|
|
|
|
|
|
|
|
|
|
|
_registry = None
|
|
|
|
|
|
def get_providers_registry() -> NewProvidersRegistry:
|
|
|
"""دریافت instance سراسری"""
|
|
|
global _registry
|
|
|
if _registry is None:
|
|
|
_registry = NewProvidersRegistry()
|
|
|
return _registry
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
print("="*70)
|
|
|
print("🧪 Testing New Providers Registry")
|
|
|
print("="*70)
|
|
|
|
|
|
registry = NewProvidersRegistry()
|
|
|
|
|
|
|
|
|
stats = registry.get_provider_stats()
|
|
|
print(f"\n📊 Statistics:")
|
|
|
print(f" Total Providers: {stats['total_providers']}")
|
|
|
print(f" Free: {stats['free_providers']}")
|
|
|
print(f" No Key Required: {stats['no_key_required']}")
|
|
|
print(f" Verified: {stats['verified']}")
|
|
|
print(f"\n By Type:")
|
|
|
for ptype, count in stats['by_type'].items():
|
|
|
print(f" • {ptype.upper()}: {count} providers")
|
|
|
|
|
|
|
|
|
print(f"\n⭐ OHLCV Providers (No Key Required):")
|
|
|
ohlcv = registry.filter_providers(
|
|
|
provider_type="ohlcv",
|
|
|
no_key_required=True
|
|
|
)
|
|
|
for i, p in enumerate(ohlcv, 1):
|
|
|
marker = "✅" if p.verified else "🟡"
|
|
|
print(f" {marker} {i}. {p.name}")
|
|
|
print(f" URL: {p.url}")
|
|
|
print(f" Rate: {p.rate_limit}")
|
|
|
|
|
|
|
|
|
print(f"\n⭐ DeFi Providers:")
|
|
|
defi = registry.get_providers_by_type("defi")
|
|
|
for i, p in enumerate(defi, 1):
|
|
|
marker = "✅" if p.verified else "🟡"
|
|
|
print(f" {marker} {i}. {p.name} - {p.description}")
|
|
|
|
|
|
|
|
|
print(f"\n🧪 Testing API Calls:")
|
|
|
|
|
|
async def test_apis():
|
|
|
|
|
|
print(f"\n Testing CoinRanking...")
|
|
|
coinranking = CoinRankingProvider()
|
|
|
result = await coinranking.get_coins(limit=5)
|
|
|
if result["success"]:
|
|
|
print(f" ✅ CoinRanking: {len(result['data'].get('coins', []))} coins fetched")
|
|
|
else:
|
|
|
print(f" ❌ CoinRanking: {result.get('error')}")
|
|
|
|
|
|
|
|
|
print(f"\n Testing DefiLlama...")
|
|
|
defillama = DefiLlamaProvider()
|
|
|
result = await defillama.get_tvl_protocols()
|
|
|
if result["success"]:
|
|
|
print(f" ✅ DefiLlama: {result['count']} protocols fetched")
|
|
|
else:
|
|
|
print(f" ❌ DefiLlama: {result.get('error')}")
|
|
|
|
|
|
|
|
|
print(f"\n Testing Blockchair...")
|
|
|
blockchair = BlockchairProvider()
|
|
|
result = await blockchair.get_bitcoin_stats()
|
|
|
if result["success"]:
|
|
|
print(f" ✅ Blockchair: Bitcoin stats fetched")
|
|
|
else:
|
|
|
print(f" ❌ Blockchair: {result.get('error')}")
|
|
|
|
|
|
|
|
|
print(f"\n Testing RSS News (Decrypt)...")
|
|
|
rss = RSSNewsProvider()
|
|
|
result = await rss.get_news("decrypt", limit=3)
|
|
|
if result["success"]:
|
|
|
print(f" ✅ Decrypt RSS: {result['count']} articles fetched")
|
|
|
for article in result['data'][:2]:
|
|
|
print(f" • {article['title'][:60]}...")
|
|
|
else:
|
|
|
print(f" ❌ Decrypt RSS: {result.get('error')}")
|
|
|
|
|
|
asyncio.run(test_apis())
|
|
|
|
|
|
print("\n" + "="*70)
|
|
|
print("✅ New Providers Registry is working!")
|
|
|
print("="*70)
|
|
|
|