Skip to main content

Sunnygram

An async, MTProto-native Telegram client library for user accounts.
The user-side sibling of Moonlygram.

Status Python versions mypy strict License

Documentation · Quickstart · Bring your session with you


Sunnygram speaks Telegram's native binary protocol instead of the HTTP Bot API, so it signs in as a person, or as a bot over MTProto, and reaches the whole client API surface. The stack is written here from scratch: crypto, transport, session, update state machine. It is not a wrapper over another library.

Where Moonlygram covers the Bot API, Sunnygram covers everything a real account can do.

Install

pip install sunnygram

No required dependencies. sunnygram[speedups] adds a native AES backend and uvloop, both picked up automatically if present. See performance.

You need an api_id and an api_hash from my.telegram.org.

Example

from sunnygram import Client, filters

app = Client("my.session", api_id=API_ID, api_hash=API_HASH)


@app.on_message(filters.private & filters.text & filters.incoming)
async def echo(client, message):
    await message.reply(f"you said: **{message.text}**")


app.run()

The first run asks for a phone number and a code. Every run after that finds the key in the session file and asks nothing.

async with Client("my.session", api_id=API_ID, api_hash=API_HASH) as app:
    me = await app.get_me()
    await app.send_message("me", f"hello from {me.full_name}")

    async for message in app.get_history("@durov", limit=20):
        if message.has_media:
            await message.download(into=f"{message.id}.bin")

    # Anything unwrapped is one invoke away, typed as what the call answers with.
    from sunnygram.raw import functions
    config = await app.invoke(functions.help.GetConfig())   # -> types.Config

Bring an existing session

imported = sunnygram.read_session("my_account.session")
await sunnygram.adopt_session(imported, "sunny.session")

Session files and session strings both read. The peer cache and any stored file_id come across with the key, and the source file is opened read-only. Bringing a project over.

What it does

Around 260 client methods:

Send text, photos, video, music, voice notes, files, albums, polls, quizzes, stickers, dice, locations, venues, contacts
Read history, search, dialogs, members, contacts
Act edit, delete, forward, pin, react, vote, mark read, typing
Administer promote, restrict, ban, titles, photos, permissions, slow mode, invite links, join requests, admin log
Talk to bots press a button by its label, inline queries, start with a parameter
Pay invoices in money or Stars, subscriptions, revenue, withdrawals, affiliate programs
Gifts the catalogue, sending, upgrading, transferring, resale, collections, auctions, crafting
Be a bot keyboards, callback queries, inline mode both ways, the command menu
Account sessions listed and terminated, the second factor, privacy, usernames
Forums topics listed, searched, opened, renamed, closed, pinned
Payments invoices, Telegram Stars, balance, ledger, refunds
Stories posted, edited, pinned, taken down, read
Statistics channel, supergroup, post and story figures, and the graphs behind them
Boosts a chat's level, who is boosting it, and lending it one of your slots
Shared folders a folder given a link, joined, kept up to date, left
Sticker sets made, added to, reordered, retitled, deleted
Later any send queued by datetime or WHEN_ONLINE

Twenty-one kinds of event, from messages and edits to inline queries, join requests, reactions and presence. Which of them a user account sees and which a bot does.

The stack under it, layer by layer:

sunnygram.tl the TL binary codec, bounds-checked against hostile input
sunnygram.raw 2495 constructors and functions at layer 228, generated from a pinned schema
sunnygram.transport TCP, four framings including abridged and padded
sunnygram.crypto the authorization handshake, AES-IGE and AES-CTR, RSA_PAD, SRP
sunnygram.session message ids, envelopes, sequence numbers, containers, replay checks
sunnygram.network the connection loop, the invoker, per-datacenter keys, the rate limiter
sunnygram.updates pts / qts / seq and gap recovery through getDifference
sunnygram.storage sqlite, in memory, or a session string
sunnygram.peers access hashes learned from every answer, in a bounded LRU
sunnygram.files multi-part transfers, cross-datacenter, CDN, stale reference refresh
sunnygram.auth phone and code, 2FA over SRP, bot token, QR
sunnygram.errors all 780 documented errors, generated, hung off their status codes

Not here: calls above the raw layer, and TLS-disguised MTProxy. Account registration is deliberate and permanent: Sunnygram signs in to accounts that already exist.

Design

  • Generated where it is mechanical. TL constructors and the error tree come from a pinned schema, drift-guarded in CI. Friendly types and client methods are written by hand.
  • Safe by default. Calls are paced, FLOOD_WAIT is honored automatically, secrets never reach a log or a repr, malformed server data fails closed.
  • Correct updates. One state machine owns the counters. Every update is delivered once and in order, or not at all.
  • Never silent. Everything the library survives on your behalf goes to the sunnygram logger, including a handler of yours that raised.
  • Never blocked. Cipher calls, the handshake's 2048-bit arithmetic and SRP's PBKDF2 all leave the event loop.
  • Conversations natively. answer = await app.ask(chat, "What is your name?"), with no state machine in between.
  • Plugins. app.load_plugins("plugins") registers every decorated handler in a package.

Rights are said the readable way round, which Telegram's own flags are not. Text is markdown unless you say otherwise, HTML if you ask, and offsets are counted in UTF-16 the way Telegram counts them.

Status

Stable. The protocol stack, the client surface and the documentation are in place. ARCHITECTURE.md has the layer stack and the numbered rules the code is held to.

Over 2200 offline tests, mypy --strict across the package, and a scripted MTProto server that completes a real handshake, plus a live tour against a real account covering sign-in, the read surface and a file round trip.

Runnable examples in examples/, each against a real account: login.py signs in and keeps the session, tour.py walks the client surface, echo.py answers anybody who writes to you, files.py round-trips a file, adopt.py takes over a session from elsewhere, ask.py holds a conversation, buttons.py is a bot with a menu, events.py answers inline queries and greets joiners, and moderate.py, topics.py, shop.py, listen.py, plugin_bot.py and get_config.py.

Development

pip install -e ".[dev]"
ruff check src tests codegen examples
mypy
pytest -q

Tests are offline. No account, no credentials, no network.

Two trees are generated and neither is edited by hand:

python codegen/refresh.py --check    # has Telegram shipped a newer layer?
python codegen/gen_tl.py             # rebuild raw/ from the pinned schema
python codegen/refresh.py --errors   # take a fresh error table
python codegen/gen_errors.py         # rebuild the error tree

See CONTRIBUTING.md and SECURITY.md.

License

Mozilla Public License 2.0. See LICENSE. Copyright © 2026 AtarixiaFamine.

File-level copyleft: use Sunnygram in a program of any licence, closed included, and publish changes to Sunnygram's own files under the same one. Your program's code is unaffected.

Release files for sunnygram 1.2.1

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

Source distribution (sdist)

Source distribution for sunnygram 1.2.1
File Size Uploaded
sunnygram-1.2.1.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for sunnygram 1.2.1
File Interpreter ABI Platform
sunnygram-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 2.0 MB

Release files / sunnygram-1.2.1.tar.gz

Download URL sunnygram-1.2.1.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
760eff71f208e93acec8646baaf25eb442c16bd39478129fb28d7b443cbeb0fa
BLAKE2b-256 checksum
How to use checksums
7103395ab02b923db04bff81cf33abf37d656257be29f742f90b7ce21284e433
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 30, 2026.

Transparency log

Release files / sunnygram-1.2.1-py3-none-any.whl

Download URL sunnygram-1.2.1-py3-none-any.whl
Size 839.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
747870c1dbc7417f4d4b75b3fd3c9429a39c18ff3a69cef1bb9aa7ba4a4f001d
BLAKE2b-256 checksum
How to use checksums
d0c6e934753e9b41300ddca2b54e68000f215f06edba01f4852c3c7f7400d9b9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.1 This release

2 release files

1.2.0

2 release files

1.0.0

2 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