GoyGram
What is this?
Ultimate split-brain Telegram framework (Python + Rust core) built for production-grade speed, control, and maximum OpSec.
Under the hood: a Python orchestration layer drives two completely independent network transports (Bot API over aiohttp + MTProto over raw TCP with full DH key exchange), both feeding into a single async event bus. Every crypto operation — AES-256-IGE for MTProto packets, AES-256-GCM for session vaults — runs in a Rust .so compiled with LTO and opt-level=3. Hand-written TL codec, no code generation at runtime. QR code login rendering in the terminal via qrcode + Rich. SRP password proofs for 2FA. And the vault: your auth key locked to your machine-id through PBKDF2-SHA256 at 600,000 iterations.
Key Features
- Split-brain architecture: ergonomic Python layer + blazing-fast Rust extension.
- Session Eater: aggressive in-memory cleanup (zeroize strategy for legacy
.sessionfiles after migration). - Vault AES-256-GCM: encrypted local session bootstrap. Key derived from machine-id + session name via PBKDF2 (or bypass with
GOYGRAM_VAULT_KEY). - TUI auth flow: terminal-first authorization workflow — phone login with SMS code, QR code scanning in ASCII art, 2FA/SRP password challenges. All Rich-styled when a TTY is present.
- Proxy support: SOCKS5 (with user/pass auth) and HTTP CONNECT tunneling for MTProto connections. Also respects
ALL_PROXY/HTTPS_PROXY/HTTP_PROXYenv vars. - Dual transport: Bot API (HTTP long-polling via aiohttp, multipart uploads, auto-webhook-clear on 409) + MTProto (raw TCP with AES-256-IGE, dynamic salt recovery on
bad_server_salt, auto-DC migration onPHONE_MIGRATE_N) — in one app runtime. - DC Routing: MTProto uses a built-in map of the five Telegram DC endpoints and selects the preferred DC, falling back to
149.154.167.50:443(DC 2). - Dynamic API dispatch: every Bot API method works via
__getattr__—sendAnimation,getUserProfilePhotos,setMyCommands, whatever. Snake_case auto-converts to CamelCase.mt_prefix routes to MTProto. - Keyboard system: inline keyboards, reply keyboards, force reply, reply removal. All with
to_dict()serialization that adapts per transport. - Forum topic management: full create/edit/close/reopen/delete lifecycle for forum topics and the General topic. Both transports supported.
- Zero-copy event objects:
MsgObj,CbObj,PollObj,MemberObjwith__slots__— no per-message dict overhead. - Composable filters: boolean AND/OR/NOT on
Filter(filters.text & ~filters.me). - Multi-session: named vaults (
session_name="worker_1") for farming multiple accounts from the same process. Separate auth keys, separate TCP connections, separateself_id. - Durable delivery state: Bot API offsets and MTProto
pts/qts/date/seqcursors are persisted atomically with restrictive permissions. - Direct media primitives: chunked MTProto
upload_file()/download_file()and Bot APIdownload_file()without a heavyweight media framework.
Installation
pip install goygram
Requires Python 3.11+. Pre-built wheels ship for Linux, Windows, macOS, and FreeBSD where the corresponding runner build succeeds. Termux is natively validated in a Termux environment; install the Python package from source there because Android/Termux wheels are not interchangeable with manylinux wheels. Rust is not required for the standard Linux, Windows, and macOS wheels. Installs aiohttp, rich, and qrcode as dependencies.
FreeBSD and Termux
FreeBSD packages are built by the release workflow inside a FreeBSD 15 VM and attached to the GitHub Release because PyPI rejects FreeBSD's nonstandard wheel platform tag. The Rust core is built in the official termux/termux-docker environment and attached as a native validation asset; Termux users should build locally from the source distribution. On a real Termux device, install the Termux toolchain and build from the source distribution:
pkg update
pkg install python rust clang
python -m pip install --no-build-isolation .
Quick Start
1) Bot API (token)
import asyncio
from goygram import GoyGram, filters
app = GoyGram(bot_token="123456:ABC_TOKEN")
@app.on_msg(filt=filters.text)
async def echo(msg):
await msg.reply("Hello from Bot API")
asyncio.run(app.run())
2) MTProto (no bot token, requires API ID + API Hash)
import asyncio
from goygram import GoyGram
app = GoyGram(api_id=123456, api_hash="0123456789abcdef0123456789abcdef") # auto-fetches Telegram DC endpoint at startup
@app.on_cmd("ping")
async def ping(msg):
await msg.reply("pong from MTProto (api_id/api_hash)")
asyncio.run(app.run())
3) Named MTProto sessions (multi-session in one folder)
import asyncio
from goygram import GoyGram
app = GoyGram(
api_id=123456,
api_hash="0123456789abcdef0123456789abcdef",
session_name="farm_worker_1",
)
asyncio.run(app.run())
- By default, session data is stored in
default.vault. - With
session_name="farm_worker_1", session data is stored infarm_worker_1.vault. - If
farm_worker_1.sessionexists, it is migrated tofarm_worker_1.vaultduring bootstrap (securely zeroized after).
Dynamic API & Methods
GoyGram can route Bot API method names dynamically, including methods that are not hardcoded as convenience methods:
- Call Bot API methods directly even if they are not explicitly hardcoded:
await app.sendDocument(chat_id=..., document=...)await app.getChat(chat_id=...)await app.getUpdates(timeout=30)
- Snake-case also works and is converted to Bot API method names:
await app.send_document(chat_id=..., document=...)->sendDocument
- MTProto actions (authorized with API ID/API Hash) are available with
mt_prefix:await app.mt_get_dialogs(limit=50)await app.mt_get_chat_full(chat_id=...)
This behavior is implemented through dynamic method resolution in the client core (__getattr__) and transport-aware request routing.
For Bot API files, await app.download_file(file_id, destination) downloads a Telegram file to memory or atomically to a local path. MTProto exposes the same low-level chunk control through app.core.mt.upload_file(...) and app.core.mt.download_file(...).
Authentication & Security
Interactive Login
On first run with MTProto, GoyGram launches a Rich-powered TUI:
GoyGram Interactive Login
? Choose login method:
> QR Code Login
Phone Number Login
Choose QR code (scan with any Telegram client) or phone number (SMS code). 2FA password is handled automatically via SRP proofs. The resulting session is stored as default.vault — AES-256-GCM encrypted, keyed to your machine.
Vault Encryption
- Algorithm: AES-256-GCM (authenticated encryption via Rust's
aes-gcmcrate) - Key derivation: PBKDF2-HMAC-SHA256, 600,000 iterations, key material =
{machine-id}:{session_name} - Override:
GOYGRAM_VAULT_KEYenv var (base64-encoded 32 bytes) bypasses PBKDF2 entirely The vault does not fall back to silently accepting plaintext after a failed decryption.
Session Migration
Telethon/Pyrogram .session files are auto-detected, read from SQLite, migrated to .vault, and securely zeroized (overwrite + fsync + unlink).
Developer Tools (Help)
Use built-in introspection tools:
app.help() # pretty DX overview in console
print(dir(app)) # inspect available shortcuts + dynamic entries
or:
from goygram.utils import print_methods
print_methods(app)
With type hints on key event objects (MsgObj, CbObj, MemberObj, PollObj) and filter primitives, modern IDE autocomplete works much better out of the box.
Filters
goygram.filters supports composable boolean operators:
from goygram import filters
smart_filter = filters.text & ~filters.me
another = filters.text | filters.me
@app.on_msg(filt=smart_filter)
async def handler(msg):
await msg.reply("Filtered")
Built-in filters: filters.text (message has text), filters.me (message from current account/bot). Compose with &, |, ~. Custom filters: Filter(lambda e: ...).
Transport Routing
Messages can be routed explicitly by transport:
# Force Bot API
await app.send_msg("bot:123456789", "via bot", via="bot")
# Force MTProto
await app.send_msg("mt:123456789", "via mt", via="mt")
Chat ID prefixes (bot: / mt:) are auto-resolved. When replying, the transport source is preserved automatically — reply to a Bot API message, it goes back via Bot API.
FSM Persistence
The default FSM remains in memory:
app = GoyGram(bot_token="123456:ABC_TOKEN")
For an external store, pass an object with load() and save(snapshot) methods:
class RedisFSM:
def __init__(self, redis):
self.redis = redis
def load(self):
return self.redis.json().get("goygram:fsm") or []
def save(self, snapshot):
self.redis.json().set("goygram:fsm", ".", snapshot)
app = GoyGram(bot_token="123456:ABC_TOKEN", fsm_backend=RedisFSM(redis))
For complete control, use fsm_on_change. It receives a JSON-compatible snapshot after every state change and can write it to Redis, PostgreSQL, a file, or another service:
def persist_fsm(snapshot):
external_store.write(snapshot)
app = GoyGram(bot_token="123456:ABC_TOKEN", fsm_on_change=persist_fsm)
The active core object is also available as app.fsm. It exposes snapshot() and restore(snapshot) for explicit checkpoints and migrations. Existing set_state, get_state, get_state_data, and clear_state behavior is unchanged.
Event Pipeline
BotNet.spin() ──→ bus.push("bot", data)
──→ Disp.consume() → your handlers
MTNet.spin() ──→ bus.push("mt", data)
Single asyncio.Queue → typed event objects (MsgObj/CbObj/PollObj/MemberObj) → handler lists in registration order. Per-handler error isolation — one crashing handler never takes down the dispatcher.
Logging
GOYGRAM_LOG=DEBUG python app.py # verbose (raw MTProto packet dumps)
GOYGRAM_LOG=INFO python app.py # default (startup, errors)
GOYGRAM_LOG=WARNING python app.py # quiet
Logger hierarchy: goygram.app, goygram.botapi, goygram.mtproto, goygram.disp, goygram.security, goygram.dc.
Architecture at a Glance
┌─────────────────────────────────────────────┐
│ GoyGram (Public API) │ ← User-facing facade
├─────────────────────────────────────────────┤
│ AppCore (Internal Engine) │ ← Config, hooks, routing
├──────────────────┬──────────────────────────┤
│ BotNet (aiohttp) │ MTNet (TCP/MTProto) │ ← Independent transports
├──────────────────┴──────────────────────────┤
│ Bus → Disp (Event Pipeline) │ ← asyncio.Queue + dispatcher
├─────────────────────────────────────────────┤
│ goygram.ext (Rust .so) — AES-IGE/AES-GCM │ ← Native crypto (LTO, opt=3)
└─────────────────────────────────────────────┘
Wiki
📚 Official documentation and Wiki. There are separate pages for using the client, Bot API, MTProto, events, bytes and TL data. 👉 Open GoyGram Pages · Open GitHub Wiki
License
See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
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 goygram-0.7.56.tar.gz.
File metadata
- Download URL: goygram-0.7.56.tar.gz
- Upload date:
- Size: 82.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
maturin/1.15.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f23ae67651af018434f49faf63d342a42d2f08ce4a1d44f1e3076df7b9cefe1e
|
|
| MD5 |
bfe649a883309a54a84c467066194043
|
|
| BLAKE2b-256 |
6e06be86bdc3f99e7abf50d3bf77b4b2434b0a855627aff7303d87e6ea003b61
|
File details
Details for the file goygram-0.7.56-cp311-abi3-win_amd64.whl.
File metadata
- Download URL: goygram-0.7.56-cp311-abi3-win_amd64.whl
- Upload date:
- Size: 315.5 kB
- Tags: CPython 3.11+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
maturin/1.15.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93221ff0601a9ee10a9d90f6fb53f07e9c0aebd28841efce0cc481782507ff6b
|
|
| MD5 |
a1d611573247fb78b50c81a73c2708f1
|
|
| BLAKE2b-256 |
adb1ca6c339668246a6570d36bc3dcbf8eacce0fd30391ee9827606705615b3f
|
File details
Details for the file goygram-0.7.56-cp311-abi3-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: goygram-0.7.56-cp311-abi3-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 402.5 kB
- Tags: CPython 3.11+, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
maturin/1.15.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dda32a8361ca442e99d9e474247dc0b0492ee3a9d0a244676c454ec5c2e58192
|
|
| MD5 |
1278777465d06f48a6411db1da781774
|
|
| BLAKE2b-256 |
e2a88b440e3a186692844407f873a4c5b2beb7358fa899f7b05c6e0e750b1448
|
File details
Details for the file goygram-0.7.56-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: goygram-0.7.56-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 374.3 kB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
maturin/1.15.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
085d42905a00d4799d07e61aa6cb8fdb8ee4c8cee87616df23fb4bf43a658c6b
|
|
| MD5 |
46c6441203a92eeadd42387f603b3560
|
|
| BLAKE2b-256 |
b82559f5c0c3c90d5627df7840a3316d69a25979c515354d5fca37d3916a5bbd
|