A Telegram userbot that watches specified groups for messages from target users and forwards notifications to your Saved Messages (or any chat you choose).
Built with Telethon. Deployed on Fly.io (free tier).
watch.py runs as a Telegram client under your own account. When a target user posts in one of the watched groups, it immediately forwards you a notification with the sender's name, group name, message text, and a direct link to the message.
- Python 3.10+
- A Telegram account
- API credentials from my.telegram.org
1. Clone the repo
git clone git@github.com:jingchenyc/telegram-notify.git
cd telegram-notify2. Install dependencies
make install3. Configure environment
cp .env.example .envEdit .env and fill in your values:
| Variable | Description |
|---|---|
TG_API_ID |
Your Telegram API ID (from my.telegram.org) |
TG_API_HASH |
Your Telegram API hash |
TG_SESSION_NAME |
Session file name (default: watch_session) |
TARGET_CHATS |
Comma-separated group usernames or numeric IDs to watch |
TARGET_USERS |
Comma-separated usernames or numeric IDs to track |
NOTIFY_TO |
Where to send notifications (me for Saved Messages, or a username/ID) |
4. Find chat and user IDs
List all groups your account has joined:
python list_chats.pyResolve usernames to numeric IDs (more reliable for watching):
make resolve5. Run locally
make watchOn first run, Telethon will prompt you to log in (phone number + verification code). A .session file is saved locally for subsequent runs.
The bot runs 24/7 on Fly.io's free tier using a shared-cpu-1x 256MB machine.
1. Install flyctl and log in
brew install flyctl
fly auth login2. Create the app
fly launch --no-deploy3. Generate a StringSession from your local session
python -c "
import asyncio
from telethon import TelegramClient
from telethon.sessions import StringSession
from dotenv import load_dotenv
load_dotenv('.env')
import os
async def main():
async with TelegramClient(os.environ['TG_SESSION_NAME'], int(os.environ['TG_API_ID']), os.environ['TG_API_HASH']) as client:
print(StringSession.save(client.session))
asyncio.run(main())
"4. Set secrets
fly secrets set \
TG_API_ID=your_api_id \
TG_API_HASH=your_api_hash \
TG_SESSION_STRING="1BVts..." \
TARGET_CHATS="-1001234567890" \
TARGET_USERS="123456789" \
NOTIFY_TO=me5. Deploy
make deploy6. Check logs
make logsYou should see:
Logged in as: Your Name (@username)
Loading dialogs...
Watching chats: [...]
Target users: [...]
Listening for messages... (Ctrl+C to stop)
telegram-notify/
├── watch.py # Main watcher — monitors groups and sends notifications
├── resolve_ids.py # Helper to look up numeric IDs for chats and users
├── list_chats.py # Helper to list all joined groups/channels
├── entrypoint.sh # Docker entrypoint
├── Dockerfile # Container definition for Fly.io
├── fly.toml # Fly.io app config
├── .env.example # Environment variable template
├── requirements.txt # Python dependencies
└── Makefile # Convenience commands (install, watch, deploy, logs)
No redeploy needed — just update the secret and the machine restarts automatically:
fly secrets set TARGET_USERS="new_user_id"
fly secrets set TARGET_CHATS="-1001234567890,-1009876543210"- Message links are only generated for supergroups and channels. Basic groups do not support direct message links.
- The
.sessionfile and.envare excluded from version control — keep them private. TG_SESSION_STRINGtakes priority overTG_SESSION_NAMEwhen set.