maxapi-python 2.2.0__tar.gz → 2.3.0__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 (245) hide show
  1. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/PKG-INFO +1 -1
  2. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/router.rst +6 -0
  3. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/chats.rst +16 -0
  4. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/client.rst +26 -3
  5. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/index.rst +1 -0
  6. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/messages.rst +2 -1
  7. maxapi_python-2.3.0/docs/release-2-3-0.rst +40 -0
  8. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/router.rst +48 -1
  9. maxapi_python-2.3.0/docs/types/contact_info.rst +6 -0
  10. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/index.rst +4 -0
  11. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/users.rst +19 -0
  12. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/pyproject.toml +1 -1
  13. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/__init__.py +1 -1
  14. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/payloads.py +6 -0
  15. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/service.py +18 -0
  16. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/service.py +3 -11
  17. maxapi_python-2.3.0/src/pymax/api/users/payloads.py +38 -0
  18. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/users/service.py +14 -1
  19. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/app.py +26 -6
  20. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/base.py +48 -3
  21. maxapi_python-2.3.0/src/pymax/dispatch/__init__.py +21 -0
  22. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/dispatcher.py +134 -16
  23. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/enums.py +1 -0
  24. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/router.py +86 -4
  25. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/chat.py +21 -0
  26. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/message.py +2 -6
  27. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/user.py +12 -1
  28. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/store.py +11 -0
  29. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/__init__.py +1 -1
  30. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/chat.py +21 -1
  31. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/message.py +3 -7
  32. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/user.py +8 -0
  33. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_chat_user_self_session_services.py +72 -0
  34. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_message_service.py +1 -3
  35. maxapi_python-2.3.0/tests/app/test_app_runtime.py +445 -0
  36. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/domain/test_bound_models.py +18 -3
  37. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/session/test_store.py +20 -0
  38. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/uv.lock +1 -1
  39. maxapi_python-2.2.0/src/pymax/api/users/payloads.py +0 -16
  40. maxapi_python-2.2.0/src/pymax/dispatch/__init__.py +0 -10
  41. maxapi_python-2.2.0/tests/app/test_app_runtime.py +0 -205
  42. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  43. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  44. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
  45. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/pull_request_template.md +0 -0
  46. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/workflows/publish.yml +0 -0
  47. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/workflows/tests.yml +0 -0
  48. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.gitignore +0 -0
  49. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.pre-commit-config.yaml +0 -0
  50. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/LICENSE +0 -0
  51. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/README.md +0 -0
  52. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/_static/.gitkeep +0 -0
  53. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/account.rst +0 -0
  54. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/auth.rst +0 -0
  55. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client-client.rst +0 -0
  56. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client-config.rst +0 -0
  57. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client-web.rst +0 -0
  58. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client.rst +0 -0
  59. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/files.rst +0 -0
  60. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/auth.rst +0 -0
  61. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/conf.py +0 -0
  62. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/examples.rst +0 -0
  63. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/faq.rst +0 -0
  64. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/files.rst +0 -0
  65. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/formatting.rst +0 -0
  66. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/getting-started.rst +0 -0
  67. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-0.rst +0 -0
  68. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-1.rst +0 -0
  69. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-2.rst +0 -0
  70. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-3.rst +0 -0
  71. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-2-0.rst +0 -0
  72. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/troubleshooting.rst +0 -0
  73. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/audio_attachment.rst +0 -0
  74. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/call_attachment.rst +0 -0
  75. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/chat.rst +0 -0
  76. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/contact_attachment.rst +0 -0
  77. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/control_attachment.rst +0 -0
  78. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/element.rst +0 -0
  79. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/enums.rst +0 -0
  80. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/file_attachment.rst +0 -0
  81. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/folder.rst +0 -0
  82. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/folder_list.rst +0 -0
  83. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/folder_update.rst +0 -0
  84. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/inline_keyboard_attachment.rst +0 -0
  85. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/message.rst +0 -0
  86. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/message_delete_event.rst +0 -0
  87. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/message_read_event.rst +0 -0
  88. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/name.rst +0 -0
  89. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/photo_attachment.rst +0 -0
  90. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/presence_event.rst +0 -0
  91. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/profile.rst +0 -0
  92. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/reaction_counter.rst +0 -0
  93. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/reaction_info.rst +0 -0
  94. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/reaction_update_event.rst +0 -0
  95. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/read_state.rst +0 -0
  96. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/session.rst +0 -0
  97. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/share_attachment.rst +0 -0
  98. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/sticker_attachment.rst +0 -0
  99. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/sync_overrides.rst +0 -0
  100. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/sync_state.rst +0 -0
  101. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/typing_event.rst +0 -0
  102. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/user.rst +0 -0
  103. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/video_attachment.rst +0 -0
  104. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/__init__.py +0 -0
  105. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/__init__.py +0 -0
  106. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/enums.py +0 -0
  107. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/payloads.py +0 -0
  108. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/service.py +0 -0
  109. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/types.py +0 -0
  110. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/binding.py +0 -0
  111. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/bots/__init__.py +0 -0
  112. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/bots/payloads.py +0 -0
  113. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/bots/service.py +0 -0
  114. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/__init__.py +0 -0
  115. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/enums.py +0 -0
  116. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/facade.py +0 -0
  117. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/__init__.py +0 -0
  118. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/enums.py +0 -0
  119. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/payloads.py +0 -0
  120. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/models.py +0 -0
  121. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/response.py +0 -0
  122. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/__init__.py +0 -0
  123. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/enums.py +0 -0
  124. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/payloads.py +0 -0
  125. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/service.py +0 -0
  126. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/__init__.py +0 -0
  127. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/enums.py +0 -0
  128. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/payloads.py +0 -0
  129. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/service.py +0 -0
  130. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/__init__.py +0 -0
  131. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/models.py +0 -0
  132. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/payloads.py +0 -0
  133. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/service.py +0 -0
  134. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/users/__init__.py +0 -0
  135. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/users/enums.py +0 -0
  136. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/__init__.py +0 -0
  137. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/base.py +0 -0
  138. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/email.py +0 -0
  139. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/models.py +0 -0
  140. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/providers.py +0 -0
  141. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/qr.py +0 -0
  142. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/service.py +0 -0
  143. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/sms.py +0 -0
  144. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/client.py +0 -0
  145. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/client_web.py +0 -0
  146. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/config.py +0 -0
  147. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/__init__.py +0 -0
  148. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/connection.py +0 -0
  149. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/pending.py +0 -0
  150. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/__init__.py +0 -0
  151. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/base.py +0 -0
  152. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/tcp.py +0 -0
  153. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/ws.py +0 -0
  154. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/mapping.py +0 -0
  155. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/resolvers.py +0 -0
  156. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/exceptions.py +0 -0
  157. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/__init__.py +0 -0
  158. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/base.py +0 -0
  159. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/file.py +0 -0
  160. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/photo.py +0 -0
  161. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/static.py +0 -0
  162. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/video.py +0 -0
  163. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/formatting/__init__.py +0 -0
  164. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/formatting/markdown.py +0 -0
  165. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/__init__.py +0 -0
  166. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/auth.py +0 -0
  167. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/base.py +0 -0
  168. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/bots.py +0 -0
  169. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/protocol.py +0 -0
  170. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/self.py +0 -0
  171. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/logging.py +0 -0
  172. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/__init__.py +0 -0
  173. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/base.py +0 -0
  174. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/enums.py +0 -0
  175. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/models.py +0 -0
  176. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/__init__.py +0 -0
  177. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/compression.py +0 -0
  178. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/framing.py +0 -0
  179. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/payload.py +0 -0
  180. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/protocol.py +0 -0
  181. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/ws/__init__.py +0 -0
  182. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/ws/protocol.py +0 -0
  183. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/py.typed +0 -0
  184. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/routers.py +0 -0
  185. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/__init__.py +0 -0
  186. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/models.py +0 -0
  187. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/protocol.py +0 -0
  188. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/__init__.py +0 -0
  189. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/navigation.py +0 -0
  190. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/payloads.py +0 -0
  191. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/service.py +0 -0
  192. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/__init__.py +0 -0
  193. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/base.py +0 -0
  194. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/tcp.py +0 -0
  195. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/websocket.py +0 -0
  196. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/__init__.py +0 -0
  197. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/__init__.py +0 -0
  198. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/audio.py +0 -0
  199. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/call.py +0 -0
  200. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/contact.py +0 -0
  201. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/control.py +0 -0
  202. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/enums.py +0 -0
  203. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/file.py +0 -0
  204. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/keyboards/__init__.py +0 -0
  205. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/keyboards/inline.py +0 -0
  206. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/photo.py +0 -0
  207. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/share.py +0 -0
  208. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/sticker.py +0 -0
  209. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/unknown.py +0 -0
  210. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/video.py +0 -0
  211. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/auth.py +0 -0
  212. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/base.py +0 -0
  213. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/bots.py +0 -0
  214. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/element.py +0 -0
  215. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/enums.py +0 -0
  216. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/error.py +0 -0
  217. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/folder.py +0 -0
  218. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/login.py +0 -0
  219. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/member.py +0 -0
  220. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/name.py +0 -0
  221. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/presence.py +0 -0
  222. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/profile.py +0 -0
  223. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/session.py +0 -0
  224. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/sync.py +0 -0
  225. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/__init__.py +0 -0
  226. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/file.py +0 -0
  227. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/mark.py +0 -0
  228. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/message.py +0 -0
  229. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/presence.py +0 -0
  230. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/reaction.py +0 -0
  231. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/typing.py +0 -0
  232. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/video.py +0 -0
  233. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/__init__.py +0 -0
  234. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_auth_service.py +0 -0
  235. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_upload_service.py +0 -0
  236. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/auth/test_auth_flows.py +0 -0
  237. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/conftest.py +0 -0
  238. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/connection/test_connection.py +0 -0
  239. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/connection/test_readers_and_transports.py +0 -0
  240. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/dispatch/test_dispatcher.py +0 -0
  241. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/domain/test_message_models.py +0 -0
  242. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/files/test_files_and_formatting.py +0 -0
  243. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/protocol/test_protocols.py +0 -0
  244. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/telemetry/test_telemetry.py +0 -0
  245. {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/test_logging.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxapi-python
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Python wrapper для API мессенджера Max
5
5
  Project-URL: Homepage, https://github.com/MaxApiTeam/PyMax
6
6
  Project-URL: Repository, https://github.com/MaxApiTeam/PyMax
@@ -5,6 +5,12 @@ Router API
5
5
  :members:
6
6
  :show-inheritance:
7
7
 
8
+ .. autoclass:: pymax.dispatch.ErrorScope
9
+ :members:
10
+
11
+ .. autoclass:: pymax.dispatch.ErrorContext
12
+ :members:
13
+
8
14
  .. autodata:: pymax.ClientRouter
9
15
 
10
16
  .. autodata:: pymax.WebRouter
@@ -142,6 +142,22 @@ login/sync, а также методы для загрузки, создания
142
142
  ``leave()`` зависит от типа чата: для группы вызывает выход из группы, для
143
143
  канала - выход из канала. Из личного диалога выйти нельзя.
144
144
 
145
+ Удалить чат
146
+ -----------
147
+
148
+ .. code-block:: python
149
+
150
+ await client.delete_chat(chat_id=123456)
151
+
152
+ Через объект ``Chat`` PyMax использует ``chat.last_event_time``:
153
+
154
+ .. code-block:: python
155
+
156
+ chat = await client.get_chat(123456)
157
+ await chat.delete(for_all=True)
158
+
159
+ После успешного удаления чат убирается из локального кеша ``client.chats``.
160
+
145
161
  Invite-ссылки
146
162
  -------------
147
163
 
@@ -231,6 +231,21 @@ PyMax потеряет token и попросит авторизацию снов
231
231
  сессии можно удалить файл ``work_dir/session_name``; тогда потребуется новая
232
232
  авторизация.
233
233
 
234
+ Повторная авторизация
235
+ ---------------------
236
+
237
+ Если нужно сбросить текущую локальную сессию и пройти авторизацию заново,
238
+ используйте ``relogin()``:
239
+
240
+ .. code-block:: python
241
+
242
+ await client.relogin()
243
+
244
+ ``relogin()`` удаляет загруженную сессию из store, закрывает текущий runtime и
245
+ по умолчанию сразу запускает клиента снова. Если token был передан через
246
+ ``ExtraConfig(token=...)``, он тоже сбрасывается; это можно отключить через
247
+ ``drop_config_token=False``.
248
+
234
249
  Reconnect
235
250
  ---------
236
251
 
@@ -242,6 +257,14 @@ Reconnect
242
257
  а новый ``App`` снова получает тот же root router. ``on_start`` вызывается
243
258
  после каждого успешного reconnect.
244
259
 
260
+ Перед повторным подключением можно зарегистрировать ``on_disconnect``:
261
+
262
+ .. code-block:: python
263
+
264
+ @client.on_disconnect()
265
+ async def disconnected(exc: Exception, reconnect: bool, delay: float) -> None:
266
+ print("connection lost:", exc, reconnect, delay)
267
+
245
268
  Отключить reconnect:
246
269
 
247
270
  .. code-block:: python
@@ -283,12 +306,12 @@ Debug-логи показывают handshake, login, входящие собы
283
306
 
284
307
  Чаты
285
308
  ``get_chat()``, ``fetch_chats()``, создание групп, invite-ссылки,
286
- участники, настройки групп и выход из групп/каналов.
309
+ участники, настройки групп, удаление чатов и выход из групп/каналов.
287
310
 
288
311
  Пользователи
289
312
  ``get_user()``, ``get_users()``, ``fetch_users()``, ``search_by_phone()``,
290
- ``add_contact()``, ``remove_contact()`` и ``get_chat_id()``. Подробнее:
291
- :doc:`users`.
313
+ ``add_contact()``, ``remove_contact()``, ``import_contacts()`` и
314
+ ``get_chat_id()``. Подробнее: :doc:`users`.
292
315
 
293
316
  Аккаунт
294
317
  ``change_profile()``, папки чатов, активные сессии, ``logout()`` и
@@ -22,6 +22,7 @@ PyMax - асинхронная Python-библиотека для Max API. Он
22
22
  :maxdepth: 1
23
23
  :caption: Новости
24
24
 
25
+ release-2-3-0
25
26
  release-2-2-0
26
27
  release-2-1-3
27
28
  release-2-1-2
@@ -51,6 +51,7 @@ Messages
51
51
 
52
52
  Через клиент то же редактирование доступно как
53
53
  ``client.edit_message(chat_id, message_id, text, ...)``.
54
+ Новые вложения передаются через ``attachments``.
54
55
 
55
56
  Отправлять сообщения
56
57
  --------------------
@@ -99,7 +100,7 @@ Messages
99
100
  Служебные события
100
101
  -----------------
101
102
 
102
- В ``2.2.0`` доступны отдельные обработчики набора текста, присутствия,
103
+ Начиная с ``2.2.0`` доступны отдельные обработчики набора текста, присутствия,
103
104
  прочтения и реакций:
104
105
 
105
106
  .. code-block:: python
@@ -0,0 +1,40 @@
1
+ PyMax 2.3.0
2
+ ===========
3
+
4
+ Изменения относительно ``2.2.0``.
5
+
6
+ Добавлено
7
+ ---------
8
+
9
+ * Несколько ``on_start``-обработчиков на одном клиенте или роутере. Все
10
+ зарегистрированные callbacks запускаются после успешного login.
11
+ * ``on_error()`` для централизованной обработки ошибок из handler-ов,
12
+ фильтров, ``on_start`` и login на этапе запуска.
13
+ * ``ErrorScope.GLOBAL`` и ``ErrorScope.LOCAL`` для выбора области действия
14
+ error-handler-а.
15
+ * ``on_disconnect()`` для реакции на сетевое отключение перед reconnect. В
16
+ callback передаются исходная ошибка, флаг reconnect и задержка.
17
+ * ``relogin()`` для удаления текущей локальной сессии и повторной авторизации.
18
+ * ``delete_chat()`` на клиенте и ``Chat.delete()`` на bound-объекте чата.
19
+ * ``import_contacts()`` и ``ContactInfo`` для импорта контактов из телефонной
20
+ книги.
21
+ * ``SessionStore.delete_all_sessions()`` для очистки встроенного SQLite-store.
22
+
23
+ Изменилось
24
+ ----------
25
+
26
+ * ``edit_message()`` и ``Message.edit()`` принимают новые вложения только через
27
+ ``attachments=[...]``.
28
+ * Тип ``attachments`` для отправки и редактирования сообщений теперь принимает
29
+ любую ``Sequence`` из ``Photo``, ``File`` и ``Video``.
30
+ * Обработанные login-ошибки на этапе ``start`` больше не приводят к запуску
31
+ ``on_start``.
32
+
33
+ Миграция
34
+ --------
35
+
36
+ * В ``edit_message()`` и ``Message.edit()`` используйте ``attachments=[...]``.
37
+ Параметр ``attachment`` удален.
38
+ * Если error-handler зарегистрирован и успешно отработал, исходная ошибка
39
+ считается обработанной. Если сам error-handler падает, исходная ошибка
40
+ продолжает распространяться.
@@ -158,7 +158,54 @@ Handler всегда вызывается как ``handler(event, client)``. Э
158
158
  print(client.me)
159
159
 
160
160
  Если включен reconnect, ``on_start`` будет вызван после каждого успешного
161
- переподключения.
161
+ переподключения. На одном клиенте или роутере можно зарегистрировать несколько
162
+ ``on_start``-обработчиков; PyMax запустит каждый из них.
163
+
164
+ Ошибки handler-ов
165
+ -----------------
166
+
167
+ ``on_error`` перехватывает ошибки из фильтров, handler-ов, ``on_start`` и login
168
+ на этапе запуска.
169
+
170
+ .. code-block:: python
171
+
172
+ from pymax import ApiError, Client, ClientRouter
173
+ from pymax.dispatch import ErrorContext, ErrorScope
174
+
175
+ client = Client(phone="+79990000000", work_dir="cache")
176
+
177
+
178
+ @client.on_error(scope=ErrorScope.GLOBAL)
179
+ async def on_err(e: Exception, ctx: ErrorContext[Client]) -> None:
180
+ if isinstance(e, ApiError) and e.message == "FAIL_LOGIN_TOKEN":
181
+ await ctx.client.relogin()
182
+
183
+
184
+ router = ClientRouter()
185
+
186
+
187
+ @router.on_error(scope=ErrorScope.LOCAL)
188
+ async def router_error(e: Exception, ctx: ErrorContext[Client]) -> None:
189
+ print("router failed:", e)
190
+
191
+ ``ErrorScope.GLOBAL`` получает ошибки из всего дерева подключенных роутеров.
192
+ ``ErrorScope.LOCAL`` получает только ошибки того router-а, на котором
193
+ зарегистрирован error-handler.
194
+
195
+ Если error-handler успешно отработал, исходная ошибка считается обработанной.
196
+ Если упал сам error-handler, исходная ошибка продолжит распространяться.
197
+
198
+ Отключение
199
+ ----------
200
+
201
+ ``on_disconnect`` вызывается при сетевой ошибке перед reconnect или перед
202
+ пробросом ошибки, если reconnect отключен.
203
+
204
+ .. code-block:: python
205
+
206
+ @client.on_disconnect()
207
+ async def disconnected(exc: Exception, reconnect: bool, delay: float) -> None:
208
+ print(exc, reconnect, delay)
162
209
 
163
210
  Raw events
164
211
  ----------
@@ -0,0 +1,6 @@
1
+ ContactInfo
2
+ ===========
3
+
4
+ .. autoclass:: pymax.types.domain.user.ContactInfo
5
+ :members:
6
+ :show-inheritance:
@@ -37,6 +37,9 @@ Types
37
37
  ``User`` и ``Profile``
38
38
  Пользователи и профиль текущего аккаунта.
39
39
 
40
+ ``ContactInfo``
41
+ Контакт телефонной книги для ``import_contacts()``.
42
+
40
43
  ``PhotoAttachment``, ``VideoAttachment``, ``FileAttachment`` и другие
41
44
  Входящие вложения в ``message.attaches``.
42
45
 
@@ -95,6 +98,7 @@ API reference
95
98
  element
96
99
  name
97
100
  user
101
+ contact_info
98
102
  profile
99
103
  session
100
104
  folder
@@ -71,6 +71,25 @@ PyMax хранит контакты, которые Max вернул на login/
71
71
  await user.add_contact()
72
72
  await user.remove_contact()
73
73
 
74
+ Импортировать контакты из телефонной книги:
75
+
76
+ .. code-block:: python
77
+
78
+ from pymax.types import ContactInfo
79
+
80
+ contacts = await client.import_contacts(
81
+ [
82
+ ContactInfo(
83
+ phone="+79990000000",
84
+ first_name="Ada",
85
+ last_name="Lovelace",
86
+ )
87
+ ]
88
+ )
89
+
90
+ ``last_name`` хранится в ``ContactInfo``, но текущий payload импорта Max
91
+ использует только телефон и имя.
92
+
74
93
  Личный чат
75
94
  ----------
76
95
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "maxapi-python"
3
- version = "2.2.0"
3
+ version = "2.3.0"
4
4
  description = "Python wrapper для API мессенджера Max"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,4 +1,4 @@
1
- __version__ = "2.2.0"
1
+ __version__ = "2.3.0"
2
2
 
3
3
 
4
4
  from .auth import (
@@ -115,3 +115,9 @@ class JoinRequestActionPayload(CamelModel):
115
115
  type: str = "JOIN_REQUEST" # TODO: ENUMM!!!
116
116
  show_history: bool | None = True
117
117
  operation: ChatMemberOperation
118
+
119
+
120
+ class DeleteChatPayload(CamelModel):
121
+ chat_id: int
122
+ last_event_time: int
123
+ for_all: bool = True
@@ -23,6 +23,7 @@ from .payloads import (
23
23
  CreateGroupAttach,
24
24
  CreateGroupMessage,
25
25
  CreateGroupPayload,
26
+ DeleteChatPayload,
26
27
  FetchChatsPayload,
27
28
  FetchJoinRequests,
28
29
  GetChatInfoPayload,
@@ -362,3 +363,20 @@ class ChatService:
362
363
  chat_id=chat_id,
363
364
  user_ids=[user_id],
364
365
  )
366
+
367
+ async def delete_chat(
368
+ self,
369
+ chat_id: int,
370
+ last_event_time: int | None = None,
371
+ for_all: bool = True,
372
+ ) -> None:
373
+ frame = DeleteChatPayload(
374
+ chat_id=chat_id,
375
+ last_event_time=(
376
+ last_event_time if last_event_time is not None else int(time.time() * 1000)
377
+ ),
378
+ for_all=for_all,
379
+ )
380
+
381
+ await self.app.invoke(Opcode.CHAT_DELETE, frame.to_payload())
382
+ self._remove_cached_chat(chat_id)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ from collections.abc import Sequence
4
5
  from typing import TYPE_CHECKING, TypeAlias
5
6
 
6
7
  from pymax.api.binding import bind_api_model, bind_api_models
@@ -52,7 +53,7 @@ if TYPE_CHECKING:
52
53
  from pymax.app import App
53
54
 
54
55
  SendAttachment: TypeAlias = Photo | File | Video
55
- SendAttachments: TypeAlias = list[SendAttachment] | None
56
+ SendAttachments: TypeAlias = Sequence[SendAttachment] | None
56
57
 
57
58
  logger = get_logger(__name__)
58
59
 
@@ -169,24 +170,15 @@ class MessageService:
169
170
  chat_id: int,
170
171
  message_id: int,
171
172
  text: str,
172
- attachment: SendAttachment | None = None,
173
173
  attachments: SendAttachments = None,
174
174
  ) -> Message:
175
- if attachment is not None and attachments:
176
- logger.warning("both attachment and attachments provided; using attachments")
177
- attachment = None
178
-
179
- edit_attachments = attachments
180
- if attachment is not None:
181
- edit_attachments = [attachment]
182
-
183
175
  clean_text, elements = Formatter.format_markdown(text)
184
176
  frame = EditMessagePayload(
185
177
  chat_id=chat_id,
186
178
  message_id=message_id,
187
179
  text=clean_text,
188
180
  elements=elements,
189
- attachments=await self._upload_attachments(edit_attachments),
181
+ attachments=await self._upload_attachments(attachments),
190
182
  )
191
183
 
192
184
  response = await self.app.invoke(Opcode.MSG_EDIT, frame.to_payload())
@@ -0,0 +1,38 @@
1
+ from collections.abc import Iterable
2
+
3
+ from pymax.api.models import CamelModel
4
+ from pymax.types.domain import ContactInfo
5
+
6
+ from .enums import ContactAction
7
+
8
+
9
+ class FetchContactsPayload(CamelModel):
10
+ contact_ids: list[int]
11
+
12
+
13
+ class SearchByPhonePayload(CamelModel):
14
+ phone: str
15
+
16
+
17
+ class ContactActionPayload(CamelModel):
18
+ contact_id: int
19
+ action: ContactAction
20
+
21
+
22
+ class _ContactPayload(CamelModel):
23
+ first_name: str
24
+
25
+
26
+ class ImportContactsPayload(CamelModel):
27
+ contact_list: dict[str, _ContactPayload] # phone -> contact payload
28
+
29
+ @classmethod
30
+ def from_contacts(cls, contacts: Iterable[ContactInfo]) -> "ImportContactsPayload":
31
+ return cls(
32
+ contact_list={
33
+ contact.phone: _ContactPayload(
34
+ first_name=contact.first_name,
35
+ )
36
+ for contact in contacts
37
+ }
38
+ )
@@ -10,12 +10,13 @@ from pymax.api.response import (
10
10
  )
11
11
  from pymax.logging import get_logger
12
12
  from pymax.protocol import InboundFrame, Opcode
13
- from pymax.types.domain import Session, User
13
+ from pymax.types.domain import ContactInfo, Session, User
14
14
 
15
15
  from .enums import ContactAction, UserPayloadKey
16
16
  from .payloads import (
17
17
  ContactActionPayload,
18
18
  FetchContactsPayload,
19
+ ImportContactsPayload,
19
20
  SearchByPhonePayload,
20
21
  )
21
22
 
@@ -122,5 +123,17 @@ class UserService:
122
123
  self.app.users.pop(contact_id, None)
123
124
  return True
124
125
 
126
+ async def import_contacts(self, contacts: list[ContactInfo]) -> list[User]:
127
+ frame = ImportContactsPayload.from_contacts(contacts)
128
+
129
+ response = await self.app.invoke(Opcode.SYNC, frame.to_payload())
130
+
131
+ users = parse_payload_list(
132
+ response, UserPayloadKey.CONTACTS, User
133
+ ) # TODO: maybe also return phone mapping?
134
+
135
+ # {contacts: [...], phones: {data[0]: server_phone}}
136
+ return [self._cache_user(user) for user in users]
137
+
125
138
  def get_chat_id(self, first_user_id: int, second_user_id: int) -> int:
126
139
  return first_user_id ^ second_user_id
@@ -1,12 +1,12 @@
1
1
  import asyncio
2
- from typing import Any, Generic, TypeVar
2
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar
3
3
 
4
4
  from pymax.api import ApiFacade
5
5
  from pymax.auth import AuthFlow
6
6
  from pymax.config import ClientConfig
7
7
  from pymax.connection import ConnectionManager
8
8
  from pymax.dispatch import Dispatcher
9
- from pymax.dispatch.router import Router
9
+ from pymax.dispatch.router import EventType, Router
10
10
  from pymax.exceptions import ApiError
11
11
  from pymax.logging import get_logger
12
12
  from pymax.protocol import Command, InboundFrame, OutboundFrame
@@ -17,8 +17,11 @@ from pymax.telemetry import TelemetryService
17
17
  from pymax.types import MaxApiError, Message
18
18
  from pymax.types.domain import Chat, Profile, User
19
19
 
20
+ if TYPE_CHECKING:
21
+ from pymax.base import BaseClient
22
+
20
23
  logger = get_logger(__name__)
21
- ClientT = TypeVar("ClientT")
24
+ ClientT = TypeVar("ClientT", bound="BaseClient")
22
25
 
23
26
 
24
27
  class App(Generic[ClientT]):
@@ -124,9 +127,26 @@ class App(Generic[ClientT]):
124
127
  self.session = session_data
125
128
 
126
129
  logger.debug("logging in")
127
- response = await self.api.auth.login(
128
- self.config.device.user_agent,
129
- )
130
+
131
+ try:
132
+ response = await self.api.auth.login(
133
+ self.config.device.user_agent,
134
+ )
135
+ except Exception as e:
136
+ handled = False
137
+ if self.dispatcher.client is not None:
138
+ handled = await self.dispatcher.emit_error(
139
+ e,
140
+ EventType.ON_START,
141
+ None,
142
+ self.dispatcher.root_router,
143
+ None,
144
+ )
145
+ if not handled:
146
+ raise
147
+
148
+ await self.close()
149
+ return
130
150
 
131
151
  if response.token is not None and response.token != self.session.token:
132
152
  await self.store.update_token(self.session.token, response.token)
@@ -5,7 +5,8 @@ from abc import ABC, abstractmethod
5
5
  from typing import TYPE_CHECKING, Any, Generic, TypeVar
6
6
  from uuid import uuid4
7
7
 
8
- from pymax.dispatch import Router
8
+ from pymax.dispatch import ErrorScope, Router
9
+ from pymax.dispatch.router import DisconnectDecorator, ErrorDecorator
9
10
  from pymax.infra import BaseMixin
10
11
  from pymax.logging import get_logger
11
12
 
@@ -128,6 +129,10 @@ class BaseClient(BaseMixin, ABC, Generic[ClientT]):
128
129
  while True:
129
130
  try:
130
131
  await self._app.start()
132
+ if not self._app.started:
133
+ await self.close()
134
+ return
135
+
131
136
  await self._app.dispatcher.emit_start(self)
132
137
  await self._connection.wait_closed()
133
138
  except asyncio.CancelledError:
@@ -138,14 +143,21 @@ class BaseClient(BaseMixin, ABC, Generic[ClientT]):
138
143
  EOFError,
139
144
  OSError,
140
145
  TimeoutError,
141
- ):
146
+ ) as e:
142
147
  await self.close()
148
+ await self._app.dispatcher.emit_disconnect(
149
+ e,
150
+ self.extra_config.reconnect,
151
+ self.extra_config.reconnect_delay,
152
+ )
153
+
143
154
  if not self.extra_config.reconnect:
144
155
  raise
145
156
 
146
- logger.exception(
157
+ logger.debug(
147
158
  "client connection failed; reconnecting in %s seconds",
148
159
  self.extra_config.reconnect_delay,
160
+ exc_info=True,
149
161
  )
150
162
  await asyncio.sleep(self.extra_config.reconnect_delay)
151
163
  self._reset_runtime()
@@ -237,6 +249,39 @@ class BaseClient(BaseMixin, ABC, Generic[ClientT]):
237
249
  """Регистрирует обработчик исходных входящих frame-ов."""
238
250
  return self._router.on_raw(*filters)
239
251
 
252
+ def on_error(self, scope: ErrorScope = ErrorScope.GLOBAL) -> ErrorDecorator[ClientT]:
253
+ """Регистрирует обработчик ошибок dispatch-а и запуска клиента."""
254
+ return self._router.on_error(scope)
255
+
256
+ def on_disconnect(self) -> DisconnectDecorator:
257
+ """Регистрирует обработчик сетевого отключения перед reconnect."""
258
+ return self._router.on_disconnect()
259
+
240
260
  def include_router(self, router: Router[ClientT]) -> None:
241
261
  """Подключает дочерний router к root router клиента."""
242
262
  self._router.include_router(router)
263
+
264
+ async def relogin(self: ClientT, drop_config_token: bool = True, start: bool = True) -> None: # noqa: PYI019
265
+ """Удаляет текущую локальную сессию и запускает авторизацию заново.
266
+
267
+ Args:
268
+ drop_config_token: Сбросить token, переданный через ``ExtraConfig``.
269
+ start: Сразу запустить клиента после сброса runtime.
270
+ """
271
+ store = self._app.store
272
+ session = self._app.session
273
+
274
+ if session is None:
275
+ raise RuntimeError("Cannot relogin before session is loaded")
276
+
277
+ await store.delete_session(session.token)
278
+ await self.close()
279
+
280
+ if drop_config_token:
281
+ self.extra_config.token = None
282
+ self._config.token = None
283
+
284
+ self._reset_runtime()
285
+
286
+ if start:
287
+ await self.start()
@@ -0,0 +1,21 @@
1
+ from .dispatcher import Dispatcher
2
+ from .enums import EventType
3
+ from .router import (
4
+ ClientRouter,
5
+ DisconnectCallback,
6
+ DisconnectDecorator,
7
+ ErrorContext,
8
+ ErrorScope,
9
+ Router,
10
+ )
11
+
12
+ __all__ = (
13
+ "ClientRouter",
14
+ "DisconnectCallback",
15
+ "DisconnectDecorator",
16
+ "Dispatcher",
17
+ "ErrorContext",
18
+ "ErrorScope",
19
+ "EventType",
20
+ "Router",
21
+ )