fix(hands): migrate Reddit auth to client_credentials, document Twitter OAuth 1.0a

Reddit:
- Remove REDDIT_USERNAME and REDDIT_PASSWORD requirements (deprecated
  password grant)
- Switch to grant_type=client_credentials (app-only auth)
- Update system_prompt and SKILL.md auth examples

Twitter:
- Add optional OAuth 1.0a credentials (API key/secret, access
  token/secret) for user-context operations
- Document Bearer Token vs OAuth 1.0a authentication modes in SKILL.md
- Note which endpoints require user-context auth
This commit is contained in:
Evan Hu committed 2026-03-22 20:28:16 +09:00
1 parent 8923c4e0c3
commit fb45e39dcb
4 files changed
+94 -43

No files matched your search

+5 -40
View File
@@ -44,7 +44,7 @@ steps = [
"Select 'script' as the app type", "Select 'script' as the app type",
"Fill in name and redirect URI (http://localhost:8080)", "Fill in name and redirect URI (http://localhost:8080)",
"Copy the client ID (under the app name) and secret", "Copy the client ID (under the app name) and secret",
"Set REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, REDDIT_USERNAME, and REDDIT_PASSWORD as environment variables", "Set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET as environment variables",
"Restart LibreFang or reload config for the change to take effect", "Restart LibreFang or reload config for the change to take effect",
] ]
@@ -70,42 +70,6 @@ steps = [
"Restart LibreFang or reload config for the change to take effect", "Restart LibreFang or reload config for the change to take effect",
] ]
[[requires]]
key = "REDDIT_USERNAME"
label = "Reddit Username"
requirement_type = "api_key"
check_value = "REDDIT_USERNAME"
description = "Reddit account username. Required for OAuth2 password-grant authentication to post and comment."
[requires.install]
signup_url = "https://www.reddit.com/register"
docs_url = "https://www.reddit.com/dev/api/"
env_example = "REDDIT_USERNAME=your_reddit_username"
estimated_time = "1-2 min"
steps = [
"Use the Reddit account username you want the hand to act as",
"Set REDDIT_USERNAME as an environment variable",
"Restart LibreFang or reload config for the change to take effect",
]
[[requires]]
key = "REDDIT_PASSWORD"
label = "Reddit Password"
requirement_type = "api_key"
check_value = "REDDIT_PASSWORD"
description = "Reddit account password. Required for OAuth2 password-grant authentication to post and comment."
[requires.install]
signup_url = "https://www.reddit.com/register"
docs_url = "https://www.reddit.com/dev/api/"
env_example = "REDDIT_PASSWORD=your_reddit_password"
estimated_time = "1-2 min"
steps = [
"Use the password for the Reddit account configured in REDDIT_USERNAME",
"Set REDDIT_PASSWORD as an environment variable",
"Restart LibreFang or reload config for the change to take effect",
]
# ─── Configurable settings ─────────────────────────────────────────────────── # ─── Configurable settings ───────────────────────────────────────────────────
[[settings]] [[settings]]
@@ -260,19 +224,20 @@ Detect the operating system:
python -c "import platform; print(platform.system())" python -c "import platform; print(platform.system())"
``` ```
Authenticate with Reddit API using OAuth2: Authenticate with Reddit API using OAuth2 client_credentials (app-only auth):
``` ```
curl -s -X POST "https://www.reddit.com/api/v1/access_token" \ curl -s -X POST "https://www.reddit.com/api/v1/access_token" \
-u "$REDDIT_CLIENT_ID:$REDDIT_CLIENT_SECRET" \ -u "$REDDIT_CLIENT_ID:$REDDIT_CLIENT_SECRET" \
-d "grant_type=password&username=$REDDIT_USERNAME&password=$REDDIT_PASSWORD" \ -d "grant_type=client_credentials" \
-A "LibreFang Reddit Hand/1.0" \ -A "LibreFang Reddit Hand/1.0" \
-o reddit_auth.json -o reddit_auth.json
``` ```
Extract the access_token from the response for subsequent API calls. Extract the access_token from the response for subsequent API calls.
Note: This is app-only authentication — most read endpoints work, but posting and commenting require the app's identity. No user-level context is available.
Recover state: Recover state:
1. memory_recall `reddit_hand_state` — load previous monitoring history and stats 1. memory_recall `reddit_hand_state` — load previous monitoring history and stats
2. Read **User Configuration** for subreddits, monitor_mode, content_style, approval_mode, etc. 2. Read **Hand Settings** for subreddits, monitor_mode, content_style, approval_mode, etc.
3. file_read `reddit_queue.json` if it exists — pending posts/replies 3. file_read `reddit_queue.json` if it exists — pending posts/replies
4. knowledge_query for previously tracked threads and engagement data 4. knowledge_query for previously tracked threads and engagement data
+5 -3
View File
@@ -13,15 +13,17 @@ runtime: prompt_only
Reddit API requires OAuth2 authentication for all endpoints. Reddit API requires OAuth2 authentication for all endpoints.
**Step 1: Get access token**: **Step 1: Get access token (app-only / client_credentials)**:
```bash ```bash
curl -s -X POST "https://www.reddit.com/api/v1/access_token" \ curl -s -X POST "https://www.reddit.com/api/v1/access_token" \
-u "$REDDIT_CLIENT_ID:$REDDIT_CLIENT_SECRET" \ -u "$REDDIT_CLIENT_ID:$REDDIT_CLIENT_SECRET" \
-d "grant_type=password&username=$REDDIT_USERNAME&password=$REDDIT_PASSWORD" \ -d "grant_type=client_credentials" \
-A "LibreFang Reddit Hand/1.0" -A "LibreFang Reddit Hand/1.0"
``` ```
Response: `{"access_token": "...", "token_type": "bearer", "expires_in": 86400, "scope": "*"}` Response: `{"access_token": "...", "token_type": "bearer", "expires_in": 86400, "scope": "*"}`
> **Note:** `client_credentials` provides app-only access. Most read endpoints (listing posts, fetching comments, searching) work normally. Posting and commenting use the app's identity. For full user-level actions (e.g., voting, managing subscriptions), the more complex OAuth2 authorization code flow is required.
**All subsequent requests** must include: **All subsequent requests** must include:
``` ```
Authorization: Bearer $ACCESS_TOKEN Authorization: Bearer $ACCESS_TOKEN
@@ -101,7 +103,7 @@ curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
| Type | Limit | Window | | Type | Limit | Window |
|------|-------|--------| |------|-------|--------|
| OAuth authenticated | 10 requests | 1 minute | | OAuth authenticated | 10 requests | 1 minute |
| With user-level auth | 30 requests | 1 minute | | With app-only auth | 30 requests | 1 minute |
| Posting | ~1 post | 10 minutes (varies by karma) | | Posting | ~1 post | 10 minutes (varies by karma) |
| Commenting | ~1 comment | varies by karma | | Commenting | ~1 comment | varies by karma |
+78
View File
@@ -59,6 +59,84 @@ steps = [
"Restart LibreFang or reload config for the change to take effect", "Restart LibreFang or reload config for the change to take effect",
] ]
[[requires]]
key = "TWITTER_API_KEY"
label = "Twitter API Key (Consumer Key)"
requirement_type = "api_key"
check_value = "TWITTER_API_KEY"
optional = true
description = "OAuth 1.0a Consumer Key. Optional — only needed for user-context operations (liking, retweeting, following as a specific user)."
[requires.install]
signup_url = "https://developer.twitter.com/en/portal/dashboard"
docs_url = "https://developer.twitter.com/en/docs/authentication/oauth-1-0a"
env_example = "TWITTER_API_KEY=your_api_key_here"
estimated_time = "5-10 min"
steps = [
"Go to developer.twitter.com and open your App settings",
"Navigate to 'Keys and tokens' page",
"Copy the 'API Key' (also called Consumer Key)",
"Set it as TWITTER_API_KEY environment variable",
]
[[requires]]
key = "TWITTER_API_SECRET"
label = "Twitter API Secret (Consumer Secret)"
requirement_type = "api_key"
check_value = "TWITTER_API_SECRET"
optional = true
description = "OAuth 1.0a Consumer Secret. Optional — only needed alongside TWITTER_API_KEY for user-context operations."
[requires.install]
signup_url = "https://developer.twitter.com/en/portal/dashboard"
docs_url = "https://developer.twitter.com/en/docs/authentication/oauth-1-0a"
env_example = "TWITTER_API_SECRET=your_api_secret_here"
estimated_time = "2-3 min"
steps = [
"On the same 'Keys and tokens' page as the API Key",
"Copy the 'API Secret' (also called Consumer Secret)",
"Set it as TWITTER_API_SECRET environment variable",
]
[[requires]]
key = "TWITTER_ACCESS_TOKEN"
label = "Twitter Access Token"
requirement_type = "api_key"
check_value = "TWITTER_ACCESS_TOKEN"
optional = true
description = "OAuth 1.0a user Access Token. Optional — only needed for user-context operations."
[requires.install]
signup_url = "https://developer.twitter.com/en/portal/dashboard"
docs_url = "https://developer.twitter.com/en/docs/authentication/oauth-1-0a"
env_example = "TWITTER_ACCESS_TOKEN=your_access_token_here"
estimated_time = "2-3 min"
steps = [
"On the 'Keys and tokens' page, scroll to 'Authentication Tokens'",
"Generate an Access Token and Secret",
"Copy the Access Token",
"Set it as TWITTER_ACCESS_TOKEN environment variable",
]
[[requires]]
key = "TWITTER_ACCESS_TOKEN_SECRET"
label = "Twitter Access Token Secret"
requirement_type = "api_key"
check_value = "TWITTER_ACCESS_TOKEN_SECRET"
optional = true
description = "OAuth 1.0a user Access Token Secret. Optional — only needed alongside TWITTER_ACCESS_TOKEN for user-context operations."
[requires.install]
signup_url = "https://developer.twitter.com/en/portal/dashboard"
docs_url = "https://developer.twitter.com/en/docs/authentication/oauth-1-0a"
env_example = "TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret_here"
estimated_time = "2-3 min"
steps = [
"Generated alongside the Access Token above",
"Copy the Access Token Secret",
"Set it as TWITTER_ACCESS_TOKEN_SECRET environment variable",
]
# ─── Configurable settings ─────────────────────────────────────────────────── # ─── Configurable settings ───────────────────────────────────────────────────
[[settings]] [[settings]]
+6
View File
@@ -19,6 +19,12 @@ Authorization: Bearer $TWITTER_BEARER_TOKEN
**Environment variable**: `TWITTER_BEARER_TOKEN` **Environment variable**: `TWITTER_BEARER_TOKEN`
**Authentication modes**:
- **Bearer Token only** (default): Sufficient for posting tweets, reading timelines, and searching. All core functionality works with just the Bearer Token.
- **Bearer Token + OAuth 1.0a** (optional): Required for user-context operations such as liking tweets, retweeting, following/unfollowing, and accessing DMs. Set `TWITTER_API_KEY`, `TWITTER_API_SECRET`, `TWITTER_ACCESS_TOKEN`, and `TWITTER_ACCESS_TOKEN_SECRET` to enable these features.
> **Note**: The Like endpoint (`POST /2/users/:id/likes`) and Retweet endpoint (`POST /2/users/:id/retweets`) require OAuth 1.0a User Context authentication. If only Bearer Token is configured, these operations will be skipped with a warning.
### Core Endpoints ### Core Endpoints
**Get authenticated user info**: **Get authenticated user info**: