#!/usr/bin/env python3 """ Free Providers Loader ===================== بارگذاری همه منابع رایگان از all_apis_merged_2025.json و تبدیل به فرمت config.py """ import json import os import re import logging from typing import Dict, List, Any, Optional from pathlib import Path logger = logging.getLogger(__name__) # مسیر فایل registry REGISTRY_FILE_PATHS = [ "all_apis_merged_2025.json", "/mnt/data/all_apis_merged_2025.json", os.path.join(os.getcwd(), "all_apis_merged_2025.json") ] def load_registry_file() -> Dict[str, Any]: """بارگذاری فایل registry""" for path in REGISTRY_FILE_PATHS: if os.path.exists(path): try: with open(path, 'r', encoding='utf-8') as f: data = json.load(f) logger.info(f"✅ Loaded registry from {path}") return data except Exception as e: logger.error(f"❌ Failed to load {path}: {e}") continue logger.warning("⚠️ Registry file not found") return {} def extract_free_providers_from_content(content: str) -> List[Dict[str, Any]]: """استخراج provider های رایگان از محتوای متنی""" providers = [] # Market Data APIs (FREE) free_market_apis = [ { "id": "coingecko", "name": "CoinGecko", "base_url": "https://api.coingecko.com/api/v3", "category": "market_data", "free": True, "rate_limit": {"requests_per_minute": 50} }, { "id": "coincap", "name": "CoinCap", "base_url": "https://api.coincap.io/v2", "category": "market_data", "free": True, "rate_limit": {"requests_per_minute": 200} }, { "id": "coinpaprika", "name": "CoinPaprika", "base_url": "https://api.coinpaprika.com/v1", "category": "market_data", "free": True, "rate_limit": {"requests_per_month": 20000} }, { "id": "binance_public", "name": "Binance Public API", "base_url": "https://api.binance.com/api/v3", "category": "market_data", "free": True }, { "id": "nomics", "name": "Nomics", "base_url": "https://api.nomics.com/v1", "category": "market_data", "free": True }, { "id": "messari", "name": "Messari", "base_url": "https://data.messari.io/api/v1", "category": "market_data", "free": True }, { "id": "coinlore", "name": "CoinLore", "base_url": "https://api.coinlore.net/api", "category": "market_data", "free": True }, { "id": "coindesk", "name": "CoinDesk", "base_url": "https://api.coindesk.com/v1", "category": "market_data", "free": True }, { "id": "mobula", "name": "Mobula API", "base_url": "https://api.mobula.io/api/1", "category": "market_data", "free": True }, { "id": "diadata", "name": "DIA Data", "base_url": "https://api.diadata.org/v1", "category": "market_data", "free": True }, { "id": "coinstats", "name": "CoinStats", "base_url": "https://api.coinstats.app/public/v1", "category": "market_data", "free": True } ] # News APIs (FREE) free_news_apis = [ { "id": "cryptopanic", "name": "CryptoPanic", "base_url": "https://cryptopanic.com/api/v1", "category": "news", "free": True }, { "id": "reddit_crypto", "name": "Reddit Crypto", "base_url": "https://www.reddit.com/r/CryptoCurrency", "category": "news", "free": True, "rate_limit": {"requests_per_minute": 60} }, { "id": "cryptocontrol", "name": "CryptoControl", "base_url": "https://cryptocontrol.io/api/v1/public", "category": "news", "free": True }, { "id": "coindesk_rss", "name": "CoinDesk RSS", "base_url": "https://www.coindesk.com/arc/outboundfeeds/rss", "category": "news", "free": True }, { "id": "cointelegraph_rss", "name": "CoinTelegraph RSS", "base_url": "https://cointelegraph.com/rss", "category": "news", "free": True }, { "id": "decrypt_rss", "name": "Decrypt RSS", "base_url": "https://decrypt.co/feed", "category": "news", "free": True }, { "id": "bitcoin_magazine_rss", "name": "Bitcoin Magazine RSS", "base_url": "https://bitcoinmagazine.com/.rss/full", "category": "news", "free": True } ] # Sentiment APIs (FREE) free_sentiment_apis = [ { "id": "alternative_me_fng", "name": "Alternative.me Fear & Greed", "base_url": "https://api.alternative.me/fng", "category": "sentiment", "free": True }, { "id": "cfgi", "name": "CFGI API", "base_url": "https://api.cfgi.io/v1", "category": "sentiment", "free": True }, { "id": "coingecko_community", "name": "CoinGecko Community Data", "base_url": "https://api.coingecko.com/api/v3", "category": "sentiment", "free": True }, { "id": "messari_social", "name": "Messari Social", "base_url": "https://data.messari.io/api/v1", "category": "sentiment", "free": True } ] # Block Explorer APIs (FREE) free_explorer_apis = [ { "id": "blockscout_eth", "name": "BlockScout (Ethereum)", "base_url": "https://eth.blockscout.com/api", "category": "blockchain_explorer", "free": True }, { "id": "blockchair_eth", "name": "Blockchair (Ethereum)", "base_url": "https://api.blockchair.com/ethereum", "category": "blockchain_explorer", "free": True, "rate_limit": {"requests_per_day": 1440} }, { "id": "blockchair_bsc", "name": "Blockchair (BSC)", "base_url": "https://api.blockchair.com/binance-smart-chain", "category": "blockchain_explorer", "free": True, "rate_limit": {"requests_per_day": 1440} }, { "id": "blockchair_tron", "name": "Blockchair (TRON)", "base_url": "https://api.blockchair.com/tron", "category": "blockchain_explorer", "free": True, "rate_limit": {"requests_per_day": 1440} }, { "id": "ethplorer", "name": "Ethplorer", "base_url": "https://api.ethplorer.io", "category": "blockchain_explorer", "free": True, "api_key": "freekey" }, { "id": "trongrid", "name": "TronGrid", "base_url": "https://api.trongrid.io", "category": "blockchain_explorer", "free": True }, { "id": "tronstack", "name": "TronStack", "base_url": "https://api.tronstack.io", "category": "blockchain_explorer", "free": True } ] # Whale Tracking APIs (FREE) free_whale_apis = [ { "id": "clankapp", "name": "ClankApp", "base_url": "https://clankapp.com/api", "category": "whale_tracking", "free": True }, { "id": "bitquery", "name": "BitQuery", "base_url": "https://graphql.bitquery.io", "category": "whale_tracking", "free": True, "rate_limit": {"queries_per_month": 10000} } ] # RPC Nodes (FREE) free_rpc_nodes = [ { "id": "publicnode_eth", "name": "PublicNode (Ethereum)", "base_url": "https://ethereum.publicnode.com", "category": "rpc_node", "free": True }, { "id": "ankr_eth", "name": "Ankr (Ethereum)", "base_url": "https://rpc.ankr.com/eth", "category": "rpc_node", "free": True }, { "id": "llamanodes_eth", "name": "LlamaNodes (Ethereum)", "base_url": "https://eth.llamarpc.com", "category": "rpc_node", "free": True }, { "id": "cloudflare_eth", "name": "Cloudflare (Ethereum)", "base_url": "https://cloudflare-eth.com", "category": "rpc_node", "free": True }, { "id": "drpc_eth", "name": "dRPC (Ethereum)", "base_url": "https://eth.drpc.org", "category": "rpc_node", "free": True }, { "id": "bsc_official", "name": "BSC Official RPC", "base_url": "https://bsc-dataseed.binance.org", "category": "rpc_node", "free": True }, { "id": "ankr_bsc", "name": "Ankr (BSC)", "base_url": "https://rpc.ankr.com/bsc", "category": "rpc_node", "free": True }, { "id": "publicnode_bsc", "name": "PublicNode (BSC)", "base_url": "https://bsc-rpc.publicnode.com", "category": "rpc_node", "free": True }, { "id": "polygon_official", "name": "Polygon Official", "base_url": "https://polygon-rpc.com", "category": "rpc_node", "free": True }, { "id": "ankr_polygon", "name": "Ankr (Polygon)", "base_url": "https://rpc.ankr.com/polygon", "category": "rpc_node", "free": True } ] # On-Chain Analytics (FREE) free_onchain_apis = [ { "id": "thegraph", "name": "The Graph", "base_url": "https://api.thegraph.com/subgraphs/name", "category": "onchain_analytics", "free": True }, { "id": "intotheblock", "name": "IntoTheBlock", "base_url": "https://api.intotheblock.com/v1", "category": "onchain_analytics", "free": True } ] all_free = ( free_market_apis + free_news_apis + free_sentiment_apis + free_explorer_apis + free_whale_apis + free_rpc_nodes + free_onchain_apis ) return all_free def get_all_free_providers() -> Dict[str, Any]: """دریافت همه provider های رایگان""" registry_data = load_registry_file() # استخراج از محتوای فایل providers = [] raw_files = registry_data.get('raw_files', []) for file_data in raw_files: content = file_data.get('content', '') providers.extend(extract_free_providers_from_content(content)) # استخراج کلیدها discovered_keys = registry_data.get('discovered_keys', {}) return { "providers": providers, "discovered_keys": discovered_keys, "total_count": len(providers), "free_count": len(providers) # همه رایگان هستند } def convert_to_config_format(providers_data: Dict[str, Any]) -> Dict[str, Any]: """تبدیل به فرمت EXTERNAL_PROVIDERS در config.py""" providers = providers_data.get("providers", []) discovered_keys = providers_data.get("discovered_keys", {}) config_providers = {} for provider in providers: provider_id = provider.get("id", "").lower() if not provider_id: continue # استخراج API key از discovered_keys api_key = None if provider_id in discovered_keys: keys = discovered_keys[provider_id] if keys: api_key = keys[0] elif "etherscan" in provider_id: keys = discovered_keys.get("etherscan", []) if keys: api_key = keys[0] elif "bscscan" in provider_id or "bsc" in provider_id: keys = discovered_keys.get("bscscan", []) if keys: api_key = keys[0] elif "tronscan" in provider_id or "tron" in provider_id: keys = discovered_keys.get("tronscan", []) if keys: api_key = keys[0] # تنظیمات rate limit rate_limit = provider.get("rate_limit", {}) if isinstance(rate_limit, dict): rate_limit_config = {} if "requests_per_minute" in rate_limit: rate_limit_config["requests_per_minute"] = rate_limit["requests_per_minute"] if "requests_per_day" in rate_limit: rate_limit_config["requests_per_day"] = rate_limit["requests_per_day"] if "requests_per_month" in rate_limit: rate_limit_config["requests_per_month"] = rate_limit["requests_per_month"] if "queries_per_month" in rate_limit: rate_limit_config["queries_per_month"] = rate_limit["queries_per_month"] else: rate_limit_config = {} config_providers[provider_id] = { "enabled": True, "api_key": os.getenv(f"{provider_id.upper()}_API_KEY") or api_key or provider.get("api_key"), "base_url": provider.get("base_url", ""), "timeout": 10.0, "priority": 3, # Free providers have lower priority "category": provider.get("category", "generic"), "rate_limit": rate_limit_config or { "requests_per_minute": 10, "requests_per_day": 1000 } } return config_providers