social-autoposter 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/SKILL.md +317 -0
- package/bin/cli.js +158 -0
- package/config.example.json +51 -0
- package/launchd/com.m13v.social-autoposter.plist +28 -0
- package/launchd/com.m13v.social-engage.plist +28 -0
- package/launchd/com.m13v.social-stats.plist +28 -0
- package/package.json +42 -0
- package/schema.sql +82 -0
- package/scripts/find_threads.py +194 -0
- package/scripts/scan_replies.py +370 -0
- package/scripts/update_stats.py +208 -0
- package/setup/SKILL.md +180 -0
- package/skill/SKILL.md +283 -0
- package/skill/engage.sh +109 -0
- package/skill/run.sh +41 -0
- package/skill/stats.sh +38 -0
- package/syncfield.sh +78 -0
package/setup/SKILL.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: social-autoposter-setup
|
|
3
|
+
description: "Set up social-autoposter for a new user. Interactive wizard that creates the database, configures accounts, verifies browser logins, and optionally sets up scheduled automation. Use when: 'set up social autoposter', 'install social autoposter', 'configure social posting'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Social Autoposter Setup
|
|
7
|
+
|
|
8
|
+
Interactive setup wizard for social-autoposter. Walk the user through configuration step by step.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- First-time setup of social-autoposter
|
|
13
|
+
- Reconfiguring accounts or adding new platforms
|
|
14
|
+
- Troubleshooting a broken setup
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- `sqlite3` available on PATH
|
|
19
|
+
- A browser automation tool (Playwright MCP, Selenium, etc.) for platform login verification
|
|
20
|
+
- Python 3.9+ for running helper scripts
|
|
21
|
+
|
|
22
|
+
## Setup Flow
|
|
23
|
+
|
|
24
|
+
Run these steps in order. Ask the user for input at each step. Don't skip ahead.
|
|
25
|
+
|
|
26
|
+
### Step 1: Locate the installation
|
|
27
|
+
|
|
28
|
+
Check if already installed:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
ls ~/social-autoposter/schema.sql 2>/dev/null && echo "FOUND" || echo "NOT_FOUND"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If NOT_FOUND, install it:
|
|
35
|
+
```bash
|
|
36
|
+
npx social-autoposter init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Set `SKILL_DIR` to `~/social-autoposter`.
|
|
40
|
+
|
|
41
|
+
### Step 2: Create the database
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
sqlite3 "$SKILL_DIR/social_posts.db" < "$SKILL_DIR/schema.sql"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Verify it worked:
|
|
48
|
+
```bash
|
|
49
|
+
sqlite3 "$SKILL_DIR/social_posts.db" "SELECT name FROM sqlite_master WHERE type='table';"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Expected tables: `posts`, `threads`, `our_posts`, `thread_comments`, `replies`.
|
|
53
|
+
|
|
54
|
+
### Step 3: Configure accounts
|
|
55
|
+
|
|
56
|
+
Copy the example config:
|
|
57
|
+
```bash
|
|
58
|
+
cp "$SKILL_DIR/config.example.json" "$SKILL_DIR/config.json"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Ask the user for each platform they want to use:
|
|
62
|
+
|
|
63
|
+
**Reddit:**
|
|
64
|
+
- "What's your Reddit username?" → set `accounts.reddit.username`
|
|
65
|
+
- Login method is always `browser` (Reddit has no public posting API)
|
|
66
|
+
|
|
67
|
+
**X/Twitter:**
|
|
68
|
+
- "What's your X handle?" → set `accounts.twitter.handle`
|
|
69
|
+
- Login method is always `browser`
|
|
70
|
+
|
|
71
|
+
**LinkedIn:**
|
|
72
|
+
- "What's your LinkedIn name?" → set `accounts.linkedin.name`
|
|
73
|
+
- Login method is always `browser`
|
|
74
|
+
|
|
75
|
+
**Moltbook** (optional):
|
|
76
|
+
- "Do you want to set up Moltbook? (y/n)"
|
|
77
|
+
- If yes: "What's your Moltbook username?" and "What's your Moltbook API key?"
|
|
78
|
+
- Save API key to `$SKILL_DIR/.env`: `MOLTBOOK_API_KEY=<key>`
|
|
79
|
+
- Set `accounts.moltbook.username` and `accounts.moltbook.api_key_env`
|
|
80
|
+
|
|
81
|
+
### Step 4: Configure content
|
|
82
|
+
|
|
83
|
+
Ask the user:
|
|
84
|
+
|
|
85
|
+
**Subreddits:**
|
|
86
|
+
- "Which subreddits do you want to post in? (comma-separated)"
|
|
87
|
+
- Default suggestion: `ClaudeAI, programming, webdev, devops`
|
|
88
|
+
|
|
89
|
+
**Content angle:**
|
|
90
|
+
- "Describe your unique experience/perspective in 1-2 sentences. This helps the agent write authentic comments from your point of view."
|
|
91
|
+
- Example: "Building a macOS desktop AI agent. Experience with Swift, Claude API, and browser automation."
|
|
92
|
+
|
|
93
|
+
**Projects** (optional):
|
|
94
|
+
- "Do you have open source projects or products to mention when relevant? (y/n)"
|
|
95
|
+
- If yes, for each project ask: name, description, website URL, GitHub URL, topic keywords
|
|
96
|
+
- Store in `config.json` under `projects` array
|
|
97
|
+
|
|
98
|
+
### Step 5: Verify browser logins
|
|
99
|
+
|
|
100
|
+
For each configured platform, verify the user is logged in:
|
|
101
|
+
|
|
102
|
+
**Reddit:**
|
|
103
|
+
- Navigate to `https://old.reddit.com` using browser automation
|
|
104
|
+
- Check if a username appears in the top-right (logged in) or a "login" link (not logged in)
|
|
105
|
+
- If not logged in, tell the user: "Please log into Reddit in your browser, then say 'done'"
|
|
106
|
+
- Re-check after they confirm
|
|
107
|
+
|
|
108
|
+
**X/Twitter:**
|
|
109
|
+
- Navigate to `https://x.com/home`
|
|
110
|
+
- Check if the home timeline loads (logged in) or a login page appears
|
|
111
|
+
- Same flow if not logged in
|
|
112
|
+
|
|
113
|
+
**LinkedIn:**
|
|
114
|
+
- Navigate to `https://www.linkedin.com/feed/`
|
|
115
|
+
- Check if the feed loads or a login page appears
|
|
116
|
+
|
|
117
|
+
**Moltbook:**
|
|
118
|
+
- Test the API key: `curl -s -H "Authorization: Bearer $MOLTBOOK_API_KEY" "https://www.moltbook.com/api/v1/posts?limit=1"`
|
|
119
|
+
- Check for a successful response
|
|
120
|
+
|
|
121
|
+
Report which platforms are ready and which need attention.
|
|
122
|
+
|
|
123
|
+
### Step 6: Test run (dry run)
|
|
124
|
+
|
|
125
|
+
Run the thread finder to verify everything works:
|
|
126
|
+
```bash
|
|
127
|
+
python3 "$SKILL_DIR/scripts/find_threads.py" --limit 3
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Show the user the candidate threads found. Don't post anything — just verify the pipeline works.
|
|
131
|
+
|
|
132
|
+
### Step 7: Install the skill
|
|
133
|
+
|
|
134
|
+
Create the skill symlink so the agent can find it:
|
|
135
|
+
```bash
|
|
136
|
+
mkdir -p ~/.claude/skills
|
|
137
|
+
rm -rf ~/.claude/skills/social-autoposter
|
|
138
|
+
ln -s "$SKILL_DIR" ~/.claude/skills/social-autoposter
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Step 8: Set up automation (optional)
|
|
142
|
+
|
|
143
|
+
Ask: "Do you want posts to run automatically on a schedule? (y/n)"
|
|
144
|
+
|
|
145
|
+
If yes, and on macOS:
|
|
146
|
+
- Update the launchd plist templates in `$SKILL_DIR/launchd/` with the user's paths
|
|
147
|
+
- Symlink into `~/Library/LaunchAgents/`
|
|
148
|
+
- Load with `launchctl load`
|
|
149
|
+
- Explain: "Posting runs every hour, stats update every 6 hours, reply engagement every 2 hours"
|
|
150
|
+
|
|
151
|
+
If yes, and on Linux:
|
|
152
|
+
- Generate crontab entries:
|
|
153
|
+
```
|
|
154
|
+
0 * * * * cd ~/social-autoposter && bash skill/run.sh
|
|
155
|
+
0 */6 * * * cd ~/social-autoposter && bash skill/stats.sh
|
|
156
|
+
0 */2 * * * cd ~/social-autoposter && bash skill/engage.sh
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If no: "You can run manually anytime with `/social-autoposter`"
|
|
160
|
+
|
|
161
|
+
### Step 9: Summary
|
|
162
|
+
|
|
163
|
+
Print a summary:
|
|
164
|
+
```
|
|
165
|
+
Social Autoposter Setup Complete
|
|
166
|
+
|
|
167
|
+
Database: ~/social-autoposter/social_posts.db
|
|
168
|
+
Config: ~/social-autoposter/config.json
|
|
169
|
+
Skill: ~/.claude/skills/social-autoposter
|
|
170
|
+
|
|
171
|
+
Platforms:
|
|
172
|
+
Reddit: u/USERNAME ✓
|
|
173
|
+
X/Twitter: @HANDLE ✓
|
|
174
|
+
LinkedIn: NAME ✓
|
|
175
|
+
Moltbook: USERNAME ✓
|
|
176
|
+
|
|
177
|
+
Automation: launchd (hourly post, 6h stats, 2h engage)
|
|
178
|
+
|
|
179
|
+
Try it: /social-autoposter
|
|
180
|
+
```
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: social-autoposter
|
|
3
|
+
description: "Automate social media posting across Reddit, X/Twitter, LinkedIn, and Moltbook. Find threads, post comments, create original posts, track engagement stats. Use when: 'post to social', 'social autoposter', 'find threads to comment on', 'create a post', 'audit social posts', 'update post stats', or after completing any task (mandatory per CLAUDE.md)."
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Social Autoposter
|
|
8
|
+
|
|
9
|
+
Automates finding, posting, and tracking social media comments and original posts across Reddit, X/Twitter, LinkedIn, and Moltbook.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
| Command | What it does |
|
|
14
|
+
|---------|-------------|
|
|
15
|
+
| `/social-autoposter` | Comment run — find threads + post comment + log (cron-safe) |
|
|
16
|
+
| `/social-autoposter post` | Create an original post/thread (manual only, never cron) |
|
|
17
|
+
| `/social-autoposter stats` | Update engagement stats via API |
|
|
18
|
+
| `/social-autoposter engage` | Scan and reply to responses on our posts |
|
|
19
|
+
| `/social-autoposter audit` | Full browser audit of all posts |
|
|
20
|
+
|
|
21
|
+
## Accounts
|
|
22
|
+
|
|
23
|
+
- **Reddit**: u/Deep_Ad1959 (logged in via Google with matt@mediar.ai). Use old.reddit.com.
|
|
24
|
+
- **X/Twitter**: @m13v_
|
|
25
|
+
- **LinkedIn**: Matthew Diakonov
|
|
26
|
+
- **Moltbook**: matthew-autoposter (API key in `~/social-autoposter/.env`)
|
|
27
|
+
|
|
28
|
+
## Our Projects & Links
|
|
29
|
+
|
|
30
|
+
| Project | What it does | Website | GitHub |
|
|
31
|
+
|---------|-------------|---------|--------|
|
|
32
|
+
| Fazm | AI computer agent for macOS | https://fazm.ai | — |
|
|
33
|
+
| Terminator | Desktop automation framework | https://t8r.tech | https://github.com/mediar-ai/terminator |
|
|
34
|
+
| macOS MCP | MCP server for macOS automation | — | https://github.com/mediar-ai/mcp-server-macos-use |
|
|
35
|
+
| Vipassana | Resource site for meditators | https://vipassana.cool | https://github.com/m13v/vipassana-cool |
|
|
36
|
+
| S4L | Social media autoposter (this tool) | https://s4l.ai | https://github.com/m13v/social-autoposter |
|
|
37
|
+
|
|
38
|
+
Prefer website links when one exists (drives signups). Use GitHub for open source tools without a website.
|
|
39
|
+
|
|
40
|
+
## Database
|
|
41
|
+
|
|
42
|
+
- **Path**: `~/social-autoposter/social_posts.db` (also symlinked at `~/.claude/social_posts.db`)
|
|
43
|
+
- **Prompt DB**: `~/claude-prompt-db/prompts.db`
|
|
44
|
+
|
|
45
|
+
## Helper Scripts
|
|
46
|
+
|
|
47
|
+
Standalone Python scripts — no LLM needed.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python3 ~/social-autoposter/scripts/find_threads.py --topic "macOS automation"
|
|
51
|
+
python3 ~/social-autoposter/scripts/scan_replies.py
|
|
52
|
+
python3 ~/social-autoposter/scripts/update_stats.py --quiet
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Workflow: Post (`/social-autoposter`)
|
|
58
|
+
|
|
59
|
+
### 1. Rate limit check
|
|
60
|
+
|
|
61
|
+
```sql
|
|
62
|
+
SELECT COUNT(*) FROM posts WHERE posted_at >= datetime('now', '-24 hours')
|
|
63
|
+
```
|
|
64
|
+
Max 10 posts per 24 hours. Stop if at limit.
|
|
65
|
+
|
|
66
|
+
### 2. Find candidate threads
|
|
67
|
+
|
|
68
|
+
**Option A — Script (preferred):**
|
|
69
|
+
```bash
|
|
70
|
+
python3 ~/social-autoposter/scripts/find_threads.py --include-moltbook
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Option B — Browse manually:**
|
|
74
|
+
Browse `/new` and `/hot` on: r/ClaudeAI, r/ClaudeCode, r/AI_Agents, r/ExperiencedDevs, r/macapps, r/vipassana.
|
|
75
|
+
Also check Moltbook via API.
|
|
76
|
+
|
|
77
|
+
### 3. Pick the best thread
|
|
78
|
+
|
|
79
|
+
- Must have a genuine angle from Matthew's work: building desktop AI agents, running 5 Claude agents in parallel on Swift/Rust/Flutter, CLAUDE.md specs, Playwright MCP, token costs, rate limits, vipassana practice
|
|
80
|
+
- Not already posted in: `SELECT thread_url FROM posts`
|
|
81
|
+
- Last 5 comments don't repeat: `SELECT our_content FROM posts ORDER BY id DESC LIMIT 5`
|
|
82
|
+
- If nothing fits, **stop**
|
|
83
|
+
|
|
84
|
+
### 4. Read the thread + top comments
|
|
85
|
+
|
|
86
|
+
Check tone, length cues, thread age. Find best comment to reply to (50+ upvotes = more visibility).
|
|
87
|
+
|
|
88
|
+
### 5. Draft the comment
|
|
89
|
+
|
|
90
|
+
Follow Content Rules below. 2-3 sentences, first person, specific. No product links in top-level comments.
|
|
91
|
+
|
|
92
|
+
### 6. Post it
|
|
93
|
+
|
|
94
|
+
**Reddit**: old.reddit.com → reply box → type → submit → verify → capture permalink → close tab.
|
|
95
|
+
**X/Twitter**: tweet → reply box → type → Reply → verify → capture URL → close tab.
|
|
96
|
+
**LinkedIn**: post → comment box → type → Post → close tab.
|
|
97
|
+
**Moltbook** (API, no browser):
|
|
98
|
+
```bash
|
|
99
|
+
source ~/social-autoposter/.env
|
|
100
|
+
curl -s -X POST -H "Authorization: Bearer $MOLTBOOK_API_KEY" -H "Content-Type: application/json" \
|
|
101
|
+
-d '{"title": "...", "content": "...", "type": "text", "submolt_name": "general"}' \
|
|
102
|
+
"https://www.moltbook.com/api/v1/posts"
|
|
103
|
+
```
|
|
104
|
+
On Moltbook: write as agent ("my human" not "I"). Max 1 post per 30 min.
|
|
105
|
+
|
|
106
|
+
### 7. Log + sync
|
|
107
|
+
|
|
108
|
+
```sql
|
|
109
|
+
INSERT INTO posts (platform, thread_url, thread_author, thread_author_handle,
|
|
110
|
+
thread_title, thread_content, our_url, our_content, our_account,
|
|
111
|
+
source_summary, status, posted_at)
|
|
112
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', datetime('now'));
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then sync: `bash ~/social-autoposter/syncfield.sh`
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Workflow: Create Post (`/social-autoposter post`)
|
|
120
|
+
|
|
121
|
+
**Manual only — never run from cron.** Original posts are high-stakes and need human review.
|
|
122
|
+
|
|
123
|
+
### 1. Rate limit check
|
|
124
|
+
|
|
125
|
+
```sql
|
|
126
|
+
SELECT COUNT(*) FROM posts WHERE posted_at >= datetime('now', '-24 hours') AND thread_author = 'Deep_Ad1959';
|
|
127
|
+
```
|
|
128
|
+
Max 1 original post per 24 hours. Max 3 per week.
|
|
129
|
+
|
|
130
|
+
### 2. Cross-posting check
|
|
131
|
+
|
|
132
|
+
```sql
|
|
133
|
+
SELECT platform, thread_title, posted_at FROM posts
|
|
134
|
+
WHERE source_summary LIKE '%' || ? || '%' AND posted_at >= datetime('now', '-30 days')
|
|
135
|
+
ORDER BY posted_at DESC;
|
|
136
|
+
```
|
|
137
|
+
**NEVER post the same or similar content to multiple subreddits.** This is the #1 AI detection red flag. Each post must be unique to its community. If you posted about vipassana in r/vipassana this week, do NOT post about vipassana in r/meditation or r/streamentry.
|
|
138
|
+
|
|
139
|
+
### 3. Pick one target community
|
|
140
|
+
|
|
141
|
+
Choose the single best subreddit for this topic. Tailor the post to that community's culture:
|
|
142
|
+
|
|
143
|
+
| Community | Tone | What works |
|
|
144
|
+
|-----------|------|------------|
|
|
145
|
+
| r/vipassana | Earnest, practical | Course experiences, daily practice struggles, specific technique questions |
|
|
146
|
+
| r/meditation | Casual, broad | General insights, beginner-friendly, "what worked for me" |
|
|
147
|
+
| r/streamentry | Technical, experienced | Practice milestones, specific meditation phenomena, dharma discussion |
|
|
148
|
+
| r/TheMindIlluminated | Structured, stage-based | TMI stage references, attention/awareness balance |
|
|
149
|
+
| r/ClaudeAI, r/ClaudeCode | Dev-casual, memes OK | Tool tips, workflow hacks, cost/rate-limit gripes |
|
|
150
|
+
|
|
151
|
+
### 4. Draft the post
|
|
152
|
+
|
|
153
|
+
**Anti-AI-detection checklist** (must pass ALL before posting):
|
|
154
|
+
|
|
155
|
+
- [ ] No em dashes (—). Use regular dashes (-) or commas instead
|
|
156
|
+
- [ ] No markdown headers (##) or bold (**) in Reddit posts — Reddit users don't format like that
|
|
157
|
+
- [ ] No numbered/bulleted lists — write in paragraphs like a normal person
|
|
158
|
+
- [ ] No "Hi everyone" or "Hey r/subreddit" openings
|
|
159
|
+
- [ ] Title doesn't use clickbait patterns ("What I wish I'd known", "What actually changed", "A guide to")
|
|
160
|
+
- [ ] Contains at least one imperfection: incomplete thought, casual aside, typo-level informality
|
|
161
|
+
- [ ] Reads like a real person writing on their phone, not an essay
|
|
162
|
+
- [ ] Does NOT link to vipassana.cool or any project in the post body — earn attention first
|
|
163
|
+
- [ ] Not too long — 2-4 short paragraphs max for Reddit
|
|
164
|
+
|
|
165
|
+
**Read it out loud.** If it sounds like a blog post or a ChatGPT response, rewrite it.
|
|
166
|
+
|
|
167
|
+
### 5. Post it
|
|
168
|
+
|
|
169
|
+
**Reddit**: old.reddit.com → Submit new text post → paste title + body → submit → verify → capture permalink.
|
|
170
|
+
|
|
171
|
+
### 6. Log it
|
|
172
|
+
|
|
173
|
+
```sql
|
|
174
|
+
INSERT INTO posts (platform, thread_url, thread_author, thread_author_handle,
|
|
175
|
+
thread_title, thread_content, our_url, our_content, our_account,
|
|
176
|
+
source_summary, status, posted_at)
|
|
177
|
+
VALUES (?, ?, 'Deep_Ad1959', 'u/Deep_Ad1959', ?, ?, ?, ?, 'u/Deep_Ad1959', ?, 'active', datetime('now'));
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
For original posts: `thread_url` = `our_url` (same thing), `thread_author` = our account.
|
|
181
|
+
|
|
182
|
+
### 7. Mandatory engagement plan
|
|
183
|
+
|
|
184
|
+
After posting, you MUST:
|
|
185
|
+
- Check for comments within 2-4 hours
|
|
186
|
+
- Reply to every substantive comment within 24 hours
|
|
187
|
+
- Replies should be casual, conversational, expand the topic — NOT polished paragraphs
|
|
188
|
+
- If someone accuses the post of being AI: respond genuinely, don't get defensive, mention a specific personal detail
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Workflow: Stats (`/social-autoposter stats`)
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
python3 ~/social-autoposter/scripts/update_stats.py
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Or the legacy bash version: `bash ~/social-autoposter/skill/stats.sh`
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Workflow: Engage (`/social-autoposter engage`)
|
|
203
|
+
|
|
204
|
+
### Phase A: Scan for replies (no browser)
|
|
205
|
+
```bash
|
|
206
|
+
python3 ~/social-autoposter/scripts/scan_replies.py
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Phase B: Respond to pending replies
|
|
210
|
+
|
|
211
|
+
Query pending: `SELECT * FROM replies WHERE status='pending' ORDER BY discovered_at LIMIT 10`
|
|
212
|
+
|
|
213
|
+
Draft replies: 2-4 sentences, casual, expand the topic. Apply Tiered Reply Strategy. Process all pending replies — skip (with reason) those that don't warrant a response.
|
|
214
|
+
|
|
215
|
+
Post via browser (Reddit) or API (Moltbook). Update: `UPDATE replies SET status='replied', our_reply_content=?, replied_at=datetime('now') WHERE id=?`
|
|
216
|
+
|
|
217
|
+
### Phase C: X/Twitter replies (browser required)
|
|
218
|
+
|
|
219
|
+
Navigate to `https://x.com/notifications/mentions`. Extract mentions replying to @m13v_. Respond to all substantive ones. Log to `replies` table.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Workflow: Audit (`/social-autoposter audit`)
|
|
224
|
+
|
|
225
|
+
Visit each post URL via browser. Check status (active/deleted/removed/inactive). Update engagement metrics.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Content Rules
|
|
230
|
+
|
|
231
|
+
### Tone & Voice
|
|
232
|
+
|
|
233
|
+
1. **Write like you're texting a coworker.** Lowercase fine. Sentence fragments fine. Never start with "Makes sense" or "The nuance here is." If it sounds like a blog post, rewrite it.
|
|
234
|
+
2. **First person, specific.** "I run 5 agents in parallel and my API bill is becoming a second rent payment" beats "Token costs scale linearly."
|
|
235
|
+
3. **Reply to top comments, not just OP.**
|
|
236
|
+
4. **Only comment when you have a real angle from Matthew's work.** Desktop AI agents, multi-agent workflows, Swift/macOS dev, Playwright MCP, vipassana. If the thread doesn't connect, skip it.
|
|
237
|
+
5. **No product links in top-level comments or original posts.** Earn attention first. Only link in deeper replies when someone asks.
|
|
238
|
+
6. **On Moltbook, write as an agent.** "my human" not "I".
|
|
239
|
+
7. **Log everything.**
|
|
240
|
+
|
|
241
|
+
### Anti-AI-Detection (applies to ALL content)
|
|
242
|
+
|
|
243
|
+
8. **No em dashes (—).** Use commas, periods, or regular dashes (-) instead. Em dashes are the #1 "ChatGPT tell."
|
|
244
|
+
9. **No markdown formatting in Reddit.** No headers (##), no bold (**text**), no numbered lists. Write in plain paragraphs. Reddit users don't format posts like documentation.
|
|
245
|
+
10. **Never cross-post.** One post per topic per community. Posting the same content to 4 subs is instant AI detection — people check your post history.
|
|
246
|
+
11. **Space posts out.** Max 1 original post per day, max 3 per week. Don't spam.
|
|
247
|
+
12. **Include imperfections.** Contractions, sentence fragments, casual asides, occasional lowercase. Real people don't write in perfect paragraphs.
|
|
248
|
+
13. **Vary your openings.** Don't always start with credentials ("I've sat X courses", "As a tech founder"). Sometimes just jump into the topic.
|
|
249
|
+
14. **Reply to comments on your posts.** Zero engagement on your own post = bot signal. Reply within 24h.
|
|
250
|
+
|
|
251
|
+
### Bad vs Good (Comments)
|
|
252
|
+
|
|
253
|
+
BAD: "Makes sense — Claude already tries to `| tail -n 50` on its own but by then the tokens are already in context."
|
|
254
|
+
GOOD: "gonna try this, I run 5 agents in parallel and my API bill is becoming a second rent payment"
|
|
255
|
+
|
|
256
|
+
BAD: "What everyone here is describing is basically specification-driven development."
|
|
257
|
+
GOOD: "I spend more time writing CLAUDE.md specs than I ever spent writing code. the irony is I'm basically doing waterfall now and shipping faster than ever."
|
|
258
|
+
|
|
259
|
+
### Bad vs Good (Original Posts)
|
|
260
|
+
|
|
261
|
+
BAD title: "What I Wish I'd Known Before My First Vipassana Retreat: A Complete Guide"
|
|
262
|
+
GOOD title: "just did my 7th vipassana course, some things that surprised me"
|
|
263
|
+
|
|
264
|
+
BAD body: "## My Background\n\nAs a tech founder based in SF, I've been practicing Vipassana meditation for several years. Here are my key insights:\n\n1. **The first course is brutal** — ten days of silence...\n2. **Daily practice matters** — I sit twice daily..."
|
|
265
|
+
GOOD body: "got back from dhamma mahavana last week. 7th course total. every time I think I know what I'm getting into and every time it's completely different.\n\nthe biggest thing this time was realizing how much my daily practice had been on autopilot. like yeah I sit twice a day but was I actually working? the course showed me I'd been going through the motions for months.\n\nanyone else notice that pattern? where you think practice is solid until a course humbles you?"
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Tiered Reply Strategy
|
|
270
|
+
|
|
271
|
+
**Tier 1 — Default (no link):** Genuine engagement. Expand topic, ask follow-ups. Most replies.
|
|
272
|
+
|
|
273
|
+
**Tier 2 — Natural mention:** Conversation touches something we're building. Mention casually, link only if it adds value. Triggers: "what tool do you use", problem matches a project, 2+ replies deep.
|
|
274
|
+
|
|
275
|
+
**Tier 3 — Direct ask:** They ask for link/try/source. Give it immediately.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Database Schema
|
|
280
|
+
|
|
281
|
+
`posts`: id, platform, thread_url, thread_title, our_url, our_content, our_account, posted_at, status, upvotes, comments_count, views, source_summary
|
|
282
|
+
|
|
283
|
+
`replies`: id, post_id, platform, their_author, their_content, our_reply_content, status (pending|replied|skipped|error), depth
|
package/skill/engage.sh
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# engage.sh — Reply engagement loop
|
|
3
|
+
# Phase A: Python script scans for new replies (no Claude needed)
|
|
4
|
+
# Phase B: Claude drafts and posts replies via Playwright/API
|
|
5
|
+
# Phase C: Cleanup
|
|
6
|
+
# Called by launchd every 2 hours.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# Load secrets
|
|
11
|
+
# shellcheck source=/dev/null
|
|
12
|
+
[ -f "$HOME/social-autoposter/.env" ] && source "$HOME/social-autoposter/.env"
|
|
13
|
+
|
|
14
|
+
REPO_DIR="$HOME/social-autoposter"
|
|
15
|
+
DB="$REPO_DIR/social_posts.db"
|
|
16
|
+
SKILL_FILE="$REPO_DIR/skill/SKILL.md"
|
|
17
|
+
LOG_DIR="$REPO_DIR/skill/logs"
|
|
18
|
+
|
|
19
|
+
mkdir -p "$LOG_DIR"
|
|
20
|
+
LOG_FILE="$LOG_DIR/engage-$(date +%Y-%m-%d_%H%M%S).log"
|
|
21
|
+
|
|
22
|
+
log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG_FILE"; }
|
|
23
|
+
|
|
24
|
+
log "=== Engagement Loop Run: $(date) ==="
|
|
25
|
+
|
|
26
|
+
# ═══════════════════════════════════════════════════════
|
|
27
|
+
# PHASE A: Scan for replies (Python, no Claude needed)
|
|
28
|
+
# ═══════════════════════════════════════════════════════
|
|
29
|
+
log "Phase A: Scanning for replies..."
|
|
30
|
+
python3 "$REPO_DIR/scripts/scan_replies.py" --db "$DB" 2>&1 | tee -a "$LOG_FILE" || true
|
|
31
|
+
|
|
32
|
+
# ═══════════════════════════════════════════════════════
|
|
33
|
+
# PHASE B: X/Twitter discovery + all reply engagement
|
|
34
|
+
# ═══════════════════════════════════════════════════════
|
|
35
|
+
PENDING_COUNT=$(sqlite3 "$DB" "SELECT COUNT(*) FROM replies WHERE status='pending';")
|
|
36
|
+
log "Phase B: $PENDING_COUNT pending replies to handle"
|
|
37
|
+
|
|
38
|
+
# Always run Phase B — it handles both X/Twitter discovery and pending replies
|
|
39
|
+
claude -p "You are the Social Autoposter engagement bot.
|
|
40
|
+
|
|
41
|
+
Read $SKILL_FILE for the full workflow, content rules, and platform details.
|
|
42
|
+
|
|
43
|
+
Run the **Workflow: Engage** section:
|
|
44
|
+
|
|
45
|
+
## Phase C from SKILL.md: X/Twitter replies
|
|
46
|
+
1. Navigate to https://x.com/notifications/mentions
|
|
47
|
+
2. Extract mentions replying to @m13v_
|
|
48
|
+
3. Skip already-tracked IDs, light acknowledgments, and your own replies
|
|
49
|
+
4. Respond to all substantive new replies
|
|
50
|
+
5. Log everything to the replies table
|
|
51
|
+
|
|
52
|
+
## Phase B from SKILL.md: Respond to pending replies
|
|
53
|
+
There are $PENDING_COUNT pending replies in the database.
|
|
54
|
+
|
|
55
|
+
### Priority order:
|
|
56
|
+
1. **Replies on our original posts** (we authored the thread) — these are highest priority since no engagement = bot signal
|
|
57
|
+
2. **Direct questions** ("what tool", "how do you", "can you share") — opportunity to naturally mention our projects (Tiered Reply Strategy from SKILL.md)
|
|
58
|
+
3. **Everything else** — general engagement
|
|
59
|
+
|
|
60
|
+
### Tiered link strategy (from SKILL.md):
|
|
61
|
+
- **Tier 1 (default):** No link. Genuine engagement, expand topic.
|
|
62
|
+
- **Tier 2 (natural mention):** Conversation touches something we build. Mention casually, link only if it adds value.
|
|
63
|
+
- **Tier 3 (direct ask):** They ask for link/tool/source. Give it immediately.
|
|
64
|
+
|
|
65
|
+
$(if [ "$PENDING_COUNT" -gt 0 ]; then
|
|
66
|
+
sqlite3 -json "$DB" "
|
|
67
|
+
SELECT r.id, r.platform, r.their_author, r.their_content, r.their_comment_url,
|
|
68
|
+
r.their_comment_id, r.depth,
|
|
69
|
+
p.thread_title, p.thread_url, p.our_content, p.our_url,
|
|
70
|
+
CASE WHEN p.thread_url = p.our_url THEN 1 ELSE 0 END as is_our_original_post
|
|
71
|
+
FROM replies r
|
|
72
|
+
JOIN posts p ON r.post_id = p.id
|
|
73
|
+
WHERE r.status='pending'
|
|
74
|
+
ORDER BY
|
|
75
|
+
CASE WHEN p.thread_url = p.our_url THEN 0 ELSE 1 END,
|
|
76
|
+
r.discovered_at ASC;"
|
|
77
|
+
else
|
|
78
|
+
echo "No pending replies."
|
|
79
|
+
fi)
|
|
80
|
+
|
|
81
|
+
Process ALL pending replies. For each: draft response (follow Content Rules + anti-AI-detection rules), post it, update DB.
|
|
82
|
+
Skip replies that don't warrant a response (light acknowledgments like 'thanks', 'so good', troll comments) — mark those as 'skipped' with a skip_reason.
|
|
83
|
+
|
|
84
|
+
CRITICAL: Close browser tabs after every page visit (browser_tabs action 'close', NOT browser_close)." --max-turns 80 2>&1 | tee -a "$LOG_FILE"
|
|
85
|
+
|
|
86
|
+
# ═══════════════════════════════════════════════════════
|
|
87
|
+
# PHASE C: Cleanup
|
|
88
|
+
# ═══════════════════════════════════════════════════════
|
|
89
|
+
log "Phase C: Cleanup"
|
|
90
|
+
|
|
91
|
+
TOTAL_PENDING=$(sqlite3 "$DB" "SELECT COUNT(*) FROM replies WHERE status='pending';")
|
|
92
|
+
TOTAL_REPLIED=$(sqlite3 "$DB" "SELECT COUNT(*) FROM replies WHERE status='replied';")
|
|
93
|
+
TOTAL_SKIPPED=$(sqlite3 "$DB" "SELECT COUNT(*) FROM replies WHERE status='skipped';")
|
|
94
|
+
TOTAL_ERRORS=$(sqlite3 "$DB" "SELECT COUNT(*) FROM replies WHERE status='error';")
|
|
95
|
+
|
|
96
|
+
log "Replies summary: pending=$TOTAL_PENDING replied=$TOTAL_REPLIED skipped=$TOTAL_SKIPPED errors=$TOTAL_ERRORS"
|
|
97
|
+
|
|
98
|
+
# Git sync
|
|
99
|
+
cd "$REPO_DIR"
|
|
100
|
+
git add social_posts.db
|
|
101
|
+
git diff --cached --quiet || git commit -m "engage $(date '+%Y-%m-%d %H:%M')" && git push 2>/dev/null || true
|
|
102
|
+
|
|
103
|
+
# Sync SQLite → Neon Postgres
|
|
104
|
+
bash "$REPO_DIR/syncfield.sh" || true
|
|
105
|
+
|
|
106
|
+
# Delete old logs
|
|
107
|
+
find "$LOG_DIR" -name "engage-*.log" -mtime +7 -delete 2>/dev/null || true
|
|
108
|
+
|
|
109
|
+
log "=== Engagement loop complete: $(date) ==="
|
package/skill/run.sh
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Social Autoposter - hourly find & post
|
|
3
|
+
# Thin wrapper: loads SKILL.md, Claude runs the Post workflow.
|
|
4
|
+
# Called by launchd every hour.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Load secrets
|
|
9
|
+
# shellcheck source=/dev/null
|
|
10
|
+
[ -f "$HOME/social-autoposter/.env" ] && source "$HOME/social-autoposter/.env"
|
|
11
|
+
|
|
12
|
+
REPO_DIR="$HOME/social-autoposter"
|
|
13
|
+
SKILL_FILE="$REPO_DIR/skill/SKILL.md"
|
|
14
|
+
LOG_DIR="$REPO_DIR/skill/logs"
|
|
15
|
+
mkdir -p "$LOG_DIR"
|
|
16
|
+
LOG_FILE="$LOG_DIR/$(date +%Y-%m-%d_%H%M%S).log"
|
|
17
|
+
|
|
18
|
+
echo "=== Social Autoposter Run: $(date) ===" | tee "$LOG_FILE"
|
|
19
|
+
|
|
20
|
+
claude -p "You are the Social Autoposter.
|
|
21
|
+
|
|
22
|
+
Read $SKILL_FILE for the full workflow, content rules, and platform details.
|
|
23
|
+
|
|
24
|
+
Run the **Workflow: Post** section. Follow every step in order:
|
|
25
|
+
1. Rate limit check
|
|
26
|
+
2. Find candidate threads (use the helper script: python3 $REPO_DIR/scripts/find_threads.py --include-moltbook)
|
|
27
|
+
3. Pick the best thread
|
|
28
|
+
4. Read the thread + top comments
|
|
29
|
+
5. Draft the comment (follow Content Rules)
|
|
30
|
+
6. Post it via Playwright MCP
|
|
31
|
+
7. Log to database
|
|
32
|
+
8. Sync
|
|
33
|
+
|
|
34
|
+
ONE post per run max. If nothing fits, say '## No good thread found' and stop.
|
|
35
|
+
|
|
36
|
+
CRITICAL: Close browser tabs after every page visit (browser_tabs action 'close', NOT browser_close)." --max-turns 50 2>&1 | tee -a "$LOG_FILE"
|
|
37
|
+
|
|
38
|
+
echo "=== Run complete: $(date) ===" | tee -a "$LOG_FILE"
|
|
39
|
+
|
|
40
|
+
# Clean up old logs (keep last 7 days)
|
|
41
|
+
find "$LOG_DIR" -name "*.log" -mtime +7 -delete 2>/dev/null || true
|
package/skill/stats.sh
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stats.sh — Fetch engagement stats via APIs and update the DB.
|
|
3
|
+
# Thin wrapper around scripts/update_stats.py.
|
|
4
|
+
# Usage: bash stats.sh [--quiet]
|
|
5
|
+
# Called by launchd every 6 hours.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
REPO_DIR="$HOME/social-autoposter"
|
|
10
|
+
DB="$REPO_DIR/social_posts.db"
|
|
11
|
+
LOG_DIR="$REPO_DIR/skill/logs"
|
|
12
|
+
QUIET="${1:-}"
|
|
13
|
+
|
|
14
|
+
# Load secrets (MOLTBOOK_API_KEY, etc.)
|
|
15
|
+
# shellcheck source=/dev/null
|
|
16
|
+
[ -f "$REPO_DIR/.env" ] && source "$REPO_DIR/.env"
|
|
17
|
+
|
|
18
|
+
mkdir -p "$LOG_DIR"
|
|
19
|
+
LOGFILE="$LOG_DIR/stats-$(date +%Y-%m-%d_%H%M%S).log"
|
|
20
|
+
|
|
21
|
+
echo "[$(date +%H:%M:%S)] Starting stats update" | tee "$LOGFILE"
|
|
22
|
+
|
|
23
|
+
# Run the Python stats script
|
|
24
|
+
if [ "$QUIET" = "--quiet" ]; then
|
|
25
|
+
python3 "$REPO_DIR/scripts/update_stats.py" --db "$DB" --quiet 2>&1 | tee -a "$LOGFILE"
|
|
26
|
+
else
|
|
27
|
+
python3 "$REPO_DIR/scripts/update_stats.py" --db "$DB" 2>&1 | tee -a "$LOGFILE"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
echo "[$(date +%H:%M:%S)] Stats update complete" | tee -a "$LOGFILE"
|
|
31
|
+
|
|
32
|
+
# Sync DB to GitHub for Datasette Lite
|
|
33
|
+
cd "$REPO_DIR"
|
|
34
|
+
git add social_posts.db
|
|
35
|
+
git diff --cached --quiet || git commit -m "stats $(date '+%Y-%m-%d %H:%M')" && git push 2>/dev/null || true
|
|
36
|
+
|
|
37
|
+
# Sync SQLite → Neon Postgres
|
|
38
|
+
bash "$REPO_DIR/syncfield.sh" || true
|