""" Fetches crypto news from NewsData.io free tier (200 requests/day). Docs: https://newsdata.io/documentation """ import os import logging import requests from datetime import datetime, timezone logger = logging.getLogger(__name__) NEWSDATA_BASE = "https://newsdata.io/api/1/news" CRYPTO_QUERIES = [ "bitcoin", "ethereum crypto", "crypto regulation", "DeFi blockchain", "crypto ETF", ] class NewsDataCollector: def __init__(self): self.api_key = os.getenv("NEWSDATA_API_KEY", "") def _is_available(self) -> bool: return bool(self.api_key) def fetch_all(self) -> list[dict]: if not self._is_available(): logger.info("[NewsData] No API key — skipping") return [] articles: list[dict] = [] seen_urls: set[str] = set() for query in CRYPTO_QUERIES: try: fetched = self._fetch(query) for a in fetched: if a["url"] not in seen_urls: seen_urls.add(a["url"]) articles.append(a) logger.debug(f"[NewsData] '{query}': {len(fetched)} articles") except Exception as exc: logger.warning(f"[NewsData] Failed for '{query}': {exc}") return articles def _fetch(self, query: str) -> list[dict]: params = { "apikey": self.api_key, "q": query, "language": "en", "category": "business,technology", } resp = requests.get(NEWSDATA_BASE, params=params, timeout=15) resp.raise_for_status() data = resp.json() results = [] for item in data.get("results", []): pub_raw = item.get("pubDate", "") try: published = datetime.strptime(pub_raw, "%Y-%m-%d %H:%M:%S").replace( tzinfo=timezone.utc ).isoformat() except Exception: published = datetime.now(timezone.utc).isoformat() title = item.get("title") or "" content = item.get("content") or item.get("description") or "" results.append({ "source": f"NewsData/{item.get('source_id', 'unknown')}", "title": title, "summary": content[:500], "url": item.get("link") or item.get("source_url", ""), "published": published, "type": "news", "score": 0, "text": f"{title}. {content}".strip(), }) return results