Zam: A Telegram Bot for capturing and posting tweets to Telegram channels
This program is dedicated to Roohollah Zam. He was an Iranian activist and journalist. Zam played a high-profile role in the 2017–2018 Iranian protests, to which he devoted special coverage at the time. In June 2020, an Iran court found him guilty of "corruption on earth" for running a popular anti-government forum, which officials said had incited the 2017–2018 Iranian protests. He was sentenced to death by the regime court and was executed on 12 December 2020. |
Zam is a Telegram bot that captures tweets as screenshots and allows admins to schedule them for posting to a Telegram channel. It uses a priority-based queue system where admin requests are processed before user suggestions.
Note: Looking for the Twitter API version? The legacy code that uses the Twitter/X API (Tweepy) is available in the
legacybranch. This version uses screenshot capture instead, as the Twitter API became restricted/expensive after the X acquisition.
- 📸 Tweet Screenshot Capture: Captures tweets as high-quality screenshots using headless Chrome with full Persian/Arabic font support
- 🔄 Priority Queue System: Admin tweets are processed before user suggestions
- ⏰ Smart Auto-Scheduling: Intelligent scheduling algorithm with peak hour optimization and minimum gap enforcement
- 🐳 Docker Containerized: Easy deployment with Docker Compose
- 🗄️ PostgreSQL Database: Reliable data storage with connection pooling
- 👥 Multi-Bot Architecture: Separate bots for admins and user suggestions
- 🚦 Rate Limiting: Configurable hourly limits for user suggestions
- 🇮🇷 Persian Calendar Support: Displays dates in Persian/Jalali calendar
- 📊 Monitoring Dashboard (
/stats): Real-time statistics including queue status, scheduled posts, and peak hour availability - ⏰ Manual & Auto Scheduling: Choose specific times or let the smart algorithm pick optimal slots
- 📈 Visual Progress Bars: See hourly slot availability at a glance
- 🎯 Interactive Menu: User-friendly button-based navigation
- 📤 Tweet Submission: Easy tweet URL submission with queue position feedback
- 💬 Categorized Feedback: Users can send suggestions, bug reports, or questions to admins
- 📊 Submission Tracking: Users can view their remaining hourly submissions
-
Clone the repository
git clone https://github.com/AminAlam/Zam.git cd Zam -
Configure environment variables
cp .env.example .env
Edit
.envwith your credentials (see Configuration section). -
Build and start the containers
docker compose up --build -d
-
View logs
docker compose logs -f app
-
Stop the application
docker compose down
This project uses uv for fast, reliable Python package management.
-
Install uv (if not already installed)
# macOS/Linux curl -LsSf https://astral.sh/uv/install.sh | sh # Or with Homebrew brew install uv # Or with pip pip install uv
-
Clone the repository
git clone https://github.com/AminAlam/Zam.git cd Zam -
Install dependencies
# Install all dependencies (including dev) uv sync # Or without dev dependencies uv sync --no-dev
-
Install Chromium (required for tweet-capture)
# Ubuntu/Debian sudo apt-get install chromium chromium-driver # macOS brew install chromium
-
Set up PostgreSQL database
# Create database and run init.sql psql -U postgres -c "CREATE DATABASE zam_db;" psql -U postgres -d zam_db -f src/database/init.sql
-
Configure environment variables
cp .env.example .env # Edit .env with your credentials -
Run the application
uv run python -m src.main
# Sync dependencies from lockfile
uv sync
# Add a new dependency
uv add <package>
# Add a dev dependency
uv add --dev <package>
# Update all dependencies
uv lock --upgrade
# Run any command in the virtual environment
uv run <command>
# Run tests
uv run pytest
# Run the linter
uv run ruff check .Create a .env file in the project root with the following variables:
DB_HOST=db # Use 'localhost' for manual installation
DB_PORT=5432
DB_USER=zam
DB_PASSWORD=your_secure_password
DB_NAME=zam_dbGet bot tokens from @BotFather on Telegram.
ADMIN_TELEGRAM_BOT=your_admin_bot_token
SUGGESTIONS_TELEGRAM_BOT=your_suggestions_bot_tokenGet chat IDs by forwarding a message from the channel to @userinfobot.
MAIN_CHANNEL_CHAT_ID=your_main_channel_id
ADMIN_CHAT_ID=your_admin_chat_id
SUGGESTIONS_CHAT_ID=your_suggestions_chat_idCHANNEL_NAME=@YourChannelName
ADMIN_IDS=admin1,admin2,admin3 # Telegram usernames without @uv run python -m src.main --help
Usage: main.py [OPTIONS]
Zam: A Telegram Bot for posting tweets in a Telegram channel
Options:
--time_diff TEXT Difference between server time and target
timezone. Format: HOURS:MINUTES (default: 3:30)
--mahsa_message BOOLEAN Enable Mahsa Amini memorial timer message
--reference_snapshot BOOLEAN Include snapshots of quoted/replied tweets
--num_tweets_to_preserve INTEGER RANGE
Number of tweets to keep in database [500-5000]
--user_tweet_limit INTEGER RANGE
Hourly limit per user for suggestions [0-120]
Set to 0 for unlimited
--help Show this message and exit# Run with custom options
docker compose run app uv run python -m src.main --time_diff 3:30 --user_tweet_limit 5Admin Bot:
- Send a tweet URL (twitter.com or x.com) to add it to the queue
/start- Start the bot/queue- View current queue status/stats- View comprehensive channel statistics:📊 Channel Statistics 📝 Queue Status: • Pending captures: 3 • Currently processing: 1 📅 Scheduled Posts: • Awaiting posting: 8 • Next post: 20:15 (in 12 min) 📈 Today's Activity: • Posts sent: 24 ⏰ Next 6 Hours Availability: 20:00 ████████░░ 4/6 21:00 ██████░░░░ 3/6 22:00 ██░░░░░░░░ 1/6 23:00 ░░░░░░░░░░ FULL
Suggestions Bot:
/start- Open the interactive menu with options:- 📤 Submit Tweet: Send a tweet URL to suggest for the channel
- 💬 Send Feedback: Send a message to admins (categorized as suggestion, bug report, or question)
- 📊 My Remaining Submissions: Check your hourly submission limit status
- Submit a Tweet: Send a tweet URL to either the admin or suggestions bot
- Queue Processing: The tweet is added to a priority queue (admin tweets have higher priority)
- Screenshot Capture: A background worker captures the tweet as a screenshot
- Admin Review: The captured tweet is sent to the admin channel with scheduling options
- Schedule or Post: Admins can schedule the tweet manually or use Auto timing for smart scheduling
- Channel Posting: At the scheduled time, the tweet is posted to the main channel
The "Auto timing" feature uses an intelligent algorithm to schedule tweets:
- Peak Hour Optimization: More tweets are scheduled during high-engagement hours (8 PM - 1 AM)
- Quiet Hour Reduction: Fewer tweets during low-activity hours (2 AM - 6 AM)
- Minimum Gap Enforcement: Ensures at least 5 minutes between consecutive tweets
- Next-Day Rollover: Automatically schedules for tomorrow if today's slots are full
Hour Weight Distribution:
| Period | Hours | Relative Weight |
|---|---|---|
| Quiet | 2-6 AM | Low (0.3x) |
| Morning | 7-11 AM | Medium (0.7x) |
| Afternoon | 12-7 PM | Normal (0.8x) |
| Evening | 8-10 PM | High (1.5x) |
| Night | 11 PM-1 AM | High (1.3x) |
┌─────────────────────────────────────────────────────────┐
│ Docker Compose │
│ ┌─────────────────────────────────────────────────┐ │
│ │ App Container (uv + Python 3.11) │ │
│ │ ┌─────────────┐ ┌─────────────────────────┐ │ │
│ │ │ Admin Bot │ │ Suggestions Bot │ │ │
│ │ └──────┬──────┘ └───────────┬─────────────┘ │ │
│ │ │ │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Queue Worker │ │ │
│ │ │ (Priority-based) │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Tweet Capture │ │ │
│ │ │ (Headless Chrome) │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PostgreSQL Container │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Tables: tweets, tweet_queue, states, │ │ │
│ │ │ tweets_line, errors, user_feedback │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Zam/
├── pyproject.toml # Project configuration & dependencies
├── uv.lock # Lockfile for reproducible builds
├── .python-version # Python version specification
├── Dockerfile # Docker build with uv
├── docker compose.yml # Container orchestration
├── src/ # Main application code
│ ├── main.py # Entry point
│ ├── telegram_backend.py # Telegram bot handlers
│ ├── twitter_backend.py # Tweet capture & queue
│ ├── utils.py # Utility functions
│ ├── ocr.py # OCR text extraction
│ ├── migrations.py # Database migrations
│ └── database/ # Database module
│ ├── database.py # Database operations
│ └── init.sql # Schema definitions
├── tweetcapture/ # Local tweet screenshot package
│ ├── pyproject.toml # Package configuration
│ └── tweetcapture/ # Package source
└── tests/ # Test suite
# Run all unit tests
uv run pytest tests/ -m "not integration and not slow"
# Run with coverage
uv run pytest tests/ --cov=src --cov-report=html
# Run integration tests (requires Chrome/Chromium)
uv run pytest tests/test_integration.py -m "integration"
# Run all tests
uv run pytesttests/test_twitter_backend.py- URL parsing, queue management teststests/test_utils.py- Utility function teststests/test_database.py- Database operation teststests/test_integration.py- End-to-end integration tests
Tests run automatically on pull requests with the following workflow:
- Approval Gate: PRs require manual approval before tests run (protects against malicious code)
- Unit Tests: Fast tests that don't require external services
- Integration Tests: Tests with PostgreSQL and Chrome for screenshot capture
- Docker Build: Verifies the Docker image builds correctly
The test suite uses Jack Dorsey's first tweet (https://x.com/jack/status/20) as a stable reference for screenshot capture tests.
This project is open source and available under the MIT License.