spluspy 2.1.0__tar.gz → 2.1.2__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 (307) hide show
  1. {spluspy-2.1.0 → spluspy-2.1.2}/CHANGELOG.md +5 -0
  2. {spluspy-2.1.0 → spluspy-2.1.2}/PKG-INFO +1 -1
  3. {spluspy-2.1.0 → spluspy-2.1.2}/pyproject.toml +1 -1
  4. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/__version__.py +1 -1
  5. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/version.py +1 -1
  6. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/chat_mixin.py +466 -0
  7. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/client.py +143 -1
  8. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/config.py +2 -2
  9. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/sync/client.py +3 -0
  10. {spluspy-2.1.0 → spluspy-2.1.2}/.gitignore +0 -0
  11. {spluspy-2.1.0 → spluspy-2.1.2}/CONTRIBUTING.md +0 -0
  12. {spluspy-2.1.0 → spluspy-2.1.2}/Dockerfile +0 -0
  13. {spluspy-2.1.0 → spluspy-2.1.2}/LICENSE +0 -0
  14. {spluspy-2.1.0 → spluspy-2.1.2}/MANIFEST.in +0 -0
  15. {spluspy-2.1.0 → spluspy-2.1.2}/README.md +0 -0
  16. {spluspy-2.1.0 → spluspy-2.1.2}/README.rst +0 -0
  17. {spluspy-2.1.0 → spluspy-2.1.2}/examples/01_hello_bot.py +0 -0
  18. {spluspy-2.1.0 → spluspy-2.1.2}/examples/02_echo_bot.py +0 -0
  19. {spluspy-2.1.0 → spluspy-2.1.2}/examples/03_command_bot.py +0 -0
  20. {spluspy-2.1.0 → spluspy-2.1.2}/examples/04_private_only.py +0 -0
  21. {spluspy-2.1.0 → spluspy-2.1.2}/examples/05_group_only.py +0 -0
  22. {spluspy-2.1.0 → spluspy-2.1.2}/examples/06_regex_bot.py +0 -0
  23. {spluspy-2.1.0 → spluspy-2.1.2}/examples/07_user_filter.py +0 -0
  24. {spluspy-2.1.0 → spluspy-2.1.2}/examples/08_combined_filters.py +0 -0
  25. {spluspy-2.1.0 → spluspy-2.1.2}/examples/09_inline_keyboard.py +0 -0
  26. {spluspy-2.1.0 → spluspy-2.1.2}/examples/10_reply_keyboard.py +0 -0
  27. {spluspy-2.1.0 → spluspy-2.1.2}/examples/11_message_actions.py +0 -0
  28. {spluspy-2.1.0 → spluspy-2.1.2}/examples/12_media_bot.py +0 -0
  29. {spluspy-2.1.0 → spluspy-2.1.2}/examples/13_fsm_bot.py +0 -0
  30. {spluspy-2.1.0 → spluspy-2.1.2}/examples/14_plugin_bot.py +0 -0
  31. {spluspy-2.1.0 → spluspy-2.1.2}/examples/15_middleware_bot.py +0 -0
  32. {spluspy-2.1.0 → spluspy-2.1.2}/examples/16_scheduler_bot.py +0 -0
  33. {spluspy-2.1.0 → spluspy-2.1.2}/examples/17_conversation.py +0 -0
  34. {spluspy-2.1.0 → spluspy-2.1.2}/examples/18_chat_management.py +0 -0
  35. {spluspy-2.1.0 → spluspy-2.1.2}/examples/19_event_types.py +0 -0
  36. {spluspy-2.1.0 → spluspy-2.1.2}/examples/20_sync_bot.py +0 -0
  37. {spluspy-2.1.0 → spluspy-2.1.2}/examples/21_album_handler.py +0 -0
  38. {spluspy-2.1.0 → spluspy-2.1.2}/examples/22_download_bot.py +0 -0
  39. {spluspy-2.1.0 → spluspy-2.1.2}/examples/23_url_button.py +0 -0
  40. {spluspy-2.1.0 → spluspy-2.1.2}/examples/24_string_session.py +0 -0
  41. {spluspy-2.1.0 → spluspy-2.1.2}/examples/25_error_handling.py +0 -0
  42. {spluspy-2.1.0 → spluspy-2.1.2}/examples/25_working_bot.py +0 -0
  43. {spluspy-2.1.0 → spluspy-2.1.2}/examples/26_message_entities.py +0 -0
  44. {spluspy-2.1.0 → spluspy-2.1.2}/examples/27_search_messages.py +0 -0
  45. {spluspy-2.1.0 → spluspy-2.1.2}/examples/28_poll_bot.py +0 -0
  46. {spluspy-2.1.0 → spluspy-2.1.2}/examples/29_dice_bot.py +0 -0
  47. {spluspy-2.1.0 → spluspy-2.1.2}/examples/30_cache_bot.py +0 -0
  48. {spluspy-2.1.0 → spluspy-2.1.2}/examples/31_reaction_bot.py +0 -0
  49. {spluspy-2.1.0 → spluspy-2.1.2}/examples/32_mark_read_bot.py +0 -0
  50. {spluspy-2.1.0 → spluspy-2.1.2}/examples/33_context_manager.py +0 -0
  51. {spluspy-2.1.0 → spluspy-2.1.2}/examples/34_user_info.py +0 -0
  52. {spluspy-2.1.0 → spluspy-2.1.2}/examples/35_chat_info.py +0 -0
  53. {spluspy-2.1.0 → spluspy-2.1.2}/examples/36_forward_copy.py +0 -0
  54. {spluspy-2.1.0 → spluspy-2.1.2}/examples/37_admin_bot.py +0 -0
  55. {spluspy-2.1.0 → spluspy-2.1.2}/examples/38_media_filters.py +0 -0
  56. {spluspy-2.1.0 → spluspy-2.1.2}/examples/39_status_bot.py +0 -0
  57. {spluspy-2.1.0 → spluspy-2.1.2}/examples/40_delete_detection.py +0 -0
  58. {spluspy-2.1.0 → spluspy-2.1.2}/examples/41_read_detection.py +0 -0
  59. {spluspy-2.1.0 → spluspy-2.1.2}/examples/42_raw_events.py +0 -0
  60. {spluspy-2.1.0 → spluspy-2.1.2}/examples/43_poll_handler.py +0 -0
  61. {spluspy-2.1.0 → spluspy-2.1.2}/examples/44_reaction_handler.py +0 -0
  62. {spluspy-2.1.0 → spluspy-2.1.2}/examples/45_typing_handler.py +0 -0
  63. {spluspy-2.1.0 → spluspy-2.1.2}/examples/46_get_messages.py +0 -0
  64. {spluspy-2.1.0 → spluspy-2.1.2}/examples/47_iter_messages.py +0 -0
  65. {spluspy-2.1.0 → spluspy-2.1.2}/examples/48_get_members.py +0 -0
  66. {spluspy-2.1.0 → spluspy-2.1.2}/examples/49_resolve_username.py +0 -0
  67. {spluspy-2.1.0 → spluspy-2.1.2}/examples/50_invoke.py +0 -0
  68. {spluspy-2.1.0 → spluspy-2.1.2}/setup.py +0 -0
  69. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/__init__.py +0 -0
  70. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/__init__.py +0 -0
  71. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/__init__.py +0 -0
  72. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/entitycache.py +0 -0
  73. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/messagebox.py +0 -0
  74. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/session.py +0 -0
  75. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/__init__.py +0 -0
  76. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/account.py +0 -0
  77. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/auth.py +0 -0
  78. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/bots.py +0 -0
  79. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/buttons.py +0 -0
  80. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/chats.py +0 -0
  81. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/dialogs.py +0 -0
  82. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/downloads.py +0 -0
  83. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/messageparse.py +0 -0
  84. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/messages.py +0 -0
  85. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/soroushclient.py +0 -0
  86. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/telegrambaseclient.py +0 -0
  87. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/updates.py +0 -0
  88. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/uploads.py +0 -0
  89. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/users.py +0 -0
  90. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/__init__.py +0 -0
  91. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/aes.py +0 -0
  92. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/aesctr.py +0 -0
  93. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/authkey.py +0 -0
  94. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/cdndecrypter.py +0 -0
  95. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/factorization.py +0 -0
  96. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/fast_aes.pyx +0 -0
  97. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/fast_rsa.pyx +0 -0
  98. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/libssl.py +0 -0
  99. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/rsa.py +0 -0
  100. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/custom.py +0 -0
  101. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/__init__.py +0 -0
  102. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/common.py +0 -0
  103. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/rpcbaseerrors.py +0 -0
  104. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/rpcerrorlist.py +0 -0
  105. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/__init__.py +0 -0
  106. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/album.py +0 -0
  107. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/callbackquery.py +0 -0
  108. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/chataction.py +0 -0
  109. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/common.py +0 -0
  110. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/inlinequery.py +0 -0
  111. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/messagedeleted.py +0 -0
  112. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/messageedited.py +0 -0
  113. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/messageread.py +0 -0
  114. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/newmessage.py +0 -0
  115. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/raw.py +0 -0
  116. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/userupdate.py +0 -0
  117. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/__init__.py +0 -0
  118. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/binaryreader.py +0 -0
  119. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/html.py +0 -0
  120. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/markdown.py +0 -0
  121. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/messagepacker.py +0 -0
  122. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/functions.py +0 -0
  123. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/helpers.py +0 -0
  124. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/hints.py +0 -0
  125. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/__init__.py +0 -0
  126. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/authenticator.py +0 -0
  127. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/__init__.py +0 -0
  128. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/connection.py +0 -0
  129. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/http.py +0 -0
  130. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpabridged.py +0 -0
  131. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpfull.py +0 -0
  132. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpintermediate.py +0 -0
  133. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpmtproxy.py +0 -0
  134. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpobfuscated.py +0 -0
  135. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/websocket.py +0 -0
  136. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/mtprotoplainsender.py +0 -0
  137. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/mtprotosender.py +0 -0
  138. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/mtprotostate.py +0 -0
  139. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/requeststate.py +0 -0
  140. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/password.py +0 -0
  141. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/requestiter.py +0 -0
  142. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/__init__.py +0 -0
  143. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/abstract.py +0 -0
  144. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/memory.py +0 -0
  145. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/sqlite.py +0 -0
  146. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/string.py +0 -0
  147. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sync.py +0 -0
  148. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/__init__.py +0 -0
  149. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/alltlobjects.py +0 -0
  150. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/__init__.py +0 -0
  151. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/gzippacked.py +0 -0
  152. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/messagecontainer.py +0 -0
  153. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/rpcresult.py +0 -0
  154. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/tlmessage.py +0 -0
  155. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/__init__.py +0 -0
  156. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/adminlogevent.py +0 -0
  157. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/button.py +0 -0
  158. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/chatgetter.py +0 -0
  159. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/conversation.py +0 -0
  160. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/dialog.py +0 -0
  161. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/draft.py +0 -0
  162. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/file.py +0 -0
  163. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/forward.py +0 -0
  164. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inlinebuilder.py +0 -0
  165. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inlineresult.py +0 -0
  166. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inlineresults.py +0 -0
  167. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inputsizedfile.py +0 -0
  168. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/message.py +0 -0
  169. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/messagebutton.py +0 -0
  170. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/participantpermissions.py +0 -0
  171. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/qrlogin.py +0 -0
  172. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/sendergetter.py +0 -0
  173. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/types.py +0 -0
  174. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/__init__.py +0 -0
  175. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/account.py +0 -0
  176. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/auth.py +0 -0
  177. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/bots.py +0 -0
  178. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/channels.py +0 -0
  179. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/chatlists.py +0 -0
  180. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/conference.py +0 -0
  181. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/contacts.py +0 -0
  182. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/folders.py +0 -0
  183. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/help.py +0 -0
  184. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/langpack.py +0 -0
  185. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/messages.py +0 -0
  186. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/payments.py +0 -0
  187. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/phone.py +0 -0
  188. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/photos.py +0 -0
  189. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/premium.py +0 -0
  190. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/stats.py +0 -0
  191. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/stories.py +0 -0
  192. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/thirdParty.py +0 -0
  193. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/updates.py +0 -0
  194. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/upload.py +0 -0
  195. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/users.py +0 -0
  196. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/patched/__init__.py +0 -0
  197. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/tlobject.py +0 -0
  198. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/__init__.py +0 -0
  199. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/account.py +0 -0
  200. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/auth.py +0 -0
  201. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/bots.py +0 -0
  202. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/channels.py +0 -0
  203. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/chatlists.py +0 -0
  204. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/conference.py +0 -0
  205. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/contacts.py +0 -0
  206. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/help.py +0 -0
  207. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/messages.py +0 -0
  208. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/payments.py +0 -0
  209. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/phone.py +0 -0
  210. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/photos.py +0 -0
  211. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/premium.py +0 -0
  212. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/stats.py +0 -0
  213. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/stickers.py +0 -0
  214. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/storage.py +0 -0
  215. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/stories.py +0 -0
  216. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/thirdParty.py +0 -0
  217. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/update.py +0 -0
  218. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/updates.py +0 -0
  219. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/upload.py +0 -0
  220. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/users.py +0 -0
  221. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/types.py +0 -0
  222. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/utils.py +0 -0
  223. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/admin.py +0 -0
  224. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/afk.py +0 -0
  225. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/cli.py +0 -0
  226. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/__init__.py +0 -0
  227. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/conversation.py +0 -0
  228. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/media_mixin.py +0 -0
  229. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/compat.py +0 -0
  230. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/errors/__init__.py +0 -0
  231. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/errors/exceptions.py +0 -0
  232. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/__init__.py +0 -0
  233. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/album.py +0 -0
  234. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/base.py +0 -0
  235. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/callback.py +0 -0
  236. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/chat_action.py +0 -0
  237. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/edited.py +0 -0
  238. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/inline.py +0 -0
  239. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/message.py +0 -0
  240. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/message_deleted.py +0 -0
  241. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/message_read.py +0 -0
  242. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/user_update.py +0 -0
  243. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/filters/__init__.py +0 -0
  244. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/filters/filters.py +0 -0
  245. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/fsm/__init__.py +0 -0
  246. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/fsm/state.py +0 -0
  247. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/middleware/__init__.py +0 -0
  248. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/middleware/base.py +0 -0
  249. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/middleware/rate_limit.py +0 -0
  250. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/mirror.py +0 -0
  251. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/__init__.py +0 -0
  252. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/bot.py +0 -0
  253. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/chat.py +0 -0
  254. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/enums.py +0 -0
  255. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/media.py +0 -0
  256. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/message.py +0 -0
  257. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/objects.py +0 -0
  258. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/user.py +0 -0
  259. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/__init__.py +0 -0
  260. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/client.py +0 -0
  261. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/connection.py +0 -0
  262. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/http_client.py +0 -0
  263. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/mtproto.py +0 -0
  264. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/protocols.py +0 -0
  265. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/spluspy_adapter.py +0 -0
  266. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/transport.py +0 -0
  267. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/plugins/__init__.py +0 -0
  268. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/plugins/loader.py +0 -0
  269. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/py.typed +0 -0
  270. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/scheduler/__init__.py +0 -0
  271. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/scheduler/scheduler.py +0 -0
  272. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/__init__.py +0 -0
  273. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/base.py +0 -0
  274. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/memory.py +0 -0
  275. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/sqlite.py +0 -0
  276. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/string.py +0 -0
  277. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/__init__.py +0 -0
  278. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/base.py +0 -0
  279. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/entity_cache.py +0 -0
  280. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/memory.py +0 -0
  281. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/postgres.py +0 -0
  282. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/redis.py +0 -0
  283. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/sqlite.py +0 -0
  284. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/sync/__init__.py +0 -0
  285. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/__init__.py +0 -0
  286. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/batch.py +0 -0
  287. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/cache.py +0 -0
  288. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/chat_lock.py +0 -0
  289. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/file_transfer.py +0 -0
  290. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/helpers.py +0 -0
  291. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/logger.py +0 -0
  292. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/rate_limiter.py +0 -0
  293. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/resolver.py +0 -0
  294. {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/version_check.py +0 -0
  295. {spluspy-2.1.0 → spluspy-2.1.2}/tests/__init__.py +0 -0
  296. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_client.py +0 -0
  297. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_filters.py +0 -0
  298. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_fsm.py +0 -0
  299. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_fsm_enhanced.py +0 -0
  300. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_middleware.py +0 -0
  301. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_rate_limiter.py +0 -0
  302. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_resolver.py +0 -0
  303. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_scheduler.py +0 -0
  304. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_session.py +0 -0
  305. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_storage.py +0 -0
  306. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_types.py +0 -0
  307. {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_utils.py +0 -0
@@ -5,6 +5,11 @@ All notable changes to SplusPy will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [2.1.1] - 2026-07-12
9
+
10
+ ### Changed
11
+ - Version bump to 2.1.1
12
+
8
13
  ## [2.1.0] - 2026-07-12
9
14
 
10
15
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spluspy
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: A modern async Python library for interacting with Soroush Plus
5
5
  Project-URL: Homepage, https://github.com/Itskillmaster/spluspy
6
6
  Project-URL: Documentation, https://github.com/Itskillmaster/spluspy#readme
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "spluspy"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "A modern async Python library for interacting with Soroush Plus"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  """SplusPy version information."""
2
2
 
3
- __version__ = "2.1.0"
3
+ __version__ = "2.1.2"
4
4
  __author__ = "Ali Mirshekari"
5
5
  __license__ = "MIT"
6
6
  __url__ = "https://github.com/Itskillmaster/spluspy"
@@ -1,3 +1,3 @@
1
1
  # Versions should comply with PEP440.
2
2
  # This line is parsed in setup.py:
3
- __version__ = '1.1.3'
3
+ __version__ = '2.1.2'
@@ -398,6 +398,472 @@ class ChatManagementMixin:
398
398
  )
399
399
  return False
400
400
 
401
+ async def set_chat_permissions(
402
+ self,
403
+ chat_id: Union[int, str],
404
+ send_messages: Optional[bool] = None,
405
+ send_media: Optional[bool] = None,
406
+ send_stickers: Optional[bool] = None,
407
+ send_gifs: Optional[bool] = None,
408
+ send_polls: Optional[bool] = None,
409
+ embed_links: Optional[bool] = None,
410
+ invite_users: Optional[bool] = None,
411
+ change_info: Optional[bool] = None,
412
+ pin_messages: Optional[bool] = None,
413
+ ) -> bool:
414
+ """Set the default chat permissions (default banned rights) for a group.
415
+
416
+ Controls what actions regular members are allowed to perform.
417
+ This applies to ALL non-admin members of the group.
418
+
419
+ Args:
420
+ chat_id: Channel/supergroup identifier (int ID, @username, etc.).
421
+ send_messages: ``True`` to ALLOW sending text messages,
422
+ ``False`` to RESTRICT. ``None`` keeps current state.
423
+ send_media: ``True`` to ALLOW sending media (photos, videos, etc.),
424
+ ``False`` to RESTRICT. ``None`` keeps current state.
425
+ send_stickers: ``True`` to ALLOW sending stickers,
426
+ ``False`` to RESTRICT. ``None`` keeps current state.
427
+ send_gifs: ``True`` to ALLOW sending GIFs,
428
+ ``False`` to RESTRICT. ``None`` keeps current state.
429
+ send_polls: ``True`` to ALLOW sending polls,
430
+ ``False`` to RESTRICT. ``None`` keeps current state.
431
+ embed_links: ``True`` to ALLOW embedding links in messages,
432
+ ``False`` to RESTRICT. ``None`` keeps current state.
433
+ invite_users: ``True`` to ALLOW inviting other users,
434
+ ``False`` to RESTRICT. ``None`` keeps current state.
435
+ change_info: ``True`` to ALLOW changing group info,
436
+ ``False`` to RESTRICT. ``None`` keeps current state.
437
+ pin_messages: ``True`` to ALLOW pinning messages,
438
+ ``False`` to RESTRICT. ``None`` keeps current state.
439
+
440
+ Returns:
441
+ ``True`` on success, ``False`` on failure.
442
+
443
+ Note:
444
+ In MTProto, ``ChatBannedRights`` flags are **positive-ban**:
445
+ setting a flag to ``True`` means the user is BANNED from that
446
+ action. This method inverts the semantics so that ``True``
447
+ means "allowed" (intuitive for the developer).
448
+
449
+ Example:
450
+ >>> # Lock everything: no messages, no media, no stickers
451
+ >>> await client.set_chat_permissions(chat_id,
452
+ ... send_messages=False, send_media=False,
453
+ ... send_stickers=False)
454
+ >>>
455
+ >>> # Only restrict media; keep everything else as-is
456
+ >>> await client.set_chat_permissions(chat_id,
457
+ ... send_media=False)
458
+ >>>
459
+ >>> # Unlock everything
460
+ >>> await client.set_chat_permissions(chat_id,
461
+ ... send_messages=True, send_media=True,
462
+ ... send_stickers=True, send_gifs=True,
463
+ ... send_polls=True, embed_links=True,
464
+ ... invite_users=True, change_info=True,
465
+ ... pin_messages=True)
466
+ """
467
+ client = self._get_client()
468
+ if not client:
469
+ logger.warning("set_chat_permissions skipped: client not connected")
470
+ return False
471
+
472
+ try:
473
+ from spluspy._engine import functions, types
474
+ except ImportError:
475
+ from telethon import functions, types
476
+
477
+ try:
478
+ # Resolve chat_id to InputPeer
479
+ input_peer = await self._resolve_to_input_peer(client, chat_id)
480
+ if input_peer is None:
481
+ # Fallback: try get_input_entity directly
482
+ try:
483
+ input_peer = await client.get_input_entity(chat_id)
484
+ except Exception:
485
+ logger.error(
486
+ "set_chat_permissions: cannot resolve %s to InputPeer",
487
+ chat_id,
488
+ )
489
+ return False
490
+
491
+ # Map developer-friendly booleans to MTProto banned-rights.
492
+ # In MTProto: True = BANNED (restricted).
493
+ # Here: True = ALLOWED (not banned), False = RESTRICTED (banned).
494
+ # None means "don't change" (omit from ChatBannedRights).
495
+
496
+ def _to_banned(val: Optional[bool]) -> Optional[bool]:
497
+ """Convert developer-friendly bool to MTProto banned flag.
498
+
499
+ True (allowed) -> False (not banned)
500
+ False (restricted) -> True (banned)
501
+ None -> None (omit)
502
+ """
503
+ if val is None:
504
+ return None
505
+ return not val # Invert: allowed -> not banned, restricted -> banned
506
+
507
+ banned_rights = types.ChatBannedRights(
508
+ until_date=None,
509
+ view_messages=False, # Never restrict viewing messages
510
+ send_messages=_to_banned(send_messages),
511
+ send_media=_to_banned(send_media),
512
+ send_stickers=_to_banned(send_stickers),
513
+ send_gifs=_to_banned(send_gifs),
514
+ send_games=_to_banned(send_media), # Games use media permission
515
+ send_inline=_to_banned(send_media), # Inline bots use media permission
516
+ embed_links=_to_banned(embed_links),
517
+ send_polls=_to_banned(send_polls),
518
+ change_info=_to_banned(change_info),
519
+ invite_users=_to_banned(invite_users),
520
+ pin_messages=_to_banned(pin_messages),
521
+ manage_topics=_to_banned(change_info), # Topics use change_info permission
522
+ send_photos=_to_banned(send_media),
523
+ send_videos=_to_banned(send_media),
524
+ send_roundvideos=_to_banned(send_media),
525
+ send_audios=_to_banned(send_media),
526
+ send_voices=_to_banned(send_media),
527
+ send_docs=_to_banned(send_media),
528
+ send_plain=_to_banned(send_messages),
529
+ )
530
+
531
+ request = functions.messages.EditChatDefaultBannedRightsRequest(
532
+ peer=input_peer,
533
+ banned_rights=banned_rights,
534
+ )
535
+
536
+ await client(request)
537
+ logger.debug(
538
+ "set_chat_permissions: updated default permissions for %s",
539
+ chat_id,
540
+ )
541
+ return True
542
+
543
+ except Exception as exc:
544
+ logger.error(
545
+ "set_chat_permissions(chat=%s) failed: %s",
546
+ chat_id, exc,
547
+ )
548
+ return False
549
+
550
+ async def set_chat_title(
551
+ self,
552
+ chat_id: Union[int, str],
553
+ title: str,
554
+ ) -> bool:
555
+ """Change the title of a channel or supergroup.
556
+
557
+ Uses ``channels.EditTitleRequest`` to update the group/channel name.
558
+
559
+ Args:
560
+ chat_id: Channel/supergroup identifier (int ID, @username, etc.).
561
+ title: New title for the chat (1–128 characters).
562
+
563
+ Returns:
564
+ ``True`` on success, ``False`` on failure.
565
+
566
+ Example:
567
+ >>> await client.set_chat_title(-1001234567890, "New Group Name")
568
+ """
569
+ client = self._get_client()
570
+ if not client:
571
+ logger.warning("set_chat_title skipped: client not connected")
572
+ return False
573
+
574
+ try:
575
+ from spluspy._engine import functions
576
+ except ImportError:
577
+ from telethon import functions
578
+
579
+ try:
580
+ input_channel = await self._resolve_to_input_channel(client, chat_id)
581
+ if input_channel is None:
582
+ logger.error("set_chat_title: cannot resolve %s to InputChannel", chat_id)
583
+ return False
584
+
585
+ request = functions.channels.EditTitleRequest(
586
+ channel=input_channel,
587
+ title=title,
588
+ )
589
+ await client(request)
590
+ logger.debug("set_chat_title: updated title for %s", chat_id)
591
+ return True
592
+
593
+ except Exception as exc:
594
+ logger.error("set_chat_title(chat=%s) failed: %s", chat_id, exc)
595
+ return False
596
+
597
+ async def set_chat_about(
598
+ self,
599
+ chat_id: Union[int, str],
600
+ about: str,
601
+ ) -> bool:
602
+ """Change the about/description text of a chat.
603
+
604
+ Uses ``messages.EditChatAboutRequest`` to update the chat bio.
605
+ Works for both basic groups and supergroups/channels.
606
+
607
+ Args:
608
+ chat_id: Chat identifier (int ID, @username, etc.).
609
+ about: New about text (0–70 characters). Pass empty string to clear.
610
+
611
+ Returns:
612
+ ``True`` on success, ``False`` on failure.
613
+
614
+ Example:
615
+ >>> await client.set_chat_about(-1001234567890, "Welcome to our group!")
616
+ """
617
+ client = self._get_client()
618
+ if not client:
619
+ logger.warning("set_chat_about skipped: client not connected")
620
+ return False
621
+
622
+ try:
623
+ from spluspy._engine import functions
624
+ except ImportError:
625
+ from telethon import functions
626
+
627
+ try:
628
+ input_peer = await self._resolve_to_input_peer(client, chat_id)
629
+ if input_peer is None:
630
+ # Fallback: try get_input_entity directly
631
+ try:
632
+ input_peer = await client.get_input_entity(chat_id)
633
+ except Exception:
634
+ logger.error("set_chat_about: cannot resolve %s to InputPeer", chat_id)
635
+ return False
636
+
637
+ request = functions.messages.EditChatAboutRequest(
638
+ peer=input_peer,
639
+ about=about,
640
+ )
641
+ await client(request)
642
+ logger.debug("set_chat_about: updated about for %s", chat_id)
643
+ return True
644
+
645
+ except Exception as exc:
646
+ logger.error("set_chat_about(chat=%s) failed: %s", chat_id, exc)
647
+ return False
648
+
649
+ async def set_chat_photo(
650
+ self,
651
+ chat_id: Union[int, str],
652
+ file_path: str,
653
+ ) -> bool:
654
+ """Upload and set a new photo for a channel or supergroup.
655
+
656
+ Reads the photo from *file_path*, uploads it to Telegram servers,
657
+ then applies it via ``channels.EditPhotoRequest``.
658
+
659
+ Args:
660
+ chat_id: Channel/supergroup identifier (int ID, @username, etc.).
661
+ file_path: Local filesystem path to the image file
662
+ (JPEG, PNG, or GIF recommended).
663
+
664
+ Returns:
665
+ ``True`` on success, ``False`` on failure.
666
+
667
+ Example:
668
+ >>> await client.set_chat_photo(-1001234567890, "/path/to/photo.jpg")
669
+ """
670
+ client = self._get_client()
671
+ if not client:
672
+ logger.warning("set_chat_photo skipped: client not connected")
673
+ return False
674
+
675
+ try:
676
+ from spluspy._engine import functions, types
677
+ except ImportError:
678
+ from telethon import functions, types
679
+
680
+ try:
681
+ input_channel = await self._resolve_to_input_channel(client, chat_id)
682
+ if input_channel is None:
683
+ logger.error("set_chat_photo: cannot resolve %s to InputChannel", chat_id)
684
+ return False
685
+
686
+ # Read and upload the photo file
687
+ import os
688
+ import hashlib
689
+
690
+ file_path_str = str(file_path)
691
+ file_size = os.path.getsize(file_path_str)
692
+ file_name = os.path.basename(file_path_str)
693
+
694
+ with open(file_path_str, "rb") as f:
695
+ file_data = f.read()
696
+
697
+ # Use client's built-in upload helper if available,
698
+ # otherwise do a manual upload via upload.saveFilePart
699
+ file_id = int.from_bytes(os.urandom(8), "big", signed=True)
700
+ CHUNK_SIZE = 512 * 1024 # 512 KB per part
701
+ total_parts = max(1, (file_size + CHUNK_SIZE - 1) // CHUNK_SIZE)
702
+
703
+ for part_idx in range(total_parts):
704
+ offset = part_idx * CHUNK_SIZE
705
+ chunk = file_data[offset:offset + CHUNK_SIZE]
706
+
707
+ if total_parts > 1:
708
+ result = await client(functions.upload.SaveBigFilePartRequest(
709
+ file_id=file_id,
710
+ file_part=part_idx,
711
+ file_total_parts=total_parts,
712
+ bytes=chunk,
713
+ ))
714
+ else:
715
+ result = await client(functions.upload.SaveFilePartRequest(
716
+ file_id=file_id,
717
+ file_part=part_idx,
718
+ bytes=chunk,
719
+ ))
720
+
721
+ if not result:
722
+ logger.error("set_chat_photo: upload failed at part %d", part_idx)
723
+ return False
724
+
725
+ uploaded_file = types.InputFile(
726
+ id=file_id,
727
+ parts=total_parts,
728
+ name=file_name,
729
+ md5_checksum="",
730
+ )
731
+
732
+ photo_obj = types.InputChatUploadedPhoto(file=uploaded_file)
733
+
734
+ request = functions.channels.EditPhotoRequest(
735
+ channel=input_channel,
736
+ photo=photo_obj,
737
+ )
738
+ await client(request)
739
+ logger.debug("set_chat_photo: updated photo for %s", chat_id)
740
+ return True
741
+
742
+ except Exception as exc:
743
+ logger.error("set_chat_photo(chat=%s) failed: %s", chat_id, exc)
744
+ return False
745
+
746
+ async def start_group_call(
747
+ self,
748
+ chat_id: Union[int, str],
749
+ title: Optional[str] = None,
750
+ ) -> bool:
751
+ """Start a new group call (voice chat) in a supergroup.
752
+
753
+ Uses ``phone.CreateGroupCallRequest`` to initiate the call.
754
+ The caller must have admin rights with ``manage_call`` enabled.
755
+
756
+ Args:
757
+ chat_id: Supergroup/channel identifier (int ID, @username, etc.).
758
+ title: Optional title for the group call (e.g. "Team Meeting").
759
+
760
+ Returns:
761
+ ``True`` on success, ``False`` on failure.
762
+
763
+ Raises:
764
+ No exceptions — errors are caught and logged.
765
+
766
+ Example:
767
+ >>> await client.start_group_call(-1001234567890, title="Daily Standup")
768
+ """
769
+ client = self._get_client()
770
+ if not client:
771
+ logger.warning("start_group_call skipped: client not connected")
772
+ return False
773
+
774
+ try:
775
+ from spluspy._engine import functions
776
+ except ImportError:
777
+ from telethon import functions
778
+
779
+ try:
780
+ input_peer = await self._resolve_to_input_peer(client, chat_id)
781
+ if input_peer is None:
782
+ # Fallback: try get_input_entity directly
783
+ try:
784
+ input_peer = await client.get_input_entity(chat_id)
785
+ except Exception:
786
+ logger.error("start_group_call: cannot resolve %s to InputPeer", chat_id)
787
+ return False
788
+
789
+ import os
790
+ random_id = int.from_bytes(os.urandom(4), "big", signed=True)
791
+
792
+ request = functions.phone.CreateGroupCallRequest(
793
+ peer=input_peer,
794
+ random_id=random_id,
795
+ title=title,
796
+ )
797
+ await client(request)
798
+ logger.debug("start_group_call: started call in %s", chat_id)
799
+ return True
800
+
801
+ except Exception as exc:
802
+ logger.error("start_group_call(chat=%s) failed: %s", chat_id, exc)
803
+ return False
804
+
805
+ async def stop_group_call(
806
+ self,
807
+ chat_id: Union[int, str],
808
+ ) -> bool:
809
+ """Stop (discard) an active group call in a supergroup.
810
+
811
+ Fetches the current group call via ``channels.GetFullChannelRequest``,
812
+ extracts the ``InputGroupCall`` from the ``full_chat.call`` attribute,
813
+ and sends ``phone.DiscardGroupCallRequest`` to end it.
814
+
815
+ Args:
816
+ chat_id: Supergroup/channel identifier (int ID, @username, etc.).
817
+
818
+ Returns:
819
+ ``True`` on success, ``False`` on failure (including when no
820
+ active call exists).
821
+
822
+ Example:
823
+ >>> await client.stop_group_call(-1001234567890)
824
+ """
825
+ client = self._get_client()
826
+ if not client:
827
+ logger.warning("stop_group_call skipped: client not connected")
828
+ return False
829
+
830
+ try:
831
+ from spluspy._engine import functions, types
832
+ except ImportError:
833
+ from telethon import functions, types
834
+
835
+ try:
836
+ input_channel = await self._resolve_to_input_channel(client, chat_id)
837
+ if input_channel is None:
838
+ logger.error("stop_group_call: cannot resolve %s to InputChannel", chat_id)
839
+ return False
840
+
841
+ # Fetch full channel info to get the active group call
842
+ full_chat_result = await client(functions.channels.GetFullChannelRequest(
843
+ channel=input_channel,
844
+ ))
845
+
846
+ full_chat = getattr(full_chat_result, "full_chat", None)
847
+ if full_chat is None:
848
+ logger.warning("stop_group_call: no full_chat returned for %s", chat_id)
849
+ return False
850
+
851
+ input_group_call = getattr(full_chat, "call", None)
852
+ if input_group_call is None:
853
+ logger.warning("stop_group_call: no active group call in %s", chat_id)
854
+ return False
855
+
856
+ request = functions.phone.DiscardGroupCallRequest(
857
+ call=input_group_call,
858
+ )
859
+ await client(request)
860
+ logger.debug("stop_group_call: stopped call in %s", chat_id)
861
+ return True
862
+
863
+ except Exception as exc:
864
+ logger.error("stop_group_call(chat=%s) failed: %s", chat_id, exc)
865
+ return False
866
+
401
867
  # ── Helper Methods ────────────────────────────────────────────
402
868
 
403
869
  async def _resolve_to_input_channel(
@@ -545,6 +545,46 @@ class Client(MediaMixin, ChatManagementMixin):
545
545
  """Register a handler for edited messages."""
546
546
  return self.on(MessageEditedEventBuilder(**kwargs))
547
547
 
548
+ def on_edited_message(self, *message_filters: Any, **kwargs: Any) -> Callable[[Handler], Handler]:
549
+ """Register a handler for edited messages with optional filters.
550
+
551
+ The handler receives ``(client, message)`` as arguments.
552
+ Multiple filter arguments are combined with ``&`` (AND).
553
+
554
+ Example:
555
+ >>> @bot.on_edited_message(filters.text & filters.private)
556
+ ... async def on_edited(client, message):
557
+ ... await message.reply("Edited!")
558
+
559
+ >>> @bot.on_edited_message(filters.group, filters.photo)
560
+ ... async def on_group_edit(client, message):
561
+ ... ...
562
+ """
563
+ from spluspy.filters.filters import Filter, AndFilter, CallableFilter
564
+
565
+ priority = kwargs.pop("priority", HandlerPriority.NORMAL)
566
+ builder = MessageEditedEventBuilder(**kwargs)
567
+ if hasattr(builder, '_priority'):
568
+ builder._priority = priority
569
+
570
+ composed: Any = None
571
+ for f in message_filters:
572
+ if isinstance(f, Filter):
573
+ composed = f if composed is None else AndFilter(composed, f)
574
+ elif callable(f):
575
+ cf = CallableFilter(f)
576
+ composed = cf if composed is None else AndFilter(composed, cf)
577
+
578
+ def decorator(func: Handler) -> Handler:
579
+ self._handlers.setdefault(MessageEdited, []).append(
580
+ (builder, func, composed)
581
+ )
582
+ self._handlers[MessageEdited].sort(
583
+ key=lambda x: x[0].priority if hasattr(x[0], 'priority') else HandlerPriority.NORMAL
584
+ )
585
+ return func
586
+ return decorator
587
+
548
588
  def on_callback_query(self, **kwargs: Any) -> Callable[[Handler], Handler]:
549
589
  """Callback queries are not supported on Soroush+ user sessions.
550
590
 
@@ -740,9 +780,111 @@ class Client(MediaMixin, ChatManagementMixin):
740
780
  task.add_done_callback(self._handler_tasks.discard)
741
781
 
742
782
  client.add_event_handler(_wrapped, event)
783
+ elif isinstance(builder, MessageEditedEventBuilder):
784
+ _filter = composed_filter
785
+
786
+ sig = inspect.signature(func)
787
+ _param_count = len(sig.parameters)
788
+
789
+ async def _wrapped_edited(evt, _f=_filter, _pc=_param_count):
790
+ raw_msg = evt.message
791
+
792
+ if _f is not None and isinstance(_f, _FilterBase):
793
+ _lightweight = _PreMessage(raw_msg)
794
+ if not _f(_lightweight):
795
+ return
796
+
797
+ sender_id = None
798
+ if hasattr(raw_msg, 'sender_id'):
799
+ sender_id = raw_msg.sender_id
800
+ elif hasattr(raw_msg, 'from_id') and raw_msg.from_id:
801
+ fid = raw_msg.from_id
802
+ sender_id = getattr(fid, 'user_id', None) if hasattr(fid, 'user_id') else (fid if isinstance(fid, int) else None)
803
+
804
+ chat_id = None
805
+ peer = getattr(raw_msg, 'peer_id', None)
806
+ if peer is not None:
807
+ chat_id = (
808
+ getattr(peer, 'channel_id', None)
809
+ or getattr(peer, 'chat_id', None)
810
+ or getattr(peer, 'user_id', None)
811
+ )
812
+ if not chat_id:
813
+ peer_id_val = getattr(peer, 'id', None)
814
+ if peer_id_val:
815
+ constructor_name = type(peer).__name__
816
+ if "Channel" in constructor_name:
817
+ chat_id = -1000000000000 - peer_id_val
818
+ elif "Chat" in constructor_name:
819
+ chat_id = -peer_id_val
820
+ else:
821
+ chat_id = peer_id_val
822
+ if not chat_id:
823
+ prop_val = getattr(raw_msg, 'chat_id', None)
824
+ if prop_val and prop_val != raw_msg.id:
825
+ chat_id = prop_val
826
+ if not chat_id:
827
+ chat_obj = getattr(raw_msg, 'chat', None)
828
+ if chat_obj is not None:
829
+ cid = getattr(chat_obj, 'id', None)
830
+ if cid and cid != raw_msg.id:
831
+ chat_id = cid
832
+ if not chat_id:
833
+ chat_id = getattr(raw_msg, 'group_guid', None)
834
+ if not chat_id:
835
+ chat_id = 0
836
+
837
+ reply_to_id = None
838
+ reply_to_sender_id = None
839
+ _rid, _rsid = _extract_reply_fields(raw_msg)
840
+ reply_to_id = _rid
841
+ reply_to_sender_id = _rsid
842
+
843
+ media_obj = getattr(raw_msg, 'media', None)
844
+ fwd = getattr(raw_msg, 'fwd_from', None)
845
+
846
+ message = Message(
847
+ id=raw_msg.id,
848
+ text=getattr(raw_msg, 'message', '') or '',
849
+ chat_id=chat_id,
850
+ sender_id=sender_id,
851
+ is_private=getattr(raw_msg, 'is_private', True),
852
+ is_group=getattr(raw_msg, 'is_group', False),
853
+ is_channel=getattr(raw_msg, 'is_channel', False),
854
+ reply_to=reply_to_id,
855
+ reply_to_sender_id=reply_to_sender_id,
856
+ media=media_obj,
857
+ forward_origin=fwd,
858
+ raw=getattr(raw_msg, '__dict__', {}),
859
+ )
860
+ message._client = self
861
+
862
+ async def _run_handler() -> None:
863
+ try:
864
+ if _pc >= 2:
865
+ await func(self, message)
866
+ else:
867
+ splus_event = MessageEdited(message=message)
868
+ splus_event._set_client(self)
869
+ await func(splus_event)
870
+ except TypeError:
871
+ try:
872
+ await func(self, message)
873
+ except Exception:
874
+ splus_event = MessageEdited(message=message)
875
+ splus_event._set_client(self)
876
+ await func(splus_event)
877
+ except Exception as exc:
878
+ logger.error("Handler error: %s: %s", type(exc).__name__, exc)
879
+ traceback.print_exc()
880
+
881
+ task = asyncio.create_task(_run_handler())
882
+ self._handler_tasks.add(task)
883
+ task.add_done_callback(self._handler_tasks.discard)
884
+
885
+ client.add_event_handler(_wrapped_edited, se.MessageEdited())
743
886
  else:
744
887
  event_map = {
745
- MessageEditedEventBuilder: se.MessageEdited,
746
888
  CallbackQueryEventBuilder: se.CallbackQuery,
747
889
  InlineQueryEventBuilder: se.InlineQuery,
748
890
  ChatActionEventBuilder: se.ChatAction,