Add cryptocurrency price command via CoinGecko API
New features: - !crypto <SYMBOL> command to query cryptocurrency prices - CoinGeckoAPI integration (free, no API key required) - Support for 20+ popular cryptocurrencies (BTC, ETH, DOGE, etc.) - 24-hour price change and volume data - Colored embeds matching price movement - Alias: !c for quick crypto queries Uses CoinGecko's free /simple/price endpoint with 30 calls/min rate limit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
113
crypto_api.py
Normal file
113
crypto_api.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import requests
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CoinGeckoAPI:
|
||||
"""CoinGecko implementation for cryptocurrency price data."""
|
||||
|
||||
BASE_URL = "https://api.coingecko.com/api/v3"
|
||||
|
||||
# Common crypto symbol to CoinGecko ID mapping
|
||||
SYMBOL_MAP = {
|
||||
'BTC': 'bitcoin',
|
||||
'ETH': 'ethereum',
|
||||
'USDT': 'tether',
|
||||
'BNB': 'binancecoin',
|
||||
'SOL': 'solana',
|
||||
'XRP': 'ripple',
|
||||
'USDC': 'usd-coin',
|
||||
'ADA': 'cardano',
|
||||
'DOGE': 'dogecoin',
|
||||
'TRX': 'tron',
|
||||
'DOT': 'polkadot',
|
||||
'MATIC': 'matic-network',
|
||||
'LTC': 'litecoin',
|
||||
'SHIB': 'shiba-inu',
|
||||
'AVAX': 'avalanche-2',
|
||||
'LINK': 'chainlink',
|
||||
'UNI': 'uniswap',
|
||||
'XLM': 'stellar',
|
||||
'ATOM': 'cosmos',
|
||||
'XMR': 'monero'
|
||||
}
|
||||
|
||||
def get_crypto_price(self, symbol: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Retrieve cryptocurrency price data from CoinGecko.
|
||||
|
||||
Args:
|
||||
symbol: Crypto symbol (e.g., BTC, ETH, DOGE)
|
||||
|
||||
Returns:
|
||||
Dictionary with crypto data or None if unavailable
|
||||
"""
|
||||
try:
|
||||
# Convert symbol to CoinGecko ID
|
||||
symbol = symbol.upper()
|
||||
coin_id = self.SYMBOL_MAP.get(symbol, symbol.lower())
|
||||
|
||||
# Fetch price data
|
||||
url = f"{self.BASE_URL}/simple/price"
|
||||
params = {
|
||||
'ids': coin_id,
|
||||
'vs_currencies': 'usd',
|
||||
'include_24hr_change': 'true',
|
||||
'include_24hr_vol': 'true',
|
||||
'include_last_updated_at': 'true'
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params, timeout=10)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if coin_id not in data:
|
||||
logger.warning(f"No data available for crypto: {symbol}")
|
||||
return None
|
||||
|
||||
crypto_data = data[coin_id]
|
||||
current_price = crypto_data.get('usd', 0)
|
||||
change_percent = crypto_data.get('usd_24h_change', 0)
|
||||
|
||||
if current_price == 0:
|
||||
logger.warning(f"Invalid price data for crypto: {symbol}")
|
||||
return None
|
||||
|
||||
# Calculate previous price from 24h change
|
||||
change_decimal = change_percent / 100
|
||||
previous_price = current_price / (1 + change_decimal)
|
||||
change_dollar = current_price - previous_price
|
||||
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'coin_id': coin_id,
|
||||
'current_price': round(current_price, 2),
|
||||
'previous_price': round(previous_price, 2),
|
||||
'change_dollar': round(change_dollar, 2),
|
||||
'change_percent': round(change_percent, 2),
|
||||
'volume_24h': crypto_data.get('usd_24h_vol', 0)
|
||||
}
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Error fetching data for {symbol}: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error fetching data for {symbol}: {e}")
|
||||
return None
|
||||
|
||||
def is_available(self) -> bool:
|
||||
"""
|
||||
Check if CoinGecko API is accessible.
|
||||
|
||||
Returns:
|
||||
True if accessible, False otherwise
|
||||
"""
|
||||
try:
|
||||
url = f"{self.BASE_URL}/ping"
|
||||
response = requests.get(url, timeout=5)
|
||||
return response.status_code == 200
|
||||
except Exception as e:
|
||||
logger.error(f"CoinGecko API unavailable: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user