Monitor DMV appointment availability and get instant Telegram notifications when appointments open up.
- π― Monitor multiple locations simultaneously
- π Filter appointments by maximum date
- π± Telegram notifications when appointments become available
- β° Configurable polling intervals
- π« Anti-spam: Sends one notification, then waits 1 hour
- π Runs continuously in the background
- π Detailed logging
# Clone or download this repository
cd DMVWatcher
# Create virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
playwright install chromium- Open Telegram, search for @BotFather
- Send
/newbotand follow the prompts - Save the bot token (looks like:
1234567890:ABCdef...)
You must send a message to your bot first!
- Search for your bot by username (e.g.,
@your_bot_name_bot) - Start a chat and send any message (e.g., "Hello")
- Visit:
https://api.telegram.org/botYOUR_BOT_TOKEN/getUpdates - Look for
"chat":{"id":123456789}- that number is your chat ID
Alternative: Use @RawDataBot - it will show your user ID directly.
Copy config.example.json to config.json:
cp config.example.json config.jsonEdit config.json:
{
"targets": ["Mercer"],
"monitoring_duration_minutes": 120,
"polling_interval_seconds": 300,
"max_date": "01/18/2026",
"url": "https://telegov.njportal.com/njmvcmobileunit/AppointmentWizard/265",
"telegram": {
"bot_token": "your_bot_token_here",
"chat_id": "your_chat_id_here"
}
}Or use environment variables (recommended for sensitive data):
Create .env file:
TELEGRAM_BOT_TOKEN='your_bot_token_here'
TELEGRAM_CHAT_ID='your_chat_id_here'Environment variables take priority over config.json for Telegram credentials.
# Foreground (for testing)
python dmvwatcher.py --config config.json
# Background (recommended)
./scripts/run_background.sh| Option | Description | Default |
|---|---|---|
targets |
Array of county names to monitor (e.g., ["Mercer", "Camden"]) |
Required |
monitoring_duration_minutes |
How long to monitor (0 = indefinite) | 0 |
polling_interval_seconds |
Seconds between checks | 300 (5 minutes) |
max_date |
Only alert if appointment is before this date (MM/DD/YYYY) | None |
url |
DMV appointment page URL | Default NJ URL |
telegram.bot_token |
Your Telegram bot token | Required |
telegram.chat_id |
Your Telegram chat ID | Required |
python dmvwatcher.py [OPTIONS]
Options:
--targets, -t County names to monitor (comma-separated)
--duration, -d Monitoring duration in minutes (0 = indefinite)
--interval, -i Polling interval in seconds
--max-date Maximum appointment date (MM/DD/YYYY)
--config, -c Path to config file
--url DMV appointment page URLExamples:
# Using config file
python dmvwatcher.py --config config.json
# Using command-line arguments
python dmvwatcher.py --targets Mercer,Camden --duration 120 --interval 300
# Mix both (CLI overrides config file)
python dmvwatcher.py --config config.json --interval 600# Start
./scripts/run_background.sh
# Stop
./scripts/stop_background.sh
# View logs
tail -f dmvwatcher.log# Install (runs on boot, auto-restarts if crashes)
cp scripts/com.dmvwatcher.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.dmvwatcher.plist
launchctl start com.dmvwatcher
# Stop
launchctl stop com.dmvwatcher
# Uninstall
launchctl unload ~/Library/LaunchAgents/com.dmvwatcher.plist
rm ~/Library/LaunchAgents/com.dmvwatcher.plist# Start
nohup python dmvwatcher.py --config config.json > dmvwatcher.log 2>&1 &
# Stop (find PID first)
ps aux | grep dmvwatcher.py
kill <PID># Using screen
screen -S dmvwatcher
python dmvwatcher.py --config config.json
# Press Ctrl+A then D to detach
# Reattach later
screen -r dmvwatcher- Monitors specified DMV locations (by county name)
- Checks every N seconds (configurable) for appointment availability
- Detects when "Make Appointment" button appears (source of truth)
- Filters by date if
max_dateis set - Sends ONE Telegram notification when appointment found
- Waits 1 hour after notification to avoid spam
- Continues monitoring after the wait period
- Console: Real-time status updates
- Log file:
dmvwatcher.log(detailed logging, auto-rotated) - Status file:
status.json(last known status, not committed to git) - Telegram: Instant notifications when appointments available
DMVWatcher/
βββ dmvwatcher.py # Main entry point
βββ config.example.json # Example configuration
βββ requirements.txt # Python dependencies
βββ README.md # This file
βββ src/ # Source code
β βββ config.py # Configuration loading
β βββ notifications.py # Telegram notifications
β βββ watcher.py # Core monitoring logic
βββ scripts/ # Helper scripts
β βββ run_background.sh # Start in background
β βββ stop_background.sh # Stop background process
β βββ com.dmvwatcher.plist # macOS Launch Agent
βββ tests/ # Test files
βββ test_telegram_final.py # Telegram bot test
"Chat not found" error:
- Make sure you sent at least one message to your bot first
- Double-check chat_id is correct (your user ID)
- Try running
python tests/test_telegram_final.pyto test
"Unauthorized" error:
- Check bot token is correct (no extra spaces or quotes)
- Verify bot was created successfully via BotFather
Bot not sending messages:
- Ensure you sent a message to the bot first (required by Telegram)
- Check
.envfile has correct values (orconfig.json) - Verify bot is not blocked
No appointments detected:
- Check
targetsmatch county names exactly (case-insensitive) - Verify URL is correct
- Check logs:
tail -f dmvwatcher.log - Ensure "Make Appointment" button exists on the page
Script stops running:
- Check logs for errors
- Verify internet connection
- Check if Playwright browser crashed (restart script)
False positives:
- The script uses "Make Appointment" button as source of truth
- If button exists, appointment is available
- Check logs to see what was detected
Test your Telegram bot setup:
python tests/test_telegram_final.pyThis will verify your bot token and chat_id are correct.
- Python 3.8+
- Playwright (Chromium browser)
- Telegram bot token and chat ID
- Internet connection
Personal use project - modify as needed.
- Logs and status files are not committed to git (see
.gitignore) .envfile should never be committed (contains sensitive tokens)- The script creates a fresh browser session for each check to avoid caching issues
- Anti-spam logic: After sending a notification, waits 1 hour before checking again