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.
at main 370 lines 8.3 kB view raw view rendered
1# tweets-2-bsky 2 3Crosspost posts from Twitter/X to Bluesky with thread support, media handling, account mapping, and a web dashboard. 4 5## Quick Start (Easy Mode) 6 7If you are comfortable with terminal basics but do not want to manage PM2 manually, use the installer script. 8 9### 1) Clone the repo 10 11```bash 12git clone https://github.com/j4ckxyz/tweets-2-bsky 13cd tweets-2-bsky 14``` 15 16### 2) Run install + background start 17 18```bash 19chmod +x install.sh 20./install.sh 21``` 22 23What this does by default: 24 25- installs dependencies 26- builds server + web dashboard 27- creates/updates `.env` with sensible defaults (`PORT=3000`, generated `JWT_SECRET` if missing) 28- starts in the background 29 - uses PM2 if installed 30 - otherwise uses `nohup` 31- prints your local web URL (for example `http://localhost:3000`) 32 33### 3) Open the dashboard 34 35Open the printed URL in your browser, then: 36 371. Register the first user (this user becomes admin). 382. Add Twitter cookies in Settings. 393. Add at least one mapping. 404. Click `Run now`. 41 42### Useful installer commands 43 44```bash 45./install.sh --no-start 46./install.sh --start-only 47./install.sh --stop 48./install.sh --status 49./install.sh --port 3100 50./install.sh --host 127.0.0.1 51./install.sh --skip-native-rebuild 52``` 53 54If you prefer full manual setup, skip to [Manual Setup](#manual-setup-technical). 55 56## Linux VPS Without Domain (Secure HTTPS via Tailscale) 57 58If you host on a public VPS (Linux) and do not own a domain, use the server installer: 59 60```bash 61chmod +x install-server.sh 62./install-server.sh 63``` 64 65What this does: 66 67- runs the normal app install/build/start flow 68- auto-selects a free local app port if your chosen/default port is already in use 69- forces the app to bind locally only (`HOST=127.0.0.1`) 70- installs and starts Tailscale if needed 71- configures `tailscale serve` on a free HTTPS port so your dashboard is reachable over Tailnet HTTPS 72- prints the final Tailnet URL to open from any device authenticated on your Tailscale account 73 74Optional non-interactive login: 75 76```bash 77./install-server.sh --auth-key <TS_AUTHKEY> 78``` 79 80Optional fixed Tailscale HTTPS port: 81 82```bash 83./install-server.sh --https-port 443 84``` 85 86Optional public exposure (internet) with Funnel: 87 88```bash 89./install-server.sh --funnel 90``` 91 92Notes: 93 94- this does **not** replace or delete `install.sh`; it wraps server-hardening around it 95- normal updates still use `./update.sh` and keep your local `.env` values 96- if you already installed manually, this is still safe to run later 97 98## What This Project Does 99 100- crossposts tweets and threads to Bluesky 101- handles images, videos, GIFs, quote tweets, and link cards 102- stores processed history in SQLite to avoid reposting 103- supports multiple Twitter source usernames per Bluesky target 104- provides both: 105 - web dashboard workflows 106 - CLI workflows (including cron-friendly mode) 107 108## Requirements 109 110- Node.js 22+ 111- npm 112- git 113 114Optional but recommended: 115 116- PM2 (for managed background runtime) 117- Chrome/Chromium (used for some quote-tweet screenshot fallbacks) 118- build tools for native modules (`better-sqlite3`) if your platform needs source compilation 119 120## Manual Setup (Technical) 121 122### Standard run (foreground) 123 124```bash 125git clone https://github.com/j4ckxyz/tweets-2-bsky 126cd tweets-2-bsky 127npm install 128npm run build 129npm start 130``` 131 132Open: [http://localhost:3000](http://localhost:3000) 133 134### Set environment values explicitly 135 136```bash 137cat > .env <<'EOF' 138PORT=3000 139JWT_SECRET=replace-with-a-strong-random-secret 140EOF 141``` 142 143### Rebuild native modules after Node version changes 144 145```bash 146npm run rebuild:native 147npm run build 148``` 149 150## First-Time Setup via CLI (Alternative to Web Forms) 151 152```bash 153npm run cli -- setup-twitter 154npm run cli -- add-mapping 155npm run cli -- run-now 156``` 157 158## Recommended Command Examples 159 160Always invoke CLI commands as: 161 162```bash 163npm run cli -- <command> 164``` 165 166### Status and basic operations 167 168```bash 169npm run cli -- status 170npm run cli -- list 171npm run cli -- recent-activity --limit 20 172``` 173 174### Credentials and configuration 175 176```bash 177npm run cli -- setup-twitter 178npm run cli -- setup-ai 179npm run cli -- set-interval 5 180``` 181 182### Mapping management 183 184```bash 185npm run cli -- add-mapping 186npm run cli -- edit-mapping <mapping-id-or-handle> 187npm run cli -- remove <mapping-id-or-handle> 188``` 189 190### Running syncs 191 192```bash 193npm run cli -- run-now 194npm run cli -- run-now --dry-run 195npm run cli -- run-now --web 196``` 197 198### Backfill and history import 199 200```bash 201npm run cli -- backfill <mapping-id-or-handle> --limit 50 202npm run cli -- import-history <mapping-id-or-handle> --limit 100 203npm run cli -- clear-cache <mapping-id-or-handle> 204``` 205 206### Dangerous operation (admin workflow) 207 208```bash 209npm run cli -- delete-all-posts <mapping-id-or-handle> 210``` 211 212### Config export/import 213 214```bash 215npm run cli -- config-export ./tweets-2-bsky-config.json 216npm run cli -- config-import ./tweets-2-bsky-config.json 217``` 218 219Mapping references accept: 220 221- mapping ID 222- Bluesky handle/identifier 223- Twitter username 224 225## Cron / CLI-Only Operation 226 227Run every 5 minutes: 228 229```cron 230*/5 * * * * cd /path/to/tweets-2-bsky && /usr/bin/npm run cli -- run-now >> /tmp/tweets-2-bsky.log 2>&1 231``` 232 233Run one backfill once: 234 235```bash 236npm run cli -- backfill <mapping-id-or-handle> --limit 50 237``` 238 239## Background Runtime Options 240 241### Option A: use `install.sh` (recommended) 242 243```bash 244./install.sh 245./install.sh --status 246./install.sh --stop 247``` 248 249### Option B: manage PM2 directly 250 251```bash 252pm2 start dist/index.js --name tweets-2-bsky 253pm2 logs tweets-2-bsky 254pm2 restart tweets-2-bsky --update-env 255pm2 save 256``` 257 258### Option C: no PM2 (nohup) 259 260```bash 261mkdir -p data/runtime 262nohup npm start > data/runtime/tweets-2-bsky.log 2>&1 & 263echo $! > data/runtime/tweets-2-bsky.pid 264``` 265 266Stop nohup process: 267 268```bash 269kill "$(cat data/runtime/tweets-2-bsky.pid)" 270``` 271 272## Updating 273 274Use: 275 276```bash 277./update.sh 278``` 279 280`update.sh`: 281 282- stashes local uncommitted changes before pull and restores them after update 283- pulls latest code (supports non-`origin` remotes and detached-head recovery) 284- installs dependencies 285- rebuilds native modules when Node ABI changed 286- builds server + web dashboard 287- restarts existing runtime for PM2 **or** nohup mode 288- preserves local `config.json` and `.env` with backup/restore 289 290Useful update flags: 291 292```bash 293./update.sh --no-restart 294./update.sh --skip-install --skip-build 295./update.sh --remote origin --branch main 296``` 297 298## Data, Config, and Security 299 300Local files: 301 302- `config.json`: mappings, credentials, users, app settings (sensitive; do not share) 303- `data/database.sqlite`: processed tweet history and metadata 304- `.env`: runtime environment variables (`PORT`, `JWT_SECRET`, optional overrides) 305 306Security notes: 307 308- first registered dashboard user is admin 309- after bootstrap, only admins can create additional dashboard users 310- users can sign in with username or email 311- non-admin users only see mappings they created by default 312- admins can grant fine-grained permissions (view all mappings, manage groups, queue backfills, run-now, etc.) 313- only admins can view or edit Twitter/AI provider credentials 314- admin user management never exposes other users' password hashes in the UI 315- if `JWT_SECRET` is missing, server falls back to an insecure default; set your own secret in `.env` 316- prefer Bluesky app passwords (not your full account password) 317 318### Multi-User Access Control 319 320- bootstrap account: 321 - the first account created through the web UI becomes admin 322 - open registration is automatically disabled after this 323- admin capabilities: 324 - create, edit, reset password, and delete dashboard users 325 - assign role (`admin` or `user`) and per-user permissions 326 - filter the Accounts page by creator to review each user's mappings 327- deleting a user: 328 - disables that user's mappings so crossposting stops 329 - leaves already-published Bluesky posts untouched 330- self-service security: 331 - every user can change their own password 332 - users can change their own email after password verification 333 334## Development 335 336### Start backend/scheduler from source 337 338```bash 339npm run dev 340``` 341 342### Start Vite web dev server 343 344```bash 345npm run dev:web 346``` 347 348### Build and quality checks 349 350```bash 351npm run build 352npm run typecheck 353npm run lint 354``` 355 356## Troubleshooting 357 358See: `TROUBLESHOOTING.md` 359 360Common recovery after changing Node versions: 361 362```bash 363npm run rebuild:native 364npm run build 365npm start 366``` 367 368## License 369 370MIT