"""Fetch world news from RSS feeds and save for the daily brief page.""" import json import os import feedparser from datetime import datetime, timezone, timedelta OUTPUT = "/var/www/nav/data/news.json" tz = timezone(timedelta(hours=8)) # RSS feeds - balanced selection of world news sources (Chinese weighted) FEEDS = [ {"name": "BBC World", "url": "http://feeds.bbci.co.uk/news/world/rss.xml", "lang": "en"}, {"name": "\u65b0\u6d6a\u65b0\u95fb", "url": "https://rss.sina.com.cn/news/china.xml", "lang": "zh"}, {"name": "\u6d8c\u62a5", "url": "https://www.ybai.com/wp-json/wp/v2/posts?per_page=10", "lang": "zh"}, {"name": "BBC \u4e2d\u6587", "url": "https://www.bbc.com/zhongwen/simp/index.xml", "lang": "zh"}, {"name": "HN", "url": "https://hnrss.org/frontpage?count=10", "lang": "en"}, {"name": "Reuters", "url": "https://www.reutersagency.com/feed/", "lang": "en"}, ] MAX_PER_FEED = 10 MAX_TOTAL = 40 def fetch_news(): items = [] for feed_info in FEEDS: try: feed = feedparser.parse(feed_info["url"]) for entry in feed.entries[:MAX_PER_FEED]: items.append({ "title": entry.get("title", ""), "link": entry.get("link", ""), "summary": entry.get("summary", "")[:200], "published": entry.get("published", ""), "source": feed_info["name"], "lang": feed_info["lang"], }) print(f" {feed_info['name']}: {len(feed.entries[:MAX_PER_FEED])} items") except Exception as e: print(f" {feed_info['name']}: ERROR - {e}") # Sort by published time (newest first), but keep feeds mixed items.sort(key=lambda x: x.get("published", ""), reverse=True) result = { "updated_at": datetime.now(tz).strftime("%Y-%m-%d %H:%M"), "total": len(items), "items": items[:MAX_TOTAL], "sources": [f["name"] for f in FEEDS], } os.makedirs(os.path.dirname(OUTPUT), exist_ok=True) with open(OUTPUT, "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2) print(f"\nTotal: {len(items)} items -> {OUTPUT}") return result if __name__ == "__main__": print(f"Fetching news at {datetime.now(tz).strftime('%Y-%m-%d %H:%M')}") fetch_news()