Skip to main content

An experimental full MTProto Python client library

Project description

๐Ÿ“ก TGLib

PyPI Version Python Versions License Stars Status

An experimental, full-featured MTProto Python client library for Telegram.

Built from scratch ยท Async-first ยท Lightweight ยท Telethon-style API


โš ๏ธ TGLib is in early development. APIs may change without notice. Use in production at your own risk. Contributions and feedback are very welcome!


โœจ Features

Feature Description
๐Ÿ” Full MTProto Low-level Telegram protocol implementation
โšก Async-first Built entirely on asyncio
๐Ÿ”„ Sync & Async Use whichever style you prefer
๐Ÿ—„๏ธ Session Persistence SQLite, memory, and string sessions via aiosqlite
๐Ÿ”’ Fast Encryption Auto-selects the fastest available AES backend
๐Ÿค– Bot & User Client Supports both bot tokens and phone login
๐Ÿ“ก Rich Event System NewMessage, Edited, Deleted, CallbackQuery, ChatAction, Raw
๐Ÿชถ Lightweight Minimal dependencies, maximum control
๐Ÿ Python 3.10+ Modern Python features throughout

๐Ÿ“ฆ Installation

# Recommended (with fastest crypto accelerator)
pip install tglib[cipheron]

# Alternative accelerator
pip install tglib[cryptogram]

# Basic install (pure Python fallback)
pip install tglib

# All accelerators โ€” TGLib picks the fastest automatically
pip install tglib[all]

From source:

git clone https://github.com/ankit-chaubey/TGLib.git
cd TGLib
pip install -e .[cipheron]

๐Ÿ”’ Crypto Backend Priority

TGLib automatically selects the fastest available AES/factorization backend:

Priority Package Description
1st โœ… cipheron ARM-CE / AES-NI via OpenSSL EVP โ€” recommended
2nd cryptogram AES-NI with pure-Python fallback
3rd cryptg Legacy C extension (compatibility)
4th pycryptodome CTR/CBC only; IGE via Python
5th pyaes Pure-Python fallback (always available)

๐Ÿš€ Quick Start

Get your api_id and api_hash from my.telegram.org. Never hardcode credentials โ€” use environment variables!

export API_ID=your_api_id
export API_HASH=your_api_hash

๐Ÿ“– Usage Styles

1. Async (recommended)

import asyncio, os
from tglib import TelegramClient

client = TelegramClient(
    session="my_session",
    api_id=int(os.environ["API_ID"]),
    api_hash=os.environ["API_HASH"],
)

async def main():
    await client.start(phone="+1234567890")
    me = await client.get_me()
    print(f"Logged in as: {me.first_name}")
    await client.disconnect()

asyncio.run(main())

2. Async Context Manager (cleanest)

import asyncio, os
from tglib import TelegramClient

async def main():
    async with TelegramClient(
        session="my_session",
        api_id=int(os.environ["API_ID"]),
        api_hash=os.environ["API_HASH"],
    ) as client:
        me = await client.get_me()
        print(f"Logged in as: {me.first_name}")

asyncio.run(main())

3. Sync (beginner-friendly, no asyncio needed)

import os
from tglib import TelegramClient

with TelegramClient(
    session="my_session",
    api_id=int(os.environ["API_ID"]),
    api_hash=os.environ["API_HASH"],
) as client:
    me = client.run(client.get_me())
    print(f"Logged in as: {me.first_name}")

๐Ÿค– Bot Examples

Respond to a command:

import asyncio, os
from tglib import TelegramClient, events

bot = TelegramClient("bot", int(os.environ["API_ID"]), os.environ["API_HASH"])

@bot.on(events.NewMessage(pattern="/start"))
async def start(event):
    await event.reply("Bot is running! ๐Ÿ‘‹")

asyncio.run(bot.start(bot_token=os.environ["BOT_TOKEN"]))
asyncio.run(bot.run_until_disconnected())

Userbot โ€” respond to a pattern:

from tglib import TelegramClient, events

client = TelegramClient("session", API_ID, API_HASH)

@client.on(events.NewMessage(pattern="(?i)hello"))
async def greet(event):
    await event.reply("Hello there! ๐Ÿ‘‹")

async def main():
    await client.start(phone="+1234567890")
    await client.run_until_disconnected()

asyncio.run(main())

๐Ÿ’ฌ Sessions

# SQLite (default)
client = TelegramClient("my_session", api_id, api_hash)

# In-memory (no disk writes)
from tglib.sessions import MemorySession
client = TelegramClient(MemorySession(), api_id, api_hash)

# String session (env-var / database friendly)
from tglib.sessions import StringSession

# Generate once:
client = TelegramClient(StringSession(), api_id, api_hash)
await client.start(phone="+1234567890")
print(client.session.save())  # โ†’ "1AQABAAHd..."  โ† store this!

# Reuse:
client = TelegramClient(StringSession("1AQABAAHd..."), api_id, api_hash)

๐Ÿ“ก Events

from tglib import events

# New messages with regex pattern
@client.on(events.NewMessage(pattern=r"(?i)^/help"))
async def handler(event):
    await event.reply("Help text here")

# Outgoing messages only
@client.on(events.NewMessage(outgoing=True, pattern="!ping"))
async def pong(event):
    await event.reply("Pong!")

# Edited messages
@client.on(events.MessageEdited)
async def on_edit(event):
    print("Edited:", event.text)

# Deleted messages
@client.on(events.MessageDeleted)
async def on_del(event):
    print("Deleted IDs:", event.deleted_ids)

# Inline button presses (bots)
@client.on(events.CallbackQuery(data=b"btn_ok"))
async def on_btn(event):
    await event.answer("OK!", alert=False)
    await event.edit("Button pressed โœ…")

# Chat actions (joins, leaves, title changes)
@client.on(events.ChatAction)
async def on_action(event):
    if event.user_joined:
        await event.respond("Welcome!")

# Raw updates (any type)
@client.on(events.Raw)
async def raw(update):
    print(type(update).__name__, update)

# Filtered raw updates
from tglib.tl import types
@client.on(events.Raw(types.UpdateUserStatus))
async def status_change(update):
    print("Status:", update.status)

Dynamic handler management:

client.add_event_handler(my_handler, events.NewMessage(pattern="test"))
client.remove_event_handler(my_handler)

๐Ÿ“จ Messaging

# Send text (Markdown or HTML)
await client.send_message("me", "**Bold** and _italic_", parse_mode="md")
await client.send_message(chat_id, "<b>HTML</b> <i>text</i>", parse_mode="html")

# Reply, forward, pin
await client.send_message(chat, "Hello", reply_to=msg_id)
await client.forward_messages(target_chat, [msg_id], from_chat)
await client.pin_message(chat, msg_id)
await client.unpin_message(chat, msg_id)
await client.unpin_all_messages(chat)

# Edit and delete
await client.edit_message(chat, msg_id, "Updated text")
await client.delete_messages(chat, [msg_id1, msg_id2], revoke=True)

# Fetch messages
msgs = await client.get_messages(chat, limit=50)
msg  = await client.get_messages(chat, ids=[123, 456])

# Iterate messages
async for msg in client.iter_messages(chat, limit=500):
    print(msg.id, msg.message)

async for msg in client.iter_messages(chat, reverse=True, limit=200):
    ...

async for msg in client.iter_messages(chat, search="hello"):
    ...

messages = await client.iter_messages(chat, limit=100).collect()

๐Ÿ“ Upload & Download

# Send files
await client.send_file(chat, "photo.jpg", caption="My photo")
await client.send_file(chat, "video.mp4", supports_streaming=True)
await client.send_file(chat, "doc.pdf", force_document=True)
await client.send_file(chat, "https://example.com/image.jpg")

# Album (up to 10 at a time)
await client.send_file(chat, ["photo1.jpg", "photo2.jpg", "photo3.jpg"])

# Voice / video note
await client.send_file(chat, "voice.ogg", voice_note=True)
await client.send_file(chat, "round.mp4", video_note=True)

# Download
path = await client.download_media(message)
path = await client.download_media(message, "/downloads/file.jpg")
data = await client.download_media(message, bytes)  # in-memory
path = await client.download_profile_photo("@username")

# Streaming
with open("video.mp4", "wb") as f:
    async for chunk in client.iter_download(message.media):
        f.write(chunk)

๐Ÿ” Entity Resolution

user = await client.get_entity("me")
user = await client.get_entity("@username")
user = await client.get_entity("+1234567890")
user = await client.get_entity(123456789)
chat = await client.get_entity(-1001234567890)

# InputPeer (for raw API calls)
peer = await client.get_input_entity("@channel")

# Numeric ID
uid = await client.get_peer_id("@username")

โš™๏ธ Raw API

from tglib.tl import functions, types

# Get full chat info
full = await client(functions.channels.GetFullChannelRequest(
    channel=await client.get_input_entity("@channel")
))

# Update profile
await client(functions.account.UpdateProfileRequest(
    first_name="New Name", bio="tglib user"
))

๐Ÿ”ค Text Formatting

from tglib.extensions import markdown, html

text, entities = markdown.parse("**Bold** and __italic__ and `code`")
md_text = markdown.unparse(text, entities)

text, entities = html.parse("<b>Bold</b> and <a href='https://t.me'>link</a>")
html_text = html.unparse(text, entities)

Markdown syntax: **bold**, __italic__, ~~strikethrough~~, `code`, ||spoiler||, [text](url)

HTML tags: <b>, <i>, <u>, <s>, <code>, <pre>, <a href="...">, <blockquote>, <spoiler>


๐Ÿ—ƒ๏ธ Entity Cache

print(client._entity_cache.stats())
# {'total': 42, 'live': 40, 'expired': 2, 'max_size': 10000, 'ttl': 3600}

from tglib.entitycache import EntityType
client._entity_cache.put(12345, EntityType.USER, access_hash=987654321)
client._entity_cache.invalidate(12345)

โ™พ๏ธ Keep Alive

# Async
await client.run_until_disconnected()

# Sync
client.run(client.run_until_disconnected())

๐Ÿ”ง Dependencies

Package Purpose
pyaes AES encryption for MTProto
pycryptodome RSA and additional crypto
aiosqlite Async session storage
pillow (optional) Auto-resize photos before upload
aiofiles (optional) Async file I/O

๐Ÿค Contributing

Contributions are very welcome!

# Fork the repo, then:
git clone https://github.com/YOUR_USERNAME/TGLib.git
cd TGLib
pip install -e .
  1. Create a branch: git checkout -b feature/your-feature
  2. Make your changes
  3. Push and open a Pull Request

๐Ÿ› Issues & Feedback

Found a bug or have a suggestion? ๐Ÿ‘‰ Open an issue


๐Ÿ™ Acknowledgements

TGLib builds upon the shoulders of:

  • Telethon by Lonami (MIT License) Core entity resolution logic, upload/download chunking, event system design, HTML/Markdown parsers, string session format, and more. Without Lonami's incredible work, TGLib would not exist.

All Telethon-derived code retains its original MIT License. See LICENSE for full details.


๐Ÿ“„ License

This project is licensed under the MIT License see the LICENSE file for details.


๐Ÿ‘ค Author

Ankit Chaubey


Made with โค๏ธ and Python

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tglib-0.1.4.tar.gz (472.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tglib-0.1.4-py3-none-any.whl (525.8 kB view details)

Uploaded Python 3

File details

Details for the file tglib-0.1.4.tar.gz.

File metadata

  • Download URL: tglib-0.1.4.tar.gz
  • Upload date:
  • Size: 472.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for tglib-0.1.4.tar.gz
Algorithm Hash digest
SHA256 d093db4a6c8ec9b6f4d607a397e496b92e0759ff9eb27c33993d319d5600ab43
MD5 428da04283a327d9eec76f9f25365410
BLAKE2b-256 a92aa023bb42a6a16454b9ae8d5ae48f0bc00e5159554520c99b8f30a382b0e6

See more details on using hashes here.

File details

Details for the file tglib-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: tglib-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 525.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for tglib-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 510365ffa9fb42afc6e069bb63f03e3f352efdb50214d96d1915c3d3af103236
MD5 f3779e7b6e47b0a7aa173376a2225229
BLAKE2b-256 8ee6969015855e36ce9acf60ba332ac17d8e0a764c404a5628d8e81f5b62b836

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page