A simple tool which lets you scrape twitter accounts and crosspost them to bluesky accounts! Comes with a CLI and a webapp for managing profiles! Works with images/videos/link embeds/threads.
1# tweets-2-bsky
2
3Cross-post from Twitter/X to Bluesky with thread support, media handling, account mapping, and a web dashboard.
4
5This repo is also mirrored on Tangled: [j4ck.xyz/tweets2bsky](https://tangled.org/j4ck.xyz/tweets2bsky)
6
7## How It Works (Simple)
8
91. You connect one or more Twitter/X source accounts to a Bluesky account.
102. The app reads tweets from X using `@the-convocation/twitter-scraper` with your cookies (`auth_token` + `ct0`).
113. It posts to Bluesky using the official AT Protocol client (`@atproto/api`).
124. It tracks what was already posted in SQLite so it does not repost duplicates.
135. A scheduler runs automatically, and you can also trigger `Run now` from the dashboard or CLI.
14
15## Installation (Pick One Path)
16
17Use either:
18
19- Docker (recommended)
20- Source install (PM2 or manual runtime)
21
22Do not do both on the same machine unless you intentionally want two separate deployments.
23
24### Option A: Docker (Recommended)
25
26Prerequisite: Docker Desktop (macOS/Windows) or Docker Engine (Linux).
27
28Start with the included compose file:
29
30```bash
31docker compose up -d
32```
33
34Open `http://localhost:3000`.
35
36If you prefer `docker run`:
37
38```bash
39docker run -d \
40 --name tweets-2-bsky \
41 -p 3000:3000 \
42 -v tweets2bsky_data:/app/data \
43 --restart unless-stopped \
44 j4ckxyz/tweets-2-bsky:latest
45```
46
47Important: keep a persistent volume (`-v tweets2bsky_data:/app/data`) so mappings/history survive container recreation.
48
49Useful Docker commands:
50
51```bash
52docker logs -f tweets-2-bsky
53docker exec -it tweets-2-bsky bun dist/cli.js status
54docker stop tweets-2-bsky
55docker start tweets-2-bsky
56```
57
58Update Docker deployment:
59
60```bash
61docker pull j4ckxyz/tweets-2-bsky:latest
62docker stop tweets-2-bsky
63docker rm tweets-2-bsky
64docker run -d \
65 --name tweets-2-bsky \
66 -p 3000:3000 \
67 -v tweets2bsky_data:/app/data \
68 --restart unless-stopped \
69 j4ckxyz/tweets-2-bsky:latest
70```
71
72Alternative image: `ghcr.io/j4ckxyz/tweets-2-bsky:latest`.
73
74### Option B: Source Install (PM2 or Manual)
75
76Prerequisites:
77
78- `git`
79- Bun 1.x+ (the installer auto-installs/upgrades Bun when needed)
80- PM2 (optional, but recommended for background runtime)
81
82Clone and install:
83
84```bash
85git clone https://github.com/j4ckxyz/tweets-2-bsky
86cd tweets-2-bsky
87chmod +x install.sh
88./install.sh
89```
90
91`install.sh` does install/build/start and uses:
92
93- PM2 when PM2 is available
94- `nohup` when PM2 is not installed
95
96Useful installer commands:
97
98```bash
99./install.sh --status
100./install.sh --stop
101./install.sh --start-only
102./install.sh --no-start
103./install.sh --port 3100
104```
105
106#### PM2 Manual Runtime (if you want direct PM2 control)
107
108```bash
109bun install
110bun run build
111pm2 start "$HOME/.bun/bin/bun" --name tweets-2-bsky --cwd "$PWD" -- dist/index.js
112pm2 logs tweets-2-bsky
113pm2 save
114```
115
116#### Manual Foreground Runtime (no PM2)
117
118```bash
119bun install
120bun run build
121bun run start
122```
123
124#### Manual Nohup Runtime (no PM2)
125
126```bash
127mkdir -p data/runtime
128nohup bun run start > data/runtime/tweets-2-bsky.log 2>&1 &
129echo $! > data/runtime/tweets-2-bsky.pid
130```
131
132Stop nohup process:
133
134```bash
135kill "$(cat data/runtime/tweets-2-bsky.pid)"
136```
137
138## First-Time Setup (After Install)
139
1401. Open `http://localhost:3000`.
1412. Register the first user (this account becomes admin).
1423. In Settings, add Twitter cookies (`auth_token`, `ct0`; backup pair optional).
1434. Add a mapping (Twitter source usernames -> Bluesky account).
1445. Click `Run now`.
145
146## Twitter/X Integration Notes
147
148- This project does not use Twitter's paid official API.
149- It uses `@the-convocation/twitter-scraper` and authenticated browser cookies to read account/tweet data.
150- Required cookies: `auth_token` and `ct0`.
151- If cookies expire, update them in Settings.
152- Keep cookies private; they are sensitive credentials.
153
154For some quote-tweet screenshot fallbacks, Chromium is used (bundled in Docker, optional dependency for source installs).
155
156## CLI Quick Commands
157
158Always run CLI commands as:
159
160```bash
161bun run cli -- <command>
162```
163
164Common commands:
165
166```bash
167bun run cli -- status
168bun run cli -- list
169bun run cli -- run-now
170bun run cli -- run-now --dry-run
171bun run cli -- add-mapping
172bun run cli -- backfill <mapping-id-or-handle> --limit 50
173```
174
175## Updating
176
177Source installs:
178
179```bash
180./update.sh
181```
182
183Useful flags:
184
185```bash
186./update.sh --no-restart
187./update.sh --skip-install --skip-build
188```
189
190## Data and Security
191
192Important files:
193
194- `config.json` (mappings, credentials, users)
195- `data/database.sqlite` (processed history)
196- `data/.jwt-secret` (generated signing key when `JWT_SECRET` is unset)
197- `.env` (runtime env values)
198
199Security basics:
200
201- First registered user becomes admin.
202- Prefer Bluesky app passwords instead of your full Bluesky password.
203- Set an explicit `JWT_SECRET` in `.env` for predictable secret management.
204- Keep `config.json`, cookie values, and `.env` private.
205
206## Development
207
208```bash
209bun run dev
210bun run dev:web
211bun run build
212bun run typecheck
213bun run lint
214```
215
216## Troubleshooting
217
218See `TROUBLESHOOTING.md`.
219
220Common native module recovery:
221
222```bash
223bun run rebuild:native
224bun run build
225bun run start
226```
227
228## License
229
230MIT