A Telegram userbot that runs on a personal account (Pyrogram, not the Bot API), with anti-delete logging, self-destructing media capture, auto read-receipts, an authorization layer, group moderation, live data scrapers, and a multimodal AI assistant powered by the Google Gemini API.
Interface language: every message the bot sends into Telegram is in Persian (Farsi). Logs, error messages and all documentation are in English. If you need an English chat interface, the user-facing strings live in
plugins/andutils/helpers.pyand are straightforward to translate — see Coding standards.
This project runs on your real Telegram account, not on a bot account.
- Running a userbot is generally interpreted as a violation of the Telegram Terms of Service. Unusual behaviour (spam, bulk actions) can get your account limited or banned. You accept that risk.
data/my_account.sessionis equivalent to full access to your account. Never commit it, send it to anyone, or leave it anywhere public.data/logger.dbcontains real private messages.
Both are gitignored by default.
| Area | What it does |
|---|---|
| 🗑 Anti-delete | Private messages (both directions) are cached for 12 hours; a deletion is reported to Saved Messages — photos, voices, stickers, GIFs and video notes come back as the file itself, with the send and delete times |
| 🕵️ TTL media guard | Timed and view-once media — photo, video, video note, voice, GIF, audio, file — is saved before it can be viewed |
| 👁 Auto-seen | Automatic read receipts for selected chats |
| 👮 Member control | Kick, ban and mute (with optional duration) in a group |
| 🌐 Global actions | .gban / .gmute across every managed group, enforced on later messages too |
| 🔐 Authorization layer | Owner-only commands separated from authorized users |
| 🧠 AI assistant | Streaming Q&A with support for images, voice, video, PDFs and replies |
| 🔊 Voice | Speech-to-text (.stt) and text to a real Telegram voice message (.tts) |
| 🌐 AI tools | Translation, OCR, summarization, image generation |
| 📈 Live data | Crypto prices in Toman, gold prices, weather, prayer times |
| 🛡 Resilience | Clock synchronization, Pyrogram peer-id range patch, log noise throttling |
- Python 3.13
API_IDandAPI_HASHfrom my.telegram.org- A Google Gemini API key from aistudio.google.com/apikey (optional — needed only for the AI commands)
ffmpeg(optional — without it,.ttsreturns a WAV file instead of a voice message)
git clone <repo-url> && cd Telegram_Account
python3.13 -m venv venv
./venv/bin/pip install -r requirements.txt
cp .env.example .env # then fill in the values
./venv/bin/python main.pyOn the first run you are asked for your phone number and a login code. The session is stored
in data/ and you are not asked again.
cp .env.example .env # then fill in the values
docker compose up --buildThe first login has to be interactive. If
data/my_account.sessiondoes not exist yet, either log in once with Option 1, or run the container withdocker compose run --rm userbotso you can type the code.
Only
./datais mounted into the container; the code ships inside the image, so code changes requiredocker compose up --build.
All settings come from a .env file. A fully commented sample lives in
.env.example.
| Variable | Required | Default | Description |
|---|---|---|---|
API_ID |
✅ | — | From my.telegram.org |
API_HASH |
✅ | — | From my.telegram.org |
LOG_LEVEL |
➖ | INFO |
Log level (DEBUG also enables Pyrogram's own logs) |
AI_API_KEYS |
➖ | — | Gemini keys, comma-separated (rotated automatically on quota errors) |
AI_MODEL |
➖ | gemini-3.6-flash |
Chat model |
AI_IMAGE_MODEL |
➖ | gemini-3.1-flash-image |
Image generation model (requires a paid plan) |
AI_TTS_MODEL |
➖ | gemini-3.1-flash-tts-preview |
Text-to-speech model |
AI_TTS_VOICE |
➖ | Kore |
Default voice (.tts voices lists them) |
AI_TIMEOUT |
➖ | 120 |
Max wait per response chunk (seconds) |
AI_MAX_TURNS |
➖ | 12 |
Conversation memory kept per chat, in messages |
AI_MAX_CONCURRENCY |
➖ | 5 |
Max concurrent AI requests |
AI_MEDIA_MAX_MB |
➖ | 25 |
Max size of a file forwarded to the model |
TTL_GUARD |
➖ | true |
Save self-destructing private media before it is viewed |
TTL_GUARD_LOCAL_COPY |
➖ | false |
Keep an extra copy on disk under data/ttl_media/ |
TTL_GUARD_DEBUG |
➖ | false |
Attach the raw media structure to a failed rescue report |
TTL_MEDIA_RETENTION_DAYS |
➖ | 2 |
How long local copies stay on disk before being swept |
DISPLAY_TIMEZONE |
➖ | Asia/Tehran |
Timezone for every time shown in a Telegram message |
DELETE_CACHE_MEDIA |
➖ | true |
Pre-download small media so a deletion report survives an expired file reference |
DELETE_CACHE_MAX_MB |
➖ | 5 |
Largest media that gets pre-downloaded |
DELETE_CACHE_TOTAL_MB |
➖ | 500 |
Hard ceiling for data/deleted_media/ |
GBAN_ENFORCE |
➖ | true |
Apply the global lists when a listed user posts in a managed group |
GLOBAL_ACTION_DELAY_MS |
➖ | 350 |
Delay between requests during global operations (ms) |
CLOCK_SYNC |
➖ | true |
Clock synchronization (a slow clock kills the session) |
CLOCK_SYNC_INTERVAL |
➖ | 1800 |
Re-measurement interval (seconds) |
CLOCK_SYNC_TIMEOUT |
➖ | 5 |
Time-server timeout — added directly to startup latency |
If the host clock is known to be correct,
CLOCK_SYNC=falsecuts roughly 8 seconds off startup time.
Prefixes: . or /
There are exactly two levels:
- Owner — the account the bot runs on. Nothing to register; Pyrogram identifies it with
the
filters.mefilter. Configuration commands (.adduser,.addseen,.ai) are owner-only. - Authorized users — ids added with
.adduser, stored in theauthorized_userstable. Every other command (AUTH_FILTER = filters.me | authorized users) works for them too.
Any message from anyone else is ignored completely — the bot does not even reply with an error to someone outside these two groups.
⚠️ "Authorized user" is a high trust level, not a limited role. Such a user can run.send(reading messages from any of your private chats) and.gban(banning across every group you manage). Only grant it to someone you would trust with the account itself — or change the decorators on the sensitive commands inplugins/tofilters.me.
| Command | Purpose |
|---|---|
.adduser / .deluser |
Add or remove an authorized user |
.addseen / .delseen |
Add or remove an auto-seen chat |
.ai model | models | persona | reset | status |
AI configuration |
| Command | Purpose |
|---|---|
.help / .cmds |
Full command list |
.ping |
Liveness check |
.id |
Show chat and user ids |
.weather <city> |
Weather |
.gold |
Gold and coin prices |
.price / .market |
Live USDT/BTC/ETH prices in Toman (60 s cache) |
.azan <city> |
Prayer times |
.del <n> |
Delete the last n messages without triggering the anti-delete alert (max 200, sent in batches of 100) |
.send <count> <user_id> |
Relay the last count messages of a private chat into the current chat (max 50) |
.ttl / .ttlguard |
TTL media guard status and per-run counters |
The target is chosen by replying to the user's message, or by passing a numeric id or
username. Durations are written without a space: 30m 2h 1d 1w.
| Command | Purpose |
|---|---|
.kick [reason] |
Remove from this group without banning — the user can rejoin |
.ban [duration] [reason] |
Ban in this group; e.g. .ban 2h spam |
.unban |
Lift a ban (supergroups only) |
.mute [duration] [reason] |
Silence a member (supergroups only); e.g. .mute 30m |
.unmute |
Lift user-level restrictions (supergroups only) |
Telegram reinterprets any duration under 30 seconds or over 366 days as "forever", so both are rejected instead of silently doing something else. For a permanent ban, pass no duration.
Applied to every group where this account has the "ban users" right, and recorded in the database as well.
| Command | Purpose |
|---|---|
.gban [reason] / .ungban (.gunban) |
Global ban and its removal |
.gmute [reason] / .ungmute (.gunmute) |
Global mute and its removal (supergroups only) |
.gbanlist / .gmutelist |
Show the contents of the lists |
.gstatus / .gadmin |
Managed-group count, list sizes and enforcement stats |
.fakegban / .fgban |
Prank — imitates .gban and does nothing at all |
⚠️ These commands fire dozens of consecutive requests at Telegram. The delay between them (GLOBAL_ACTION_DELAY_MS) is deliberate; lowering it raises the risk of the account being limited.
⚠️ Like the other utilities, these are gated byAUTH_FILTER, meaning authorized users can run them too. If you do not want that reach, change the decorators inplugins/group_admin.pytofilters.me.
| Command | Purpose |
|---|---|
.ask <question> |
Question with a streamed answer (attachments and replies supported) |
.r / .re / .about |
Ask about the replied-to message and its media |
.rp / .suggest |
Draft three replies to the replied-to message |
.sum [n] / .tldr |
Summarize the replied-to message, or the last n messages (max 300) |
.tr [lang] <text> / .translate |
Translate text, a reply, or text inside an image |
.ocr / .readimg |
Extract text from an image or PDF |
.stt / .v2t / .voice2text |
Transcribe voice, audio or video |
.tts [voice] <text> / .say |
Turn text into a voice message |
.img <prompt> / .image / .draw |
Generate or edit an image (requires a paid key) |
.aistop |
Stop an in-flight generation |
AI commands accept a model alias and
extendedas leading words:.ask pro extended ...or.sum lite 100
.
├── main.py bootstrap: patches, clock sync, init_db, Client, idle, shutdown
├── config.py env loading with fail-fast validation
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── .env.example configuration sample
├── ARCHITECTURE.md architecture notes and design decisions
│
├── data/ ⚠️ runtime state — fully gitignored
│ ├── my_account.session Telegram session (= full account access)
│ ├── logger.db SQLite database (real private messages)
│ └── ttl_media/ local copies of TTL media (only when enabled)
│
├── plugins/ handlers — auto-discovered by Pyrogram
│ ├── general.py authorization commands + general utilities
│ ├── logger.py anti-delete cache and deletion reports
│ ├── auto_seen.py automatic read receipts
│ ├── ai_assistant.py .ask / .r / .rp / .sum — streaming assistant
│ ├── ai_tools.py .tr / .ocr / .img
│ ├── voice_tools.py .stt / .tts
│ ├── ttl_guard.py self-destructing media capture (raw handler, group -1)
│ └── group_admin.py member control + global actions (enforcement, group -2)
│
└── utils/ helper modules (no dependency on the handlers)
├── paths.py project paths — single source of truth
├── db_manager.py aiosqlite access + RAM caches
├── helpers.py respond(), respond_long(), @safe_handler, flood_seconds()
├── net.py shared HTTP layer for the information modules
├── ai_engines.py Gemini transport: SSE, files, images, TTS, key rotation
├── ai_context.py Telegram message → model input
├── media.py media inspect/download + PCM→WAV/OGG conversion
├── pyrogram_patch.py runtime patches for the pinned library
├── clock.py clock offset probe (SNTP + HTTP-Date)
├── logging_setup.py log configuration and noise throttling
├── weather_api.py Open-Meteo
├── gold_api.py TGJU
├── azan_api.py AlAdhan
└── currency_api.py Wallex → Ramzinex with automatic failover
The architecture, the reasoning behind each decision and the implementation details are
documented in ARCHITECTURE.md.
- Never commit
data/. The session file grants full access to the account. - Keys belong in
.envonly..envis gitignored; keep.env.exampleas the template. - If a key is ever committed by accident, revoke it — removing it from the code is not enough, since it stays in the git history.
- Model and persona commands are deliberately owner-only: changing the persona is a configuration-injection path.
- Note that
.sendlets authorized users republish messages from any of the owner's private chats into a chat they can see. Restrict that decorator tofilters.meif you do not want that level of access. - The TTL media guard deliberately retains media the sender expected to disappear. The
sender gets no notification (unlike a screenshot in a secret chat) and the media stays
"unviewed" on their side. This means other people's private content accumulates in
data/and in your Saved Messages — setTTL_GUARD=falseto turn it off.
Errors and logs are deliberately English (easier to paste into issues and terminals without right-to-left reordering), while the messages sent inside Telegram are Persian.
| Message | Meaning and fix |
|---|---|
configuration error: API_ID is not set |
.env is missing or empty. Run cp .env.example .env and fill in the values. |
API_HASH does not look like a 32-character hex string |
The value copied from my.telegram.org is incomplete. |
could not start the Telegram client |
Wrong API_ID/API_HASH, no network, or a corrupted session file. If the session is corrupt, delete data/my_account.session and log in again. |
database initialisation failed |
No write permission on data/, or logger.db is corrupted. |
AI_API_KEYS is not set |
Only the AI commands are disabled; the rest of the bot runs fine. |
found the legacy AI_API_KEY (singular) |
Old .env: the previous service's key is not read. Put the Gemini key in AI_API_KEYS. |
... is not a Google model name |
AI_MODEL is left over from the previous service (e.g. gpt-5-mini); it is ignored and the default is used. |
system clock is N s behind |
The host clock is slow. msg_id is corrected automatically, but the real fix is NTP on the host. |
FloodWait Ns ... |
Telegram is rate-limiting. Under 60 seconds it waits and retries; above that the operation is abandoned. |
.tts returns a WAV file instead of a voice message |
ffmpeg is not installed. The feature still works, only the presentation differs. |
.img returns a quota error |
Google's image models have a zero quota on the free plan. |
| The container keeps restarting | restart: unless-stopped ignores the exit code, so a configuration error (code 2) creates a restart loop. Check docker compose logs; the cause is almost always an incomplete .env. |
| The bot does not respond to a command | The sender is neither the owner nor on the authorized list — the silence is deliberate. Add them with .adduser from your own account. |
Exit codes (for systemd and Docker):
2 configuration · 3 creating data/ · 4 database · 5 connecting to Telegram ·
1 anything unhandled
Issues and pull requests are welcome. If you send a change, please follow the rules below so
review goes quickly — the reasoning behind each one is documented in ARCHITECTURE.md.
The repository has no automated test suite. Verification is done headlessly, and none of it requires connecting to Telegram:
- Handlers are plain async functions — call them directly with a fake
messageexposingchat,command,from_user,outgoing,edit_textandreply_text. @Client.on_messageon the class returns the function unchanged and only attaches.handlersto it; assert that attribute to confirm registration.- Test the
utils/*_api.pymodules against fixtures rather than live requests — the Iranian services they scrape are not reachable from everywhere. - Point
db_manager.DB_PATHat a temporary file. Never test against the realdata/logger.db.
Core rules (in full in ARCHITECTURE.md §10):
- Async only — no blocking calls (
requests,time.sleep, synchronoussqlite3) in handlers. - The information modules in
utils/return display-ready Persian strings and never raise into a handler. - Use
respond(), notmessage.edit_text— another user's message cannot be edited. - Wrap every command handler in
@safe_handler. - Escape user content (prefer HTML +
html.escape()). - Language: everything that goes to Telegram is Persian, everything that goes to
logor into an exception message is English. (The one exception isMediaError, whose text is shown to the user directly.) - Comments in Persian, identifiers in English — explain why, not what.
- New dependency ⇒ pin it in
requirements.txt. - Schema change ⇒ idempotent migration in
init_db().
Released under the MIT License — you are free to use, modify, redistribute and even commercialize it; the only condition is keeping the copyright and license text in your own copies.
The software is provided "as is" and comes with no warranty. As noted above, running a userbot is generally interpreted as a violation of Telegram's Terms of Service; whatever happens to your account is your responsibility, not the author's.