Skip to content

Bluesky, X/Twitter & Reddit

Open-web social channels that respond to mentions, DMs, and replies.

This page covers Revka’s three open-web social channels: Bluesky (AT Protocol), X/Twitter (API v2), and Reddit (OAuth2). Unlike workspace chat or messaging apps, these channels operate on public social networks: your agent watches its own notification feed, and responds when it is @-mentioned, replied to, or (on X and Reddit) sent a direct message. Use this page when you need exact config keys, auth model, and the mention-driven behavior for each platform.

All three are polling channels. None requires a public inbound port — Revka connects out to the platform API on a short interval and pulls new notifications, so there is no webhook to expose. All three are always compiled in (no Cargo feature flag). Configure them under [channels_config] in ~/.revka/config.toml, then restart the daemon to apply changes. For the trait model, delivery modes, and allowlist semantics shared by every channel, see Channels overview.

ChannelAuthReceive modePoll intervalTriggers onDMs
BlueskyApp password → JWTNotification polling5smentions, repliesNo
X/TwitterOAuth 2.0 bearer tokenMentions-timeline polling15smentionsSend only
RedditOAuth2 refresh tokenInbox polling5susername mentions, comment replies, DMsYes

The Bluesky channel speaks the AT Protocol against https://bsky.social/xrpc. It authenticates with your handle and an app password, polls your notification feed every 5 seconds, and replies as a post that threads onto the original. It only acts on notifications whose reason is mention or reply — likes, follows, and reposts are ignored.

[channels_config.bluesky]
handle = "mybot.bsky.social"
app_password = "xxxx-xxxx-xxxx-xxxx"
FieldTypeDefaultMeaning
handlestringYour bot’s Bluesky handle, e.g. mybot.bsky.social. Required.
app_passwordstringAn app-specific password generated in Bluesky settings. Required.

Bluesky does not use your main account password. Generate a scoped, revocable app password under Settings → Privacy and security → App passwords in the Bluesky client, then put it in app_password.

On startup Revka calls com.atproto.server.createSession with the handle and app password and receives a short-lived access JWT plus a longer-lived refresh JWT. AT Protocol access tokens last roughly two hours; Revka refreshes well before expiry via com.atproto.server.refreshSession, and if a refresh ever fails it falls back to a full re-authentication automatically. You do not manage tokens yourself — only the app password.

  • Revka polls app.bsky.notification.listNotifications (25 at a time) every 5 seconds.
  • Only mention and reply notifications are processed. The bot’s own posts and already-read notifications are skipped.
  • Each handled notification is marked seen via app.bsky.notification.updateSeen, so it is not reprocessed.
  • Replies are created with com.atproto.repo.createRecord into the app.bsky.feed.post collection, with the reply’s root/parent set so the post threads correctly.
  • Posts are capped at Bluesky’s 300-character limit; longer agent output is truncated with a trailing ....

This channel has no DM support — it works purely through public mentions and replies.

The X/Twitter channel uses the X (Twitter) API v2 at https://api.x.com/2 with an OAuth 2.0 app-only bearer token. It watches the authenticated bot account’s mentions, strips the leading @bot token, and replies in-thread. Sending also supports direct messages.

[channels_config.twitter]
bearer_token = "AAAA..."
allowed_users = ["*"]
FieldTypeDefaultMeaning
bearer_tokenstringOAuth 2.0 app-only (app-level) bearer token from your X developer app. Required.
allowed_usersarray[]Usernames or numeric user IDs allowed to interact. [] denies all; ["*"] allows everyone.

The bearer token is app-level, not tied to a single end user. Create an X developer app, then copy its OAuth 2.0 bearer token into bearer_token. On startup Revka calls GET /2/users/me to resolve the authenticated account’s user ID — this both validates the token (it is also the health check) and identifies which mentions belong to the bot.

  • Revka polls GET /2/users/{id}/mentions every 15 seconds, requesting author_id, conversation_id, and created_at, and expanding author usernames. It uses since_id so each poll only returns tweets newer than the last one seen.
  • The bot’s own tweets are skipped, and a 10,000-entry deduplication set prevents reprocessing the same tweet.
  • The sender (resolved username, falling back to author ID) is checked against allowed_users.
  • The leading @bot (and any other leading @mentions) is stripped from the text before it reaches the model; if nothing remains, the mention is ignored.
  • Replies are posted via POST /2/tweets with reply.in_reply_to_tweet_id. Output longer than the 280-character tweet limit is split at word boundaries into a reply thread, each tweet chained to the previous one.

Outbound send chooses its action from the recipient prefix:

  • tweet:<id> (or a bare tweet ID) → a threaded reply via POST /2/tweets.
  • dm:<user_id> → a direct message via POST /2/dm_conversations/with/{user_id}/messages.

Inbound traffic for this channel is mention-driven; DMs are primarily an outbound capability your agent or workflows can use.

The Reddit channel is an OAuth2 bot that polls the account’s unread inbox every 5 seconds and handles three kinds of items: username mentions, comment replies, and private messages (DMs). It replies to comments as comments and to DMs as DMs. An optional subreddit filter narrows it to a single community.

[channels_config.reddit]
client_id = "reddit-client-id"
client_secret = "reddit-client-secret"
refresh_token = "reddit-refresh-token"
username = "MyBotName"
subreddit = "MySubreddit" # optional
FieldTypeDefaultMeaning
client_idstringOAuth2 client ID from your Reddit app. Required.
client_secretstringOAuth2 client secret from your Reddit app. Required.
refresh_tokenstringLong-lived OAuth2 refresh token for persistent access. Required.
usernamestringThe bot’s Reddit username, without the u/ prefix. Used for self-message filtering. Required.
subredditstringOptional filter (without the r/ prefix). When set, only items from this subreddit are processed.

Reddit issues short-lived access tokens, so Revka uses a refresh token for durable access. On startup and whenever the access token nears expiry, Revka POSTs to https://www.reddit.com/api/v1/access_token with HTTP Basic auth (client_id:client_secret) and grant_type=refresh_token, then uses the returned access token as a bearer credential against https://oauth.reddit.com. The token is cached and refreshed automatically (60 seconds before its stated expiry). All requests send a descriptive User-Agent, as Reddit requires.

  • Revka polls GET /message/unread (25 at a time) every 5 seconds, then marks the fetched items read via POST /api/read_message so they are not reprocessed.
  • Items authored by the bot itself (case-insensitive match on username) and items with an empty body are skipped.
  • If subreddit is set, items from any other subreddit are dropped.
  • Reply routing depends on item type:
    • Comment replies / mentions (Reddit fullnames t1_…, t3_…, t4_…) → a public comment via POST /api/comment with thing_id.
    • Direct messages → a private reply to the author via POST /api/compose (default subject Message from Revka).

X/Twitter uses allowed_users with the standard rules:

  • An empty list ([]) denies all senders.
  • A single "*" entry allows all senders.
  • Otherwise, only listed usernames or numeric user IDs are accepted.

Bluesky and Reddit do not expose an allowed_users field of their own. Bluesky responds to any non-self mention/reply; Reddit responds to any non-self inbox item, optionally narrowed by subreddit. Start by confirming delivery, then use the subreddit filter (Reddit) or X’s allowed_users to tighten who your agent answers. For the full allowlist model across channels, see Channels overview.

  • Bot connects but never replies. Confirm the trigger type is supported: Bluesky and Reddit act on mentions/replies/DMs, not likes or upvotes. For X, make sure the sender passes allowed_users (use ["*"] while testing).
  • X bot goes quiet. Check for 429 rate-limit back-off in the logs; the free tier is easily exhausted. Verify the bearer token with the users/me call that runs at startup and as the health check.
  • Reddit auth fails on boot. A failed token refresh means the refresh_token, client_id, or client_secret is wrong or revoked — re-run the external OAuth2 flow to mint a fresh refresh token.
  • Bluesky auth fails. Regenerate the app password in Bluesky settings; an expired or revoked app password breaks createSession.

Raise per-channel log detail to diagnose:

Terminal window
RUST_LOG=revka::channels::bluesky=debug,revka::channels::twitter=debug,revka::channels::reddit=debug revka daemon