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.
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/PKG-INFO +1 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/router.rst +6 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/chats.rst +16 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/client.rst +26 -3
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/index.rst +1 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/messages.rst +2 -1
- maxapi_python-2.3.0/docs/release-2-3-0.rst +40 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/router.rst +48 -1
- maxapi_python-2.3.0/docs/types/contact_info.rst +6 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/index.rst +4 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/users.rst +19 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/pyproject.toml +1 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/__init__.py +1 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/payloads.py +6 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/service.py +18 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/service.py +3 -11
- maxapi_python-2.3.0/src/pymax/api/users/payloads.py +38 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/users/service.py +14 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/app.py +26 -6
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/base.py +48 -3
- maxapi_python-2.3.0/src/pymax/dispatch/__init__.py +21 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/dispatcher.py +134 -16
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/enums.py +1 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/router.py +86 -4
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/chat.py +21 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/message.py +2 -6
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/user.py +12 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/store.py +11 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/__init__.py +1 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/chat.py +21 -1
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/message.py +3 -7
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/user.py +8 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_chat_user_self_session_services.py +72 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_message_service.py +1 -3
- maxapi_python-2.3.0/tests/app/test_app_runtime.py +445 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/domain/test_bound_models.py +18 -3
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/session/test_store.py +20 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/uv.lock +1 -1
- maxapi_python-2.2.0/src/pymax/api/users/payloads.py +0 -16
- maxapi_python-2.2.0/src/pymax/dispatch/__init__.py +0 -10
- maxapi_python-2.2.0/tests/app/test_app_runtime.py +0 -205
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/pull_request_template.md +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/workflows/publish.yml +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.github/workflows/tests.yml +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.gitignore +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/.pre-commit-config.yaml +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/LICENSE +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/README.md +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/_static/.gitkeep +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/account.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/auth.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client-client.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client-config.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client-web.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/client.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/api/files.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/auth.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/conf.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/examples.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/faq.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/files.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/formatting.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/getting-started.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-0.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-1.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-2.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-1-3.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/release-2-2-0.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/troubleshooting.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/audio_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/call_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/chat.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/contact_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/control_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/element.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/enums.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/file_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/folder.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/folder_list.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/folder_update.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/inline_keyboard_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/message.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/message_delete_event.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/message_read_event.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/name.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/photo_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/presence_event.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/profile.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/reaction_counter.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/reaction_info.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/reaction_update_event.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/read_state.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/session.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/share_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/sticker_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/sync_overrides.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/sync_state.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/typing_event.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/user.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/docs/types/video_attachment.rst +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/auth/types.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/binding.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/bots/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/bots/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/bots/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/chats/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/facade.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/messages/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/models.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/response.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/self/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/session/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/models.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/uploads/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/users/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/api/users/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/email.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/models.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/providers.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/qr.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/auth/sms.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/client.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/client_web.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/config.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/connection.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/pending.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/tcp.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/connection/readers/ws.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/mapping.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/dispatch/resolvers.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/exceptions.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/file.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/photo.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/static.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/files/video.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/formatting/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/formatting/markdown.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/auth.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/bots.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/protocol.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/infra/self.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/logging.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/models.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/compression.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/framing.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/payload.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/tcp/protocol.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/ws/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/protocol/ws/protocol.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/py.typed +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/routers.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/models.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/session/protocol.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/navigation.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/payloads.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/telemetry/service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/tcp.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/transport/websocket.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/audio.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/call.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/contact.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/control.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/file.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/keyboards/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/keyboards/inline.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/photo.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/share.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/sticker.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/unknown.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/attachments/video.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/auth.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/base.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/bots.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/element.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/enums.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/error.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/folder.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/login.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/member.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/name.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/presence.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/profile.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/session.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/domain/sync.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/file.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/mark.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/message.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/presence.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/reaction.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/typing.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/src/pymax/types/events/video.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/__init__.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_auth_service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/api/test_upload_service.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/auth/test_auth_flows.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/conftest.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/connection/test_connection.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/connection/test_readers_and_transports.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/dispatch/test_dispatcher.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/domain/test_message_models.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/files/test_files_and_formatting.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/protocol/test_protocols.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/telemetry/test_telemetry.py +0 -0
- {maxapi_python-2.2.0 → maxapi_python-2.3.0}/tests/test_logging.py +0 -0
|
@@ -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()
|
|
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()`` и
|
|
@@ -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
|
-
|
|
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
|
----------
|
|
@@ -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
|
|
|
@@ -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 =
|
|
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(
|
|
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
|
-
|
|
128
|
-
|
|
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.
|
|
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
|
+
)
|