Skip to main content

GoyGram

GoyGram Logo

Python 3.11+ Rust Core License: AGPL v3 PyPI version PyPI downloads Telegram API Security Docs & Wiki

What is this?

Ultimate hybrid 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

  • Hybrid architecture: ergonomic Python layer + blazing-fast Rust extension.
  • Session zeroize: aggressive in-memory cleanup (zeroize strategy for legacy .session files 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_PROXY env 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 on PHONE_MIGRATE_N) — in one app runtime.
  • Bot over MTProto: pass bot_token + api_id/api_hash to authorize a bot through auth.importBotAuthorization and switch between via="api" and via="mtproto" in the same 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.
  • One dynamic event object: Obj with kind-dispatch; MsgObj, CbObj, PollObj, MemberObj, InlineObj are aliases — lazy raw-field access, no model registry, no per-kind classes.
  • 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, separate self_id.
  • Portable sessions: a single Session object doubles as memory, file (.vault), and encrypted string (export_string() / from_string()). Rename-safe vaults let you name the file by self_id after login.
  • Durable delivery state: Bot API offsets and MTProto pts/qts/date/seq cursors are persisted atomically with restrictive permissions.
  • Direct media primitives: chunked MTProto upload_file()/download_file() and Bot API download_file() without a heavyweight media framework.

Benchmarks

Cold import, memory footprint, and MTProto crypto (AES-256-IGE) measured against telethon, pyrogram, aiogram and python-telegram-bot. Full methodology in benchmarks/.

goygram telethon pyrogram aiogram python-telegram-bot
cold import (ms) 74 272 436 2699 141
RSS delta (MB) 13 48 35 152 19
AES-256-IGE (MB/s, 64 KiB) 1094 14 228 — —
loads message (ops/s) 567,799 — — — —
loads updateNewMessage (ops/s) 235,798 — — — —

The crypto runs in Rust with AES-NI intrinsics selected at runtime (built in, no separate C extension; tgcrypto 1.2.5 measures 234 MB/s on the same box), GoyGram starts ~36× faster than aiogram, and uses ~12× less memory. Realistic updateNewMessage loads latency: p50 3.7 µs, p99 9.1 µs.

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 in farm_worker_1.vault.
  • If farm_worker_1.session exists, it is migrated to farm_worker_1.vault during bootstrap (securely zeroized after).

4) Bot over MTProto (auth.importBotAuthorization)

A bot can run over raw MTProto instead of the Bot API HTTP transport. Pass bot_token together with api_id/api_hash and GoyGram authorizes the bot through auth.importBotAuthorization — the MTProto equivalent of the Bot API token handshake (with automatic USER_MIGRATE_N DC migration):

import asyncio
from goygram import GoyGram

app = GoyGram(
    bot_token="123456:ABC_TOKEN",
    api_id=123456,
    api_hash="0123456789abcdef0123456789abcdef",
    default_transport="mtproto",   # prefer MTProto for outgoing calls
)

@app.on_cmd("ping")
async def ping(msg):
    await msg.reply("pong via MTProto")

asyncio.run(app.run())

Both transports stay available in one runtime. Switch per call with via="api" (Bot API) or via="mtproto" (MTProto):

await app.send_msg("123456789", "via Bot API", via="api")
await app.send_msg("123456789", "via MTProto", via="mtproto")

default_transport sets the default when via is omitted: "api", "mtproto", or "auto" (Bot API if a token is present, else MTProto).

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-gcm crate)
  • Key derivation: PBKDF2-HMAC-SHA256, 600,000 iterations, key material = {machine-id}:{session_name}
  • Override: GOYGRAM_VAULT_KEY env 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).

Session: memory, file, and portable string

Every app exposes app.session — a single Session object that is the session in memory, in a file (.vault), and as a portable encrypted string at the same time. No separate MemorySession / StringSession / SQLiteSession classes and no painful conversions.

from goygram import GoyGram, Session

app = GoyGram(api_id=123456, api_hash="0123456789abcdef0123456789abcdef")

# after authorization, read the account id and name the file by it (rename-safe):
await app.session.save(f"{app.session.self_id}.vault")

# or keep it as an encrypted, portable string (not plaintext like Telethon/Pyrogram):
token = app.session.export_string()   # AES-256-GCM encrypted, machine-locked
sess = Session.from_string(token)     # one call to restore
  • Rename-safe vaults: the encryption key no longer depends on the file name, so you can log in first and name/rename the session file afterwards (e.g. by self_id).
  • Encrypted string sessions: export_string() / from_string() carry the session as an authenticated, machine-locked blob — unlike Telethon's and Pyrogram's plaintext StringSession.
  • One object, three forms: session.data, session.save(path), session.load(path), session.export_string(), session.from_string(s). self_id, is_bot, auth_key, server_salt, and dc are exposed as properties.
  • Backward compatible: legacy vaults (and .session migrations) still decrypt; new vaults are written with a GGV2 header that the reader auto-detects.

Pass an existing session explicitly:

app = GoyGram(api_id=..., api_hash=..., session=Session.from_string(token))
app = GoyGram(api_id=..., api_hash=..., session=Session(name="worker_1"))

The constructor still accepts session_name="..." for the plain file-backed case.

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 aliases (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 api", via="api")

# Force MTProto
await app.send_msg("mt:123456789", "via mtproto", via="mtproto")

via="api" is an alias for the Bot API transport and via="mtproto" for MTProto (the short forms via="bot" / via="mt" still work). 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 → dynamic event objects (MsgObj/CbObj/PollObj/MemberObj/InlineObj, aliases of one Obj) → 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.

Release files for goygram 0.7.94

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for goygram 0.7.94
File Size Uploaded
goygram-0.7.94.tar.gz 121.8 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for goygram 0.7.94
File Interpreter ABI Platform
goygram-0.7.94-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
goygram-0.7.94-cp311-abi3-manylinux_2_34_x86_64.whl CPython 3.11 abi3 Linux glibc 2.34+ x86-64 Details
goygram-0.7.94-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details

Total release size: 1.3 MB

Release files / goygram-0.7.94.tar.gz

Download URL goygram-0.7.94.tar.gz
Size 121.8 kB
Tags Source
SHA-256 checksum
How to use checksums
32d0fad04a6a4fa6dda355a2ce5f36f150e305719b0bf6ce7858e2f064b075b8
BLAKE2b-256 checksum
How to use checksums
0815e845d5c5c176d1511b74c1c8c923d82e5077c531b7de5937f20ab5a3e4cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / goygram-0.7.94-cp311-abi3-win_amd64.whl

Download URL goygram-0.7.94-cp311-abi3-win_amd64.whl
Size 341.2 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
66b2fc59e6356c93d3e621d765a04e725336753fe5048d974d5e4865e872b1cd
BLAKE2b-256 checksum
How to use checksums
c4776b31a43bce67a3225a612fd1e2316bd9ea0eed4f7a42f03e0cbd9a52e120
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / goygram-0.7.94-cp311-abi3-manylinux_2_34_x86_64.whl

Download URL goygram-0.7.94-cp311-abi3-manylinux_2_34_x86_64.whl
Size 427.4 kB
Tags CPython 3.11 Linux glibc 2.34+ x86-64 abi3
SHA-256 checksum
How to use checksums
5279e068bb2f311890aaf56bf6bde4a7b985315e6041dd0a74c74a74a2e8bd4c
BLAKE2b-256 checksum
How to use checksums
18abb16f54c23f71324fe344e29f1956e3fce50fadb3b846184852cc33f1c428
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / goygram-0.7.94-cp311-abi3-macosx_11_0_arm64.whl

Download URL goygram-0.7.94-cp311-abi3-macosx_11_0_arm64.whl
Size 397.1 kB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
4d6c45c9e891bbf2044a9de74584c915570028ad737a50d36abc7c4c989f3eb5
BLAKE2b-256 checksum
How to use checksums
d83b5061b8d2bf085d855dc93236c9502c8ae0ff7455f53851ef6eae4312c7e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release history Release notifications | RSS feed

0.8.7

4 release files

0.8.6

4 release files

0.8.5

4 release files

0.8.4

4 release files

0.8.3

4 release files

0.8.2

4 release files

0.8.1

4 release files

0.8.0

4 release files

0.7.99

4 release files

0.7.98

4 release files

0.7.97

4 release files

0.7.96

4 release files

0.7.95

4 release files

This release

0.7.94 This release

4 release files

0.7.93

4 release files

0.7.92

4 release files

0.7.91

4 release files

0.7.90

4 release files

0.7.89

4 release files

0.7.88

4 release files

0.7.87

4 release files

0.7.86

4 release files

0.7.85

4 release files

0.7.84

4 release files

0.7.82

4 release files

0.7.81

4 release files

0.7.80

4 release files

0.7.79

4 release files

0.7.78

4 release files

0.7.77

4 release files

0.7.76

4 release files

0.7.75

4 release files

0.7.63

4 release files

0.7.62

4 release files

0.7.61

4 release files

0.7.60

4 release files

0.7.59

4 release files

0.7.58

4 release files

0.7.57

4 release files

0.7.56

4 release files

0.7.55

4 release files

0.7.54

4 release files

0.7.53

4 release files

0.7.52

4 release files

0.7.51

4 release files

0.7.50

4 release files

0.7.49

4 release files

0.7.48

4 release files

0.7.47

4 release files

0.7.46

4 release files

0.7.45

4 release files

0.7.35

4 release files

0.7.34

4 release files

0.7.33

4 release files

0.7.32

4 release files

0.7.31

4 release files

0.7.30

4 release files

0.7.29

4 release files

0.7.28

4 release files

0.7.27

4 release files

0.7.26

4 release files

0.7.25

4 release files

0.7.24

4 release files

0.7.23

4 release files

0.7.22

4 release files

0.7.21

4 release files

0.7.20

4 release files

0.7.19

4 release files

0.7.18

4 release files

0.7.17

4 release files

0.7.16

4 release files

0.7.15

4 release files

0.7.14

4 release files

0.7.13

4 release files

0.7.9

4 release files

0.7.8

4 release files

0.7.7

4 release files

0.7.6

4 release files

0.7.5

4 release files

0.7.4

4 release files

0.7.3

4 release files

0.7.2

4 release files

0.7.1

4 release files

0.7.0

4 release files

0.6.9

4 release files

0.6.8

4 release files

0.6.7

4 release files

0.6.6

4 release files

0.6.5

4 release files

0.6.4

4 release files

0.6.3

4 release files

0.6.2

4 release files

0.6.1

4 release files

0.6.0

4 release files

0.5.7

4 release files

0.5.6

4 release files

0.5.5

4 release files

0.5.3

4 release files

0.5.1

4 release files

0.5.0

4 release files

0.4.9

4 release files

0.4.8

4 release files

0.4.7

4 release files

0.4.2

4 release files

0.4.1

4 release files

0.4.0

4 release files

0.3.9

4 release files

0.3.8

4 release files

0.3.7

4 release files

0.3.6

4 release files

0.3.5

4 release files

0.3.4

4 release files

0.3.2

4 release files

0.3.0

4 release files

0.1.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page