Tägliche Nachrichten per Mail – automatisch, kostenlos, selbst gebaut
Jeden Abend um 18 Uhr möchte ich wissen, was den Tag bewegt hat. Nicht die Startseite irgendeines Nachrichtenportals mit ihren Clickbait-Überschriften. Nicht ein Newsletter, den jemand anderes für eine andere Zielgruppe zusammengestellt hat. Sondern meine Nachrichten – mit meinen Themen, in meiner Sprache, in meinem Format.
Also habe ich mir das gebaut.
Die Idee
Das Konzept ist simpel: Ein Python-Script läuft einmal täglich, fragt Gemini nach den wichtigsten Nachrichten des Tages, und schickt das Ergebnis als formatierte HTML-Mail an meine Adresse. Fertig.
Die interessante Frage war: Wo läuft das Script? Ein eigener Server wäre Overkill. fly.io, Sprites, Cloud Functions – alles möglich, aber mit mehr Aufwand als nötig. Die Antwort lag direkt im Repository: GitHub Actions.
Das Repository existiert sowieso, weil ich Prompt und Konfiguration versionieren will. Wenn ich schon ein Repo habe, kann GitHub Actions den Cron-Job übernehmen – kostenlos, präzise, ohne zusätzliche Infrastruktur.
Die Architektur
GitHub Repository
├── run.py ← das eigentliche Script
├── config.json ← Themen, Empfänger, Format
├── prompt.md ← Anweisungen ans KI-Modell
└── .github/
└── workflows/
└── news.yml ← Cron-Job, täglich 18:00 Uhr
Der Ablauf bei jedem Run:
- GitHub Actions startet täglich um 16:00 UTC (= 18:00 CEST)
run.pylädtconfig.jsonundprompt.mdaus dem Repo- Gemini 2.5 Flash recherchiert per Google Search Grounding die Nachrichten
- Das Script formatiert das Ergebnis als HTML-Mail
- Resend verschickt die Mail
- Der Runner beendet sich – keine laufenden Kosten
Das Herzstück: Google Search Grounding
Der entscheidende Unterschied zu einem normalen LLM-Call ist Google Search Grounding. Gemini hat damit direkten Zugriff auf aktuelle Webinhalte – kein Halluzinieren von gestrigen Nachrichten, keine veralteten Trainingsdaten.
Der API-Call sieht so aus:
body = {
"system_instruction": {"parts": [{"text": prompt}]},
"contents": [{"role": "user", "parts": [{"text": user_message}]}],
"tools": [{"google_search": {}}] # ← das ist der Schlüssel
}
response = requests.post(
f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={api_key}",
json=body,
timeout=60
)
Der google_search-Parameter aktiviert das Grounding. Gemini entscheidet wann und wonach es sucht – gesteuert durch den Prompt.
Der Prompt als Steuerzentrale
Der Prompt liegt als prompt.md Im Repository. Das ist der Hebel: Wer die Struktur der Mail ändern will, ändert eine Markdown-Datei
und pusht. Beim nächsten Run um 18 Uhr ist die neue Version aktiv –
kein Deployment, kein Rebuild.
Mein Prompt definiert eine feste Ausgabestruktur mit sieben Abschnitten: Top-Nachrichten, Thema des Tages, Deutschland, International, Technologie, Markt und Morgen im Blick. Dazu Stilregeln: immer Deutsch, keine Meinungen, keine Artikel älter als 24 Stunden, Quellenangaben in Klammern.
## Ausgabestruktur
### Top-Nachrichten
5–7 Schlagzeilen. **Fette Schlagzeile**, 2–3 Sätze Einordnung. *(Quelle)*
### Thema des Tages
Das wichtigste Ereignis (~150 Wörter): Was? Hintergrund? Was folgt?
### Markt
- DAX (Schluss, Veränderung in %)
- EUR/USD
- Ölpreis Brent (USD/Barrel)
...
Die Konfiguration
Themen und Empfänger stehen in config.json:
{
"recipient_email": "[email protected]",
"sender_name": "Abendedition",
"topics": [
"Automotive & Elektromobilität",
"Künstliche Intelligenz & Technologie",
"Deutsche Fiskal- und Wirtschaftspolitik",
"Energie & Klimapolitik"
],
"subject_template": "📰 Abendedition – {date}"
}
Der topics-Array landet im Prompt unter “Meine Themen” – Gemini sucht
dann gezielt nach Neuigkeiten aus diesen Bereichen.
Der GitHub Actions Workflow
# .github/workflows/news.yml
name: Daily News Mailer
on:
schedule:
- cron: '0 16 * * *' # 16:00 UTC = 18:00 CEST
jobs:
send-news:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run news mailer
run: pip install requests && python run.py
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
SENDER_EMAIL: ${{ secrets.SENDER_EMAIL }}
Secrets werden unter Repository → Settings → Secrets and variables → Actions Hinterlegt – nie im Code, nie in Dateien.
Im Winter muss der Cron auf. 0 17 * * * angepasst werden (CET statt CEST).
Was es kostet
| Dienst | Free Tier | Mein Verbrauch |
|---|---|---|
| Gemini 2.5 Flash | 500 Requests/Tag | 1–2/Tag |
| Resend | 100 Mails/Tag | 1–2/Tag |
| GitHub Actions | 2.000 Min/Monat | ~2 Min/Tag |
Summe: 0 Euro pro Monat. Dauerhaft, keine Kreditkarte nötig.
Was ich gelernt habe
Prompt-Engineering ist das eigentliche Produkt. Das Python-Script ist in einer Stunde geschrieben. Den Prompt so zu formulieren, dass Gemini verlässlich die richtige Struktur liefert, keine Links erfindet und tatsächlich nur aktuelle Artikel verwendet – das hat mehr Iterationen gebraucht als der Code.
GitHub Actions als Cron ist unterschätzt. Für alles, was täglich einmal läuft und kein dauerhaft laufendes Deployment braucht, ist es die unkomplizierteste Lösung.