Back to feed
Sam Berlin, Germany

Build an AI-Powered Good News Feed with RSS and OpenRouter

I read the news every day, but it had become increasingly depressing and was making me miserable. What bothered me most was that it also felt inaccurate: although bad things are happening, there have been many positive developments over the past five years that rarely receive sustained coverage. Major news sites might publish an article or two about them, but those stories are quickly buried under negativity. I wanted a way to get only positive news stories in my feed each day. Keyword filters did not work: “record” and “breakthrough” also appear in stories about record wildfire seasons, while “war” can appear in “war ends.” Off-the-shelf sentiment analysis was not useful either. A happy press release about layoffs can be classified as positive, while a dry factual story about a disease being eliminated may be classified as neutral. So I trained a basic artificial version of my personality using a series of prompts about what I consider positive in the world. I connected it to Mistral through OpenRouter and gave it access to public RSS feeds from news sites I already trusted. This eventually became Rally News, which I published on Google Play. iOS has been more difficult. The app surfaces positive stories from more than 20 news sites in an endless scroll, giving me an alternative to my uncomfortable TikTok addiction. Because I made it public, I decided not to host article text: publishers keep their traffic and revenue, while the tool remains ethical. The system runs on a GitHub Actions cron job that pushes stories to a PHP and MySQL database. I built the app without coding experience for about $25 per month. Step-by-step: 1. I collected RSS feeds from established publishers I already trusted and stored the list as configuration. I started with about 10 feeds instead of a few hundred so I could realistically read the output. 2. I set up a scheduled GitHub Actions cron job to run a Python script that pulls new items from every feed. 3. I deduplicated incoming articles against the database using the URL and a normalized title. Syndicated stories frequently reappear under slightly different URLs, and I did not want to pay to evaluate the same article twice. 4. I wrote the filter prompt as a long persona document rather than a one-line instruction. It explains what I consider progress, what I consider a puff piece, and which cases should fail—for example, celebrity news is not good news, a company announcing an intention is not the same as taking action, and a local feel-good story without wider significance does not qualify. 5. I sent each new article to an LLM through OpenRouter and required JSON output containing a pass-or-fail decision and a short justification. 6. For the first few weeks, I read the justifications every day. Whenever I disagreed with the model, I added a new rule to the persona document. That review loop required nearly all of the actual work. 7. I wrote passing articles to MySQL with only the headline, source, link, and metadata, leaving the article body with the publisher. 8. I pointed the website and mobile app to the same database. 9. I added a second GitHub Actions job that assembles a daily newsletter from the same data through Brevo, allowing one evaluation pass to feed three surfaces.

Industries
#aggregator#app#news#positivity

Tools used

0 comments

Read the Community guidelines

No comments yet. Be the first to weigh in.

Current rank #22 Upvotes 0