A background worker that monitors a Twitter account and sends new tweets to a Discord channel via webhook.
- Monitors a specified Twitter account for new tweets
- Fetches up to 5 tweets every 8 hours
- Sends new tweets to Discord via webhook
- Runs as a containerized background service
- Prevents duplicate tweet notifications
- Persistent state tracking - survives container restarts
- Dual deployment modes - continuous or CronJob-based
Create a .env file in the project root with the following variables:
# Twitter API Configuration
BEARER_TOKEN=your_twitter_bearer_token_here
# Twitter username to monitor (without @ symbol)
TWITTER_USERNAME=username_to_monitor
# Discord Webhook URL
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your_webhook_url_here
# Run Mode (optional, defaults to "continuous")
# - "continuous": Traditional mode with 8-hour sleep loop (for Docker Compose)
# - "once": Run a single check and exit (for Kubernetes CronJob)
RUN_MODE=continuous- Go to Twitter Developer Portal
- Create a new app or use an existing one
- Generate a Bearer Token
- Add the Bearer Token to your
.envfile
- Go to your Discord server settings
- Navigate to Integrations > Webhooks
- Create a new webhook
- Copy the webhook URL and add it to your
.envfile
# Build and start the service
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the service
docker-compose downIf you prefer to use the pre-built image from GitHub Container Registry:
# Pull the latest image
docker pull ghcr.io/your-username/tweetcordbot:latest
# Run the container
docker run -d \
--name tweetcord-bot \
--env-file .env \
--restart unless-stopped \
ghcr.io/your-username/tweetcordbot:latestReplace your-username with your GitHub username.
This repository includes a GitHub Actions workflow that automatically builds and pushes Docker images to GitHub Container Registry (ghcr.io).
- Automatic builds on push to main/master branches
- Tag-based releases (e.g.,
v1.0.0) - Multi-platform builds (linux/amd64, linux/arm64)
- Pull request validation (builds but doesn't push)
- Manual trigger support via GitHub Actions UI
- Build caching for faster subsequent builds
- Push to main branch: Automatically builds and pushes
latesttag - Create a release tag:
git tag v1.0.0 git push origin v1.0.0
- Manual trigger: Go to Actions tab → "Build and Push Docker Image" → "Run workflow"
The workflow creates multiple tags:
latest- Latest build from main branchmain- Latest build from main branchv1.0.0- Specific version tagsv1.0- Major.minor version tags
For development with live code reloading, uncomment the development volumes in docker-compose.yml:
volumes:
- .:/app
command: python -m pip install --upgrade pip && python main.py-
Install dependencies:
pip install -r requirements.txt
-
Set up your
.envfile as described above -
Run the application:
python main.py
The bot now maintains state across restarts using a file-based system:
- Last seen tweet ID is stored in
logs/last_tweet_id.txt - On startup, the bot loads the previous state
- Docker Compose automatically mounts
./logsdirectory for persistence - No more duplicate tweets after container restarts!
- The bot fetches the user ID for the specified Twitter username
- Loads the last seen tweet ID from
logs/last_tweet_id.txt(if exists) - Every 8 hours (or on single run), it fetches the 5 most recent tweets
- Compares new tweets against the last seen tweet ID
- Any new tweets are sent to Discord in chronological order
- Saves the latest tweet ID to disk
- In continuous mode, sleeps for 8 hours before the next check
Best for simple deployments or single-host environments:
docker-compose up -dThe bot runs continuously with 8-hour sleep cycles. State persists in ./logs directory.
Best for Kubernetes clusters - runs on a schedule without keeping a pod alive:
Advantages:
- No wasted resources during sleep periods
- Kubernetes-native scheduling
- Automatic cleanup of old jobs
- Better for cluster restarts
See full deployment guide: kubernetes/DEPLOYMENT.md
Quick Start:
# Create secrets
kubectl create secret generic tweetcordbot-secrets \
--from-literal=bearer-token="YOUR_TOKEN" \
--from-literal=twitter-username="YOUR_USERNAME" \
--from-literal=discord-webhook-url="YOUR_WEBHOOK_URL"
# Deploy
kubectl apply -f kubernetes/pvc.yaml
kubectl apply -f kubernetes/cronjob.yaml
# Monitor
kubectl get cronjobs
kubectl logs -l app=tweetcordbot --tail=50The application logs all activities including:
- Tweet monitoring status
- New tweet detection
- Discord webhook delivery status
- Error messages
View logs with:
docker-compose logs -f tweetcord-bot-
Rate Limit Exceeded: The bot includes automatic rate limit handling and will wait 15 minutes before retrying.
-
Invalid Bearer Token: Ensure your Twitter Bearer Token is correct and has the necessary permissions.
-
Discord Webhook Failed: Verify your Discord webhook URL is correct and the webhook is still active.
-
User Not Found: Make sure the Twitter username exists and is spelled correctly (without @ symbol).
-
Duplicate tweets after restart:
- Ensure the
logs/directory is properly mounted (Docker Compose does this automatically) - For Kubernetes, verify PVC is bound:
kubectl get pvc - Check state file exists:
docker exec tweetcord-bot cat /app/logs/last_tweet_id.txt
- Ensure the
# Docker Compose
docker-compose ps
docker-compose logs --tail=50 tweetcord-bot
docker-compose restart
# Kubernetes CronJob
kubectl get cronjobs
kubectl get jobs --sort-by=.metadata.creationTimestamp
kubectl logs -l app=tweetcordbot --tail=50
kubectl create job --from=cronjob/tweetcordbot-cronjob manual-test