ferogram 0.5.2b2__tar.gz → 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. {ferogram-0.5.2b2 → ferogram-0.5.3}/.github/workflows/publish.yml +20 -15
  2. ferogram-0.5.3/CHANGELOG.md +249 -0
  3. {ferogram-0.5.2b2 → ferogram-0.5.3}/Cargo.lock +41 -9
  4. {ferogram-0.5.2b2 → ferogram-0.5.3}/Cargo.toml +9 -6
  5. {ferogram-0.5.2b2 → ferogram-0.5.3}/PKG-INFO +2 -2
  6. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/__init__.py +1 -1
  7. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/client.py +662 -190
  8. ferogram-0.5.3/ferogram/logging.py +176 -0
  9. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/codegen.py +6 -7
  10. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/proxy.py +2 -2
  11. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/tl.py +95 -65
  12. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/updates.py +4 -2
  13. {ferogram-0.5.2b2 → ferogram-0.5.3}/pyproject.toml +2 -2
  14. ferogram-0.5.3/src/connection.rs +789 -0
  15. ferogram-0.5.3/src/lib.rs +118 -0
  16. ferogram-0.5.3/src/message_box.rs +311 -0
  17. ferogram-0.5.2b2/CHANGELOG.md +0 -201
  18. ferogram-0.5.2b2/ferogram/logging.py +0 -52
  19. ferogram-0.5.2b2/src/connection.rs +0 -400
  20. ferogram-0.5.2b2/src/lib.rs +0 -38
  21. {ferogram-0.5.2b2 → ferogram-0.5.3}/.github/workflows/compile-check.yml +0 -0
  22. {ferogram-0.5.2b2 → ferogram-0.5.3}/.gitignore +0 -0
  23. {ferogram-0.5.2b2 → ferogram-0.5.3}/.readthedocs.yaml +0 -0
  24. {ferogram-0.5.2b2 → ferogram-0.5.3}/FEATURES.md +0 -0
  25. {ferogram-0.5.2b2 → ferogram-0.5.3}/LICENSE-APACHE +0 -0
  26. {ferogram-0.5.2b2 → ferogram-0.5.3}/LICENSE-MIT +0 -0
  27. {ferogram-0.5.2b2 → ferogram-0.5.3}/Makefile +0 -0
  28. {ferogram-0.5.2b2 → ferogram-0.5.3}/README.md +0 -0
  29. {ferogram-0.5.2b2 → ferogram-0.5.3}/assets/architecture.svg +0 -0
  30. {ferogram-0.5.2b2 → ferogram-0.5.3}/build.rs +0 -0
  31. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/admin_tools.py +0 -0
  32. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/command_bot.py +0 -0
  33. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/echo_bot.py +0 -0
  34. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/group_management.py +0 -0
  35. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/media_bot.py +0 -0
  36. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/raw_invoke.py +0 -0
  37. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/rich_message.py +0 -0
  38. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/search_bot.py +0 -0
  39. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/send_hi.py +0 -0
  40. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/send_media.py +0 -0
  41. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/send_message.py +0 -0
  42. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/update_handlers.py +0 -0
  43. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/user_management.py +0 -0
  44. {ferogram-0.5.2b2 → ferogram-0.5.3}/examples/userbot.py +0 -0
  45. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/api.tl +0 -0
  46. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/filters.py +0 -0
  47. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/keyboards.py +0 -0
  48. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/py.typed +0 -0
  49. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/__init__.py +0 -0
  50. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/api/__init__.py +0 -0
  51. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/api/functions.py +0 -0
  52. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/api/types.py +0 -0
  53. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/__init__.py +0 -0
  54. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/_tl_schema.py +0 -0
  55. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/__init__.py +0 -0
  56. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/account.py +0 -0
  57. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/aicompose.py +0 -0
  58. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/auth.py +0 -0
  59. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/bots.py +0 -0
  60. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/channels.py +0 -0
  61. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/chatlists.py +0 -0
  62. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/communities.py +0 -0
  63. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/contacts.py +0 -0
  64. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/ephemeral.py +0 -0
  65. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/folders.py +0 -0
  66. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/fragment.py +0 -0
  67. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/help.py +0 -0
  68. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/langpack.py +0 -0
  69. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/messages.py +0 -0
  70. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/payments.py +0 -0
  71. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/phone.py +0 -0
  72. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/photos.py +0 -0
  73. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/premium.py +0 -0
  74. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/smsjobs.py +0 -0
  75. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/stats.py +0 -0
  76. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/stickers.py +0 -0
  77. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/stories.py +0 -0
  78. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/updates.py +0 -0
  79. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/upload.py +0 -0
  80. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/functions/users.py +0 -0
  81. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/__init__.py +0 -0
  82. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/_base.py +0 -0
  83. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/account.py +0 -0
  84. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/aicompose.py +0 -0
  85. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/auth.py +0 -0
  86. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/bots.py +0 -0
  87. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/channels.py +0 -0
  88. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/chatlists.py +0 -0
  89. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/communities.py +0 -0
  90. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/contacts.py +0 -0
  91. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/fragment.py +0 -0
  92. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/help.py +0 -0
  93. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/messages.py +0 -0
  94. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/payments.py +0 -0
  95. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/phone.py +0 -0
  96. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/photos.py +0 -0
  97. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/premium.py +0 -0
  98. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/smsjobs.py +0 -0
  99. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/stats.py +0 -0
  100. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/stickers.py +0 -0
  101. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/storage.py +0 -0
  102. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/stories.py +0 -0
  103. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/updates.py +0 -0
  104. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/upload.py +0 -0
  105. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/raw/generated/types/users.py +0 -0
  106. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/rich.py +0 -0
  107. {ferogram-0.5.2b2 → ferogram-0.5.3}/ferogram/types.py +0 -0
  108. {ferogram-0.5.2b2 → ferogram-0.5.3}/src/pipelined.rs +0 -0
  109. {ferogram-0.5.2b2 → ferogram-0.5.3}/src/raw.rs +0 -0
  110. {ferogram-0.5.2b2 → ferogram-0.5.3}/src/session.rs +0 -0
  111. {ferogram-0.5.2b2 → ferogram-0.5.3}/src/srp.rs +0 -0
  112. {ferogram-0.5.2b2 → ferogram-0.5.3}/tests/test_rich.py +0 -0
  113. {ferogram-0.5.2b2 → ferogram-0.5.3}/tests/test_tl_roundtrip.py +0 -0
@@ -15,7 +15,7 @@ on:
15
15
  - linux
16
16
  - macos
17
17
  - windows
18
- - android
18
+ - android-termux
19
19
  - sdist
20
20
  publish:
21
21
  description: "Upload to PyPI after build"
@@ -102,8 +102,14 @@ jobs:
102
102
  name: wheels-windows
103
103
  path: dist
104
104
 
105
- android:
106
- if: github.event_name == 'push' || inputs.job == 'all' || inputs.job == 'android'
105
+ # Builds for Termux (the terminal app), not for embedding inside a
106
+ # general Android APK. This links against Termux's own Python package
107
+ # and Termux's runtime library layout - a wheel from this job is only
108
+ # installable via `pip install` inside Termux. It will not work with
109
+ # app-embedded interpreters like Chaquopy or python-for-android; those
110
+ # bundle their own CPython and need a separate build target entirely.
111
+ android-termux:
112
+ if: github.event_name == 'push' || inputs.job == 'all' || inputs.job == 'android-termux'
107
113
  runs-on: ubuntu-latest
108
114
  strategy:
109
115
  fail-fast: false
@@ -128,7 +134,7 @@ jobs:
128
134
  with:
129
135
  python-version: "3.13"
130
136
 
131
- - name: Setup Android NDK r26d
137
+ - name: Setup Android NDK r26d (cross-compiler for the Termux target)
132
138
  uses: nttld/setup-ndk@v1
133
139
  id: ndk
134
140
  with:
@@ -190,7 +196,7 @@ jobs:
190
196
  echo "TERMUX_PYVER=$PYVER" >> $GITHUB_ENV
191
197
  echo "Linking against Termux libpython $PYVER ($SO_FILE)"
192
198
 
193
- - name: Build Android wheel
199
+ - name: Build Termux wheel
194
200
  env:
195
201
  ANDROID_NDK_HOME: ${{ steps.ndk.outputs.ndk-path }}
196
202
  ANDROID_API_LEVEL: ${{ matrix.api }}
@@ -217,14 +223,13 @@ jobs:
217
223
  export AR_${TARGET_LOWER}=$NDK_BIN/llvm-ar
218
224
  export CARGO_TARGET_${TARGET_UPPER}_LINKER=$NDK_BIN/${CLANG_PREFIX}-clang
219
225
 
220
- # Link against Termux's real libpython<version>.so (not an empty
221
- # stub) so the build succeeds. This still embeds a version-pinned
222
- # DT_NEEDED at this point - the next step rewrites it to be
223
- # version-agnostic, since bionic requires DT_NEEDED to resolve by
224
- # exact filename at dlopen time and won't fall back to whatever
225
- # Python happens to already be resident in the process, unlike
226
- # glibc.
227
- export RUSTFLAGS="-L native=$TERMUX_LIBDIR"
226
+ # Link against Termux's real libpython<version>.so, and embed the
227
+ # rpath Termux's own binaries carry (/data/data/com.termux/files/usr/lib)
228
+ # so the loader actually searches that directory at dlopen time.
229
+ # Without this, DT_NEEDED entries resolve to nothing even when the
230
+ # target file exists right there, since bionic doesn't search
231
+ # $PREFIX/lib by default the way it does system paths.
232
+ export RUSTFLAGS="-L native=$TERMUX_LIBDIR -C link-arg=-Wl,-rpath=/data/data/com.termux/files/usr/lib"
228
233
 
229
234
  maturin build \
230
235
  --release \
@@ -254,7 +259,7 @@ jobs:
254
259
 
255
260
  - uses: actions/upload-artifact@v4
256
261
  with:
257
- name: wheels-android-${{ matrix.target }}
262
+ name: wheels-android-termux-${{ matrix.target }}
258
263
  path: dist
259
264
 
260
265
  sdist:
@@ -272,7 +277,7 @@ jobs:
272
277
  path: dist
273
278
 
274
279
  publish:
275
- needs: [linux, macos, windows, android, sdist]
280
+ needs: [linux, macos, windows, android-termux, sdist]
276
281
  if: |
277
282
  always() &&
278
283
  (github.event_name == 'push' || inputs.publish == true) &&
@@ -0,0 +1,249 @@
1
+ ## 0.5.3 (2026-07-25)
2
+
3
+ - Added `Chat.is_community` and `Dialog.community_id`/`Dialog.is_community` for community support.
4
+ - `get_dialogs()` and `get_pinned_dialogs()` now handle `dialogCommunity` via `community_id` instead of assuming a `peer` field is present.
5
+ - Peer cache now tracks communities separately from channels instead of collapsing them together. `chatEmpty` is no longer cached as a basic chat, and a stored non-zero access hash can no longer get overwritten by a zero one.
6
+ - `serialize_object` now dispatches to the generated `to_bytes()` implementation before falling back to generic field serialization, about 1.7x faster. Output verified byte-identical across all 2,437 constructors in the schema.
7
+ - Fixed a TL codegen bug where a constructor's `flags:#` word was assumed to always be first on the wire and hoisted to the front of `from_bytes()`/`to_bytes()` unconditionally. `poll#966e2dbf`, `wallPaper#a437c3ed`, and `wallPaperNoFile#e0804116` declare `id:long` before `flags:#`, so those fields got read and written swapped, producing a garbage flags word and eventually overrunning the buffer. This was the root cause of the corruption seen in `updates.getDifference`. Codegen now tracks each flag marker's real position in the schema instead of assuming it comes first.
8
+ - Fixed a related codegen bug: `exportedChatlistInvite` and `stories.allStoriesNotModified` declare `flags:#` with no field gated on it. Codegen treated "no field uses this flag group" as "no flags word on the wire" and silently dropped 4 bytes, misaligning every field after it. Flag group presence is now determined from declared markers, not field usage.
9
+ - `ferogram/raw/tl.py`'s runtime fallback interpreter (`_read_value`/`serialize_object`) had the same "flags always first" bug. Fixed to read and write each flag group at its correct wire position.
10
+ - Regenerated all 1,642 generated types and 795 functions from `api.tl`. `tests/test_tl_roundtrip.py` (4,874 constructor x optional-field combinations) now passes.
11
+ - Fixed `join_chat()` and `join_invite_link()` assuming a bare `Updates` response, breaking on `messages.ChatInviteJoinResult`. The `Ok` variant now resolves and caches the joined peer; the `WebView` variant returns `None` since it needs an interactive bot flow that isn't supported yet.
12
+ - Fixed `promote_participant(rights=...)` merging requested rights into a fixed base set instead of replacing it, so limited admin permissions could never actually be granted. It now replaces the requested rights as intended.
13
+ - Fixed `send_message()`, `edit_message()`, and `forward_messages()` ignoring caller-supplied values for `no_webpage`, `silent`, `schedule_date`, and `send_as` in favor of hardcoded defaults. These are now respected.
14
+ - Fixed `send_invoice(test=...)`, `add_contact(add_phone_privacy_exception=...)`, and `get_blocked_users(my_stories_from=...)` ignoring the supplied value and always sending a hardcoded default.
15
+
16
+ ## 0.5.2 (2026-07-17)
17
+
18
+ - Added `Client.get_chat_photos(peer, limit)`: photo/avatar history for groups and channels (`get_profile_photos` only ever worked for users). Current photo comes from the chat's full info, so it survives message deletion; older photos come from `messageActionChatEditPhoto` search history.
19
+ - Added fast re-auth support: `sign_out()` captures a `future_auth_token` when Telegram returns one and persists it via a new `DcConnection.get_future_auth_token`/`set_future_auth_token` pair (rides along in the existing session blob, no new session format code needed here). `request_login_code()` replays it automatically, so a returning session can skip code entry entirely (`auth.sentCodeSuccess`).
20
+ - MTProto handshake fix upstream (dc-tagged `p_q_inner_data` was being rejected by Telegram with RPC 444 outside DC2), pulled in transitively via `ferogram-mtsender`.
21
+ - Layer Upgrade to 228
22
+ ## 0.5.1 (2026-06-29)
23
+
24
+ ## What's Changed
25
+
26
+ ### Message API Improvements
27
+
28
+ - `Message.reply()` now creates a proper quoted reply using `reply_to=self.id`.
29
+ - Added `Message.respond()` for sending messages to the same chat without replying.
30
+ - Added `pin()`, `forward_to()`, `get_sender()`, `get_chat()`, and `get_reply_message()` helpers.
31
+ - Added `reply_photo()` and `reply_document()` convenience methods.
32
+ - Improved `delete()` to correctly handle channels and supergroups.
33
+
34
+ ### Client Enhancements
35
+
36
+ - Added `reply_to` support to `send_message()`, `send_photo()`, and `send_document()`.
37
+ - Added `Client.get_user()` and `Client.get_chat()` helpers.
38
+ - `edit_message()` now accepts `reply_markup`.
39
+ - `pin_message()` now supports a `notify` parameter.
40
+ - Improved `delete_messages_in()` for channel-aware message deletion.
41
+ - Improved sent message extraction fallback behavior with warning logging and preserved context.
42
+
43
+ ### Update Improvements
44
+
45
+ - Added convenience methods to `CallbackQuery`:
46
+ - `respond()`
47
+ - `reply()`
48
+ - `edit_message_text()`
49
+ - `get_sender()`
50
+
51
+ - Added convenience methods to `InlineQuery`:
52
+ - `answer()`
53
+ - `get_sender()`
54
+
55
+ - Added sender/entity helpers for additional update types, including:
56
+ - `InlineSend`
57
+ - `UserStatus`
58
+ - `ChatAction`
59
+ - `JoinRequest`
60
+ - `ParticipantUpdate`
61
+ - `MessageReaction`
62
+ - `ChatBoost`
63
+ - `ShippingQuery`
64
+ - `PreCheckoutQuery`
65
+
66
+ ### Dispatch Improvements
67
+
68
+ - `_dispatch()` now binds `_client` for all update types.
69
+ - Added `_client` support to remaining update wrappers, including `RawUpdate`.
70
+ - Improved sent message extraction fallback handling.
71
+
72
+ ## 0.5.0 (2026-06-28)
73
+
74
+ ### Architecture
75
+
76
+ The Rust dependency has changed from the monolithic `ferogram` crate to five focused crates from the ferogram core:
77
+
78
+ - `ferogram-mtsender` - MTProto sender, message framing, and acknowledgement
79
+ - `ferogram-session` - session storage (file, sqlite, libsql, memory, string)
80
+ - `ferogram-connect` - TCP/TLS transport and DC routing
81
+ - `ferogram-tl-types` - generated TL type definitions
82
+ - `ferogram-crypto` - AES-IGE, Diffie-Hellman, and SHA helpers
83
+
84
+ These five crates form the battle-tested lower layer of ferogram. They change rarely and are stable across minor versions. All high-level logic (message parsing, update dispatch, peer resolution, serialization, deserialization) now lives in Python. This split means the compiled extension is smaller and faster to build, and Python-side behaviour can be updated without recompiling the Rust extension.
85
+
86
+ The compiled extension now uses `abi3-py39` (was `abi3-py313`), so a single wheel runs on Python 3.9 and later.
87
+
88
+ ### Added
89
+
90
+ - **`TransferHandle`** - pause, resume, and cancel any upload or download in flight. Create a `TransferHandle`, pass it to `upload_with_progress` or `download_with_progress`, then call `.pause()`, `.resume()`, or `.cancel()` from any coroutine. `.progress()` returns a dict with `done`, `total`, `elapsed_ms`, `percent`, `speed_bps`, `eta_secs`, `speed_human`, and `bytes_human`.
91
+ - **`TransferCancelled` exception** - raised when a `TransferHandle` is cancelled mid-transfer.
92
+ - **`keyboards` module** - `InlineKeyboard`, `InlineButton`, `ReplyKeyboard`, `ReplyButton`, `RemoveKeyboard`, and `ForceReply` are now pure Python and importable from `ferogram` directly.
93
+ - **`types` module** - all entity types (`User`, `Message`, `Chat`, `Dialog`, `ChatMember`, `UserFull`, `Authorization`, `ForumTopic`, `BotInfo`, `InviteLinkMember`, `ReadParticipant`, `AdminLogEvent`, `StickerSetInfo`, `BroadcastStats`, `MegagroupStats`, `NotifySettings`) moved from Rust to Python dataclasses. The public API is identical.
94
+ - **`updates` module** - update wrapper types (`NewMessage`, `EditedMessage`, `MessageDeletion`, `CallbackQuery`, `InlineQuery`, `InlineSend`, `UserStatus`, `ParticipantUpdate`, `JoinRequest`, `MessageReaction`, `PollVote`, `BotStopped`, `ShippingQuery`, `PreCheckoutQuery`, `ChatBoost`, `RawUpdate`) moved from Rust to Python dataclasses.
95
+ - **`rich` module** - `send_rich_message`, `edit_rich_message`, `send_rich_message_draft`, and `get_rich_message` via a `_RichMixin`. Supports Telegram rich text blocks including headers, tables, collages, task lists, footnotes, math, time stamps, and custom emoji via Markdown or HTML input.
96
+ - **Automatic TL codegen at build time** - `build.rs` now runs `ferogram/raw/codegen.py` during `maturin develop` / `pip install .`. Set `FEROGRAM_SKIP_CODEGEN=1` to skip if you only changed Rust. The codegen uses the same Python interpreter maturin selected.
97
+ - **`DcConnection` and `srp_calculate`** exposed from the Rust extension for use by the pure-Python `Client` class.
98
+ - **`LAYER` constant** exported from `ferogram.raw.generated._tl_schema` and used automatically in `invokeWithLayer` wrappers.
99
+ - **`all_updates` filter** - replaces the old `all` filter (which shadowed the built-in).
100
+ - **`filters` improvements** - all filters now unwrap `NewMessage` / `EditedMessage` wrapper objects before inspecting the inner `Message`. Previously filters broke when the dispatcher passed wrapper objects instead of bare messages.
101
+
102
+ ### Changed
103
+
104
+ - `Message`, `User`, `Chat`, and all entity types are now Python dataclasses instead of PyO3 structs. They serialize to / from the TL dict representation in Python. No change to the public API.
105
+ - `CallbackQuery`, `InlineQuery`, and all update types are now Python dataclasses.
106
+ - `InlineKeyboard`, `ReplyKeyboard`, and related keyboard builders are now pure Python (`ferogram/keyboards.py`). Previously they were Rust structs.
107
+ - Filters rewritten to correctly unwrap update wrapper objects. `reply` filter now checks `reply_to_msg_id`; `forwarded` checks `forward_from_id`; `media` checks the `media` field directly.
108
+ - `codegen.py` doubled in size (321 to 691 lines): now generates specialized `to_bytes()` methods using `struct.pack` inline and a CID-dispatch `from_bytes()` router, producing faster serialization and deserialization than the previous schema-dict approach.
109
+ - `ferogram-py` version bump to 0.5.0. Core crates pinned to `ferogram-*` 0.6.3.
110
+
111
+ ### Removed
112
+
113
+ - Direct dependency on the monolithic `ferogram` crate. The five focused crates replace it.
114
+ - `hex` crate dependency (was `0.4`, now unused).
115
+ - `all` filter removed and replaced with `all_updates` to avoid shadowing Python's built-in `all`.
116
+
117
+
118
+ ## 0.4.1 (2026-06-03)
119
+
120
+ Patch release. No API changes. Identical to 0.4.0 except for dependency pins and minor internal fixes.
121
+
122
+
123
+ ## 0.4.0 (2026-06-01)
124
+
125
+ ### Added
126
+
127
+ - **Session backends** - `FileSession`, `MemorySession`, `StringSession`, `SqliteSession`, `LibSqlSession`, and `CustomSession` are now first-class types importable from `ferogram`. Pass any of them as the `session` argument to `Client`. Plain string names still work as before.
128
+ - `SqliteSession("name")` stores the session in a SQLite database.
129
+ - `MemorySession()` keeps the session in memory only; nothing is written to disk.
130
+ - `StringSession("AQA...")` resumes from a base64 string, useful for serverless or env-var based deployments.
131
+ - `LibSqlSession.local/remote/replica/memory(...)` targets a local file, remote Turso database, embedded replica, or in-memory libSQL database.
132
+ - `CustomSession(obj)` wraps any Python object implementing `save(bytes)`, `load() -> bytes | None`, and `delete()`.
133
+ - **`channel_kind()` on Message** - `await msg.channel_kind()` returns `"megagroup"`, `"broadcast"`, `"gigagroup"`, or `None`. Uses the peer cache; no RPC.
134
+ - **`is_megagroup()` on Message** - `await msg.is_megagroup()` returns `True` for supergroups.
135
+ - **`is_broadcast()` on Message** - `await msg.is_broadcast()` returns `True` for broadcast channels.
136
+ - **`StopPropagation` and `ContinuePropagation`** - two exceptions exported from `ferogram` to control handler dispatch. `StopPropagation` stops all further group processing. `ContinuePropagation` skips the current handler and continues to the next match in the same group.
137
+ - **Handler groups** - all `on_*` decorators now accept `group: int = 0`. Handlers run in ascending group order; lower group numbers run first.
138
+ - **`add_handler` / `remove_handler`** - register and deregister handlers at runtime without decorators.
139
+ - **Worker pool** - a fixed pool of `workers` coroutines (default 4) now dispatches updates from a bounded `asyncio.Queue`. Replaces the old unbounded `asyncio.create_task` approach. Provides natural backpressure under load.
140
+ - **`workers` kwarg on `Client`** - controls pool size.
141
+ - **`parse_mode` kwarg on `Client`** - sets a global default parse mode applied to every `send_message` call that does not pass its own `parse_mode`.
142
+ - **`flood_sleep_threshold` kwarg on `Client`** - maps to the `AutoSleep` retry policy in the Rust core. Flood waits under this value are slept through automatically; waits above it are raised as exceptions.
143
+ - **`download_with_progress(peer, msg_id, path, on_progress)`** - download media with a progress callback `on_progress(done, total)`.
144
+ - **`upload_with_progress(path, on_progress)`** - upload a file with a progress callback. Returns a handle string accepted by `send_file`.
145
+ - **ferogram 0.6.0 as core dependency** - includes `Client.channel_kind_of(channel_id)` which backs the new `Message` methods above.
146
+
147
+ ### Changed
148
+
149
+ - `no_webpage` now defaults to `True` in `send_message` and `edit_message`.
150
+ - `upload_file(data, name, mime)` replaced by `upload(Cursor(data), name)` internally; no change to the Python API.
151
+ - Session resolution order is now `session_string > session object`. `in_memory=True` is deprecated in favor of `MemorySession()`.
152
+ - Handler storage changed from `list` to `dict[group, list]`; dispatch now iterates groups in sorted order.
153
+ - `_resolve_pm` is now used internally to merge per-call and global `parse_mode`.
154
+
155
+ ### Fixed
156
+
157
+ - `channel_kind`, `is_megagroup`, `is_broadcast` on `Message` no longer call `get_chat_full` (an RPC) on every invocation. They read from the peer cache via `Client.channel_kind_of`.
158
+ - Unnecessary `as i64` casts removed from `message.rs`.
159
+ - Nested `if let` blocks collapsed to `if let ... && ...`.
160
+ - `match ... { Some(m) => m, None => return None }` replaced with `as_ref()?`.
161
+
162
+
163
+ ## 0.3.0 (2026-05-16)
164
+
165
+ ### ferogram core upgraded to 0.5.0
166
+
167
+ The Rust dependency is now ferogram 0.5.0.
168
+
169
+ ### send_poll: full PollBuilder kwargs exposed
170
+
171
+ `send_poll` now accepts all options the Rust `PollBuilder` supports: `public_voters`,
172
+ `shuffle_answers`, `hide_results_until_close`, `close_period`, `close_date`, and `solution`.
173
+ Previously only `quiz`, `correct_index`, and `multiple_choice` were wired through.
174
+
175
+ ### edit_chat_default_banned_rights: send_reactions added
176
+
177
+ The `restrictions` dict now accepts `"send_reactions"` as a key. Mirrors the new
178
+ `send_reactions` field on `BannedRightsBuilder` added in ferogram 0.5.0.
179
+
180
+ ### poll_results method
181
+
182
+ `poll_results(peer, msg_id)` is now the canonical way to fetch poll stats. Returns
183
+ the votes graph as a JSON string. The old `get_poll_results(peer, msg_id, poll_hash)`
184
+ is kept for backward compat but is deprecated; the `poll_hash` parameter is ignored
185
+ because ferogram 0.5.0 dropped the underlying API call it relied on.
186
+
187
+
188
+ ## 0.2.3 (2026-05-14)
189
+
190
+ ### Client Builder
191
+
192
+ Added 20 new kwargs to `Client(...)` so you can configure everything at construction time instead of touching internals.
193
+
194
+ Network options: `proxy` (SOCKS5 or MTProxy t.me link), `allow_ipv6`, `dc_addr`, `probe_transport`, `resilient_connect`.
195
+
196
+ Session backend: `session_string` and `in_memory` as alternatives to the default session file. Priority is `in_memory > session_string > file`.
197
+
198
+ Update handling: `catch_up`, `pfs`, `update_queue_capacity`, `update_overflow` (`"drop_oldest"` or `"drop_newest"`), `low_memory_mode`.
199
+
200
+ InitConnection identity: `device`, `system_version`, `app_version`, `lang_code`, `system_lang_code`, `lang_pack`.
201
+
202
+ Experimental: `allow_missing_channel_hash`, `auto_resolve_peers`.
203
+
204
+ ### Inline Bots
205
+
206
+ `InlineArticle`, `InlinePhoto`, and `InlineDocument` now expose all their fields. Previously only the 3 required fields worked. New fields: `description`, `url`, `thumb_url`, `reply_markup` on articles; `photo_url`, `photo_width`, `photo_height`, `thumb_url`, `mime_type`, `reply_markup` on photos; `document_url`, `mime_type`, `description`, `thumb_url`, `reply_markup` on documents.
207
+
208
+ `answer_inline_query` now accepts `next_offset` for pagination and `switch_pm` to show a "go to PM" button in the results list.
209
+
210
+ `answer_inline_query_articles` is now accessible from Python. Takes a plain list of `(id, title, text)` tuples and supports `next_offset`.
211
+
212
+ `edit_inline_message` now accepts `reply_markup` so you can attach or update a keyboard when editing.
213
+
214
+ `InlineSend` now exposes `msg_id_bytes` and `msg_id_dc` so you can build an `InlineMessageId` from the send event and edit the message later.
215
+
216
+ ### Participants
217
+
218
+ Ten new methods for managing group and channel members.
219
+
220
+ `get_participants(peer, limit=200)` fetches all members as a list of `ChatMember`.
221
+
222
+ `get_participants_filtered(peer, filter, limit)` fetches a filtered subset. Filter values: `"recent"`, `"admins"`, `"kicked"`, `"banned"`, `"bots"`.
223
+
224
+ `kick_participant(peer, user)` removes a user from the chat. They can rejoin if the chat is public or they have an invite link.
225
+
226
+ `ban_participant(peer, user)` permanently bans a user until an admin manually lifts it.
227
+
228
+ `ban_participant_until(peer, user, until_date)` bans until a unix timestamp, after which the ban lifts automatically.
229
+
230
+ `promote_participant(peer, user, rights)` promotes to admin with specific rights. Pass `rights=None` to grant all. Valid right strings: `"change_info"`, `"post_messages"`, `"edit_messages"`, `"delete_messages"`, `"ban_users"`, `"invite_users"`, `"pin_messages"`, `"add_admins"`, `"manage_call"`.
231
+
232
+ `demote_participant(peer, user)` strips all admin rights, leaving the user as a regular member.
233
+
234
+ `get_profile_photos(peer, limit=100)` returns a list of `(file_id, access_hash, dc_id)` tuples.
235
+
236
+ `search_peer(query)` searches the local peer cache by name or username and returns a list of peer identifier strings.
237
+
238
+ `signal_network_restored()` (sync, not async) tells the client network is back so it attempts reconnect immediately instead of waiting for the retry timer.
239
+
240
+ ### Reactions and Polls
241
+
242
+ `delete_reaction(peer, msg_id, participant)` was in Rust but had no Python wrapper. Now exposed. Removes a specific user's reaction from a message, admin only.
243
+
244
+ `get_poll_results(peer, msg_id, poll_hash)` was in Rust but had no Python wrapper. Now exposed. Fetches and caches the latest poll results from Telegram.
245
+
246
+
247
+ ## 0.2.2
248
+
249
+ Previous release.
@@ -52,6 +52,15 @@ version = "1.0.102"
52
52
  source = "registry+https://github.com/rust-lang/crates.io-index"
53
53
  checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
54
54
 
55
+ [[package]]
56
+ name = "arc-swap"
57
+ version = "1.9.2"
58
+ source = "registry+https://github.com/rust-lang/crates.io-index"
59
+ checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
60
+ dependencies = [
61
+ "rustversion",
62
+ ]
63
+
55
64
  [[package]]
56
65
  name = "async-stream"
57
66
  version = "0.3.6"
@@ -393,7 +402,7 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
393
402
  [[package]]
394
403
  name = "ferogram-connect"
395
404
  version = "0.6.4"
396
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
405
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
397
406
  dependencies = [
398
407
  "ferogram-crypto",
399
408
  "ferogram-mtproto",
@@ -409,7 +418,7 @@ dependencies = [
409
418
  [[package]]
410
419
  name = "ferogram-crypto"
411
420
  version = "0.6.4"
412
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
421
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
413
422
  dependencies = [
414
423
  "aes",
415
424
  "ctr",
@@ -423,10 +432,19 @@ dependencies = [
423
432
  "tracing",
424
433
  ]
425
434
 
435
+ [[package]]
436
+ name = "ferogram-msgbox"
437
+ version = "0.6.4"
438
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
439
+ dependencies = [
440
+ "ferogram-tl-types",
441
+ "tracing",
442
+ ]
443
+
426
444
  [[package]]
427
445
  name = "ferogram-mtproto"
428
446
  version = "0.6.4"
429
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
447
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
430
448
  dependencies = [
431
449
  "ferogram-crypto",
432
450
  "ferogram-tl-types",
@@ -439,7 +457,7 @@ dependencies = [
439
457
  [[package]]
440
458
  name = "ferogram-mtsender"
441
459
  version = "0.6.4"
442
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
460
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
443
461
  dependencies = [
444
462
  "ferogram-connect",
445
463
  "ferogram-crypto",
@@ -453,25 +471,28 @@ dependencies = [
453
471
 
454
472
  [[package]]
455
473
  name = "ferogram-py"
456
- version = "0.5.2"
474
+ version = "0.5.3"
457
475
  dependencies = [
458
476
  "base64 0.22.1",
459
477
  "ferogram-connect",
460
478
  "ferogram-crypto",
479
+ "ferogram-msgbox",
461
480
  "ferogram-mtsender",
462
481
  "ferogram-session",
463
482
  "ferogram-tl-types",
464
483
  "getrandom 0.2.17",
484
+ "log",
465
485
  "pyo3",
466
486
  "pyo3-async-runtimes",
467
487
  "pyo3-build-config",
488
+ "pyo3-log",
468
489
  "tokio",
469
490
  ]
470
491
 
471
492
  [[package]]
472
493
  name = "ferogram-session"
473
494
  version = "0.6.4"
474
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
495
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
475
496
  dependencies = [
476
497
  "base64 0.22.1",
477
498
  "libsql",
@@ -484,7 +505,7 @@ dependencies = [
484
505
  [[package]]
485
506
  name = "ferogram-tl-gen"
486
507
  version = "0.6.4"
487
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
508
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
488
509
  dependencies = [
489
510
  "ferogram-tl-parser",
490
511
  ]
@@ -492,12 +513,12 @@ dependencies = [
492
513
  [[package]]
493
514
  name = "ferogram-tl-parser"
494
515
  version = "0.6.4"
495
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
516
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
496
517
 
497
518
  [[package]]
498
519
  name = "ferogram-tl-types"
499
520
  version = "0.6.4"
500
- source = "git+https://github.com/ankit-chaubey/ferogram?rev=f747bf418da2f8d9f6288cc5aa56875034c112f9#f747bf418da2f8d9f6288cc5aa56875034c112f9"
521
+ source = "git+https://github.com/ankit-chaubey/ferogram?rev=358ee50#358ee5013befb4a288d4363d81bda0e4e38ef7e1"
501
522
  dependencies = [
502
523
  "ferogram-tl-gen",
503
524
  "ferogram-tl-parser",
@@ -1423,6 +1444,17 @@ dependencies = [
1423
1444
  "pyo3-build-config",
1424
1445
  ]
1425
1446
 
1447
+ [[package]]
1448
+ name = "pyo3-log"
1449
+ version = "0.12.4"
1450
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1451
+ checksum = "45192e5e4a4d2505587e27806c7b710c231c40c56f3bfc19535d0bb25df52264"
1452
+ dependencies = [
1453
+ "arc-swap",
1454
+ "log",
1455
+ "pyo3",
1456
+ ]
1457
+
1426
1458
  [[package]]
1427
1459
  name = "pyo3-macros"
1428
1460
  version = "0.24.2"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "ferogram-py"
3
- version = "0.5.2"
3
+ version = "0.5.3"
4
4
  edition = "2024"
5
5
  description = "Python MTProto client based on ferogram (Rust MTProto)"
6
6
  build = "build.rs"
@@ -12,16 +12,19 @@ name = "_ferogram"
12
12
  crate-type = ["cdylib"]
13
13
 
14
14
  [dependencies]
15
- ferogram-mtsender = { git = "https://github.com/ankit-chaubey/ferogram", rev = "f747bf418da2f8d9f6288cc5aa56875034c112f9" }
16
- ferogram-session = { git = "https://github.com/ankit-chaubey/ferogram", rev = "f747bf418da2f8d9f6288cc5aa56875034c112f9", features = ["sqlite-session", "libsql-session"] }
17
- ferogram-connect = { git = "https://github.com/ankit-chaubey/ferogram", rev = "f747bf418da2f8d9f6288cc5aa56875034c112f9" }
18
- ferogram-tl-types = { git = "https://github.com/ankit-chaubey/ferogram", rev = "f747bf418da2f8d9f6288cc5aa56875034c112f9" }
19
- ferogram-crypto = { git = "https://github.com/ankit-chaubey/ferogram", rev = "f747bf418da2f8d9f6288cc5aa56875034c112f9" }
15
+ ferogram-mtsender = { git = "https://github.com/ankit-chaubey/ferogram", rev = "358ee50" }
16
+ ferogram-session = { git = "https://github.com/ankit-chaubey/ferogram", rev = "358ee50", features = ["sqlite-session", "libsql-session"] }
17
+ ferogram-connect = { git = "https://github.com/ankit-chaubey/ferogram", rev = "358ee50" }
18
+ ferogram-tl-types = { git = "https://github.com/ankit-chaubey/ferogram", rev = "358ee50" }
19
+ ferogram-crypto = { git = "https://github.com/ankit-chaubey/ferogram", rev = "358ee50" }
20
+ ferogram-msgbox = { git = "https://github.com/ankit-chaubey/ferogram", rev = "358ee50" }
20
21
  getrandom = "0.2"
21
22
  base64 = "0.22"
22
23
  pyo3 = { version = "0.24", features = ["extension-module", "abi3-py39"] }
23
24
  pyo3-async-runtimes = { version = "0.24", features = ["tokio-runtime"] }
24
25
  tokio = { version = "1", features = ["full"] }
26
+ pyo3-log = "0.12"
27
+ log = "0.4"
25
28
 
26
29
  [profile.release]
27
30
  strip = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ferogram
3
- Version: 0.5.2b2
3
+ Version: 0.5.3
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -10,7 +10,7 @@ Classifier: Topic :: Communications :: Chat
10
10
  Classifier: Topic :: Software Development :: Libraries
11
11
  License-File: LICENSE-APACHE
12
12
  License-File: LICENSE-MIT
13
- Summary: A modern, elegant, asynchronous MTProto framework for building Telegram user clients and bots in Python.
13
+ Summary: A modern, elegant, asynchronous MTProto framework for building Telegram clients and bots in Python.
14
14
  Keywords: telegram,mtproto,rust,client,bot,ferogram
15
15
  Author-email: Ankit Chaubey <ankitchaubey.dev@gmail.com>
16
16
  License: MIT OR Apache-2.0
@@ -35,7 +35,7 @@ from ._ferogram import (
35
35
  SqliteSession, LibSqlSession, CustomSession,
36
36
  )
37
37
 
38
- # All types now pure Python
38
+ # Types are pure Python
39
39
  from .types import User, Message, Chat, Dialog, ChatMember, UserFull
40
40
  from .types import Authorization, ForumTopic, BotInfo
41
41
  from .types import InviteLinkMember, ReadParticipant, AdminLogEvent