Agent-friendly Telegram CLI built on Telethon -- read, write, archive your Telegram from the terminal.
Project description
tgctl
Agent-friendly Telegram CLI built on Telethon. Read, write, archive, and listen to your own Telegram account from the terminal with JSON output, idempotency, audit logging, multi-account stores, and explicit safety gates for autonomous-agent use.
tgctl is for developers and local agents that need a predictable Telegram
surface without running a bot account or handing control to a GUI client. It
uses your normal Telegram account through Telethon and stores a local SQLite
cache for fast offline queries.
Names: the PyPI distribution is
tgctl(in thekubectl/flyctlfamily — Telegram control). The binary onPATHistg. The Python import isfrom tgcli import Client. The GitHub repo istg-cli.
Quick start
pip install tgctl
echo "TG_API_ID=12345678" > .env
echo "TG_API_HASH=abcdef0123456789abcdef0123456789" >> .env
tg login
tg stats
Get TG_API_ID and TG_API_HASH from https://my.telegram.org/apps.
CLI examples
# Sync chat metadata (no messages, fast)
tg discover
# Backfill messages from your top 10 chats
tg backfill --max-chats 10 --per-chat 200
# Search cached messages
tg search "shipping" --json
# Show recent messages from a specific chat
tg show @username --limit 20
# Send a message (--allow-write required for any Telegram write)
tg send @username "hello from tg-cli" --allow-write
# Pipe stdin as the message body
echo "multi-line\nbody" | tg send @username - --allow-write
# Reply to a specific message
tg send @username "thanks!" --reply-to 12345 --allow-write
# Listen for incoming messages — streams JSON envelopes to stdout
tg listen
# Forum topics
tg topic-create -1001234567890 "Q1 planning" --allow-write
tg send -1001234567890 "kickoff" --topic 42 --allow-write
# Destructive ops require typed --confirm matching the resolved chat id
tg delete-msg @username 99 100 --allow-write --confirm 12345
# Health check
tg doctor --json
Every command supports --json for clean agent integration. --read-only (or TG_READONLY=1) globally rejects writes for a hardened script.
Features
- 62 commands covering read, write, media, folder, topic, account, admin, and destructive operations
- Multi-account mode with isolated session, database, and audit paths per account
- JSON envelope output for clean agent integration
- Local SQLite cache for fast reads and archival workflows
- Write safety through
--allow-write,--dry-run, idempotency keys, and append-only audit logs - Destructive safety through typed
--confirm <id>checks - Read-only mode for trusted inspection with
--read-onlyorTG_READONLY=1
Commands at a glance
| Command | Purpose |
|---|---|
account-sessions |
List authenticated Telegram sessions |
accounts-add |
Create a local account store |
accounts-list |
List local account stores |
accounts-remove |
Delete a local account store |
accounts-show |
Show current account paths |
accounts-use |
Switch the default local account |
backfill |
Pull historical messages into SQLite |
block-user |
Block a user or bot |
chats-info |
Show cached chat metadata |
contacts |
List synced contacts |
delete-msg |
Delete one or more messages |
discover |
Scan dialogs without fetching messages |
doctor |
Diagnose env, session, DB, schema, and optional live API |
edit-msg |
Edit one of your own text messages |
folder-add-chat |
Add a chat to a Telegram folder |
folder-create |
Create a Telegram folder |
folder-delete |
Delete a Telegram folder |
folder-edit |
Edit a Telegram folder |
folder-remove-chat |
Remove a chat from a Telegram folder |
folder-show |
Show one Telegram folder |
folders-list |
List Telegram folders |
folders-reorder |
Reorder Telegram folders |
forward |
Forward one cached message |
get-msg |
Get one cached message by id |
leave-chat |
Leave a group, supergroup, or channel |
list-msgs |
List cached messages from one chat |
listen |
Capture new incoming messages forever |
login |
Run one-time interactive auth |
mark-read |
Mark all messages in a chat as read |
me |
Print authenticated user info |
pin-msg |
Pin a message |
react |
Add a reaction to a message |
search |
Search cached messages in one chat |
send |
Send a text message |
show |
Print messages from one chat |
stats |
Show a database summary |
sync-contacts |
Pull phone-book contacts from Telegram |
terminate-session |
Terminate a Telegram session |
topic-create |
Create a forum topic |
topic-edit |
Edit a forum topic |
topic-pin |
Pin a forum topic |
topic-unpin |
Unpin a forum topic |
topics-list |
List forum topics in a supergroup |
unblock-user |
Unblock a user or bot |
unpin-msg |
Unpin a message |
unread |
List chats with unread messages |
Why tg-cli?
Telegram tooling usually optimizes for one of four shapes: interactive TUIs,
file-transfer utilities, bot APIs, or MCP servers. tg-cli is narrower and
more scriptable: a local user-account CLI with stable exit codes, JSON output,
SQLite-backed reads, an audit trail, and explicit write gates. It is designed
for shell scripts and coding agents that need to inspect or operate on the
user's own Telegram account while keeping every write intentional.
Configuration
Paths and credentials can be provided through environment variables or a local
.env file.
| Variable | Purpose |
|---|---|
TG_API_ID |
Telegram API id from my.telegram.org |
TG_API_HASH |
Telegram API hash from my.telegram.org |
TG_SESSION_PATH |
Telethon session file path |
TG_DB_PATH |
SQLite cache path |
TG_AUDIT_PATH |
Append-only audit log path |
TG_MEDIA_DIR |
Media output directory |
TG_ACCOUNT |
Active local account name |
TG_READONLY |
Reject Telegram-side and local DB writes when set to 1 |
TG_ALLOW_WRITE |
Allow writes without passing --allow-write each time |
Multi-account
tg accounts-add work
tg --account work login
tg accounts-use work
tg accounts-show
Each account stores its own session, database, and audit log under
accounts/<NAME>/. The default account remains default.
Safety
Writes are blocked unless you pass --allow-write or set TG_ALLOW_WRITE=1.
Destructive commands also require a typed --confirm <id> value matched against
the resolved chat, user, message, folder, or session id. --dry-run reports
what would happen without calling Telegram. --read-only and TG_READONLY=1
reject both Telegram-side writes and local DB writers.
Every invocation appends audit entries to audit.log. Write commands record
pre-call and post-call entries with the same request_id; idempotency keys can
replay a cached successful envelope instead of calling Telegram again.
Architecture
tgcli.__main__ builds the argparse surface and dispatches into command modules
under tgcli/commands/. Commands share the same output envelope, resolver,
safety, idempotency, and audit helpers. Telethon handles Telegram API access;
SQLite stores chats, messages, contacts, folders, topics, and idempotency state.
tg command
-> argparse
-> command runner
-> safety gates + resolver + audit
-> Telethon and/or SQLite
-> JSON envelope or human output
Python SDK
For in-process use from another Python app — skip the subprocess overhead:
from tgcli import Client
c = Client() # default account
# Read paths
me = c.me()
summary = c.stats(min_msgs=10)
history = c.messages.show(chat_id=12345, limit=50)
# Write paths reuse the CLI's safety gates
result = c.messages.send(
chat=12345,
text="hello",
allow_write=True, # required, mirrors --allow-write
idempotency_key="abc123", # optional replay protection
)
# Dry-run any write to preview without calling Telegram
preview = c.messages.send(
chat=12345, text="hi", allow_write=True, dry_run=True
)
assert preview["dry_run"] is True
The SDK reuses the CLI's safety gates — calling a write method without
allow_write=True raises tgcli.safety.WriteDisallowed. Destructive
admin methods accept confirm=<resolved-id> matching the CLI
--confirm flag.
Multi-account: v0.4.0 SDK is single-account-per-process. Set
TG_ACCOUNT=<name> BEFORE importing tgcli, then construct
Client(account="<name>"). Mismatched constructions raise
RuntimeError. For concurrent multi-account work, run one process
per account.
The SDK exposes a curated subset of runners (me, stats,
messages.send, messages.show, admin.chat_title) for v1.0. All
62 commands remain available through the CLI surface; open an issue
if you need a specific runner exposed in the SDK.
Contributing
See AGENTS.md for the project's working conventions and the git commit history for the design progression.
License
MIT - see LICENSE.
Credits
Built on Telethon, the pure-Python MTProto client by Lonami, distributed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tgctl-1.0.1.tar.gz.
File metadata
- Download URL: tgctl-1.0.1.tar.gz
- Upload date:
- Size: 57.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cf4628e5ee1a64896e6492d20c55a3342fe8836234a32a79203882ff63873eb
|
|
| MD5 |
71f5df6444dca3e1c0f1b9686a26de6c
|
|
| BLAKE2b-256 |
7c8b71d13a9f074ab5b824d5f87e87fe4ae084fd8e1861260af01daf28516162
|
Provenance
The following attestation bundles were made for tgctl-1.0.1.tar.gz:
Publisher:
release.yml on b1rd33/tg-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tgctl-1.0.1.tar.gz -
Subject digest:
0cf4628e5ee1a64896e6492d20c55a3342fe8836234a32a79203882ff63873eb - Sigstore transparency entry: 1474979832
- Sigstore integration time:
-
Permalink:
b1rd33/tg-cli@2143053477b25fe37256a0e55c660236e91580ea -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/b1rd33
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2143053477b25fe37256a0e55c660236e91580ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file tgctl-1.0.1-py3-none-any.whl.
File metadata
- Download URL: tgctl-1.0.1-py3-none-any.whl
- Upload date:
- Size: 66.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11f93d56a8b70fdd1a1f50954806dd9ab599d3e2b1497d6890d37f8024abe867
|
|
| MD5 |
c0e4ef8b34d5238efcc38494dd4c092d
|
|
| BLAKE2b-256 |
7f54feed4ffc821f0780c481f7d78dd045822f83e64d218585348bfc2d1605f1
|
Provenance
The following attestation bundles were made for tgctl-1.0.1-py3-none-any.whl:
Publisher:
release.yml on b1rd33/tg-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tgctl-1.0.1-py3-none-any.whl -
Subject digest:
11f93d56a8b70fdd1a1f50954806dd9ab599d3e2b1497d6890d37f8024abe867 - Sigstore transparency entry: 1474979884
- Sigstore integration time:
-
Permalink:
b1rd33/tg-cli@2143053477b25fe37256a0e55c660236e91580ea -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/b1rd33
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2143053477b25fe37256a0e55c660236e91580ea -
Trigger Event:
push
-
Statement type: