maxapi-python 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.
- maxapi_python-2.1.2/.github/workflows/publish.yml +64 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/PKG-INFO +1 -1
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/account.rst +15 -10
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/index.rst +2 -0
- maxapi_python-2.1.2/docs/release-2-1-1.rst +16 -0
- maxapi_python-2.1.2/docs/release-2-1-2.rst +32 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/pyproject.toml +1 -1
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/__init__.py +1 -1
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/bots/payloads.py +1 -1
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/bots/service.py +3 -7
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/self/service.py +11 -4
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/uploads/service.py +4 -2
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/app.py +13 -16
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/self.py +2 -6
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/transport/tcp.py +4 -2
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/folder.py +0 -6
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/login.py +7 -19
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/api/test_chat_user_self_session_services.py +1 -1
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/app/test_app_runtime.py +18 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/connection/test_readers_and_transports.py +47 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/uv.lock +1 -1
- maxapi_python-2.1.0/.github/workflows/publish.yml +0 -84
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/.github/pull_request_template.md +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/.gitignore +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/.pre-commit-config.yaml +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/LICENSE +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/README.md +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/_static/.gitkeep +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/api/auth.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/api/client.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/api/files.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/api/router.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/auth.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/chats.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/client.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/conf.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/examples.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/faq.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/files.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/formatting.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/getting-started.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/messages.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/release-2-1-0.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/router.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/troubleshooting.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/audio_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/call_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/chat.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/contact_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/control_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/element.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/enums.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/file_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/folder.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/folder_list.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/folder_update.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/index.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/inline_keyboard_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/message.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/message_delete_event.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/name.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/photo_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/profile.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/reaction_counter.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/reaction_info.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/read_state.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/session.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/share_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/sticker_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/sync_overrides.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/sync_state.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/user.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/types/video_attachment.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/docs/users.rst +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/auth/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/auth/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/auth/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/auth/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/auth/types.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/bots/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/chats/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/chats/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/chats/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/chats/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/facade.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/messages/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/messages/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/messages/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/messages/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/models.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/response.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/self/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/self/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/self/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/session/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/session/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/session/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/session/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/uploads/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/uploads/models.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/uploads/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/users/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/users/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/users/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/api/users/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/email.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/models.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/providers.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/qr.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/auth/sms.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/client.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/client_web.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/config.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/connection.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/pending.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/readers/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/readers/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/readers/tcp.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/connection/readers/ws.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/dispatch/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/dispatch/dispatcher.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/dispatch/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/dispatch/mapping.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/dispatch/resolvers.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/dispatch/router.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/exceptions.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/files/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/files/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/files/file.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/files/photo.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/files/static.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/files/video.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/formatting/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/formatting/markdown.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/auth.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/bots.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/chat.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/message.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/protocol.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/infra/user.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/logging.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/models.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/tcp/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/tcp/compression.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/tcp/framing.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/tcp/payload.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/tcp/protocol.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/ws/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/protocol/ws/protocol.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/py.typed +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/routers.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/session/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/session/models.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/session/protocol.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/session/store.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/telemetry/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/telemetry/navigation.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/telemetry/payloads.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/telemetry/service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/transport/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/transport/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/transport/websocket.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/audio.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/call.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/contact.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/control.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/file.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/keyboards/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/keyboards/inline.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/photo.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/share.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/sticker.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/attachments/video.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/auth.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/base.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/bots.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/chat.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/element.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/enums.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/error.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/member.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/message.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/name.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/presence.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/profile.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/session.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/sync.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/domain/user.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/events/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/events/file.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/events/message.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/src/pymax/types/events/video.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/__init__.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/api/test_auth_service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/api/test_message_service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/api/test_upload_service.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/auth/test_auth_flows.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/conftest.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/connection/test_connection.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/dispatch/test_dispatcher.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/domain/test_bound_models.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/files/test_files_and_formatting.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/protocol/test_protocols.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/session/test_store.py +0 -0
- {maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/telemetry/test_telemetry.py +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: release-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: false
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
package:
|
|
17
|
+
name: Build package
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout repository
|
|
22
|
+
uses: actions/checkout@v6
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Build package distributions
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Validate package distributions
|
|
33
|
+
run: uv run twine check dist/*
|
|
34
|
+
|
|
35
|
+
- name: Upload package distributions
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: package-distributions
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
name: Publish to PyPI
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
needs: [package]
|
|
45
|
+
|
|
46
|
+
environment:
|
|
47
|
+
name: pypi
|
|
48
|
+
|
|
49
|
+
permissions:
|
|
50
|
+
contents: read
|
|
51
|
+
id-token: write
|
|
52
|
+
|
|
53
|
+
steps:
|
|
54
|
+
- name: Download package distributions
|
|
55
|
+
uses: actions/download-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: package-distributions
|
|
58
|
+
path: dist/
|
|
59
|
+
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
62
|
+
|
|
63
|
+
- name: Publish package to PyPI
|
|
64
|
+
run: uv publish
|
|
@@ -32,21 +32,26 @@ Account
|
|
|
32
32
|
Фотография профиля
|
|
33
33
|
------------------
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
Передайте ``Photo`` в ``change_profile(photo=...)``, чтобы PyMax сам загрузил
|
|
36
|
+
файл и применил новый токен фотографии:
|
|
37
37
|
|
|
38
38
|
.. code-block:: python
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
print(upload_url)
|
|
40
|
+
from pymax import Photo
|
|
42
41
|
|
|
43
42
|
await client.change_profile(
|
|
44
43
|
first_name="Alex",
|
|
45
|
-
|
|
44
|
+
photo=Photo(path="avatar.jpg"),
|
|
46
45
|
)
|
|
47
46
|
|
|
48
|
-
Если
|
|
49
|
-
|
|
47
|
+
Если у вас уже есть токен фотографии от API Max, его можно передать напрямую:
|
|
48
|
+
|
|
49
|
+
.. code-block:: python
|
|
50
|
+
|
|
51
|
+
await client.change_profile(
|
|
52
|
+
first_name="Alex",
|
|
53
|
+
photo_token="PHOTO_TOKEN",
|
|
54
|
+
)
|
|
50
55
|
|
|
51
56
|
Папки чатов
|
|
52
57
|
-----------
|
|
@@ -118,9 +123,9 @@ Account
|
|
|
118
123
|
``on_start`` или после успешного ``await client.start()`` в собственном
|
|
119
124
|
lifecycle.
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
Фото профиля не обновилось
|
|
127
|
+
Если переданы и ``photo``, и ``photo_token``, PyMax загрузит ``photo`` и
|
|
128
|
+
использует новый токен загруженной фотографии.
|
|
124
129
|
|
|
125
130
|
Папка создалась, но список старый
|
|
126
131
|
Используйте ``get_folders()`` после изменения и сохраняйте новый
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
PyMax 2.1.1
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
Изменения относительно ``2.1.0``.
|
|
5
|
+
|
|
6
|
+
Добавлено
|
|
7
|
+
---------
|
|
8
|
+
|
|
9
|
+
* ``change_profile(photo=...)`` теперь загружает фотографию профиля напрямую.
|
|
10
|
+
|
|
11
|
+
Исправлено
|
|
12
|
+
----------
|
|
13
|
+
|
|
14
|
+
* TLS-handshake при TCP-подключении через proxy передает ``server_hostname``.
|
|
15
|
+
* Локальный токен обновляется после ``close_all_sessions()`` и при получении
|
|
16
|
+
нового токена во время handshake.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
PyMax 2.1.2
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
Изменения относительно ``2.1.1``.
|
|
5
|
+
|
|
6
|
+
Добавлено
|
|
7
|
+
---------
|
|
8
|
+
|
|
9
|
+
* ``get_bot_init_data()`` теперь можно вызвать без ``chat_id`` для сценариев,
|
|
10
|
+
где Max запускает web app вне конкретного чата.
|
|
11
|
+
|
|
12
|
+
Исправлено
|
|
13
|
+
----------
|
|
14
|
+
|
|
15
|
+
* ``ExtraConfig.request_timeout`` снова применяется к API-запросам по
|
|
16
|
+
умолчанию, а явный ``timeout`` в низкоуровневом вызове сохраняет приоритет.
|
|
17
|
+
* Login-ответ без нового ``token`` больше не ломает запуск клиента и не
|
|
18
|
+
перезаписывает сохраненный токен пустым значением.
|
|
19
|
+
* ``FolderList`` больше не переопределяет pydantic-итератор и не ломает
|
|
20
|
+
``dict(...)`` / стандартную сериализацию модели.
|
|
21
|
+
|
|
22
|
+
Изменилось
|
|
23
|
+
----------
|
|
24
|
+
|
|
25
|
+
* Publish workflow упрощен: сборка, проверка дистрибутивов и публикация в PyPI
|
|
26
|
+
теперь разделены на понятные шаги с artifact handoff.
|
|
27
|
+
|
|
28
|
+
Миграция
|
|
29
|
+
--------
|
|
30
|
+
|
|
31
|
+
* Если код итерировал ``FolderList`` напрямую, замените это на
|
|
32
|
+
``folder_list.folders``.
|
|
@@ -23,13 +23,9 @@ class BotsService:
|
|
|
23
23
|
async def get_init_data(
|
|
24
24
|
self,
|
|
25
25
|
bot_id: int,
|
|
26
|
-
chat_id: int,
|
|
26
|
+
chat_id: int | None = None,
|
|
27
27
|
start_param: str | None = None,
|
|
28
28
|
) -> InitData:
|
|
29
|
-
frame = RequestInitDataPayload(
|
|
30
|
-
|
|
31
|
-
)
|
|
32
|
-
response = await self.app.invoke(
|
|
33
|
-
Opcode.WEB_APP_INIT_DATA, frame.to_payload()
|
|
34
|
-
)
|
|
29
|
+
frame = RequestInitDataPayload(bot_id=bot_id, chat_id=chat_id, start_param=start_param)
|
|
30
|
+
response = await self.app.invoke(Opcode.WEB_APP_INIT_DATA, frame.to_payload())
|
|
35
31
|
return require_payload_model(response, InitData)
|
|
@@ -9,6 +9,7 @@ from pymax.api.response import (
|
|
|
9
9
|
require_payload_item_model,
|
|
10
10
|
require_payload_model,
|
|
11
11
|
)
|
|
12
|
+
from pymax.files import Photo
|
|
12
13
|
from pymax.logging import get_logger
|
|
13
14
|
from pymax.protocol import Opcode
|
|
14
15
|
from pymax.types.domain import FolderList, FolderUpdate, Profile
|
|
@@ -47,15 +48,20 @@ class SelfService:
|
|
|
47
48
|
first_name: str,
|
|
48
49
|
last_name: str | None = None,
|
|
49
50
|
description: str | None = None,
|
|
50
|
-
photo:
|
|
51
|
+
photo: Photo | None = None,
|
|
51
52
|
*,
|
|
52
53
|
photo_token: str | None = None,
|
|
53
54
|
) -> bool:
|
|
54
55
|
if photo is not None:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"Pass photo_token instead."
|
|
56
|
+
attach = await self.app.api.uploads.upload_photo(
|
|
57
|
+
photo, profile=True
|
|
58
58
|
)
|
|
59
|
+
if photo_token:
|
|
60
|
+
logger.warning(
|
|
61
|
+
"photo_token argument was provided but will be overridden by the uploaded photo token"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
photo_token = attach.photo_token
|
|
59
65
|
|
|
60
66
|
frame = ChangeProfilePayload(
|
|
61
67
|
first_name=first_name,
|
|
@@ -145,6 +151,7 @@ class SelfService:
|
|
|
145
151
|
return False
|
|
146
152
|
|
|
147
153
|
await self.app.store.update_token(self.app.session.token, token)
|
|
154
|
+
self.app.session.token = token
|
|
148
155
|
|
|
149
156
|
return True
|
|
150
157
|
|
|
@@ -51,11 +51,13 @@ class UploadService:
|
|
|
51
51
|
self.on_file_attach
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
-
async def upload_photo(
|
|
54
|
+
async def upload_photo(
|
|
55
|
+
self, photo: Photo, profile: bool = False
|
|
56
|
+
) -> AttachPhotoPayload:
|
|
55
57
|
logger.info("Uploading photo")
|
|
56
58
|
logger.debug("Preparing photo upload payload")
|
|
57
59
|
|
|
58
|
-
payload = UploadPayload().model_dump()
|
|
60
|
+
payload = UploadPayload(profile=profile).model_dump()
|
|
59
61
|
|
|
60
62
|
try:
|
|
61
63
|
data = await self.app.invoke(
|
|
@@ -33,9 +33,7 @@ class App(Generic[ClientT]):
|
|
|
33
33
|
self.dispatcher: Dispatcher[ClientT] = Dispatcher(self, root_router)
|
|
34
34
|
self.api = ApiFacade(self)
|
|
35
35
|
self.config = config
|
|
36
|
-
self.store = self.config.store or SessionStore(
|
|
37
|
-
config.work_dir, config.session_name
|
|
38
|
-
)
|
|
36
|
+
self.store = self.config.store or SessionStore(config.work_dir, config.session_name)
|
|
39
37
|
self.auth_flow = auth_flow
|
|
40
38
|
|
|
41
39
|
self.me: Profile | None = None
|
|
@@ -76,18 +74,14 @@ class App(Generic[ClientT]):
|
|
|
76
74
|
await self.connection.open()
|
|
77
75
|
|
|
78
76
|
handshake_device_id = (
|
|
79
|
-
session_data.device_id
|
|
80
|
-
if session_data
|
|
81
|
-
else self.config.device.device_id
|
|
77
|
+
session_data.device_id if session_data else self.config.device.device_id
|
|
82
78
|
)
|
|
83
79
|
logger.debug("running handshake")
|
|
84
80
|
await self.handshake(handshake_device_id)
|
|
85
81
|
except (ConnectionError, EOFError, OSError, TimeoutError) as e:
|
|
86
82
|
logger.exception("failed to connect or handshake")
|
|
87
83
|
await self.connection.close()
|
|
88
|
-
raise ConnectionError(
|
|
89
|
-
f"Failed to connect and handshake: {e}"
|
|
90
|
-
) from e
|
|
84
|
+
raise ConnectionError(f"Failed to connect and handshake: {e}") from e
|
|
91
85
|
|
|
92
86
|
self._ping_task = asyncio.create_task(self._ping_loop())
|
|
93
87
|
|
|
@@ -108,9 +102,7 @@ class App(Generic[ClientT]):
|
|
|
108
102
|
|
|
109
103
|
if not auth_result.token:
|
|
110
104
|
logger.error("authentication finished without token")
|
|
111
|
-
raise RuntimeError(
|
|
112
|
-
"Authentication failed: no token received"
|
|
113
|
-
)
|
|
105
|
+
raise RuntimeError("Authentication failed: no token received")
|
|
114
106
|
|
|
115
107
|
await self.store.save_session(
|
|
116
108
|
session_data := SessionInfo(
|
|
@@ -135,6 +127,10 @@ class App(Generic[ClientT]):
|
|
|
135
127
|
self.config.device.user_agent,
|
|
136
128
|
)
|
|
137
129
|
|
|
130
|
+
if response.token is not None and response.token != self.session.token:
|
|
131
|
+
await self.store.update_token(self.session.token, response.token)
|
|
132
|
+
self.session.token = response.token
|
|
133
|
+
|
|
138
134
|
self.me = response.profile
|
|
139
135
|
self.chats = response.chats
|
|
140
136
|
self.users[self.me.contact.id] = self.me.contact
|
|
@@ -185,7 +181,7 @@ class App(Generic[ClientT]):
|
|
|
185
181
|
opcode: int,
|
|
186
182
|
payload: dict[str, Any],
|
|
187
183
|
cmd: int = Command.REQUEST,
|
|
188
|
-
timeout: float | None =
|
|
184
|
+
timeout: float | None = None,
|
|
189
185
|
compress: bool = False,
|
|
190
186
|
) -> InboundFrame:
|
|
191
187
|
seq = self.connection.next_seq()
|
|
@@ -207,10 +203,11 @@ class App(Generic[ClientT]):
|
|
|
207
203
|
payload_keys,
|
|
208
204
|
)
|
|
209
205
|
logger.debug("Request data=%s", frame.model_dump())
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
sorted(response.payload.keys()) if response.payload else []
|
|
206
|
+
request_timeout = (
|
|
207
|
+
self.config.request_timeout if timeout is None else timeout
|
|
213
208
|
)
|
|
209
|
+
response = await self.connection.request(frame, timeout=request_timeout)
|
|
210
|
+
response_keys = sorted(response.payload.keys()) if response.payload else []
|
|
214
211
|
logger.debug(
|
|
215
212
|
"response opcode=%s cmd=%s seq=%s payload_keys=%s",
|
|
216
213
|
response.opcode,
|
|
@@ -29,17 +29,13 @@ class SelfMixin(IClientProtocol):
|
|
|
29
29
|
first_name: Имя профиля.
|
|
30
30
|
last_name: Фамилия профиля.
|
|
31
31
|
description: Описание профиля.
|
|
32
|
-
photo: Файл или объект
|
|
33
|
-
|
|
32
|
+
photo: Файл или объект фото, который нужно загрузить как новую
|
|
33
|
+
фотографию профиля.
|
|
34
34
|
photo_token: Токен фотографии, уже загруженной через API Max.
|
|
35
35
|
|
|
36
36
|
Returns:
|
|
37
37
|
``True`` после успешного обновления. Клиент также обновит
|
|
38
38
|
``client.me`` и кеш текущего контакта.
|
|
39
|
-
|
|
40
|
-
Raises:
|
|
41
|
-
NotImplementedError: Если передан ``photo`` вместо
|
|
42
|
-
``photo_token``.
|
|
43
39
|
"""
|
|
44
40
|
return await self._app.api.account.change_profile(
|
|
45
41
|
first_name=first_name,
|
|
@@ -33,10 +33,12 @@ class TCPTransport(Transport):
|
|
|
33
33
|
sock = await proxy.connect(
|
|
34
34
|
dest_host=self._host,
|
|
35
35
|
dest_port=self._port,
|
|
36
|
-
ssl=self._use_ssl,
|
|
37
36
|
)
|
|
37
|
+
server_hostname = self._host if self._use_ssl else None
|
|
38
38
|
self._reader, self._writer = await asyncio.open_connection(
|
|
39
|
-
sock=sock,
|
|
39
|
+
sock=sock,
|
|
40
|
+
ssl=self._use_ssl,
|
|
41
|
+
server_hostname=server_hostname,
|
|
40
42
|
)
|
|
41
43
|
logger.info(
|
|
42
44
|
"tcp connected via proxy %s host=%s port=%s ssl=%s",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
from collections.abc import Iterator
|
|
2
1
|
from typing import Any
|
|
3
2
|
|
|
4
3
|
from pydantic import Field
|
|
5
|
-
from typing_extensions import override
|
|
6
4
|
|
|
7
5
|
from .base import CamelModel
|
|
8
6
|
|
|
@@ -68,7 +66,3 @@ class FolderList(CamelModel):
|
|
|
68
66
|
folders: list[Folder] = Field(default_factory=list)
|
|
69
67
|
all_filter_exclude_folders: list[Any] = Field(default_factory=list)
|
|
70
68
|
folder_sync: int = 0
|
|
71
|
-
|
|
72
|
-
@override
|
|
73
|
-
def __iter__(self) -> Iterator[Folder]: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
74
|
-
yield from self.folders
|
|
@@ -16,11 +16,9 @@ class LoginConfig(CamelModel):
|
|
|
16
16
|
class LoginResponse(CamelModel):
|
|
17
17
|
chats: list[Chat] = Field(default_factory=list)
|
|
18
18
|
profile: Profile
|
|
19
|
-
messages: dict[int, list[Message]] = Field(
|
|
20
|
-
default_factory=dict
|
|
21
|
-
) # chat_id -> [message]
|
|
19
|
+
messages: dict[int, list[Message]] = Field(default_factory=dict) # chat_id -> [message]
|
|
22
20
|
contacts: list[User | None] = Field(default_factory=list)
|
|
23
|
-
token: str
|
|
21
|
+
token: str | None = None
|
|
24
22
|
time: int | None = None
|
|
25
23
|
config: LoginConfig | None = None
|
|
26
24
|
|
|
@@ -29,19 +27,9 @@ class LoginResponse(CamelModel):
|
|
|
29
27
|
config_hash = self.config.hash if self.config is not None else None
|
|
30
28
|
|
|
31
29
|
return SyncState(
|
|
32
|
-
chats_sync=(
|
|
33
|
-
|
|
34
|
-
),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
),
|
|
38
|
-
drafts_sync=(
|
|
39
|
-
sync_time if sync_time is not None else current.drafts_sync
|
|
40
|
-
),
|
|
41
|
-
presence_sync=(
|
|
42
|
-
sync_time if sync_time is not None else current.presence_sync
|
|
43
|
-
),
|
|
44
|
-
config_hash=(
|
|
45
|
-
config_hash if config_hash is not None else current.config_hash
|
|
46
|
-
),
|
|
30
|
+
chats_sync=(sync_time if sync_time is not None else current.chats_sync),
|
|
31
|
+
contacts_sync=(sync_time if sync_time is not None else current.contacts_sync),
|
|
32
|
+
drafts_sync=(sync_time if sync_time is not None else current.drafts_sync),
|
|
33
|
+
presence_sync=(sync_time if sync_time is not None else current.presence_sync),
|
|
34
|
+
config_hash=(config_hash if config_hash is not None else current.config_hash),
|
|
47
35
|
)
|
{maxapi_python-2.1.0 → maxapi_python-2.1.2}/tests/api/test_chat_user_self_session_services.py
RENAMED
|
@@ -345,7 +345,7 @@ async def test_self_service_profile_photo_folders_and_logout() -> None:
|
|
|
345
345
|
|
|
346
346
|
assert created.folder is not None
|
|
347
347
|
assert created.folder.title == "Work"
|
|
348
|
-
assert [folder.id for folder in folders] == ["folder-1"]
|
|
348
|
+
assert [folder.id for folder in folders.folders] == ["folder-1"]
|
|
349
349
|
assert updated.folder is not None
|
|
350
350
|
assert updated.folder.title == "New"
|
|
351
351
|
assert deleted.folder_sync == 4
|
|
@@ -150,3 +150,21 @@ async def test_app_invoke_turns_error_frames_into_api_error() -> None:
|
|
|
150
150
|
assert exc_info.value.error == "rate_limited"
|
|
151
151
|
assert exc_info.value.opcode == Opcode.PING
|
|
152
152
|
assert connection.sent[0][0].seq == 0
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@pytest.mark.asyncio
|
|
156
|
+
async def test_app_invoke_uses_config_timeout_and_allows_override() -> None:
|
|
157
|
+
store = RuntimeStore(
|
|
158
|
+
SessionInfo(token="token", device_id="dev", phone="+7")
|
|
159
|
+
)
|
|
160
|
+
config = make_config().model_copy(
|
|
161
|
+
update={"request_timeout": 12.5, "store": store}
|
|
162
|
+
)
|
|
163
|
+
connection = RuntimeConnection([frame({}), frame({})])
|
|
164
|
+
app: App[object] = App(connection, config, StaticAuthFlow())
|
|
165
|
+
|
|
166
|
+
await app.invoke(Opcode.PING, {"interactive": True})
|
|
167
|
+
await app.invoke(Opcode.PING, {"interactive": True}, timeout=3.0)
|
|
168
|
+
|
|
169
|
+
assert connection.sent[0][1] == 12.5
|
|
170
|
+
assert connection.sent[1][1] == 3.0
|
|
@@ -104,6 +104,53 @@ async def test_tcp_transport_connect_send_recv_and_close(
|
|
|
104
104
|
assert writer.closed is True
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
@pytest.mark.asyncio
|
|
108
|
+
async def test_tcp_transport_proxy_ssl_passes_server_hostname(
|
|
109
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
110
|
+
) -> None:
|
|
111
|
+
reader = FakeStreamReader()
|
|
112
|
+
writer = FakeStreamWriter()
|
|
113
|
+
sock = object()
|
|
114
|
+
proxy_connect_kwargs = {}
|
|
115
|
+
open_connection_kwargs = {}
|
|
116
|
+
|
|
117
|
+
class FakeProxy:
|
|
118
|
+
async def connect(self, **kwargs):
|
|
119
|
+
proxy_connect_kwargs.update(kwargs)
|
|
120
|
+
return sock
|
|
121
|
+
|
|
122
|
+
async def open_connection(*args, **kwargs):
|
|
123
|
+
open_connection_kwargs.update(kwargs)
|
|
124
|
+
return reader, writer
|
|
125
|
+
|
|
126
|
+
monkeypatch.setattr(
|
|
127
|
+
"pymax.transport.tcp.Proxy.from_url",
|
|
128
|
+
lambda proxy_url: FakeProxy(),
|
|
129
|
+
)
|
|
130
|
+
monkeypatch.setattr(
|
|
131
|
+
"pymax.transport.tcp.asyncio.open_connection", open_connection
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
transport = TCPTransport(
|
|
135
|
+
"example.test",
|
|
136
|
+
443,
|
|
137
|
+
proxy="socks5://localhost:9050",
|
|
138
|
+
use_ssl=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
await transport.connect()
|
|
142
|
+
|
|
143
|
+
assert proxy_connect_kwargs == {
|
|
144
|
+
"dest_host": "example.test",
|
|
145
|
+
"dest_port": 443,
|
|
146
|
+
}
|
|
147
|
+
assert open_connection_kwargs == {
|
|
148
|
+
"sock": sock,
|
|
149
|
+
"ssl": True,
|
|
150
|
+
"server_hostname": "example.test",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
107
154
|
class FakeWebSocket:
|
|
108
155
|
def __init__(self) -> None:
|
|
109
156
|
self.close_code = None
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
name: Upload Python Package
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
workflow_dispatch:
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: read
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
release-checks:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
17
|
-
with:
|
|
18
|
-
persist-credentials: false
|
|
19
|
-
|
|
20
|
-
- name: Set up uv
|
|
21
|
-
uses: astral-sh/setup-uv@v4
|
|
22
|
-
with:
|
|
23
|
-
python-version: "3.10"
|
|
24
|
-
enable-cache: true
|
|
25
|
-
|
|
26
|
-
- name: Check lint
|
|
27
|
-
run: uv run ruff check src tests
|
|
28
|
-
|
|
29
|
-
- name: Check formatting
|
|
30
|
-
run: uv run ruff format --check src tests
|
|
31
|
-
|
|
32
|
-
- name: Run tests
|
|
33
|
-
run: uv run pytest
|
|
34
|
-
|
|
35
|
-
- name: Build docs
|
|
36
|
-
run: uv run sphinx-build -b html docs /tmp/pymax-docs
|
|
37
|
-
|
|
38
|
-
release-build:
|
|
39
|
-
runs-on: ubuntu-latest
|
|
40
|
-
needs: release-checks
|
|
41
|
-
|
|
42
|
-
steps:
|
|
43
|
-
- uses: actions/checkout@v4
|
|
44
|
-
with:
|
|
45
|
-
persist-credentials: false
|
|
46
|
-
|
|
47
|
-
- name: Set up uv
|
|
48
|
-
uses: astral-sh/setup-uv@v4
|
|
49
|
-
with:
|
|
50
|
-
python-version: "3.10"
|
|
51
|
-
enable-cache: true
|
|
52
|
-
|
|
53
|
-
- name: Build release distributions
|
|
54
|
-
run: uv build
|
|
55
|
-
|
|
56
|
-
- name: Check distributions
|
|
57
|
-
run: uv run twine check dist/*
|
|
58
|
-
|
|
59
|
-
- name: Upload distributions
|
|
60
|
-
uses: actions/upload-artifact@v4
|
|
61
|
-
with:
|
|
62
|
-
name: release-dists
|
|
63
|
-
path: dist/
|
|
64
|
-
|
|
65
|
-
pypi-publish:
|
|
66
|
-
runs-on: ubuntu-latest
|
|
67
|
-
needs: release-build
|
|
68
|
-
environment:
|
|
69
|
-
name: pypi
|
|
70
|
-
url: https://pypi.org/project/maxapi-python/
|
|
71
|
-
|
|
72
|
-
permissions:
|
|
73
|
-
contents: read
|
|
74
|
-
id-token: write
|
|
75
|
-
|
|
76
|
-
steps:
|
|
77
|
-
- name: Retrieve release distributions
|
|
78
|
-
uses: actions/download-artifact@v4
|
|
79
|
-
with:
|
|
80
|
-
name: release-dists
|
|
81
|
-
path: dist/
|
|
82
|
-
|
|
83
|
-
- name: Publish release distributions to PyPI
|
|
84
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|