Telegram MTProto API Framework for Python
Source
•
Issues
Hawkgram
Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots
from pyrogram import Client, filters
app = Client("my_account")
@app.on_message(filters.private)
async def hello(client, message):
await message.reply("Hello from Hawkgram!")
app.run()
Hawkgram is a modern, elegant and asynchronous MTProto API framework. It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot identity (bot API alternative) using Python.
Key Features
- Ready: Install Hawkgram with pip and start building your applications right away.
- Easy: Makes the Telegram API simple and intuitive, while still allowing advanced usages.
- Elegant: Low-level details are abstracted and re-presented in a more convenient way.
- Fast: Boosted up by TgCrypto, a high-performance cryptography library written in C.
- Type-hinted: Types and methods are all type-hinted, enabling excellent editor support.
- Async: Fully asynchronous (also usable synchronously if wanted, for convenience).
- Powerful: Full access to Telegram's API to execute any official client action and more.
Installing
pip3 install hawkgram
Hawkgram keeps the pyrogram import name, so existing code keeps working unchanged.
Changelog
Hawkgram 1.0.9: security review fixes
Message.text.htmland.caption.htmlescape all of the text. Text before, after or outside of any formatting was returned as is, so a user who typed<b>or<a href="tg://user?id=…">got real formatting and mentions when a bot sent the result again with HTML parsing (and HTML injection if it was shown on a web page). A code block's language and a link's URL are now escaped too, so they can't break out of their attribute.- Clients using MongoDB sessions can upload files by path again. Since 1.0.1 the session file check raised
TypeErrorfor everysend_document,send_photo, etc. given a file path whenmongodb=was used. - Failed downloads are no longer saved as complete files. When a download broke off (a lost connection, an error
from Telegram) or a part from Telegram's CDN failed its integrity check,
download_mediasaved what it had as if it were the whole file and returned its path. It now deletes the partial file, logs the error and returnsNone, as documented.stream_mediaraises the error instead of silently ending early. - Failed uploads raise their error. When a part of a file couldn't be uploaded (a lost connection, an error
from Telegram such as a flood wait), the error was only logged: the upload went on and
save_filereturned a file with a missing part, orNone, sosend_documentand the other send methods failed later with an unrelated error. The upload now stops and the error is raised. - Requests retried after a
BadMsgNotificationreturn their real result. The notification itself was returned as the answer, so the method failed with an unrelated error (orIndexError) even when the retry worked. If the server keeps rejecting the request,BadMsgNotificationis raised. - Clients work on machines whose clock is off. Telegram rejects messages whose time is more than 30 seconds ahead of or 300 seconds behind its own, and its answers then looked too old or too new to be accepted, so requests timed out and the connection kept restarting. Telegram's time is now taken from its rejection (only when it answers a pending request) and used from then on; a warning says how far off the clock is.
delete_messagesonly deletes messages of the chat you give it. Private chats and basic groups share one sequence of message ids, and Telegram's delete request for them names no chat, sodelete_messages(chat_id, 6)deleted message 6 wherever it was. A bot that checked a user's rights in one group could be made to delete a message in another. Ids of messages in other chats are now ignored (this costs one extra request).send_payment_formno longer fails after the stars are spent. Reading the result raisedAttributeErrororKeyErrorfor some answers, or returnedNone, although the payment had gone through, so a caller retrying the "failed" call paid again. It now always returns the list of bought photos and videos, which is empty when the invoice isn't for paid media, and it reads all of them instead of only the first message's.transfer_giftandupgrade_giftno longer spend Stars unless you allow it (behaviour change). When Telegram asked for payment, they paid whatever the price was, without saying so. Now they raisePaymentRequiredby default; passmax_star_count(also onGift.transferandGift.upgrade) to pay up to that many Stars. A higher price, or one that isn't in Stars, raisesValueErrorand nothing is paid.send_resold_giftonly pays the price you agreed to (behaviour change). It paid whatever the resale price was at that moment, even if the seller had just raised it, and then failed withAttributeErroralthough the gift was bought. Passmax_star_count(usually the price you saw); without it, or if the gift now costs more,ValueErroris raised, saying the current price, and nothing is paid. The bought gift's message is returned.- Busy clients write their session file far less often. Every incoming update was committed (and synced to disk)
to the
.sessionfile on its own, several times, blocking the client meanwhile. Peers and update states are now committed together at most once a second, and when the client stops. - Downloads and uploads reuse their connections. Every file opened a new connection, and a file stored in another data center also needed a new encryption key and a new login there, every time, which made each transfer slower and could run into Telegram's rate limits. One connection per data center is now kept until the client stops, and shared with inline message edits. A connection whose login to another data center failed is no longer kept (inline edits on that data center used to keep failing).
sleep_thresholdpassed toinvokenow also applies after a request was retried because of a network error; the retry used to fall back to the default threshold and wait out flood waits you asked to receive.- A private or group message that arrives in short form no longer raises
AttributeErrorin the update task when Telegram has nothing more to send for it (an empty or too long difference). leave_chatforgets a channel's update state only after the channel was left, and also when it was given a username or link. The state used to be looked up by thechat_idas given (so usernames never matched), removed before leaving even if leaving failed, and only withskip_updates=False.delete_chat_historyno longer deletes a whole channel history when given a date range.min_date,max_dateandjust_clearwere ignored for channels and supergroups, so the whole history up tomax_idwas deleted; they now raiseValueErrorthere. The number of deleted messages is counted from all of Telegram's updates instead of raisingIndexError(after deleting) when there were none.- MongoDB sessions:
log_outreally deletes the session. Errors while deleting were silently ignored, andremove_peers=Truenever removed anything (it usedremove(), which current MongoDB drivers don't have). Peers and usernames are now removed when asked, the update states always, and errors are raised. - Downloads with problem names work on Windows. File names chosen by the sender with characters Windows doesn't
allow (
<>:"|?*) made the download fail, trailing dots and spaces were silently dropped, and device names such asCONorNUL.zipsent the file to a device; these names are now adjusted. Two downloads with the same name at the same time no longer share a temporary file (one used to fail or truncate the other). enable_cloud_passwordandchange_cloud_passwordcheck the server's parameters for the new 2FA password, like logging in already did, and refuse unknown or unsafe ones instead of hashing the new password with them.- Hashing a 2FA password (for logging in, changing or removing it, transferring chat ownership, or a bot button that asks for it) runs in a thread instead of blocking the client's event loop while it computes.
Hawkgram 1.0.8: folder links
export_folder_linkreturns the new link. It raisedNameErrorafter Telegram had already created the link, so the call failed even though the link existed.- Folder links share the right chats. A folder's pinned and included chats are each shared once, and its excluded chats never are. Folders with excluded or pinned chats used to send their included chats two or three times, and pinned chats were left out of the link.
Hawkgram 1.0.7: Python 3.14 event loops
- A client created outside of a running event loop works with
asyncio.run. A client defined at module level and started insideasyncio.run(main())now runs on that loop. Its update workers used to be scheduled on another loop that never ran, so handlers never fired andlisten/wait_for_messagefailed with "attached to a different loop". - Handlers added before the loop starts are registered at once, e.g. with
@app.on_message()at import time. They used to wait for a task on the loop the client was created with, so they were lost when the client was started withasyncio.run. Removing a handler that doesn't exist now raisesValueErrorright away. app.run()no longer depends on a current event loop. Since Python 3.14asynciono longer creates one implicitly, soapp.run()raisedRuntimeErrorwhenasyncio.run()had been used between creating the client and running it. A new loop is created when there's none, or when the current one is closed.- Callback query handlers no longer use
asyncio.iscoroutinefunction, which is deprecated since Python 3.14.
Hawkgram 1.0.6: messages with formatted dates
- Messages with a formatted date are no longer dropped.
MessageEntityTypewas missingFORMATTED_DATE, so parsing any message that contained one raisedValueErrorand the update never reached a handler. The entity now has its own type, exposes the shown point in time asMessageEntity.date, and keeps its display format when the message is sent again. - A message entity of an unknown type no longer makes the whole message unparsable. Entity types added by a newer
API layer are parsed as
MessageEntityType.UNKNOWN.
Hawkgram 1.0.5: bounded unknown_errors.txt
unknown_errors.txtno longer grows without limit. When it reaches 1 MiB it is moved tounknown_errors.txt.1, replacing the previous one, so the two files take about 2 MiB at most and the latest errors are always kept.- Unknown Telegram errors are no longer lost when
unknown_errors.txtcan't be written, for example in a read-only folder. The write failure used to replace the error itself; now the error is raised as usual.
Hawkgram 1.0.4: current key exchange encryption
- The auth key exchange uses RSA_PAD, Telegram's current scheme for encrypting the data sent to the server when a new session key is created. The data is wrapped in AES-256-IGE under a random temporary key with a SHA-256 hash before the RSA encryption, replacing the older RSA of a SHA-1 hash, the data and random padding.
- The key exchange names the data center the key is created for (
p_q_inner_data_dc), as Telegram's documentation requires together with RSA_PAD. - Telegram's current production and test server keys are included and always preferred, since Telegram only accepts
RSA_PAD with them. Test mode (
test_mode=True) uses the test servers' key, which was missing. - When Telegram rejects the key exchange, the error names the server's error code instead of
KeyError: 0. - Verified by creating new session keys on all five production data centers and on the test servers. Existing sessions are not affected.
Hawkgram 1.0.2: maintained dependencies
pyaesreplaced withcryptography. When TgCrypto isn't installed, AES now runs in compiled code from the actively maintainedcryptographypackage instead of the unmaintained pure-Pythonpyaes(last release 2017). The output is identical to before.pysocksreplaced withpython-socks. Proxy connections (SOCKS4, SOCKS5 and HTTP) use the maintainedpython-sockspackage instead ofpysocks(last release 2019). The proxy handshake no longer blocks the client while it waits. Proxy settings are unchanged.- The forked
tgcrypto-pyroforkandpymediainfo-pyroforkdependencies are capped at their latest reviewed releases, so a new release is only installed after it has been reviewed.
Reinstall Hawkgram after updating so the new dependencies are installed.
Hawkgram 1.0.1: security fixes
All the issues below were inherited from Pyrofork. Updating is strongly recommended, especially on Linux and macOS.
- Downloads could write files anywhere (Linux and macOS). A document whose file name contained backslashes could be saved outside the download folder, overwriting any file the program can write. File names from other users are now reduced to a single component, and downloads can never leave their target folder.
- Messages could freeze the client. A crafted 4096-character text took about 8 seconds to parse as Markdown, blocking all handlers; it now takes milliseconds.
- Session files were readable by other users of the computer (Linux and macOS). They are now created, and tightened if needed, with owner-only permissions.
- Session files are never uploaded by path, so a bot passing user input to
send_documentand similar methods can't be tricked into sending its own session. - Stricter protocol checks: the integrity of incoming messages is verified before they are parsed, the auth key exchange validates the server's answer before sending anything and requires the server's key confirmation, 2FA only accepts Telegram's known safe parameters, and every incoming message is checked against the clock.
- Logs no longer contain secrets: debug logs record request and response types instead of their contents.
- Hardened CI: pinned actions, read-only permissions, and building separated from publishing.
Hawkgram 1.0.0, the first release. Changes compared to Pyrofork 2.3.69 (Telegram API layer 223).
Telegram API layer 229
- Updated the Telegram API schema from layer 223 to layer 229, adding about 185 new types and methods (communities,
rich messages, ephemeral messages, AI compose tones, web browser settings and more), available through
pyrogram.raw. - Keyboards follow Telegram's new button model.
InlineKeyboardButton,KeyboardButton,LoginUrlandInlineKeyboardButtonBuywork exactly as before; only the underlying raw types changed. send_pollandstop_pollsend the pollhashthat Telegram now requires, and quiz answers are sent as option indexes. Missing vote counts in poll results are treated as 0.join_chatunderstands Telegram's new join result, including chats protected by a guard bot.
New features
- Communities: groups of supergroups, channels and bots. Create them, list the ones you joined, manage requests to
add chats, hide or remove chats, ban members and collapse them in the chat list. Communities are regular
Chatobjects (ChatType.COMMUNITY), andget_chatreturns their chats and description. - Ephemeral messages: messages visible to a single user of a chat. Send, edit and delete them, manage the welcome
messages shown to new members, and handle them with
on_ephemeral_message,on_edited_ephemeral_messageandon_deleted_ephemeral_messages. Button clicks on ephemeral messages arrive inon_callback_query. - AI compose: rewrite, proofread, translate or emojify texts with
compose_text_with_ai, manage built-in and custom tones, and pass a tone totranslate_message_text. - Rich messages: send and edit messages with headings, lists, tables and formulas from HTML or Markdown, translate
or compose them with AI, read them from
Message.rich_message, and send them as inline results. - Polls: new
send_polloptions (revoting, shuffled options, results hidden until closed, options added by users, members-only and country limits), adding and deleting options, unread votes andMessagesFilter.POLL. - Bots: create managed bots and manage their tokens and access, guard new members of a group with
answer_chat_join_queryandset_chat_join_requests, and answer guest chat queries withon_guest_chat_query. - Web browser settings, removing the reactions of a chat member, the messages of a user's personal channel, and new service messages for communities, managed bots and poll options.
Bug fixes
-
Requests with an empty list in an optional field were malformed, which could make Telegram reject them.
-
Texts proofread by AI couldn't be parsed.
-
on_purchased_paid_mediacrashed when used, and to-do completion messages had no service type. -
Methods that failed on every call now work:
- all forum topic methods (
create_forum_topic,edit_forum_topic,close_forum_topic,get_forum_topics, ...) transfer_chat_ownershipdelete_scheduled_messagesupdate_colorfor your own profileset_gift_resale_priceget_chat_gifts,get_chat_gifts_countandget_business_account_gifts
- all forum topic methods (
-
send_media_group,copy_media_groupandsend_paid_mediasent the messages but then crashed instead of returning them. -
Inline buttons with a
login_urlraised an error when sent. -
Parsing no longer crashes on Stars transactions (including Fragment and App Store sources), gifted Premium and gift codes, business bot connections, shipping addresses, solid color and gradient wallpapers, saved gifts and Stars status.
-
Fixed values that were always empty: the link returned by
export_folder_link, chat wallpaper flags, story view counts and chat theme emoji.
Behavior changes
join_chatreturnsNonewhen the join must first be approved by the chat's guard bot.delete_scheduled_messagesreturns the IDs of the messages Telegram reports as deleted.transfer_chat_ownershipalso works for basic groups, not only supergroups and channels.- The app version shown in Telegram's active sessions list is now
Hawkgram x.y.z.
Project
- Renamed to Hawkgram and published as
hawkgram; thepyrogramimport name is unchanged. - License headers name Hawkgram and Lone Hawk. All original copyright notices are kept, and the license is still LGPL-3.0-or-later.
- Removed Pyrofork links, the funding file and the deployment to Pyrofork's documentation site.
.gitignoreno longer excludes the documentation sources, the unusedMANIFEST.inis gone, and the docs no longer reference missing logo files.- All tests pass.
Credits
Hawkgram is developed by Lone Hawk. It is based on Pyrofork by Mayuri-Chan, which is itself a fork of Pyrogram by Dan.
Metadata
Release files for hawkgram 1.0.9
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hawkgram-1.0.9.tar.gz | 562.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hawkgram-1.0.9-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 6.4 MB
Release files / hawkgram-1.0.9.tar.gz
| Download URL | hawkgram-1.0.9.tar.gz |
|---|---|
| Size | 562.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07e01e621cfd08a47d3e62df7bc1a30c01fb9138170a4d02dd26e27003da4667
|
|
BLAKE2b-256 checksum How to use checksums |
5c8a6eec4a86e4f6fb2e6cfec5a90aa5eb5954919011b31690c442f36c7feef7
|
| 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 Oct 11, 2026.
Transparency logRelease files / hawkgram-1.0.9-py3-none-any.whl
| Download URL | hawkgram-1.0.9-py3-none-any.whl |
|---|---|
| Size | 5.8 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf56deb888fef69a3d5d53b6625ebbd843cdf2a46f7cd4c3228c9399a1f10cc0
|
|
BLAKE2b-256 checksum How to use checksums |
a1f3fee6e3ab9fa3a41fbf584ede3df52b4ae0062050129c845ac6d8fd3f1330
|
| 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 Oct 11, 2026.
Transparency log