Documentation
¶
Overview ¶
Package feeds assembles a source.Registry from user configuration, wiring every provider adapter into one place. The application builds a registry once and then drives it via source.Registry.Aggregate over the user's subscriptions.
Providers that work anonymously (Reddit, Hacker News, Bluesky, RSS/Atom) are always registered. Best-effort scrapers (Instagram, TikTok) register too but take optional credentials. Providers that require mandatory endpoint config (Mastodon instance, Usenet server) register only when that config is present.
When Options.Recorder is set, one shared request-logging *http.Client is built (a browser-fingerprint client whose transport records every round trip into the recorder) and threaded through every HTTP provider, so the in-app Network log can show exactly what each provider fetched. The Usenet NNTP transport is not HTTP and is not logged; only its Newznab indexer path (HTTP) is.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MediaClient ¶
MediaClient returns the HTTP client to fetch media bytes with (feed thumbnails), logging into rec when non-nil. It is deliberately the same browser-fingerprint client the providers use: media hosts fingerprint the TLS handshake exactly as the API hosts do, so a stock http.Client would silently fetch nothing. Unlike loggedClient it always returns a usable client, because a caller without a recorder still has to download.
Its transport is wrapped so every media request carries a desktop-browser User-Agent (browserUATransport): browserhttp impersonates Chrome's TLS handshake but, by its own documented contract, leaves the User-Agent to the caller, and media hosts that gate on it — reddit's preview.redd.it and i.redd.it among them — answer a stock Go client's default UA with 403, so the thumbnail silently never loads. Wrapping here fixes every media fetch at once (card thumbnails and animated GIFs alike) rather than at each call site, and, because the wrap sits outside the logging transport, the Network log records the real UA that went out.
Types ¶
type Options ¶
type Options struct {
// RedditSessionCookie authenticates Reddit reads with the user's own
// logged-in browser reddit_session cookie (bare value or a full
// "reddit_session=...; ..." Cookie string). Reddit's self-serve OAuth
// registration is effectively closed to new personal projects, so the cookie
// — which the reader can import straight from Firefox — is the practical path
// for individual read-only use. Empty falls back to the anonymous ".json"
// endpoints.
RedditSessionCookie string
// MastodonInstance (e.g. "https://mastodon.social") enables the Mastodon
// provider; MastodonToken optionally authenticates it.
MastodonInstance string
MastodonToken string
// LemmyInstance (e.g. "https://lemmy.world") enables the Lemmy provider.
LemmyInstance string
// UsenetAddr ("host:port") enables the Usenet provider; UsenetTLS selects
// implicit TLS. UsenetUsername + UsenetPassword supply AUTHINFO credentials
// for modern servers (Eternal-September text, XSUsenet binary); leave empty to
// connect anonymously to a legacy binary server (Free). UsenetIndexerURL +
// UsenetIndexerAPIKey additionally enable Newznab "search:" queries (direct
// indexer or NZBHydra2).
UsenetAddr string
UsenetTLS bool
UsenetUsername string
UsenetPassword string
UsenetIndexerURL string
UsenetIndexerAPIKey string
// Optional best-effort credentials.
InstagramSession string
TikTokMSToken string
TikTokSession string
// TwitterSession is the user's logged-in X cookie string
// ("auth_token=…; ct0=…"), which unlocks the authenticated home timeline
// (the "home"/"following" channels). Public timelines need no credential.
TwitterSession string
// Recorder, when set, captures every provider's HTTP exchanges so the app's
// Network log can display them. Nil disables logging (each provider keeps its
// own default client).
Recorder *httplog.Recorder
// SourceConcurrency caps how many subscriptions fetch at once during an
// aggregate refresh. 0 lets the registry pick its default. It exists so a
// profile with many subscriptions does not open one HTTP request per
// subscription simultaneously.
SourceConcurrency int
// FeedCacheDir, when set, is the directory the feed cache persists to, so a
// relaunch serves each subscription's last posts and skips re-fetching what is
// still fresh. Empty keeps the cache in memory only (the one-shot CLI paths and
// tests). The window front-end sets it to the per-user cache dir.
FeedCacheDir string
}
Options configures which providers get registered and with what credentials.