Reader read-aloud now streams neural WAV audio from a new POST /api/tts endpoint that shells out to Piper, instead of the browser's Web Speech voice. Each paragraph is synthesized on demand with the next one prefetched during playback, keeping the existing highlight/scroll sync. Voices are configured under [web.tts] (piper binary + voices_dir + a labelled voice list) and exposed to the client as window.PETE_TTS; the reader gets a Voice selector in the Aa menu, persisted per-device. Still a signed-in-only perk and gated on auth.
275 lines
8.2 KiB
TOML
275 lines
8.2 KiB
TOML
[matrix]
|
|
homeserver = "https://matrix.example.org"
|
|
user_id = "@pete:matrix.example.org"
|
|
password = "${PETE_PASSWORD}"
|
|
pickle_key = "${PETE_PICKLE_KEY}"
|
|
display_name = "Pete"
|
|
data_dir = "./data"
|
|
admin_room = "!adminroomid:matrix.example.org"
|
|
# Matrix user IDs allowed to run in-room commands like !post. Empty disables
|
|
# commands entirely (channel rooms may be public, so empty != "anyone").
|
|
admins = ["@you:matrix.example.org"]
|
|
|
|
[matrix.channels]
|
|
tech = "!techroomid:matrix.example.org"
|
|
politics = "!politicsroomid:matrix.example.org"
|
|
gaming = "!gamingroomid:matrix.example.org"
|
|
lego = "!legoroomid:matrix.example.org"
|
|
|
|
[posting]
|
|
enabled = true # master switch for auto-posting news to Matrix; false = web-only (commands still work)
|
|
min_interval_seconds = 300
|
|
burst_cap_count = 3
|
|
burst_cap_window_seconds = 1800
|
|
daily_cap_total = 5 # hard global cap across ALL channels (rolling 24h); 0 disables
|
|
|
|
[posting.round_robin]
|
|
enabled = false # when true, replaces immediate posting with paced rotation
|
|
interval_hours = 4 # one story per N hours, cycling through channels in sorted order
|
|
|
|
[storage]
|
|
db_path = "./data/pete.db"
|
|
recent_window_hours = 24
|
|
|
|
[web]
|
|
enabled = true
|
|
listen_addr = ":8080"
|
|
site_title = "Pete"
|
|
base_url = "https://news.parodia.dev"
|
|
|
|
# OIDC subjects allowed to view the owner-facing source-health dashboard at
|
|
# /status (per-feed poll status, failures, content stats). Requires web.auth
|
|
# below. Empty = /status returns 404 for everyone. Find a user's subject in the
|
|
# server logs ("auth: user signed in" sub=...) after they sign in once.
|
|
admin_subs = []
|
|
|
|
# Optional OIDC sign-in (Authentik). When enabled, signed-in users get their
|
|
# preferences (hidden feeds, weather location, toggles) stored server-side keyed
|
|
# by their OIDC subject and synced across devices. Anonymous visitors keep using
|
|
# browser localStorage — the site stays public. If the provider is unreachable
|
|
# at startup, Pete logs a warning and serves anonymously rather than refusing to
|
|
# boot. Create an OAuth2/OIDC provider + application in Authentik, set the
|
|
# redirect URI to <base_url>/auth/callback, then fill these in.
|
|
[web.auth]
|
|
enabled = false
|
|
issuer = "https://authentik.parodia.dev/application/o/pete/"
|
|
client_id = "${PETE_OIDC_CLIENT_ID}"
|
|
client_secret = "${PETE_OIDC_CLIENT_SECRET}"
|
|
redirect_url = "https://news.parodia.dev/auth/callback"
|
|
# HMAC key that signs the session cookie. Generate with: openssl rand -hex 32
|
|
session_secret = "${PETE_SESSION_SECRET}"
|
|
|
|
# Optional Web Push digests. When enabled, signed-in users can opt in (from the
|
|
# feed-settings panel) to a periodic "N new stories" notification, delivered via
|
|
# a service worker so it also works when the site is installed as a PWA. Push is
|
|
# signed-in only, so it needs web.auth above; it does nothing otherwise.
|
|
# Generate the VAPID keypair once with: pete -genvapid
|
|
# then paste the two keys here (the private key is a secret — treat it like a
|
|
# password). subject identifies you to the push service (a mailto: or https: URL).
|
|
[web.push]
|
|
enabled = false
|
|
vapid_public_key = "${PETE_VAPID_PUBLIC_KEY}"
|
|
vapid_private_key = "${PETE_VAPID_PRIVATE_KEY}"
|
|
subject = "mailto:admin@parodia.dev"
|
|
# How often the sender wakes to look for new stories per subscriber (minutes).
|
|
interval_minutes = 360
|
|
# Smallest number of new (non-hidden) stories that triggers a digest, so a lone
|
|
# item doesn't ping everyone.
|
|
min_stories = 3
|
|
|
|
# Server-side neural read-aloud (Piper, https://github.com/rhasspy/piper).
|
|
# When enabled, the reader's "Listen" button streams real Piper voices instead
|
|
# of the browser's robotic Web Speech voice. Signed-in only, so it needs
|
|
# [web.auth] on too. Install the piper binary and one or more voice models
|
|
# (<id>.onnx + <id>.onnx.json) into voices_dir first.
|
|
[web.tts]
|
|
enabled = false
|
|
piper_bin = "/opt/piper/piper" # path to the piper executable
|
|
voices_dir = "/opt/piper/voices" # dir holding <id>.onnx (+ .onnx.json) models
|
|
default = "en_US-amy-medium" # voice id selected until the reader picks another
|
|
# List the voices to offer, in menu order. Omit the whole [[web.tts.voices]]
|
|
# list to auto-discover every *.onnx in voices_dir (labelled by filename).
|
|
[[web.tts.voices]]
|
|
id = "en_US-amy-medium"
|
|
label = "Amy (US, female)"
|
|
[[web.tts.voices]]
|
|
id = "en_US-ryan-high"
|
|
label = "Ryan (US, male, HQ)"
|
|
|
|
# Every enabled source MUST set direct_route to a key from [matrix.channels] above.
|
|
# There is no automatic classification — Pete posts each story to its configured channel.
|
|
# Optional: language = "en" drops feed items whose per-item <language> tag is
|
|
# set and doesn't match (prefix). Useful for multilingual feeds like Politico
|
|
# Europe that publish the same story in en / fr / de side-by-side. Items with
|
|
# no language tag pass through unchanged.
|
|
[[sources]]
|
|
name = "The Guardian — World"
|
|
feed_url = "https://www.theguardian.com/world/rss"
|
|
tier = 1
|
|
poll_interval_minutes = 20
|
|
direct_route = "politics"
|
|
enabled = true
|
|
# Optional: override the User-Agent for this feed only. Leave unset to use
|
|
# Pete's honest bot UA; set a browser string for feeds whose WAF blocks bots.
|
|
# user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
|
|
|
|
[[sources]]
|
|
name = "The Guardian — Politics"
|
|
feed_url = "https://www.theguardian.com/politics/rss"
|
|
tier = 1
|
|
poll_interval_minutes = 20
|
|
direct_route = "politics"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "The Guardian — US News"
|
|
feed_url = "https://www.theguardian.com/us-news/rss"
|
|
tier = 1
|
|
poll_interval_minutes = 20
|
|
direct_route = "politics"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Ars Technica"
|
|
feed_url = "https://feeds.arstechnica.com/arstechnica/index"
|
|
tier = 1
|
|
poll_interval_minutes = 20
|
|
direct_route = "tech"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Time Extension"
|
|
feed_url = "https://www.timeextension.com/feeds/news"
|
|
tier = 1
|
|
poll_interval_minutes = 20
|
|
direct_route = "gaming"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Pitchfork"
|
|
feed_url = "https://pitchfork.com/feed/feed-news/rss"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "music"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "The Quietus"
|
|
feed_url = "https://thequietus.com/feed"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "music"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Consequence"
|
|
feed_url = "https://consequence.net/feed/"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "music"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Brooklyn Vegan"
|
|
feed_url = "https://www.brooklynvegan.com/feed/"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "music"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "NME — Music"
|
|
feed_url = "https://www.nme.com/news/music/feed"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "music"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "BBC Newsround"
|
|
feed_url = "http://feeds.bbci.co.uk/newsround/rss.xml"
|
|
tier = 1
|
|
poll_interval_minutes = 30
|
|
direct_route = "kids"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "DOGO News"
|
|
feed_url = "https://www.dogonews.com/articles.rss"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "kids"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Science News Explores"
|
|
feed_url = "https://www.snexplores.org/feed"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "kids"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "NASA for Students"
|
|
feed_url = "https://www.nasa.gov/rss/dyn/educationnews.rss"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "kids"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Post-Punk.com"
|
|
feed_url = "https://post-punk.com/feed/"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "music"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Naked Capitalism"
|
|
feed_url = "https://www.nakedcapitalism.com/feed"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "finance"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Wolf Street"
|
|
feed_url = "https://wolfstreet.com/feed/"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "finance"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "MarketBeat"
|
|
feed_url = "https://www.marketbeat.com/feed/"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "finance"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Seeking Alpha"
|
|
feed_url = "https://seekingalpha.com/feed.xml"
|
|
tier = 2
|
|
poll_interval_minutes = 60
|
|
direct_route = "finance"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "Brickset"
|
|
feed_url = "https://brickset.com/feed"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "lego"
|
|
enabled = true
|
|
|
|
[[sources]]
|
|
name = "The Brick Fan"
|
|
feed_url = "https://www.thebrickfan.com/feed/"
|
|
tier = 1
|
|
poll_interval_minutes = 60
|
|
direct_route = "lego"
|
|
enabled = true
|