FootyDraft is a Telegramβfirst bot that uses AI to help you run a high quality, onβtrend football Twitter (X) account. It continuously monitors news sources, fetches live match statistics, generates contextβaware tweet drafts (news, stats, and insights) using a large language model, and delivers them to you via a Telegram bot for manual review, copying, and optional Xquik posting. Over time, it learns from your engagement metrics to refine its writing style β all while keeping you in full control.
- Realβtime news ingestion (RSS, Reddit, Google News, APIβFootball) - 100% free
- Fast transfer news lane β dedicated highβfrequency polling (every 15β―minutes) of six fastβupdating RSS feeds for nearβrealβtime transfer gossip
- Postβmatch & halfβtime stat packs β possession, xG, shots, passes automatically generated for every finished match
- Live inβgame stat snapshots β triggered by the first goal of each half, showing key match stats in real time
- Weekly leaderboards β top scorers & assists for the World Cup and any future league (via footballβdata.org, free tier)
- Nerdy stats of the week β automatic detection of unusual match patterns (xG overperformance, possession without result, shot barrages, passing extremes, xG shutouts)
- Strong freshness filter β all news items older than 12 hours are discarded, preventing yesterdayβs stories from reappearing
- Interactive Streamlit Dashboard β view drafts, performance analytics, source health, rules, and backups in one place
- Hybrid tweet drafting with three personas (pundit, fan, analyst)
- Three variants per normal event, one per live match event
- Telegram bot for reviewing, copying, and tracking drafts
- Manual feedback loop - enter tweet engagement metrics without the X API
- Optional Xquik posting - publish approved drafts only when
XQUIK_POSTING_ENABLED=1 - Automatic database backups β compressed backup on every boot and nightly at 3β―AM UTC (Dropbox primary, Telegram secondary)
- Realβtime error tracking β Sentry integration to monitor crashes and bugs
- Builtβin uptime monitor β
/uptimecommand shows how long the bot has been running (WAT) - Gated database restore β
/restorecommand with oneβtime code, backup file picker, and automatic staging - Safe remote restart β
/restartwith twoβstep confirmation, no computer needed - Weekly analytics that suggest style improvements based on your bestβperforming tweets
- Deduplication & age filters β 12βhour age cutoff for RSS, Google News, and Reddit; contentβhash dedup prevents repeated drafts, with special dateβstamping for weekly recurring drafts
- Deployable 24/7 on Fly.io (or any Dockerβcompatible platform)
- News Fetchers pull headlines from BBC, Sky Sports, ESPN, The Guardian, Daily Mail, Reddit, Google News, and live match data from APIβFootball. A dedicated fast lane polls six transferβfocused RSS feeds every 15 minutes. Weekly leaderboard data is sourced from footballβdata.org.
- Event Classifier tags each story (goal, transfer, stats, debate, meme, etc.) using keyword rules.
- Deduplication Engine ensures the same story doesnβt generate repeated drafts within 24β―hours.
- Prompt Builder combines your accountβs persona, the selected mode (pundit/fan/analyst), active style rules, and your topβperforming tweets as fewβshot examples.
- LLM Client (Groq with Mistral fallback) generates tweet drafts.
- Telegram Bot shows pending drafts in a queue with inline βCopyβ buttons, optional
/postxpublishing, and instant liveβevent drafts. - Analytics Engine runs weekly, identifies patterns in your engagement data, and suggests naturalβlanguage rules you can approve or reject.
- Stats Engine β after every fullβtime whistle, match statistics are stored in the database. A weekly job analyses the accumulated data and creates a βNerdy Stats of the Weekβ draft highlighting unusual patterns.
- Leaderboard Engine β twice a week, top scorers and assist leaders are fetched from footballβdata.org and turned into a combined draft. Inactive leagues are automatically skipped.
- Backup Engine β automatically creates a gzipped database backup on every machine restart and daily at 3β―AM UTC, uploading to Dropbox (primary) and Telegram (secondary).
- Monitoring β the
/uptimecommand shows the botβs start time and elapsed uptime; Sentry integration tracks errors in real time. - Recovery β the
/restorecommand uses a gated oneβtime code to list the last 5 backups from Dropbox, then stages the selected backup; a/restartreboot applies it automatically.
footydraft/
βββ config/
β βββ settings.py # Environment variables & constants
β βββ personas.yaml # Persona definitions (pundit, fan, analyst)
βββ core/ # Engine (UIβagnostic)
β βββ ingestion/ # News fetchers
β β βββ base.py # Abstract BaseFetcher + NewsItem
β β βββ rss_fetcher.py # BBC, Sky, ESPN, Guardian, etc.
β β βββ reddit_fetcher.py
β β βββ google_news_fetcher.py
β β βββ api_football_fetcher.py
β β βββ espn_fetcher.py # ESPN hidden API (CL, Europa, cups)
β β βββ monitor.py # Source health monitoring
β βββ classification/
β β βββ event_tagger.py # Keywordβbased tagger
β β βββ dedup.py # Contentβhash deduplication
β βββ generation/
β β βββ prompt_builder.py
β β βββ llm_client.py # Groq (primary), Mistral (fallback)
β β βββ queue_manager.py # Draft creation & daily caps
β βββ publishing/ # Optional Xquik posting
β β βββ __init__.py
β β βββ xquik.py
β βββ analytics/
β β βββ engine.py # Weekly rule suggestions
β βββ backup.py # Dropbox & Telegram backup (gzipped)
β βββ database.py # SQLAlchemy init & session
β βββ models.py # ORM models (Draft, Tweet, Rule, etc.)
β βββ scheduler.py # Main loop
βββ bot/
β βββ handlers.py # All command handlers
β βββ keyboard.py # Inline keyboards (Copy, pagination)
β βββ main.py # Bot entry point + liveβdraft push
βββ dashboard/ # Streamlit dashboard
β βββ app.py # Main dashboard entry point
β βββ ui_components.py # Shared UI (CSS, sidebar, navigation)
β βββ utils.py # Shared utilities (Dropbox, database helpers)
β βββ requirements.txt # Dashboard dependencies
β βββ Dockerfile.dashboard # Dockerfile for dashboard service
β βββ static/ # Static assets (favicon, etc.)
β βββ pages/ # Dashboard pages
β βββ Backup_Browser.py
β βββ Database_Stats.py
β βββ Drafts.py
β βββ Live_Check.py
β βββ Performance.py
β βββ Rule_Manager.py
β βββ Source_Health.py
βββ data/ # SQLite database (autoβcreated)
βββ scripts/ # Diagnostic & utility scripts
β βββ diagnostic_tags.py # Check event classifier tag distribution
β βββ diagnostic.py # Check APIβFootball live data & classification
β βββ direct_api_check.py # Compare live=all vs date endpoints
βββ tests/ # Unit & integration tests
βββ .env.example # Environment template
βββ Dockerfile # Containerised deployment
βββ fly.toml # Fly.io configuration
βββ requirements.txt
βββ start.sh # Startup script (scheduler + bot)
βββ README.md
- Python 3.11+
- Groq API key
- Telegram bot token (from @BotFather)
- APIβFootball key for live match data
git clone https://github.com/MikkyPrestige/footydraft.git
cd footydraft
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -r requirements.txt
cp .env.example .env
# Now edit .env with your real API keyspython -c "from core.database import init_db; init_db()"Terminal 1 β Core scheduler (fetches news, generates drafts):
python -m core.schedulerTerminal 2 β Telegram bot:
python -m bot.main/startβ welcome message and command list/queueβ view pending drafts (all, normal, or live) with Copy buttons & pagination/queue normalβ only normal pending drafts/queue liveβ only live-match drafts/draftsβ browse all drafts (all,pending,held,posted) with pagination and copy buttons/drafts pendingβ only pending normal drafts/drafts heldβ only held draftsdrafts postedβ only posted drafts/leaderboardβ fetch latest top scorers & assists leaderboard/nerdystatsβ generate nerdy stats of the week draft/hold <draft_id>β quarantine a pending draft/release <draft_id>β return a held draft to the queue/posted <draft_id>β mark a draft as manually posted & link a tweet/postx <draft_id> [variant]β post an approved draft via Xquik (requiresXQUIK_POSTING_ENABLED=1)/metrics <tweet_ref> <likes> <retweets> <replies> <impressions>β enter engagement numbers/statsβ top & bottom tweets by likes (default)/impressionsβ top & bottom tweets by impressions/tweetsβ list all posted tweets with refs/rulesβ manage style rules (accept/reject autoβsuggestions)/addrule <text>β add a manual style rule/source_statusβ check health of news sources/livecheckβ force check for live matches/clearqueueβ delete all pending drafts (or onlynormal/live)/clearqueue normalβ delete only normal pending drafts/clearqueue liveβ delete only live pending drafts/uptimeβ bot uptime and start time (WAT)/restoreβ gated database restore from Dropbox backups/restartβ Restart the bot (two-step confirmation)
# Install dashboard dependencies
pip install -r dashboard/requirements.txt
# Run the dashboard
streamlit run dashboard/app.pyThe dashboard will be available at http://localhost:8501
Start both services
docker compose up -dStart only the dashboard
docker compose up -d dashboardStart only the bot
docker compose up -d botThe dashboard will be available at http://localhost:8501
The project includes a Dockerfile and fly.toml for oneβclick deployment to Fly.io.
It runs both the scheduler and the Telegram bot in a single small VM (~$1.94/month).
- Install the Fly CLI:
curl -L https://fly.io/install.sh | sh - Log in:
flyctl auth login - Create the app & volume (run inside the project folder):
flyctl apps create footydraft --yes flyctl volumes create agent_data --region iad --size 1 -a footydraft
- Set secrets (replace with your values):
flyctl secrets set OPENAI_API_KEY="sk-..." TELEGRAM_BOT_TOKEN="123:abc" ... -a footydraft - Deploy:
flyctl deploy -a footydraft - Check logs:
flyctl logs -a footydraft
Your bot will now run 24/7 without needing your computer.
The dashboard can be deployed to Streamlit Cloud for 24/7 access.
- Push your code to GitHub.
- Go to Streamlit Cloud and sign in with GitHub.
- Click "New app" and select your repository.
- Set the main file path to:
dashboard/app.py - In Settings β Secrets, add the following environment variables:
DROPBOX_APP_KEY = "your_key"
DROPBOX_APP_SECRET = "your_secret"
DROPBOX_REFRESH_TOKEN = "your_token"
TELEGRAM_BOT_TOKEN = "your_token"
ADMIN_CHAT_ID = "your_chat_id"
# Required for Xquik toggle (via Fly.io CLI only)
XQUIK_POSTING_ENABLED = "1"
# Required if using Xquik toggle (optional)
FLY_API_TOKEN = "your_token"
FLY_APP_NAME = "footydraft"
FLY_MACHINE_ID = "your_machine_id"- Click Deploy.
The dashboard is passwordβprotected. On first visit youβll see a login screen. After entering the correct password (set via PASSWORD secret in Streamlit Cloud), the session persists across page refreshes.
Use this command to add the dashboard password to your Fly.io secrets:
flyctl secrets set PASSWORD=yourβchosenβpassword -a footydraftThe dashboard will be available at https://your-app-name.streamlit.app
All important settings are in config/settings.py and can be overridden with environment variables:
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
β | API key (Groq or any OpenAIβcompatible provider) |
TELEGRAM_BOT_TOKEN |
β | Telegram bot token |
GROQ_API_KEY |
β | Groq API key (primary) |
MISTRAL_API_KEY |
β | Mistral API key (fallback when Groq is rateβlimited) |
API_FOOTBALL_KEY |
β | APIβFootball key |
FOOTBALL_DATA_KEY |
β | API key for footballβdata.org β required for weekly leaderboards |
DATABASE_URL |
sqlite:///data/agent.db |
DB connection string |
XQUIK_POSTING_ENABLED |
0 |
Set to 1 to enable /postx publishing |
XQUIK_API_KEY |
β | Xquik API key used by /postx |
XQUIK_API_BASE_URL |
https://xquik.com |
Override for custom Xquik deployments |
NORMAL_DAILY_CAP |
5 | Max normal tweets posted per day |
LIVE_MATCH_HOURLY_CAP |
10 | Max live tweets per match hour |
MAX_ITEM_AGE_HOURS |
12 | Discard news older than this |
DROPBOX_APP_KEY |
β | Dropbox app key for automatic backups |
DROPBOX_APP_SECRET |
β | Dropbox app secret |
DROPBOX_REFRESH_TOKEN |
β | Dropbox refresh token (never expires) |
SENTRY_DSN |
β | Sentry DSN for error tracking |
FLY_API_TOKEN |
β | Fly.io org token (required for /restart) |
STREAMLIT_SERVER_PORT |
8501 |
Port for Streamlit dashboard |
STREAMLIT_SERVER_ADDRESS |
0.0.0.0 |
Address for Streamlit dashboard |
Xquik posting allows the bot to automatically post tweets to X (Twitter). This is controlled via Fly.io environment variables.
Enable Xquik
flyctl secrets set XQUIK_POSTING_ENABLED=1 -a footydraft
flyctl machines restart <machine_id> -a footydraftDisable Xquik
flyctl secrets set XQUIK_POSTING_ENABLED=0 -a footydraft
flyctl machines restart <machine_id> -a footydraftGet machine ID
flyctl machines list -a footydraftThe
/postxcommand in the Telegram bot will only work whenXQUIK_POSTING_ENABLED=1
[!NOTE] The Xquik toggle has been removed from the Streamlit dashboard due to persistence issues on Streamlit Cloud. The backend functionality remains fully available via the Fly.io CLI.
# Run individual test scripts
python -m tests.test_rss_fetcher
python -m tests.test_llm_client
python -m tests.test_queue_manager
python -m tests.test_personas
python -m tests.test_prompt_builder
## more in the tests/ folder- Optional scheduled autoβposting with killβswitch
- Multiβlanguage support
Contributions are welcome! This is a personal project, but if you have ideas for improvements or new fetchers, feel free to open a PR or issue. Please keep the core UIβagnostic principle in mind.