Skip to content

feat(reddit): add Reddit connector with comment sync and OAuth reply#28

Merged
MaximeGaudin merged 7 commits into
mainfrom
feat/reddit-connector
Jun 22, 2026
Merged

feat(reddit): add Reddit connector with comment sync and OAuth reply#28
MaximeGaudin merged 7 commits into
mainfrom
feat/reddit-connector

Conversation

@MaximeGaudin

@MaximeGaudin MaximeGaudin commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a Reddit connector (void-reddit) that polls configured subreddits and filters posts by keywords and minimum score (Hacker News pattern).
  • Read-only mode uses application-only OAuth (client_credentials) with client_id + client_secret.
  • Commenting mode (optional) runs a browser OAuth authorization_code flow during void setup, stores a refresh_token, syncs post threads with comments, and supports void reply / void send --via reddit.
  • OAuth setup tries a loopback callback on localhost:8765 first; falls back to manual code paste when the port is busy, the browser cannot open, or setup runs on a remote machine.
  • Runtime tuning via void reddit config|subreddits|keywords|min-score without editing config.toml.
  • Plugin registry (refactor: connector plugin registry to eliminate parallel connector conflicts #30): Reddit registers via inventory::submit! in crates/void-cli/src/connectors/reddit.rs — no central ConnectorType enum or ConnectionSettings match arms. Settings are a generic TOML table on [[connections]].

Architecture

  • Feed view: one channel conversation per subreddit (r/{name}); matching posts appear as messages.
  • Thread view (when refresh_token is set): each matching post becomes a Thread conversation with comments as flat messages (metadata.parent_id for tree reconstruction).
  • Reply IDs: conv:msg format via ReplyIdStyle::ConvMsg (same as LinkedIn/Slack).

Config example

[[connections]]
id = "reddit"
type = "reddit"
client_id = "your-reddit-app-client-id"
client_secret = "your-reddit-app-client-secret"
refresh_token = "stored-by-setup-when-commenting-enabled"  # optional
subreddits = ["rust", "programming"]
keywords = ["ai", "llm"]
min_score = 50

[sync]
reddit_poll_interval_secs = 3600

Register a web app at https://www.reddit.com/prefs/apps with redirect URI http://localhost:8765.

Usage

void setup                              # add Reddit; optionally enable commenting
void reddit subreddits add "rust"
void reddit keywords add "ai,llm"
void reddit min-score 50
void sync --daemon
void reply <message-id> --message "Thanks!"
void send --via reddit --to <post-id> --message "Great post!"

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace
  • API: client_credentials + refresh_token grants, comment tree parsing, post_comment (wiremock)
  • Sync: filter logic, thread/message builders, comment metadata
  • Config: refresh_token parsing, secret redaction, backward compat without refresh_token
  • CLI: void reddit commands, Reddit build_reply_id conv:msg format
  • OAuth setup: callback parsing, state validation, manual code extraction
  • Registry: Reddit plugin validation, build via by_id("reddit"), badge RD

MaximeGaudin and others added 4 commits June 21, 2026 17:10
Monitor configured subreddits via Reddit application-only OAuth,
filtering posts by keywords and minimum score. Mirrors the Hacker News
read-only feed pattern with per-subreddit conversations, setup wizard
integration, and wiremock-backed API tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
The skill now detects whether it's running with a full local repo clone
(local mode) or as a cloud agent with only gh CLI access (remote mode),
and provides alternative commands for each step accordingly.

Co-authored-by: Cursor <cursoragent@cursor.com>
…score

Mirror the Hacker News `void hn` pattern with `void reddit` (alias `rd`):
config show, subreddit/keyword list/add/remove/set, and min-score updates.
Read-only list/config commands use the local cache in remote mode; writes proxy to the server.

Co-authored-by: Cursor <cursoragent@cursor.com>
Upgrade the Reddit connector from read-only feed monitoring to full
comment thread sync and CLI replies using OAuth authorization_code flow.

- Add optional refresh_token to config; setup opens browser on localhost:8765
  with manual code paste fallback for remote/headless environments
- Dual auth in RedditClient: client_credentials (read-only) or refresh_token
  (read/submit/identity scopes for commenting)
- Sync matching posts as Thread conversations with nested comments
- Implement reply() and send_message() on RedditConnector
- Change Reddit build_reply_id to conv:msg format (LinkedIn pattern)
- Add wiremock API, sync, config, OAuth, and CLI tests
- Update docs and README for commenting workflow

Co-authored-by: Cursor <cursoragent@cursor.com>
@MaximeGaudin MaximeGaudin changed the title feat(reddit): add read-only Reddit connector feat(reddit): add Reddit connector with comment sync and OAuth reply Jun 21, 2026
MaximeGaudin and others added 3 commits June 21, 2026 18:14
…eply docs

- Add a CHANGELOG [Unreleased] entry for the Reddit connector.
- Remove the dead `RedditError` enum; the crate threads `anyhow::Result`
  end-to-end and never referenced it.
- Document that `void reply` targets synced thread messages and that feed
  posts are commented on via `void send --via reddit --to <post-id>`.

Co-authored-by: Cursor <cursoragent@cursor.com>
Merge main after #30: register Reddit via inventory::submit! plugin
descriptor, migrate settings to generic TOML tables, and align with
ConnectorType string identity and Message.is_saved field.

Co-authored-by: Cursor <cursoragent@cursor.com>
@MaximeGaudin MaximeGaudin enabled auto-merge June 22, 2026 06:06
@MaximeGaudin MaximeGaudin disabled auto-merge June 22, 2026 06:06
@MaximeGaudin MaximeGaudin merged commit f331bc8 into main Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant