8d1e6ed5686d938e7c8c1fe51cc6ca561ba8795a
Pete
A Matrix news bot that ingests RSS feeds from curated sources, classifies stories using a local LLM, deduplicates semantically, and routes posts to the appropriate channel.
Features
- RSS ingestion from configurable sources (Guardian, Ars Technica) with per-source polling intervals
- Two-tier classification — Tier 1 sources use direct routing or lightweight LLM routing; Tier 2 (wire services) use a keyword gating pipeline before LLM
- Semantic deduplication — GUID exact match + LLM-based cross-source duplicate detection
- Metered release — per-channel post queues with minimum interval (5min) and burst cap (3/30min)
- Reaction tracking — records emoji reactions on posts for classifier tuning
!explainvia ❓ reaction — react with❓(or❔ ⁉ 🤔 ?) on any post; Pete fetches the article body and replies in-thread with a 3-bullet Ollama-generated TL;DR- Paywall detection — if an article's visible body text is below threshold, Pete swaps in a Wayback Machine snapshot URL for both the lead image and the posted link
- FTS5 search — full-text search across headlines and ledes
- Image validation — HEAD-based checks filter tracking pixels, uploads valid images via MXC
Channels
| Channel | Purpose |
|---|---|
tech |
Technology news, product/industry stories |
politics |
Political, policy, current events (default/catch-all) |
gaming |
Gaming news, releases, platform announcements (no active sources yet) |
Requirements
- Go 1.25+
- Ollama running locally or on a reachable host
- A Matrix account for Pete with access to target rooms
- SQLite (bundled via pure Go driver, no CGo)
- No CGo dependencies — E2EE uses pure Go crypto (goolm) via mautrix v0.28
Setup
# Clone and build
git clone <repo-url> && cd pete
go build -tags goolm .
# Create config from example
cp config.example.yaml config.yaml
# Edit config.yaml — fill in Matrix credentials, room IDs, Ollama endpoint
# First run: seed current feed items as seen (prevents flood)
./pete -config config.yaml -seed
# Post one story to verify the pipeline
./pete -config config.yaml -test
# Run
./pete -config config.yaml
Configuration
See config.example.yaml for the full structure. Key sections:
matrix— homeserver, credentials, channel room IDs, optional admin roomollama— base URL, model name, timeoutposting— rate limiting (min interval, burst cap)storage— database path, retention windowssources— RSS feeds with tier, polling interval, feed hint, optional direct routing
Environment variables can be referenced with ${VAR} syntax in the YAML.
Flags
| Flag | Description |
|---|---|
-config <path> |
Path to config file (default: config.yaml) |
-seed |
Ingest all current feed items as seen without posting, then exit |
-test |
Post one story to verify the full pipeline, then exit |
Docker
docker compose up -d
Set PETE_PASSWORD in your environment or a .env file for the Matrix password.
Architecture
RSS Feed → Poller → GUID Dedup → Canonical/Headline Dedup → Article Fetch → Store → Classifier → Semantic Dedup → Image Validate → Queue → Matrix
↓ ↓
Paywall? → Wayback Direct Route (no LLM)
or
Ollama /api/chat (Tier 1 routing)
or
Keyword Gating → Ollama (Tier 2)
Posted message ← ❓ reaction → Explainer → Article Fetch → Ollama summary → Threaded reply
Packages
| Package | Purpose |
|---|---|
internal/config |
YAML config loading with ${ENV} expansion |
internal/storage |
SQLite with WAL, FTS5, all queries |
internal/ingestion |
Per-source RSS polling, feed parsing, image validation |
internal/classifier |
Ollama client, Tier 1/2 prompts, JSON repair, keyword gating |
internal/matrix |
Password auth with device persistence, posting, threaded replies, reaction listener |
internal/poster |
Per-channel metered release queue, reaction tracking, callback hook |
internal/explainer |
❓-reaction → article fetch → Ollama summary → threaded reply |
Post Format
[lead image if valid]
**Headline** → article_url
Lede text from feed
`source name`
Gaming stories append platform tags: `source` · `nintendo-switch` · `pc`
Testing
go test ./...
Description
A Matrix news bot that ingests RSS feeds from curated sources, classifies stories using a local LLM, deduplicates semantically, and routes posts to the appropriate channel.
Languages
Go
67%
JavaScript
19.6%
HTML
8.6%
CSS
4.7%