ferogram 0.2.2__tar.gz → 0.2.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 (97) hide show
  1. ferogram-0.2.3/CHANGELOG.md +65 -0
  2. {ferogram-0.2.2 → ferogram-0.2.3}/Cargo.lock +23 -23
  3. {ferogram-0.2.2 → ferogram-0.2.3}/Cargo.toml +2 -2
  4. {ferogram-0.2.2 → ferogram-0.2.3}/FEATURES.md +320 -26
  5. {ferogram-0.2.2 → ferogram-0.2.3}/PKG-INFO +1 -1
  6. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/__init__.py +6 -0
  7. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/client.py +305 -13
  8. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/types.py +23 -3
  9. {ferogram-0.2.2 → ferogram-0.2.3}/pyproject.toml +1 -1
  10. ferogram-0.2.3/src/auth.rs +222 -0
  11. {ferogram-0.2.2 → ferogram-0.2.3}/src/client.rs +421 -44
  12. ferogram-0.2.3/src/keyboards.rs +567 -0
  13. {ferogram-0.2.2 → ferogram-0.2.3}/src/lib.rs +8 -0
  14. {ferogram-0.2.2 → ferogram-0.2.3}/src/updates.rs +17 -0
  15. ferogram-0.2.2/src/auth.rs +0 -82
  16. {ferogram-0.2.2 → ferogram-0.2.3}/.github/workflows/publish.yml +0 -0
  17. {ferogram-0.2.2 → ferogram-0.2.3}/.gitignore +0 -0
  18. {ferogram-0.2.2 → ferogram-0.2.3}/LICENSE-APACHE +0 -0
  19. {ferogram-0.2.2 → ferogram-0.2.3}/LICENSE-MIT +0 -0
  20. {ferogram-0.2.2 → ferogram-0.2.3}/README.md +0 -0
  21. {ferogram-0.2.2 → ferogram-0.2.3}/examples/admin_tools.py +0 -0
  22. {ferogram-0.2.2 → ferogram-0.2.3}/examples/command_bot.py +0 -0
  23. {ferogram-0.2.2 → ferogram-0.2.3}/examples/echo_bot.py +0 -0
  24. {ferogram-0.2.2 → ferogram-0.2.3}/examples/group_management.py +0 -0
  25. {ferogram-0.2.2 → ferogram-0.2.3}/examples/media_bot.py +0 -0
  26. {ferogram-0.2.2 → ferogram-0.2.3}/examples/raw_invoke.py +0 -0
  27. {ferogram-0.2.2 → ferogram-0.2.3}/examples/search_bot.py +0 -0
  28. {ferogram-0.2.2 → ferogram-0.2.3}/examples/send_hi.py +0 -0
  29. {ferogram-0.2.2 → ferogram-0.2.3}/examples/send_media.py +0 -0
  30. {ferogram-0.2.2 → ferogram-0.2.3}/examples/send_message.py +0 -0
  31. {ferogram-0.2.2 → ferogram-0.2.3}/examples/update_handlers.py +0 -0
  32. {ferogram-0.2.2 → ferogram-0.2.3}/examples/user_management.py +0 -0
  33. {ferogram-0.2.2 → ferogram-0.2.3}/examples/userbot.py +0 -0
  34. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/filters.py +0 -0
  35. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/logging.py +0 -0
  36. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/py.typed +0 -0
  37. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/__init__.py +0 -0
  38. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/api/__init__.py +0 -0
  39. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/api/functions.py +0 -0
  40. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/api/types.py +0 -0
  41. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/codegen.py +0 -0
  42. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/__init__.py +0 -0
  43. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/_tl_schema.py +0 -0
  44. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/__init__.py +0 -0
  45. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/account.py +0 -0
  46. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/aicompose.py +0 -0
  47. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/auth.py +0 -0
  48. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/bots.py +0 -0
  49. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/channels.py +0 -0
  50. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/chatlists.py +0 -0
  51. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/contacts.py +0 -0
  52. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/folders.py +0 -0
  53. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/fragment.py +0 -0
  54. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/help.py +0 -0
  55. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/langpack.py +0 -0
  56. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/messages.py +0 -0
  57. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/payments.py +0 -0
  58. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/phone.py +0 -0
  59. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/photos.py +0 -0
  60. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/premium.py +0 -0
  61. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/smsjobs.py +0 -0
  62. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/stats.py +0 -0
  63. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/stickers.py +0 -0
  64. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/stories.py +0 -0
  65. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/updates.py +0 -0
  66. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/upload.py +0 -0
  67. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/functions/users.py +0 -0
  68. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/__init__.py +0 -0
  69. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/_base.py +0 -0
  70. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/account.py +0 -0
  71. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/aicompose.py +0 -0
  72. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/auth.py +0 -0
  73. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/bots.py +0 -0
  74. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/channels.py +0 -0
  75. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/chatlists.py +0 -0
  76. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/contacts.py +0 -0
  77. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/fragment.py +0 -0
  78. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/help.py +0 -0
  79. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/messages.py +0 -0
  80. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/payments.py +0 -0
  81. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/phone.py +0 -0
  82. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/photos.py +0 -0
  83. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/premium.py +0 -0
  84. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/smsjobs.py +0 -0
  85. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/stats.py +0 -0
  86. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/stickers.py +0 -0
  87. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/storage.py +0 -0
  88. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/stories.py +0 -0
  89. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/updates.py +0 -0
  90. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/upload.py +0 -0
  91. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/generated/types/users.py +0 -0
  92. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/proxy.py +0 -0
  93. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw/tl.py +0 -0
  94. {ferogram-0.2.2 → ferogram-0.2.3}/ferogram/raw_api.tl +0 -0
  95. {ferogram-0.2.2 → ferogram-0.2.3}/src/message.rs +0 -0
  96. {ferogram-0.2.2 → ferogram-0.2.3}/src/raw.rs +0 -0
  97. {ferogram-0.2.2 → ferogram-0.2.3}/src/types.rs +0 -0
@@ -0,0 +1,65 @@
1
+ # Changelog
2
+
3
+ ## 0.2.3 (2026-05-14)
4
+
5
+ ### Client Builder
6
+
7
+ Added 20 new kwargs to `Client(...)` so you can configure everything at construction time instead of touching internals.
8
+
9
+ Network options: `proxy` (SOCKS5 or MTProxy t.me link), `allow_ipv6`, `dc_addr`, `probe_transport`, `resilient_connect`.
10
+
11
+ Session backend: `session_string` and `in_memory` as alternatives to the default session file. Priority is `in_memory > session_string > file`.
12
+
13
+ Update handling: `catch_up`, `pfs`, `update_queue_capacity`, `update_overflow` (`"drop_oldest"` or `"drop_newest"`), `low_memory_mode`.
14
+
15
+ InitConnection identity: `device`, `system_version`, `app_version`, `lang_code`, `system_lang_code`, `lang_pack`.
16
+
17
+ Experimental: `allow_missing_channel_hash`, `auto_resolve_peers`.
18
+
19
+ ### Inline Bots
20
+
21
+ `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.
22
+
23
+ `answer_inline_query` now accepts `next_offset` for pagination and `switch_pm` to show a "go to PM" button in the results list.
24
+
25
+ `answer_inline_query_articles` is now accessible from Python. Takes a plain list of `(id, title, text)` tuples and supports `next_offset`.
26
+
27
+ `edit_inline_message` now accepts `reply_markup` so you can attach or update a keyboard when editing.
28
+
29
+ `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.
30
+
31
+ ### Participants
32
+
33
+ Ten new methods for managing group and channel members.
34
+
35
+ `get_participants(peer, limit=200)` fetches all members as a list of `ChatMember`.
36
+
37
+ `get_participants_filtered(peer, filter, limit)` fetches a filtered subset. Filter values: `"recent"`, `"admins"`, `"kicked"`, `"banned"`, `"bots"`.
38
+
39
+ `kick_participant(peer, user)` removes a user from the chat. They can rejoin if the chat is public or they have an invite link.
40
+
41
+ `ban_participant(peer, user)` permanently bans a user until an admin manually lifts it.
42
+
43
+ `ban_participant_until(peer, user, until_date)` bans until a unix timestamp, after which the ban lifts automatically.
44
+
45
+ `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"`.
46
+
47
+ `demote_participant(peer, user)` strips all admin rights, leaving the user as a regular member.
48
+
49
+ `get_profile_photos(peer, limit=100)` returns a list of `(file_id, access_hash, dc_id)` tuples.
50
+
51
+ `search_peer(query)` searches the local peer cache by name or username and returns a list of peer identifier strings.
52
+
53
+ `signal_network_restored()` (sync, not async) tells the client network is back so it attempts reconnect immediately instead of waiting for the retry timer.
54
+
55
+ ### Reactions and Polls
56
+
57
+ `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.
58
+
59
+ `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.
60
+
61
+ ---
62
+
63
+ ## 0.2.2
64
+
65
+ Previous release.
@@ -274,9 +274,9 @@ dependencies = [
274
274
 
275
275
  [[package]]
276
276
  name = "ferogram"
277
- version = "0.4.0"
277
+ version = "0.4.1"
278
278
  source = "registry+https://github.com/rust-lang/crates.io-index"
279
- checksum = "08d5a9e0d5162e8eb981904b15c41d7f3089640632d51a3ffcab7713c4df409b"
279
+ checksum = "0b72cbc599397f85947c77d7807db9b40baa047598ef17fb7b3425adf980ce48"
280
280
  dependencies = [
281
281
  "async-trait",
282
282
  "base64",
@@ -314,9 +314,9 @@ dependencies = [
314
314
 
315
315
  [[package]]
316
316
  name = "ferogram-connect"
317
- version = "0.4.0"
317
+ version = "0.4.1"
318
318
  source = "registry+https://github.com/rust-lang/crates.io-index"
319
- checksum = "8cdc49607faad2862b676b010b02e27d13ba2047d2253362cdf958c10efff92a"
319
+ checksum = "48614f5825a4e069d8daf80998d0bda2c02b7e92e6116c789054ceef82568fd1"
320
320
  dependencies = [
321
321
  "ferogram-crypto",
322
322
  "ferogram-mtproto",
@@ -334,9 +334,9 @@ dependencies = [
334
334
 
335
335
  [[package]]
336
336
  name = "ferogram-crypto"
337
- version = "0.4.0"
337
+ version = "0.4.1"
338
338
  source = "registry+https://github.com/rust-lang/crates.io-index"
339
- checksum = "d9df71920e40c6b6938df3ac0f4e85fe13968b48f0cbb0d0c06d98020384b02b"
339
+ checksum = "7e27eef45ed7600e3b93442602f1acbc82c86cbbd8be33395c64962333f4a9fe"
340
340
  dependencies = [
341
341
  "aes",
342
342
  "ctr",
@@ -349,9 +349,9 @@ dependencies = [
349
349
 
350
350
  [[package]]
351
351
  name = "ferogram-fsm"
352
- version = "0.4.0"
352
+ version = "0.4.1"
353
353
  source = "registry+https://github.com/rust-lang/crates.io-index"
354
- checksum = "751b3d201d60c38679fb363022bc65646144943b79bc17e37997d8738e2f5143"
354
+ checksum = "b06529ae56dbc45a50f10709572fa127c5c2a58132f4b1eaa90c826e51ae40a6"
355
355
  dependencies = [
356
356
  "async-trait",
357
357
  "dashmap",
@@ -362,9 +362,9 @@ dependencies = [
362
362
 
363
363
  [[package]]
364
364
  name = "ferogram-mtproto"
365
- version = "0.4.0"
365
+ version = "0.4.1"
366
366
  source = "registry+https://github.com/rust-lang/crates.io-index"
367
- checksum = "b4aa9b9d4bf5a80270e8ee81e65055c8ed7de9b4887df7d94de48d18564c692c"
367
+ checksum = "f14d5196b3f81974f7f990c822475354af4dd46a0d49c2d648e492c6084105a5"
368
368
  dependencies = [
369
369
  "ferogram-crypto",
370
370
  "ferogram-tl-types",
@@ -377,9 +377,9 @@ dependencies = [
377
377
 
378
378
  [[package]]
379
379
  name = "ferogram-mtsender"
380
- version = "0.4.0"
380
+ version = "0.4.1"
381
381
  source = "registry+https://github.com/rust-lang/crates.io-index"
382
- checksum = "c577e17c573111afe208b935c0a91ab51ab4df4040366010b3b4418d1fbdfecc"
382
+ checksum = "f884b7cf4ff1a380559b057f569d6d884ec74405fda6f97ba79265f42ab22b3b"
383
383
  dependencies = [
384
384
  "ferogram-connect",
385
385
  "ferogram-crypto",
@@ -397,9 +397,9 @@ dependencies = [
397
397
 
398
398
  [[package]]
399
399
  name = "ferogram-parsers"
400
- version = "0.4.0"
400
+ version = "0.4.1"
401
401
  source = "registry+https://github.com/rust-lang/crates.io-index"
402
- checksum = "2616847253ad2b3c90b18f18d8f9239275c295a378ed4cd24bddaec50df3550e"
402
+ checksum = "699accf665ae781947b041f9400f1a388ede032feb0a84c97247f1e6a79e1d31"
403
403
  dependencies = [
404
404
  "ferogram-tl-types",
405
405
  "pulldown-cmark",
@@ -407,7 +407,7 @@ dependencies = [
407
407
 
408
408
  [[package]]
409
409
  name = "ferogram-py"
410
- version = "0.2.2"
410
+ version = "0.2.3"
411
411
  dependencies = [
412
412
  "ferogram",
413
413
  "hex",
@@ -418,9 +418,9 @@ dependencies = [
418
418
 
419
419
  [[package]]
420
420
  name = "ferogram-session"
421
- version = "0.4.0"
421
+ version = "0.4.1"
422
422
  source = "registry+https://github.com/rust-lang/crates.io-index"
423
- checksum = "de8ac1fbffd7bb698fe3d78038edae17544220d2cf41fe583304efa61a551e94"
423
+ checksum = "672fed4cda1078020cdfe3b0a76408224ba4dc265b26093d02342777d68cfb5f"
424
424
  dependencies = [
425
425
  "base64",
426
426
  "tracing",
@@ -428,24 +428,24 @@ dependencies = [
428
428
 
429
429
  [[package]]
430
430
  name = "ferogram-tl-gen"
431
- version = "0.4.0"
431
+ version = "0.4.1"
432
432
  source = "registry+https://github.com/rust-lang/crates.io-index"
433
- checksum = "f9278407f5912be09141fa7827e39dd72851213ecf4b7c1462094a1012ffdef9"
433
+ checksum = "b2c5767cecedb0e1df8f7c53f4dc1bfe0a3b686eb89bf4da26fa2d610e04fe8e"
434
434
  dependencies = [
435
435
  "ferogram-tl-parser",
436
436
  ]
437
437
 
438
438
  [[package]]
439
439
  name = "ferogram-tl-parser"
440
- version = "0.4.0"
440
+ version = "0.4.1"
441
441
  source = "registry+https://github.com/rust-lang/crates.io-index"
442
- checksum = "a2a592a2437f71ddda9ba45971bb70019c16a4cf5dd9afe941a7125232db1ff8"
442
+ checksum = "9b655e882facfe8fb8326b4c63ac511089ce89f38c6b208111004256e2888483"
443
443
 
444
444
  [[package]]
445
445
  name = "ferogram-tl-types"
446
- version = "0.4.0"
446
+ version = "0.4.1"
447
447
  source = "registry+https://github.com/rust-lang/crates.io-index"
448
- checksum = "f1b23759e460988fda70e73ed25ae4bf633e3430c60f8fbe4620198536238bd7"
448
+ checksum = "ed947864a8bdea862a8336a691d957f2ab8dfd8d08063a90d77d0b70f753705f"
449
449
  dependencies = [
450
450
  "ferogram-tl-gen",
451
451
  "ferogram-tl-parser",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "ferogram-py"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  edition = "2024"
5
5
  description = "Python bindings for ferogram (Rust MTProto)"
6
6
  license = "MIT OR Apache-2.0"
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
12
12
 
13
13
  [dependencies]
14
14
  hex = "0.4"
15
- ferogram = "0.4.0"
15
+ ferogram = "0.4.1"
16
16
  pyo3 = { version = "0.24", features = ["extension-module", "abi3-py313"] }
17
17
  pyo3-async-runtimes = { version = "0.24", features = ["tokio-runtime"] }
18
18
  tokio = { version = "1", features = ["full"] }
@@ -2,7 +2,7 @@
2
2
 
3
3
  Python bindings for the ferogram MTProto client.
4
4
 
5
- All client methods are async. The `peer` parameter accepts "@username", "me", or a numeric ID (int or string).
5
+ All client methods are async. The `peer` parameter accepts `"@username"`, `"me"`, or a numeric ID (int or string).
6
6
 
7
7
 
8
8
  ## Imports
@@ -16,9 +16,12 @@ from ferogram import ChatAction
16
16
  from ferogram import InlineArticle, InlinePhoto, InlineDocument
17
17
  from ferogram import InlineMessageId
18
18
  from ferogram import PrivacyKey, PrivacyRule
19
+ from ferogram import InlineButton, InlineKeyboard
20
+ from ferogram import ReplyButton, ReplyKeyboard
21
+ from ferogram import RemoveKeyboard, ForceReply
19
22
  ```
20
23
 
21
- Raw API usage (four styles, see the [Raw API](https://github.com/ankit-chaubey/ferogram-py/edit/main/FEATURES.md#raw-api) section for details):
24
+ Raw API usage (four styles, see the [Raw API](#raw-api) section for details):
22
25
 
23
26
  ```python
24
27
  # style 1: namespace proxy, no extra import needed
@@ -59,6 +62,55 @@ async with app as client:
59
62
 
60
63
  Credentials can also come from env vars: `API_ID`, `API_HASH`, `BOT_TOKEN`.
61
64
 
65
+ ### Full init signature
66
+
67
+ These are all the kwargs you can pass to `Client(...)`. Most of them you will never need, but they are all there.
68
+
69
+ ```python
70
+ app = Client(
71
+ session="mybot", # session file name (no .session extension)
72
+ api_id=123456,
73
+ api_hash="abc...",
74
+ bot_token="123:TOKEN", # omit for userbot
75
+ phone="+1234567890", # for userbot interactive login
76
+ password="2fa_password", # 2FA password if set
77
+
78
+ # Network
79
+ proxy=None, # "socks5://host:port" or an MTProxy t.me link
80
+ allow_ipv6=False, # connect over IPv6 when available
81
+ dc_addr=None, # override DC address, e.g. "149.154.167.51:443"
82
+ probe_transport=False, # try multiple transports on first connect
83
+ resilient_connect=False, # keep retrying on initial connect failure
84
+
85
+ # Session backend (priority: in_memory > session_string > session file)
86
+ session_string=None, # base64 session string instead of file
87
+ in_memory=False, # ephemeral session, nothing written to disk
88
+
89
+ # Sync and updates
90
+ catch_up=False, # fetch missed updates on reconnect
91
+ pfs=False, # Perfect Forward Secrecy for the session
92
+ update_queue_capacity=None, # max pending updates before overflow kicks in
93
+ update_overflow=None, # "drop_oldest" or "drop_newest"
94
+ low_memory_mode=False, # smaller update queue, saves RAM on constrained devices
95
+
96
+ # InitConnection identity
97
+ device=None, # device model string sent in InitConnection
98
+ system_version=None, # OS version string
99
+ app_version=None, # app version string
100
+ lang_code=None, # IETF language tag, e.g. "en"
101
+ system_lang_code=None, # system locale tag
102
+ lang_pack=None, # language pack name
103
+
104
+ # Experimental
105
+ allow_missing_channel_hash=False, # skip access hash requirement for channels
106
+ auto_resolve_peers=False, # resolve unknown peers automatically
107
+ )
108
+ ```
109
+
110
+ `proxy` supports two formats:
111
+ - SOCKS5: `"socks5://host:port"`
112
+ - MTProxy: `"https://t.me/proxy?server=...&port=...&secret=..."`
113
+
62
114
 
63
115
  ## Event Handlers
64
116
 
@@ -90,8 +142,8 @@ Handler signature: `async def handler(client, update):`
90
142
 
91
143
  ## Filters
92
144
 
93
- Import:
94
- ```
145
+ Import:
146
+ ```python
95
147
  from ferogram import filters
96
148
  ```
97
149
 
@@ -185,9 +237,11 @@ filters.not_(f1)
185
237
  ## Messaging
186
238
 
187
239
  ```python
188
- await client.send_message(peer, text, parse_mode=None)
240
+ await client.send_message(peer, text, parse_mode=None, reply_markup=None)
189
241
  # parse_mode: None (plain) | "html" | "markdown"
190
- # Note: "markdown" uses MarkdownV2 format since ferogram 0.3.9.
242
+ # reply_markup: InlineKeyboard, ReplyKeyboard, RemoveKeyboard, or ForceReply
243
+ #
244
+ # "markdown" uses MarkdownV2 format since ferogram 0.3.9.
191
245
  # __text__ = Underline (was Italic in V1)
192
246
  # ~text~ = Strike (was ~~text~~ in V1)
193
247
  # > text = Blockquote (new)
@@ -208,6 +262,7 @@ await client.pin_message(peer, message_id)
208
262
  await client.unpin_message(peer, message_id)
209
263
  await client.unpin_all_messages(peer)
210
264
  await client.get_messages_by_id(peer, [id1, id2])
265
+ await client.get_message(peer, msg_id) # -> Message | None (single fetch)
211
266
  await client.get_message_history(peer, limit=100, offset_id=0)
212
267
  await client.get_pinned_message(peer) # -> Message | None
213
268
  await client.get_reply_to_message(peer, msg_id) # -> Message | None
@@ -217,12 +272,14 @@ await client.send_reaction(peer, message_id, emoji)
217
272
  await client.read_reactions(peer)
218
273
  await client.clear_recent_reactions()
219
274
  await client.get_reaction_list(peer, msg_id, limit=100) # -> [(peer_id, emoji)]
220
- await client.delete_reaction(peer, msg_id, participant) # report/remove a user's reaction
275
+ await client.delete_reaction(peer, msg_id, participant) # remove a specific user's reaction (admin only)
276
+ async for reaction in client.iter_reaction_users(peer, msg_id, reaction=None):
277
+ ... # yields reaction objects
221
278
  await client.mark_as_read(peer)
222
279
  await client.clear_mentions(peer)
223
280
  await client.send_chat_action(peer, "typing") # or ChatAction.TYPING
224
281
  await client.send_dice(peer, emoticon="🎲")
225
- await client.translate_messages(peer, [msg_id], to_lang="en")
282
+ await client.translate_messages(peer, [msg_id], to_lang="en") # -> [str]
226
283
  await client.get_web_page_preview(text) # -> url | None
227
284
  ```
228
285
 
@@ -245,6 +302,55 @@ await message.react(emoji)
245
302
  `is_forwarded`, `post_author`, `view_count`, `reply_count`
246
303
 
247
304
 
305
+ ## Keyboards
306
+
307
+ Build and attach keyboards to messages.
308
+
309
+ ```python
310
+ from ferogram import InlineButton, InlineKeyboard
311
+ from ferogram import ReplyButton, ReplyKeyboard
312
+ from ferogram import RemoveKeyboard, ForceReply
313
+ ```
314
+
315
+ **Inline keyboard**
316
+
317
+ ```python
318
+ kb = InlineKeyboard([
319
+ [InlineButton("Click me", data="btn1"), InlineButton("Other", data="btn2")],
320
+ [InlineButton("Open URL", url="https://example.com")],
321
+ ])
322
+
323
+ await client.send_message(peer, "Pick one:", reply_markup=kb)
324
+ ```
325
+
326
+ `InlineButton` takes `text` (required) and one of `data`, `url`, `switch_inline_query`, or `switch_inline_query_current_chat`.
327
+
328
+ **Reply keyboard**
329
+
330
+ ```python
331
+ kb = ReplyKeyboard([
332
+ [ReplyButton("Option A"), ReplyButton("Option B")],
333
+ [ReplyButton("Cancel")],
334
+ ], resize=True, one_time=True)
335
+
336
+ await client.send_message(peer, "Choose:", reply_markup=kb)
337
+ ```
338
+
339
+ **Remove keyboard**
340
+
341
+ ```python
342
+ await client.send_message(peer, "Done.", reply_markup=RemoveKeyboard())
343
+ ```
344
+
345
+ **Force reply**
346
+
347
+ ```python
348
+ await client.send_message(peer, "Reply to this:", reply_markup=ForceReply())
349
+ ```
350
+
351
+ `reply_markup` works on `send_message`, `send_photo`, `send_document`, `send_file`, and `edit_message`.
352
+
353
+
248
354
  ## Media
249
355
 
250
356
  ```python
@@ -276,29 +382,178 @@ await client.send_poll(
276
382
  )
277
383
  await client.send_vote(peer, msg_id, options=[b"\x00"])
278
384
  await client.get_poll_votes(peer, msg_id, limit=100) # -> [(user_id, option_bytes)]
279
- await client.get_poll_results(peer, msg_id, poll_hash)
280
- await client.get_poll_stats(peer, msg_id) # -> views count (int)
281
- await client.delete_reaction(peer, msg_id, participant) # report/remove a user's reaction
385
+ await client.get_poll_results(peer, msg_id, poll_hash) # fetch and cache latest results
386
+ await client.get_poll_stats(peer, msg_id) # -> int (interaction count)
282
387
  ```
283
388
 
284
389
 
285
390
  ## Inline Bots
286
391
 
392
+ ### Answering callback queries
393
+
287
394
  ```python
288
395
  await client.answer_callback_query(query_id, text=None, alert=False)
396
+ ```
397
+
398
+ ### Answering inline queries
289
399
 
400
+ Use `InlineArticle`, `InlinePhoto`, or `InlineDocument` to build results and pass them to `answer_inline_query`.
401
+
402
+ ```python
290
403
  from ferogram import InlineArticle, InlinePhoto, InlineDocument
291
404
 
292
- await client.answer_inline_query(query_id, [
293
- InlineArticle(id="1", title="Title", message_text="Hello!"),
294
- InlinePhoto(id="2", title="Cat", message_text="🐱", thumb_url="..."),
295
- InlineDocument(id="3", title="File", message_text="...", url="...", mime_type="application/pdf"),
296
- ], cache_time=300, is_personal=False, next_offset=None)
405
+ await client.answer_inline_query(
406
+ query_id,
407
+ results=[...],
408
+ cache_time=300,
409
+ is_personal=False,
410
+ next_offset=None, # pass a string to enable pagination
411
+ switch_pm=None, # ("Button text", "start_param") to show a "go to PM" button
412
+ )
413
+ ```
414
+
415
+ **InlineArticle**
416
+
417
+ ```python
418
+ InlineArticle(
419
+ id="1",
420
+ title="My Article",
421
+ message_text="Text sent when user picks this result.",
422
+ description=None, # subtitle shown in the result list
423
+ url=None, # URL shown under the title
424
+ thumb_url=None, # thumbnail URL
425
+ reply_markup=None, # InlineKeyboard attached to the sent message
426
+ )
427
+ ```
428
+
429
+ **InlinePhoto**
430
+
431
+ ```python
432
+ InlinePhoto(
433
+ id="2",
434
+ title="A Photo",
435
+ message_text="Caption for the photo.",
436
+ photo_url="https://example.com/photo.jpg", # required, direct image URL
437
+ photo_width=0, # optional dimensions hint
438
+ photo_height=0,
439
+ description=None,
440
+ thumb_url=None,
441
+ mime_type="image/jpeg",
442
+ reply_markup=None,
443
+ )
444
+ ```
445
+
446
+ **InlineDocument**
447
+
448
+ ```python
449
+ InlineDocument(
450
+ id="3",
451
+ title="A File",
452
+ message_text="Here is the file.",
453
+ document_url="https://example.com/file.pdf", # required, direct file URL
454
+ mime_type="application/pdf", # required
455
+ description=None,
456
+ thumb_url=None,
457
+ reply_markup=None,
458
+ )
459
+ ```
460
+
461
+ **Simple articles shortcut**
462
+
463
+ If you only need plain text articles with no extra fields, there is a lighter helper:
464
+
465
+ ```python
466
+ await client.answer_inline_query_articles(
467
+ query_id,
468
+ articles=[
469
+ ("id1", "Title One", "Message text one"),
470
+ ("id2", "Title Two", "Message text two"),
471
+ ],
472
+ cache_time=300,
473
+ is_personal=False,
474
+ next_offset=None,
475
+ )
476
+ ```
297
477
 
478
+ ### Editing inline messages
479
+
480
+ After answering an inline query, you can edit the message later using the ID from `InlineSend`.
481
+
482
+ ```python
298
483
  from ferogram import InlineMessageId
299
- await client.edit_inline_message(InlineMessageId(dc_id=2, id_bytes=b"..."), "new text")
484
+
485
+ await client.edit_inline_message(
486
+ InlineMessageId(dc_id=2, id_bytes=b"..."),
487
+ "updated text",
488
+ reply_markup=None, # optionally attach or update the keyboard
489
+ )
490
+ ```
491
+
492
+ To get the `InlineMessageId` from a send event:
493
+
494
+ ```python
495
+ @app.on_inline_send()
496
+ async def handler(client, send):
497
+ # send.user_id int
498
+ # send.query str
499
+ # send.result_id str
500
+ # send.msg_id_bytes bytes | None (raw serialized InputBotInlineMessageId)
501
+ # send.msg_id_dc int | None (DC where the message lives)
502
+
503
+ if send.msg_id_bytes:
504
+ msg_id = InlineMessageId(send.msg_id_dc, bytes(send.msg_id_bytes))
505
+ await client.edit_inline_message(msg_id, "updated!")
506
+ ```
507
+
508
+ `msg_id_bytes` and `msg_id_dc` are `None` when the bot did not request inline feedback for this query (i.e. the result was sent without enabling `@BotFather` inline feedback).
509
+
510
+
511
+ ## Participants
512
+
513
+ These methods work on groups, supergroups, and channels.
514
+
515
+ ```python
516
+ await client.get_participants(peer, limit=200)
517
+ # -> list[ChatMember]
518
+
519
+ await client.get_participants_filtered(peer, filter="recent", limit=200)
520
+ # filter: "recent" | "admins" | "kicked" | "banned" | "bots"
521
+ # -> list[ChatMember]
522
+
523
+ await client.kick_participant(peer, user)
524
+ # Removes the user from the chat. They can rejoin if the chat is public or they have an invite link.
525
+
526
+ await client.ban_participant(peer, user)
527
+ # Permanently bans until manually lifted.
528
+
529
+ await client.ban_participant_until(peer, user, until_date)
530
+ # until_date is a unix timestamp. The ban lifts automatically after that time.
531
+
532
+ await client.promote_participant(peer, user, rights=None)
533
+ # rights is a list of permission strings. Pass None to grant all admin rights.
534
+ # Valid strings: "change_info", "post_messages", "edit_messages",
535
+ # "delete_messages", "ban_users", "invite_users",
536
+ # "pin_messages", "add_admins", "manage_call"
537
+
538
+ await client.demote_participant(peer, user)
539
+ # Removes all admin rights. The user stays as a regular member.
540
+
541
+ await client.get_profile_photos(peer, limit=100)
542
+ # -> list of (file_id, access_hash, dc_id) tuples
543
+
544
+ await client.search_peer(query)
545
+ # Search the local peer cache by name or username.
546
+ # -> list[str] of matching peer identifiers
547
+
548
+ client.signal_network_restored()
549
+ # Not async. Call this when you know network is back to trigger an immediate
550
+ # reconnect attempt instead of waiting for the retry timer.
300
551
  ```
301
552
 
553
+ ### ChatMember fields
554
+
555
+ `user_id`, `first_name`, `last_name`, `username`, `bot`, `status`, `admin_rank`, `full_name`
556
+
302
557
 
303
558
  ## Chats & Groups
304
559
 
@@ -332,10 +587,6 @@ await client.get_pinned_dialogs(folder_id=0) # 0=main, 1=archive
332
587
  await client.mark_dialog_read(peer)
333
588
  ```
334
589
 
335
- ### ChatMember fields
336
-
337
- `user_id`, `first_name`, `last_name`, `username`, `bot`, `status`, `admin_rank`, `full_name`
338
-
339
590
 
340
591
  ## Forum Topics
341
592
 
@@ -355,6 +606,35 @@ await client.all_join_requests(peer, approve=True, link=None)
355
606
  ```
356
607
 
357
608
 
609
+ ## Invite Links
610
+
611
+ ```python
612
+ await client.invite_links(peer) # -> [ExportedChatInvite]
613
+ await client.invite_links(peer, primary_only=True) # -> ExportedChatInvite (primary link)
614
+ await client.invite_links(peer, revoked=True) # -> [ExportedChatInvite] (revoked links)
615
+
616
+ async for link in client.iter_invite_links(peer, revoked=False):
617
+ ... # yields ExportedChatInvite objects
618
+
619
+ async for member in client.iter_invite_link_members(peer, link, requested=False):
620
+ ... # yields ChatInviteImporter objects
621
+
622
+ await client.edit_invite_link(
623
+ peer, link,
624
+ expire_date=None, # unix timestamp; None = no expiry
625
+ usage_limit=None, # None = unlimited
626
+ request_needed=None, # require admin approval to join
627
+ title=None,
628
+ )
629
+ await client.revoke_invite_link(peer, link) # -> revoked ExportedChatInvite
630
+ await client.delete_invite_link(peer, link) # delete a revoked link permanently
631
+ await client.clear_revoked_invite_links(peer) # delete all revoked links
632
+
633
+ await client.resolve_invite_link(link) # peek without joining -> ChatInvite
634
+ await client.join_invite_link(link) # join and return InputPeer
635
+ ```
636
+
637
+
358
638
  ## Account & Profile
359
639
 
360
640
  ```python
@@ -362,6 +642,10 @@ await client.get_me() # -> User
362
642
  await client.get_users_by_id([user_id, ...]) # -> [User | None]
363
643
  await client.get_user_full(user_id) # -> UserFull
364
644
  await client.get_dialogs(limit=100) # -> [Dialog]
645
+ async for dialog in client.iter_dialogs(limit=None):
646
+ ... # yields Dialog objects
647
+ async for msg in client.iter_messages(peer, limit=None, offset_id=0):
648
+ ... # yields Message objects, newest first
365
649
  await client.set_profile(first_name=None, last_name=None, about=None)
366
650
  await client.set_username(username)
367
651
  await client.set_online()
@@ -432,7 +716,9 @@ await client.set_privacy(PrivacyKey.PHONE_NUMBER, PrivacyRule.ALLOW_CONTACTS)
432
716
  ## Sessions & Auth
433
717
 
434
718
  ```python
435
- await client.get_authorizations()
719
+ await client.is_authorized() # -> bool
720
+ await client.login_bot(token) # sign in as bot after start()
721
+ await client.get_authorizations() # -> [Authorization]
436
722
  await client.terminate_session(hash)
437
723
 
438
724
  # QR login
@@ -455,10 +741,10 @@ await client.open_mini_app(peer, app_type="main", app_value="") # -> MiniAppSe
455
741
  ## Stats
456
742
 
457
743
  ```python
458
- await client.get_broadcast_stats(peer)
459
- await client.get_megagroup_stats(peer)
744
+ await client.get_broadcast_stats(peer) # -> BroadcastStats
745
+ await client.get_megagroup_stats(peer) # -> MegagroupStats
460
746
  await client.get_game_high_scores(peer, msg_id, user_id) # -> [(position, user_id, score)]
461
- await client.get_poll_stats(peer, msg_id) # -> views count (int)
747
+ await client.get_poll_stats(peer, msg_id) # -> int (interaction count)
462
748
  ```
463
749
 
464
750
 
@@ -475,6 +761,15 @@ await client.send_invoice(
475
761
  ```
476
762
 
477
763
 
764
+ ## Custom Emoji
765
+
766
+ ```python
767
+ await client.get_custom_emoji_documents(document_ids=[...])
768
+ # Pass a list of custom emoji document IDs.
769
+ # Returns the subset of IDs that resolved successfully.
770
+ ```
771
+
772
+
478
773
  ## Peer Resolution
479
774
 
480
775
  ```python
@@ -640,4 +935,3 @@ ChatAction.RECORD_ROUND
640
935
  ChatAction.UPLOAD_ROUND
641
936
  ChatAction.CANCEL
642
937
  ```
643
-