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.
···11# Tweets-2-Bsky
2233-A powerful tool to crosspost Tweets to Bluesky, supporting threads, videos, and high-quality images.
33+Crosspost Tweets/X posts to Bluesky with support for threads, media, and multiple account mappings.
4455## Features
6677-- 🔄 **Crossposting**: Automatically mirrors your Tweets to Bluesky.
88-- 🧵 **Thread Support**: INTELLIGENTLY handles threads, posting them as Bluesky threads.
99-- 📹 **Video & GIF Support**: Downloads and uploads videos/GIFs natively to Bluesky (not just links!).
1010-- 🖼️ **High-Quality Images**: Fetches the highest resolution images available.
1111-- 🔗 **Smart Link Expansion**: Resolves `t.co` links to their original URLs.
1212-- 👥 **Multiple Source Accounts**: Map multiple Twitter accounts to a single Bluesky profile.
1313-- ⚙️ **Web Dashboard**: Manage accounts, view status, and trigger runs via a modern UI.
1414-- 🛠️ **CLI & Web Support**: Use the command line or the web interface.
77+- Automated crossposting from Twitter/X to Bluesky
88+- Thread-aware posting
99+- Video/GIF and high-quality image handling
1010+- Multi-source account mappings per Bluesky target
1111+- React + Vite web dashboard (auto light/dark mode)
1212+- Native-styled "Already Posted" feed in dashboard
1313+- Full CLI workflows for CLI-only/cronjob usage
15141616-## Quick Start
1515+## Requirements
17161818-1. **Clone the repository:**
1919- ```bash
2020- git clone https://github.com/yourusername/tweets-2-bsky.git
2121- cd tweets-2-bsky
2222- ```
1717+- Node.js 22+
1818+- npm
1919+- Git
23202424-2. **Install dependencies:**
2525- ```bash
2626- npm install
2727- ```
2121+## Fast Setup (Web + CLI)
28222929-3. **Build the project:**
3030- ```bash
3131- npm run build
3232- ```
2323+```bash
2424+git clone https://github.com/j4ckxyz/tweets-2-bsky
2525+cd tweets-2-bsky
2626+npm install
2727+npm run build
2828+npm start
2929+```
33303434-4. **Start the server:**
3535- ```bash
3636- npm start
3737- ```
3838- Access the dashboard at `http://localhost:3000`.
3131+Open: [http://localhost:3000](http://localhost:3000)
3232+3333+Notes:
3434+- `npm install` automatically rebuilds native modules (including `better-sqlite3`) for your active Node version.
3535+- If you switch Node versions later, run `npm run rebuild:native`.
3636+3737+## CLI-Only Setup
3838+3939+1. Configure Twitter cookies:
4040+ ```bash
4141+ npm run cli -- setup-twitter
4242+ ```
4343+2. Add mapping(s):
4444+ ```bash
4545+ npm run cli -- add-mapping
4646+ ```
4747+3. Run one sync cycle now:
4848+ ```bash
4949+ npm run cli -- run-now
5050+ ```
39514052## Updating
41534242-To update to the latest version without losing your configuration:
5454+Use:
43554456```bash
4557./update.sh
4658```
47594848-This script will pull the latest code, install dependencies, and rebuild the project. **Restart your application** after running the update.
6060+What it does:
6161+- pulls latest code
6262+- installs dependencies
6363+- rebuilds native modules
6464+- builds server + web UI
6565+- restarts PM2 process (if PM2 is installed)
6666+- preserves local `config.json` via backup/restore
49675050-## Configuration & Security
6868+## CLI Commands (Feature Parity)
51695252-### Environment Variables
7070+Always use:
53715454-Create a `.env` file for security (optional but recommended):
7272+```bash
7373+npm run cli -- <command>
7474+```
55755656-```env
5757-PORT=3000
5858-JWT_SECRET=your-super-secret-key-change-this
7676+Core commands:
7777+- `setup-twitter`: Configure primary + backup Twitter cookies
7878+- `setup-ai`: Configure AI provider/API settings
7979+- `add-mapping`, `edit-mapping`, `remove`, `list`
8080+- `set-interval <minutes>`: Scheduler interval
8181+- `run-now [--dry-run] [--web]`: Run one cycle immediately (good for cron)
8282+- `backfill [mapping] --limit 15 [--dry-run] [--web]`
8383+- `import-history [mapping] --limit 15 [--dry-run] [--web]`
8484+- `clear-cache [mapping]`
8585+- `delete-all-posts [mapping]`
8686+- `recent-activity --limit 20`
8787+- `config-export [file]`
8888+- `config-import <file>`
8989+- `status`
9090+9191+Mapping arguments can be mapping ID, Bluesky handle, or Twitter username.
9292+9393+## Cronjob Example
9494+9595+Run every 5 minutes:
9696+9797+```cron
9898+*/5 * * * * cd /path/to/tweets-2-bsky && /usr/bin/npm run cli -- run-now >> /tmp/tweets-2-bsky.log 2>&1
5999```
601006161-> **⚠️ Security Note:** If you do not set `JWT_SECRET`, a fallback secret is used. For production or public-facing deployments, **YOU MUST SET A STRONG SECRET**.
101101+Backfill specific mapping once:
621026363-### Data Storage
103103+```bash
104104+npm run cli -- backfill <mapping-id-or-handle> --limit 50
105105+```
641066565-- **`config.json`**: Stores your account mappings and encrypted web user passwords. Note that Bluesky app passwords are stored in plain text here to facilitate automated login. **Do not share this file.**
6666-- **`data/database.sqlite`**: Stores the history of processed tweets to prevent duplicates.
107107+## Web Dashboard
671086868-## Usage
109109+1. Register first user (becomes admin)
110110+2. Configure Twitter + AI settings
111111+3. Add mappings
112112+4. Use:
113113+ - `Run now`
114114+ - backfill/reset actions per mapping
115115+ - config export/import
116116+ - "Already Posted" feed for native-themed post browsing
691177070-### Web Interface
118118+## Configuration & Security
711197272-1. Register your first account (this user becomes the **Admin**).
7373-2. Go to settings to configure your Twitter Auth Token and CT0 (cookies).
7474-3. Add mappings:
7575- * Enter one or more **Twitter Usernames** (comma-separated).
7676- * Enter your **Bluesky Handle** and **App Password**.
7777-4. The system will check for new tweets every 5 minutes (configurable).
120120+### Environment variables
781217979-### CLI
122122+Create `.env` (recommended):
801238181-- **Add Mapping**: `npm run cli add-mapping`
8282-- **Edit Mapping**: `npm run cli edit-mapping`
8383-- **Import History**: `npm run cli import-history`
8484-- **List Accounts**: `npm run cli list`
124124+```env
125125+PORT=3000
126126+JWT_SECRET=your-super-secret-key-change-this
127127+```
851288686-### 🤖 Gemini AI Alt Text (Optional)
129129+If `JWT_SECRET` is not set, a fallback secret is used.
871308888-This tool can automatically generate Alt Text for images using Google's Gemini AI if the original tweet lacks it.
131131+### Local data files
891329090-1. **Get an API Key**: Get a free API Key from [Google AI Studio](https://aistudio.google.com/app/apikey).
9191-2. **Configure**:
9292- * **Web UI**: Go to the Admin Dashboard and paste your key in the "Gemini AI (Alt Text)" section.
9393- * **Disable**: Leave the field empty to disable this feature (default).
133133+- `config.json`: mappings + auth settings + web users (do not share)
134134+- `data/database.sqlite`: processed tweet history
941359595-## Twitter Cookies (Auth)
136136+## Troubleshooting
961379797-You need your Twitter `auth_token` and `ct0` cookies.
9898-1. Log in to Twitter/X in your browser.
9999-2. Open Developer Tools (F12) -> Application -> Cookies.
100100-3. Copy the values for `auth_token` and `ct0`.
138138+See: `TROUBLESHOOTING.md`
139139+140140+Most common fix after changing Node versions:
141141+142142+```bash
143143+npm run rebuild:native
144144+npm run build
145145+npm start
146146+```
101147102148## License
103149104104-MIT150150+MIT
+33
TROUBLESHOOTING.md
···2424 chmod +x repair_pm2.sh
2525 ./repair_pm2.sh
2626 ```
2727+2828+### `better-sqlite3` NODE_MODULE_VERSION mismatch
2929+If startup fails with `ERR_DLOPEN_FAILED` and a `NODE_MODULE_VERSION` mismatch:
3030+3131+1. Rebuild native bindings for your active Node version:
3232+ ```bash
3333+ npm run rebuild:native
3434+ ```
3535+2. Rebuild and start:
3636+ ```bash
3737+ npm run build
3838+ npm start
3939+ ```
4040+4141+### Dashboard appears unstyled / plain text UI
4242+If the app loads but looks mostly unstyled:
4343+4444+1. Rebuild web assets:
4545+ ```bash
4646+ npm run build
4747+ ```
4848+2. Restart server:
4949+ ```bash
5050+ npm start
5151+ ```
5252+3. Hard refresh browser cache (`Cmd+Shift+R` on macOS).
5353+5454+### CLI command not recognized
5555+When using npm scripts, pass CLI args after `--`:
5656+5757+```bash
5858+npm run cli -- status
5959+```