Initial commit: Tagesbyte prototype
Flutter app that collects daily tech news from German and English RSS/Atom feeds, with a reader view, AI article summaries and daily briefings (Claude API or n8n webhook), paywall/sponsored filtering and ad/consent blocking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
f12fb1dabb
159 files changed
+8738
No files matched your search
@@ -0,0 +1,197 @@
|
||||
# Tagesbyte
|
||||
|
||||
Your daily tech news in bytes. A Flutter app that collects the day's tech news
|
||||
from German and English RSS/Atom feeds and uses AI to summarize single articles
|
||||
or write a daily briefing. It's meant to replace the n8n → Discord workflow.
|
||||
|
||||
## Features (prototype)
|
||||
|
||||
- **Today**: all articles from the enabled sources in the last 12/24/48 h, newest
|
||||
first. Filter by language or source, pull to refresh.
|
||||
- **Reading in the app**: tapping an article opens a clean **reader view** (text,
|
||||
headings, lists, quotes, images) without ads or cookie banners. The globe icon
|
||||
switches to the **original page** in an in-app WebView, for paywalls or sites
|
||||
that block reader view. **Summarize with AI** uses the same downloaded text, so
|
||||
the summary is based on the full article, not just the RSS teaser. Links in the
|
||||
briefing open the same way.
|
||||
- **Briefing**: AI picks the 6–10 most important stories of the day, merges
|
||||
coverage from different outlets, and links every topic to its sources. Every
|
||||
briefing is saved on the device (`briefings.json` in the app's private
|
||||
storage); the history button lists them by month, tap to read, swipe to delete
|
||||
(with undo).
|
||||
- **Paid articles and ads hidden**: heise+ and Golem `(g+)` are recognized by
|
||||
their title; SPIEGEL+ and t3n Pro by a marker in the article page, which the app
|
||||
checks once per new article in the background (it reads only until the marker,
|
||||
and remembers results for 14 days). Sponsored posts (`Anzeige:`) are hidden too.
|
||||
The feed header shows how many were hidden; both filters can be turned off.
|
||||
- **Ad and cookie-banner blocking** in the original-page view: requests to ~80
|
||||
ad/tracker hosts and the common consent managers (Sourcepoint, OneTrust,
|
||||
consentmanager, Usercentrics, Didomi, Cookiebot, …) are blocked, and a
|
||||
stylesheet hides leftover ad slots and banners. Nothing is accepted, so sites
|
||||
treat it like a rejection. Golem redirects to a separate consent page instead
|
||||
of showing a banner; with "Accept cookie walls" on (default), the app makes the
|
||||
same request as Golem's own "Zustimmen" button (`/abo/setconsentcookie.php`,
|
||||
valid for a day) and reuses the cookie for reader view and the in-app page.
|
||||
With it off, the app shows the teaser and an "Open website anyway" option.
|
||||
- **Sources**: 21 curated feeds (12 German, 9 English), toggle each one, or add any
|
||||
RSS/Atom feed (it's validated before being added).
|
||||
- **Dark mode**: System / Light / Dark in Settings (applies instantly). The
|
||||
original-page view follows it too: when the phone is dark, sites use their own
|
||||
dark design or Android darkens them; when only the app is dark, the app inverts
|
||||
the page colours and keeps photos and videos as they are.
|
||||
- **Settings**: AI provider (off / Claude API / n8n webhook), model, summary
|
||||
language (German/English, whatever the source language), time window, paid/ad
|
||||
filters, ad and cookie-banner blocking.
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
flutter pub get
|
||||
flutter run # pick an Android emulator/device
|
||||
flutter test # unit tests (parser, Claude request/response handling)
|
||||
LIVE_FEEDS=1 flutter test test/live_feeds_test.dart # checks every real feed
|
||||
```
|
||||
|
||||
Platform notes:
|
||||
|
||||
- **Android / iOS**: work as-is (Android 6.0+).
|
||||
- **Windows desktop**: requires Visual Studio with the "Desktop development with
|
||||
C++" workload. Reader view works; "original page" opens the browser because
|
||||
the WebView plugin has no Windows support.
|
||||
- **Web**: browsers block cross-origin feed requests (CORS). Set a CORS proxy
|
||||
prefix in Settings (only shown on web), or use the app on mobile/desktop.
|
||||
|
||||
## AI setup
|
||||
|
||||
### Option A: Claude API (direct)
|
||||
|
||||
1. Create a key at <https://console.anthropic.com/settings/keys>.
|
||||
2. Settings → AI summaries → **Claude API** → paste the key → Save.
|
||||
|
||||
The key is stored in the platform keystore (`flutter_secure_storage`) and only
|
||||
sent to `api.anthropic.com`. Model choices:
|
||||
|
||||
| Model | Use it for |
|
||||
|---|---|
|
||||
| `claude-opus-5-5` (default) | Best briefing quality |
|
||||
| `claude-sonnet-5-5` | Balanced cost and quality |
|
||||
| `claude-haiku-5-5` | Cheapest; good enough for single-article summaries |
|
||||
|
||||
Requests use low effort (summarizing is routine work) and, for the briefing,
|
||||
structured JSON output so the result maps reliably to topics and links. On Opus
|
||||
and Sonnet the app also enables Anthropic's server-side refusal fallback
|
||||
(`fallbacks: "default"`): if a safety classifier declines a request, the API
|
||||
retries it on a fallback model.
|
||||
|
||||
> Shipping the app to other people with your key inside isn't safe. For
|
||||
> personal use a key you entered yourself is fine; for distribution, route
|
||||
> requests through your own backend (e.g. the n8n option below).
|
||||
|
||||
### Option B: Your n8n workflow
|
||||
|
||||
Settings → AI summaries → **n8n webhook** → enter the production URL of a
|
||||
*Webhook* trigger node (method POST, "Respond: Using 'Respond to Webhook' node").
|
||||
The app sends JSON and expects JSON back.
|
||||
|
||||
**Daily briefing**
|
||||
|
||||
```jsonc
|
||||
// Request
|
||||
{
|
||||
"action": "daily_briefing",
|
||||
"language": "de", // "de" | "en"
|
||||
"articles": [
|
||||
{
|
||||
"id": "a1",
|
||||
"title": "…",
|
||||
"url": "https://…",
|
||||
"source": "heise online",
|
||||
"language": "de",
|
||||
"published": "2026-10-11T07:30:00.000Z",
|
||||
"excerpt": "…"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Response, option 1 (structured; topics link back to the articles)
|
||||
{
|
||||
"intro": "One or two sentences on the day.",
|
||||
"topics": [
|
||||
{ "title": "…", "summary": "…", "article_ids": ["a1", "a7"] }
|
||||
]
|
||||
}
|
||||
|
||||
// Response, option 2 (plain text, e.g. your existing Discord message)
|
||||
{ "text": "…" }
|
||||
```
|
||||
|
||||
**Single article**
|
||||
|
||||
```jsonc
|
||||
// Request
|
||||
{
|
||||
"action": "summarize_article",
|
||||
"language": "de",
|
||||
"article": { "title": "…", "url": "…", "source": "…", "published": "…", "text": "extracted article body" }
|
||||
}
|
||||
|
||||
// Response
|
||||
{ "summary": "…" } // a plain-text body also works
|
||||
```
|
||||
|
||||
Use a *Switch* node on `{{$json.body.action}}` to route the two cases to your
|
||||
existing AI nodes.
|
||||
|
||||
## News sources
|
||||
|
||||
Every feed URL below was checked to load and parse. The ★ sources are enabled by
|
||||
default.
|
||||
|
||||
| | Source | Feed | Focus |
|
||||
|---|---|---|---|
|
||||
| 🇩🇪 ★ | heise online | `heise.de/rss/heise-atom.xml` | Broad IT news, high volume (~150/day) |
|
||||
| 🇩🇪 ★ | Golem.de | `rss.golem.de/rss.php?feed=ATOM1.0` | IT news for professionals |
|
||||
| 🇩🇪 ★ | t3n | `t3n.de/rss.xml` | Digital business, startups, AI |
|
||||
| 🇩🇪 ★ | ComputerBase | `computerbase.de/rss/news.xml` | Hardware, software, gaming |
|
||||
| 🇩🇪 | SPIEGEL Netzwelt | `spiegel.de/netzwelt/index.rss` | Mainstream news magazine view |
|
||||
| 🇩🇪 | ZEIT Digital | `newsfeed.zeit.de/digital/index` | Digital policy and society |
|
||||
| 🇩🇪 | netzpolitik.org | `netzpolitik.org/feed/` | Digital rights, privacy |
|
||||
| 🇩🇪 | heise Security | `heise.de/security/rss/news-atom.xml` | Security vulnerabilities |
|
||||
| 🇩🇪 | heise Developer | `heise.de/developer/rss/news-atom.xml` | Software development |
|
||||
| 🇩🇪 | Caschys Blog | `stadt-bremerhaven.de/feed/` | Consumer tech, smartphones |
|
||||
| 🇩🇪 | derStandard Web | `derstandard.at/rss/web` | Austrian daily's tech section |
|
||||
| 🇩🇪 | Borns IT- und Windows-Blog | `borncity.com/blog/feed/` | Windows, patches, admin topics |
|
||||
| 🇬🇧 ★ | The Verge | `theverge.com/rss/index.xml` | Consumer tech, policy |
|
||||
| 🇬🇧 ★ | Ars Technica | `feeds.arstechnica.com/arstechnica/index` | In-depth tech and science |
|
||||
| 🇬🇧 ★ | TechCrunch | `techcrunch.com/feed/` | Startups, funding |
|
||||
| 🇬🇧 ★ | Hacker News | `news.ycombinator.com/rss` | Front page links |
|
||||
| 🇬🇧 | MIT Technology Review | `technologyreview.com/feed/` | AI, biotech, climate |
|
||||
| 🇬🇧 | WIRED | `wired.com/feed/rss` | Tech and culture |
|
||||
| 🇬🇧 | Engadget | `engadget.com/rss.xml` | Gadgets |
|
||||
| 🇬🇧 | The Register | `theregister.com/headlines.atom` | Enterprise IT |
|
||||
| 🇬🇧 | BleepingComputer | `bleepingcomputer.com/feed/` | Security, malware |
|
||||
|
||||
Reader view works for 20 of the 21 sources. ZEIT Digital blocks non-browser
|
||||
requests, so the app shows the teaser and offers the original page.
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
lib/
|
||||
main.dart App entry, theme
|
||||
data/default_sources.dart Curated feed list
|
||||
models/ Article, NewsSource, DailyBriefing, AppSettings
|
||||
services/
|
||||
feed_parser.dart RSS 2.0 / RSS 1.0 / Atom parser, date parsing
|
||||
feed_service.dart Parallel fetching, dedupe, time window
|
||||
article_extractor.dart Article page → reader blocks + text for the AI
|
||||
summarizer.dart Summarizer interface and shared prompt input
|
||||
claude_summarizer.dart Claude Messages API over HTTP
|
||||
n8n_summarizer.dart n8n webhook bridge
|
||||
storage.dart Settings, sources, briefing persistence
|
||||
paywall_service.dart Paid/sponsored detection (titles + page marker)
|
||||
content_blocking.dart Ad/tracker/consent blocklists for the WebView
|
||||
consent_service.dart Accepts consent walls (Golem) and keeps the cookies
|
||||
state/app_state.dart ChangeNotifier holding app state
|
||||
ui/ Screens (Today, Article, Briefing, Sources, Settings)
|
||||
```
|
||||
Reference in new issue
Block a user