An experimental full MTProto Python client library
Project description
๐ก TGLib
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 .
- Create a branch:
git checkout -b feature/your-feature - Make your changes
- 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
- ๐ง Email
- ๐ github.com/ankit-chaubey
- ๐ Telegram
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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d093db4a6c8ec9b6f4d607a397e496b92e0759ff9eb27c33993d319d5600ab43
|
|
| MD5 |
428da04283a327d9eec76f9f25365410
|
|
| BLAKE2b-256 |
a92aa023bb42a6a16454b9ae8d5ae48f0bc00e5159554520c99b8f30a382b0e6
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
510365ffa9fb42afc6e069bb63f03e3f352efdb50214d96d1915c3d3af103236
|
|
| MD5 |
f3779e7b6e47b0a7aa173376a2225229
|
|
| BLAKE2b-256 |
8ee6969015855e36ce9acf60ba332ac17d8e0a764c404a5628d8e81f5b62b836
|