minimal x (twitter) posting queue with a mandatory human step. a worker drafts short posts from the latest news headlines; everything lands in a queue. approving an item opens x.com with the text prefilled — you always press the post button yourself. no x api, no developer account, completely free.
- backend: python + fastapi (
app.py) - dashboard: single html file, black/mono/white (
dashboard.html) - worker:
worker.py(local) or github actions cron (free) - storage: one json file (
queue.json). no database, no auth (local use only).
worker.py ──▶ queue.json (pending) ──▶ dashboard ──▶ OPEN IN X ──▶ you press post
└────▶ SKIP
- the worker pulls the latest headlines from gnews (falls back to newsdata.io when the gnews daily limit is hit) and drafts up to 10 posts per run in a dry, lowercase, machine voice.
- each draft is stored with its headline and source url so you can verify the context before approving. seen urls are cached so nothing is drafted twice.
- OPEN IN X opens x.com in a new tab with the text already in the compose box (official x web intent). you press post — or close the tab.
- SKIP archives the draft without posting.
- get free news api keys:
- gnews: https://gnews.io →
NEWS_API_KEY - newsdata.io (fallback): https://newsdata.io →
NEWSDATA_API_KEY
- gnews: https://gnews.io →
cp .env.example .envand fill in the keys. never commit.env.- run
start.bat(windows) orsh start.sh(mac/linux) — installs deps, generates today's batch, opens the dashboard at http://localhost:8000.
or manually:
pip install -r requirements.txt
python worker.py
uvicorn app:app --port 8000
pending drafts on top, posted below. auto-refreshes every 30s. you must be logged in to x in your browser for the compose box to work.
set ANTHROPIC_API_KEY in .env and drafts are written by claude in the
account voice. without it, deterministic dry templates are used.
local: run python worker.py daily (task scheduler / cron).
github actions: .github/workflows/generate.yml runs the worker daily at
06:00 utc (plus a manual trigger) and commits the updated queue.json back
to the repo. add NEWS_API_KEY, NEWSDATA_API_KEY and optionally
ANTHROPIC_API_KEY as repository secrets (settings → secrets and variables →
actions), then git pull before opening the dashboard. note: in a public
repo the committed queue.json — your drafts and history — is visible to
everyone. run the worker locally if you don't want that.
| file | purpose |
|---|---|
app.py |
fastapi backend; approve / skip / queue endpoints |
worker.py |
drafts posts from news headlines |
store.py |
json queue load/save |
dashboard.html |
the entire ui |
queue.json |
pending / posted / skipped / seen-url cache |
.github/workflows/generate.yml |
daily cron worker |
- posting happens through x's official web intent and is always a manual human action — nothing is ever published automatically.
- items are archived as posted when the x tab opens; if you close the tab without sending, the dashboard and reality disagree.