added api
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import time
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import feedparser
|
||||
import httpx
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
_cache: dict[str, Any] = {"items": [], "expires_at": 0.0}
|
||||
|
||||
|
||||
class RssClient:
|
||||
def __init__(self) -> None:
|
||||
settings = get_settings()
|
||||
self.urls = settings.news_rss_urls_list
|
||||
self.cache_ttl = settings.news_cache_sec
|
||||
self.max_items = settings.news_max_items
|
||||
|
||||
def _fetch_feed(self, url: str) -> list[dict[str, str]]:
|
||||
headers = {"User-Agent": "HomeAIAssistant/1.0 (+https://assistant.grigowashere.ru)"}
|
||||
with httpx.Client(timeout=20.0, headers=headers, follow_redirects=True) as client:
|
||||
response = client.get(url)
|
||||
response.raise_for_status()
|
||||
parsed = feedparser.parse(response.content)
|
||||
|
||||
source = urlparse(url).netloc or url
|
||||
items: list[dict[str, str]] = []
|
||||
for entry in parsed.entries[: self.max_items]:
|
||||
link = (entry.get("link") or "").strip()
|
||||
title = (entry.get("title") or "").strip()
|
||||
if not title:
|
||||
continue
|
||||
items.append({
|
||||
"title": title,
|
||||
"link": link,
|
||||
"source": source,
|
||||
"published": (entry.get("published") or entry.get("updated") or "").strip(),
|
||||
})
|
||||
return items
|
||||
|
||||
def fetch_headlines(self, limit: int | None = None) -> list[dict[str, str]]:
|
||||
now = time.time()
|
||||
if _cache["items"] and now < _cache["expires_at"]:
|
||||
items = _cache["items"]
|
||||
else:
|
||||
merged: list[dict[str, str]] = []
|
||||
seen_links: set[str] = set()
|
||||
for url in self.urls:
|
||||
try:
|
||||
for item in self._fetch_feed(url):
|
||||
link = item.get("link") or item["title"]
|
||||
if link in seen_links:
|
||||
continue
|
||||
seen_links.add(link)
|
||||
merged.append(item)
|
||||
except Exception:
|
||||
continue
|
||||
_cache["items"] = merged
|
||||
_cache["expires_at"] = now + self.cache_ttl
|
||||
items = merged
|
||||
|
||||
cap = limit or self.max_items
|
||||
return items[:cap]
|
||||
Reference in New Issue
Block a user