— The dashboard half of your Developer-First Tweet Automation Toolkit
TweetX-web is a small Sinatra dashboard for the TweetX tweet scheduler. Write tweets, sort them into categories, browse what's queued, and see what has already gone out — all backed by plain CSV files.
✅ One file of Ruby, Bootstrap from a CDN, and a CSV datastore. No database, no build step.
This repo is the dashboard. The CLI that actually posts lives in TweetX — see Two repos, one tool below.
- ✍️ Add tweets in bulk, assigned to one or more categories
- 📋 Browse the queue and the published archive, with filters for category, ID and text
- 🔍 Preview the exact formatting a tweet will get before it posts
- 📋 Copy a formatted tweet to the clipboard
- 🗂 Manage the category list — add, rename, delete
- 🔐 Session-based login for a single user
git clone https://github.com/cdrrazan/TweetX-web.git
cd TweetX-web
bundle install # Ruby 3.2.2, pinned in the Gemfile
cp .env.sample .env # then fill it in
ruby app.rb # http://localhost:4567
bundle exec rackup # same thing, via config.ruTwo things will stop you booting if you skip them:
SESSION_SECRET must be at least 64 characters. Rack refuses to start otherwise:
ruby -rsecurerandom -e 'puts SecureRandom.hex(64)'Run it from the repo root. app.rb reads data/tweet_collection.csv by relative path, so it only works with the repository root as your working directory.
Set TWEETX_USER and TWEETX_PASS to whatever you want to log in with.
bundle exec rspec # 126 examples
bundle exec rubocop # -a to autocorrectBoth are clean on main. The suite never touches the network and never writes to data/ — the X client is stubbed globally and the CSV paths are redirected to a temp directory for every example.
The Tweet button in the dashboard is deliberately a simulation in this repo. It formats the tweet, returns a preview, and tells you plainly that nothing was sent. No API call, nothing written to the archive, nothing removed from the queue.
This is the safe default for anyone cloning the project. Everything needed to make it real is already in Scheduler, and the comment on POST /tweet-now/:id names the exact lines to swap in.
Before you do that, please read SECURITY.md. This app was written for localhost single-user use, and wiring up real posting means an endpoint that publishes to a live account sits behind a plaintext password check with no CSRF protection.
Short version: don't, without reading SECURITY.md first.
The login form ships with admin / password prefilled as a local convenience. Authentication is a plaintext == against two environment variables, with no rate limiting. There's no CSRF protection, and deletion happens over GET. These are documented, accepted properties of a personal tool — not a threat model for the public internet.
Plain CSV under data/, committed to git:
| File | Columns | Role |
|---|---|---|
tweet_collection.csv |
id,category,tweet |
the queue |
tweet_published.csv |
id,category,tweet,timestamp |
the archive; id is the X tweet ID |
categories.txt |
one per line | category list |
Every mutation rewrites the whole file, with no locking. "Already published" is decided by matching tweet text, not by ID, because the CLI never deletes the row it posted.
Category names are compared with == against the scheduler's hour map, so renaming a category here can quietly make a whole time slot post nothing.
TweetX-web/
├── app.rb # the entire web app: routes, helpers, auth
├── config.ru # rack entry point
├── views/ # ERB + Bootstrap 5, partials in views/partials/
├── lib/tweetx/scheduler.rb # vendored from TweetX; the only X API client
├── lib/tweetx/tweet_formatter.rb # shared by the dashboard and the CLI
├── bin/tweetx # vendored CLI
├── data/ # the CSV datastore
└── spec/ # RSpec suite
| Repo | What it is |
|---|---|
| TweetX-web (here) | the dashboard |
| TweetX | the CLI and scheduler that actually posts, plus the GitHub Actions workflow |
They share lib/tweetx/ and data/ by copy, not by dependency — no gem, no submodule. A fix to shared code needs applying in both.
Ruby 3.2 · Sinatra · Bootstrap 5 · SweetAlert2 · CSV · RSpec · RuboCop
Pull requests welcome. Read CONTRIBUTING.md first — it covers setup and the handful of things here that break silently rather than loudly. Everyone participating is expected to follow the Code of Conduct.
Found a security issue? Please don't open an issue — see SECURITY.md.
Thread support · tweet analytics · AI-assisted drafting · multi-user roles
Rajan Bhattarai — Ruby developer, product hacker, open-source enthusiast.
MIT — free for personal and commercial use.
⭐ Star the project if it helps you.