Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bb8a3f0
feat: add join_channel method and update read_message signature to ac…
ink-developer May 29, 2026
d474c5d
feat: update ci
m-xim May 29, 2026
0f497a6
style: refactor code for improved readability and formatting consistency
ink-developer May 29, 2026
c618db4
Merge branch 'dev/2.2.0' into main
m-xim May 29, 2026
c0e0d7b
Merge pull request #53 from m-xim/main
ink-developer May 29, 2026
75fc3d8
docs: update API documentation for Client and WebClient, add device t…
ink-developer May 29, 2026
e58a8c6
feat: implement binding for API models to enhance data handling acros…
ink-developer May 29, 2026
2cd291e
Merge remote-tracking branch 'origin/main' into dev/2.2.0
ink-developer Jun 8, 2026
c9ad0ce
feat: support SMS registration confirmation
ink-developer Jun 10, 2026
acecfce
fix: clarify read receipt message id types
ink-developer Jun 10, 2026
d7dabd3
chore: align ruff formatting for CI
ink-developer Jun 10, 2026
22d7efa
fix: handle non-BMP UTF-16 characters in markdown formatting
Arondy Jun 11, 2026
7d84d01
fix: support web message deletion events
ink-developer Jun 12, 2026
1db3eaa
Merge pull request #63 from Arondy/fix/non-bmp-utf16-positions
ink-developer Jun 12, 2026
29e0e43
feat: add typing event handler
ink-developer Jun 12, 2026
dfb0c5f
feat: add reaction update event handler
ink-developer Jun 12, 2026
6644e2d
feat: add get message method
ink-developer Jun 13, 2026
874c804
feat: add edit message method
ink-developer Jun 13, 2026
c70a342
feat: add get messages method
ink-developer Jun 13, 2026
8346c48
feat: add read and presence events
ink-developer Jun 13, 2026
25cf64a
fix: harden release event and auth handling
ink-developer Jun 13, 2026
b0c5fce
chore: prepare release 2.2.0
ink-developer Jun 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Tests

on:
pull_request:
push:
branches: [main, "dev/**"]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: checks-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v6
Comment on lines +22 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/tests.yml"

echo "== uses entries not pinned to 40-char SHA =="
rg -nP '^\s*uses:\s*[^@\s]+@(?!(?:[0-9a-fA-F]{40})\b)' "$file" || true

echo
echo "== checkout blocks (verify persist-credentials: false is present) =="
rg -nP '^\s*uses:\s*actions/checkout@' -A4 "$file" || true

Repository: MaxApiTeam/PyMax

Length of output: 596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/tests.yml"

echo "== occurrences of persist-credentials =="
rg -n "persist-credentials" "$file" || true

echo
echo "== full blocks around checkout steps (lines 15-35 and 40-60) =="
nl -ba "$file" | sed -n '1,120p' | sed -n '15,60p'

Repository: MaxApiTeam/PyMax

Length of output: 228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/tests.yml"

echo "== occurrences of persist-credentials =="
rg -n "persist-credentials" "$file" || true

echo
echo "== checkout-related region (15-35) =="
cat -n "$file" | sed -n '15,35p'

echo
echo "== checkout-related region (45-65) =="
cat -n "$file" | sed -n '45,65p'

Repository: MaxApiTeam/PyMax

Length of output: 1447


Pin GitHub Actions to commit SHAs and disable checkout credential persistence.

  • .github/workflows/tests.yml uses actions/checkout@v6 and astral-sh/setup-uv@v8.1.0 (tag-based, not full commit SHAs).
  • Both actions/checkout steps lack with: persist-credentials: false.
🔧 Suggested hardening diff
-      - name: Checkout repository
-        uses: actions/checkout@v6
+      - name: Checkout repository
+        uses: actions/checkout@<full-length-commit-sha>
+        with:
+          persist-credentials: false
@@
-      - name: Install uv
-        uses: astral-sh/setup-uv@v8.1.0
+      - name: Install uv
+        uses: astral-sh/setup-uv@<full-length-commit-sha>
@@
-      - name: Checkout repository
-        uses: actions/checkout@v6
+      - name: Checkout repository
+        uses: actions/checkout@<full-length-commit-sha>
+        with:
+          persist-credentials: false
@@
-      - name: Install uv
-        uses: astral-sh/setup-uv@v8.1.0
+      - name: Install uv
+        uses: astral-sh/setup-uv@<full-length-commit-sha>
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 22-23: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 23-23: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/tests.yml around lines 22 - 23, Update the GitHub Actions
workflow step that currently reads "name: Checkout repository" with "uses:
actions/checkout@v6" and any other actions referenced by tag (e.g.,
"astral-sh/setup-uv@v8.1.0") to use specific commit SHAs instead of tags, and
add "with: persist-credentials: false" under each actions/checkout step to
disable credential persistence; ensure you replace the tag references with the
corresponding full commit SHA for actions/checkout and astral-sh/setup-uv and
add the persist-credentials setting for every checkout step in the workflow.

Source: Linters/SAST tools


- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: true

- name: Install development dependencies
run: uv sync --group dev

- name: Check code formatting
run: uv run ruff format --check .

- name: Run Ruff linting
run: uv run ruff check .

tests:
name: Tests / Python ${{ matrix.python-version }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: ${{ matrix.python-version }}
enable-cache: true

- name: Install development dependencies
run: uv sync --group dev

- name: Run tests with coverage
run: |
uv run pytest \
--cov=src/pymax \
--cov-report=term-missing:skip-covered \
--cov-report=markdown-append:$GITHUB_STEP_SUMMARY
18 changes: 18 additions & 0 deletions docs/api/client-client.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Client
======

.. currentmodule:: pymax

TCP-клиент с SMS-авторизацией. Это основной клиент для long-running
подключения, обработчиков событий и mobile API Max.

.. note::

``Client`` поддерживает ``ExtraConfig.device_type`` со значениями
``ANDROID``, ``IOS`` и ``DESKTOP``. Для :meth:`Client.authorize_qr_login`
используйте ``ANDROID`` или ``IOS``: с ``DESKTOP`` подтверждение QR-входа
не работает.

.. autoclass:: Client
:members:
:inherited-members:
14 changes: 14 additions & 0 deletions docs/api/client-config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Client Config
=============

.. currentmodule:: pymax

Настройки, которые используются ``Client`` и ``WebClient``.

.. autoclass:: ExtraConfig
:members:

.. autoclass:: RegistrationConfig
:members:

.. autofunction:: configure_logging
17 changes: 17 additions & 0 deletions docs/api/client-web.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
WebClient
=========

.. currentmodule:: pymax

WebSocket-клиент с QR-авторизацией. Он подходит, когда нужно подключаться как
web-клиент Max.

.. note::

В штатной конфигурации ``WebClient`` использует только ``DeviceType.WEB``.
Параметр ``ExtraConfig.device_type`` предназначен для ``Client`` и не
меняет тип устройства ``WebClient``.

.. autoclass:: WebClient
:members:
:inherited-members:
30 changes: 19 additions & 11 deletions docs/api/client.rst
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
Client API
==========
Clients API
===========

.. currentmodule:: pymax

.. autoclass:: Client
:members:
:inherited-members:
``Client`` и ``WebClient`` используют общий набор высокоуровневых методов,
но отличаются транспортом и способом авторизации:

.. autoclass:: WebClient
:members:
:inherited-members:
``Client``
TCP-клиент с SMS-авторизацией. Поддерживает mobile ``device_type``:
``ANDROID``, ``IOS`` и ``DESKTOP``. Для подтверждения QR-входа через
:meth:`Client.authorize_qr_login` используйте ``ANDROID`` или ``IOS``:
с ``DESKTOP`` этот метод не работает.

.. autoclass:: ExtraConfig
:members:
``WebClient``
WebSocket-клиент с QR-авторизацией. В штатной конфигурации всегда
использует ``DeviceType.WEB``; ``ExtraConfig.device_type`` на него не
влияет.

.. autofunction:: configure_logging
.. toctree::
:maxdepth: 1

client-client
client-web
client-config
54 changes: 53 additions & 1 deletion docs/auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Provider нужен, когда код приходит не из консоли
Кастомный QR handler
--------------------

``QrHandler`` не должен подтверждать QR сам. Его задача - показать ссылку
Обычно ``QrHandler`` не подтверждает QR сам. Его задача - показать ссылку
пользователю: вывести в терминал, отправить в web UI или положить в лог.

.. code-block:: python
Expand All @@ -110,6 +110,33 @@ Provider нужен, когда код приходит не из консоли
qr_provider=PrintQrUrl(),
)

Если у вас уже есть запущенный и авторизованный ``Client``, QR-ссылку можно
подтвердить программно через ``authorize_qr_login()``. Это удобно, когда
``WebClient`` получает QR, а основной mobile-клиент должен разрешить вход:

.. code-block:: python

from pymax import Client, WebClient


class ConfirmQrWithClient:
def __init__(self, client: Client) -> None:
self.client = client

async def show_qr(self, qr_url: str) -> None:
await self.client.authorize_qr_login(qr_url)


mobile_client = Client(phone="+79990000000", work_dir="cache")
# mobile_client должен уже пройти start/login в вашей программе.
web_client = WebClient(qr_provider=ConfirmQrWithClient(mobile_client))

``mobile_client`` должен быть уже авторизован к моменту вызова
``show_qr()``. Для такого подтверждения используйте ``Client`` с
``device_type`` ``ANDROID`` или ``IOS``; при ``DESKTOP`` метод
``authorize_qr_login()`` не работает. ``WebClient`` в штатной конфигурации
всегда использует ``WEB``.

Полный кастомный AuthFlow
-------------------------

Expand Down Expand Up @@ -146,6 +173,31 @@ Flow должен иметь async-метод ``authenticate(app)`` и верн
Если вам нужно только передать готовый token, проще использовать
``ExtraConfig(token="TOKEN")``. Тогда custom flow не нужен.

Регистрация нового аккаунта
---------------------------

Если номер ещё не зарегистрирован в Max, после SMS-кода сервер возвращает
токен регистрации. Чтобы стандартный ``SmsAuthFlow`` завершил создание
аккаунта, передайте имя через ``RegistrationConfig``:

.. code-block:: python

from pymax import Client, ExtraConfig, RegistrationConfig

client = Client(
phone="+79990000000",
extra_config=ExtraConfig(
registration_config=RegistrationConfig(
first_name="Max",
last_name="User",
)
),
)

Для уже существующего аккаунта эта настройка не используется. Если Max
вернул токен регистрации, а ``registration_config`` не задан, авторизация
завершится с ``RuntimeError``.

Управление 2FA
--------------

Expand Down
73 changes: 71 additions & 2 deletions docs/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ Client

``Client``
TCP-клиент с авторизацией по телефону и SMS-коду. Это основной вариант.
Поддерживает ``device_type`` ``ANDROID``, ``IOS`` и ``DESKTOP``.

``WebClient``
WebSocket-клиент с QR-авторизацией.
WebSocket-клиент с QR-авторизацией. В штатной конфигурации работает как
``WEB``-устройство.

Жизненный цикл
--------------
Expand Down Expand Up @@ -85,6 +87,32 @@ Client
``extra_config``
Настройки соединения, логов, reconnect, token, device/user-agent и sync.

Тип устройства
---------------

``Client`` по умолчанию использует ``DeviceType.ANDROID``. Если нужно
поменять тип устройства, передайте ``device_type`` через ``ExtraConfig``:

.. code-block:: python

from pymax import Client, ExtraConfig
from pymax.api.session.enums import DeviceType

client = Client(
phone="+79990000000",
work_dir="cache",
extra_config=ExtraConfig(device_type=DeviceType.IOS),
)

Для ``Client`` доступны ``ANDROID``, ``IOS`` и ``DESKTOP``. Практический
нюанс: ``authorize_qr_login()`` подтверждает QR-вход только из mobile-режима,
поэтому для него используйте ``ANDROID`` или ``IOS``. С ``DESKTOP`` этот метод
не работает.

``WebClient`` сам использует ``DeviceType.WEB``. Параметр
``ExtraConfig.device_type`` относится к ``Client`` и не меняет тип устройства
``WebClient``.

Авторизация
-----------

Expand Down Expand Up @@ -119,6 +147,47 @@ Client
extra_config=ExtraConfig(token="TOKEN"),
)

Подтверждение QR-входа
----------------------

``Client`` может подтверждать QR-вход по ссылке через
``authorize_qr_login()``. Это полезно, когда QR-ссылку показал другой клиент
или ваш UI получил ее от ``WebClient``:

.. code-block:: python

async def confirm_qr(client: Client, qr_link: str) -> None:
ok = await client.authorize_qr_login(qr_link)
print("QR подтвержден:", ok)

``Client`` для такого подтверждения должен быть уже авторизован. Также
проверьте ``device_type``: используйте ``ANDROID`` или ``IOS``, не
``DESKTOP``.

Если QR создает ``WebClient``, ссылку можно перехватить в своем
``QrHandler`` и подтвердить ее уже запущенным ``Client``:

.. code-block:: python

from pymax import Client, WebClient


class ConfirmQrWithClient:
def __init__(self, client: Client) -> None:
self.client = client

async def show_qr(self, qr_url: str) -> None:
await self.client.authorize_qr_login(qr_url)


mobile_client = Client(phone="+79990000000", work_dir="cache")
# mobile_client должен уже пройти start/login в вашей программе.
web_client = WebClient(
work_dir="cache",
session_name="web.db",
qr_provider=ConfirmQrWithClient(mobile_client),
)

Сессия и sync-state
-------------------

Expand Down Expand Up @@ -226,7 +295,7 @@ Debug-логи показывают handshake, login, входящие собы
``close_all_sessions()``. Подробнее: :doc:`account`.

Auth
``set_2fa()`` и ``remove_2fa()`` для управления паролем 2FA. Подробнее:
``set_2fa()``, ``remove_2fa()`` и ``authorize_qr_login()``. Подробнее:
:doc:`auth`.

Частые ошибки
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ PyMax - асинхронная Python-библиотека для Max API. Он
:maxdepth: 1
:caption: Новости

release-2-2-0
release-2-1-3
release-2-1-2
release-2-1-1
Expand Down
Loading