Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
67896d0
fix: support binary WebSocket protocol
ink-developer Jul 5, 2026
710ed84
fix: parse video URLs by MP4 quality
ink-developer Jul 5, 2026
7cf3671
fix: ReactionUpdateEvent optional fields
ink-developer Jul 5, 2026
edf68d3
feat: add device fingerprinting to authentication
ink-developer Jul 6, 2026
a5e10d7
feat: add account presence control
ink-developer Jul 6, 2026
d43079b
feat: add poll attachments support
ink-developer Jul 6, 2026
8ec73c3
Добавлена возможность запрашивать список членов чата/группы.
Gorily Jul 16, 2026
64d8ee0
Реализация маркера
Gorily Jul 17, 2026
cfb27fd
Реализация count
Gorily Jul 20, 2026
5bef59a
chore: update protocol enums
ink-developer Jul 20, 2026
7f6e7bd
feat: support two-step mobile login
ink-developer Jul 20, 2026
6c90d4e
fix: parse poll vote details
ink-developer Jul 20, 2026
f16f668
chore: update Android app fingerprints
ink-developer Jul 20, 2026
ece7ad8
Merge pull request #75 from Gorily/feature/get-chat-members
ink-developer Jul 20, 2026
971fba0
feat: add add_admin method
ink-developer Jul 20, 2026
bf13d9b
fix: ReactionUpdateEvent.counters annotation
ink-developer Jul 20, 2026
94b60ab
feat: support voice message uploads
ink-developer Jul 20, 2026
2667f91
fix: StickerAttachment set_id type
ink-developer Jul 27, 2026
faa5ce9
fix: webosocket cap
ink-developer Jul 27, 2026
958fa06
fix: some minor fixes
ink-developer Jul 27, 2026
25e156c
fix: handle missing handshake calls seed
ink-developer Jul 29, 2026
80fb9cf
feat: add video note uploads
ink-developer Jul 29, 2026
b373f0c
feat: add is_update_available
ink-developer Jul 29, 2026
c73d520
fix: some minor fixes
ink-developer Aug 3, 2026
e4038c6
fix: stop client cleanly
ink-developer Aug 3, 2026
a80b63f
fix: correct public API types
ink-developer Aug 3, 2026
5713a82
feat: add poll voting
ink-developer Aug 3, 2026
8203548
fix: test fixes
ink-developer Aug 3, 2026
fd1133c
chore: prepare release 2.4.0
ink-developer Aug 3, 2026
6f34ae1
docs: document polls and media formats
ink-developer Aug 3, 2026
d2cc1c8
fix: reauthenticate after session revocation
ink-developer Aug 3, 2026
e3a9053
feat: add account privacy settings
ink-developer Aug 4, 2026
b1468fd
chore: update client fingerprints
ink-developer Aug 4, 2026
4b82729
docs: finalize 2.4.0 release notes
ink-developer Aug 4, 2026
f73fec6
fix: remove useless test
ink-developer Aug 4, 2026
3bcc697
fix: minor fixes before release
ink-developer Aug 4, 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
10 changes: 10 additions & 0 deletions docs/account.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ Account
photo_token="PHOTO_TOKEN",
)

Статус присутствия
------------------

``set_presence()`` меняет статус, который будет использован при следующем
login или ping:

.. code-block:: python

client.set_presence(online=True)

Папки чатов
-----------

Expand Down
8 changes: 8 additions & 0 deletions docs/api/files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ Files API
.. autoclass:: pymax.Video
:members:
:show-inheritance:

.. autoclass:: pymax.VideoNote
:members:
:show-inheritance:

.. autoclass:: pymax.Voice
:members:
:show-inheritance:
34 changes: 34 additions & 0 deletions docs/chats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,40 @@ login/sync, а также методы для загрузки, создания
``invite()`` работает только для групп и каналов. Для личного диалога тип чата
не подходит, и метод завершится ошибкой.

Участники и администраторы канала
---------------------------------

``get_chat_members()`` возвращает одну страницу участников и маркер следующей:

.. code-block:: python

members, marker = await client.get_chat_members(chat_id=123456, count=50)
for member in members:
print(member.contact.id)

if marker:
next_members, marker = await client.get_chat_members(
chat_id=123456,
marker=marker,
count=50,
)

Назначить администратора канала можно с явным набором прав:

.. code-block:: python

from pymax.api.chats import ChannelPermissions

await client.add_admin(
chat_id=123456,
user_id=111,
permissions=[
ChannelPermissions.POST_MESSAGE,
ChannelPermissions.EDIT_MESSAGE,
ChannelPermissions.DELETE_MESSAGE,
],
)

Настройки и профиль группы
--------------------------

Expand Down
26 changes: 24 additions & 2 deletions docs/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ Client
6. Вызывается ``on_start``.
7. Клиент слушает события до закрытия соединения или отмены задачи.

Если ``start()`` запущен отдельной задачей, ``stop()`` штатно закрывает
соединение и завершает эту задачу без ``CancelledError``:

.. code-block:: python

import asyncio

task = asyncio.create_task(client.start())
# ... работа приложения ...
await client.stop()
await task

Данные после login
------------------

Expand All @@ -54,6 +66,15 @@ Client
Для актуализации используйте явные методы: ``fetch_chats()``, ``get_chat()``,
``get_users()``, ``fetch_users()`` и ``fetch_history()``.

После handshake можно проверить, требует ли Max обновления версии приложения:

.. code-block:: python

@client.on_start()
async def on_start(client: Client) -> None:
if client.is_update_available():
print("Для выбранной версии приложения доступно обновление")

Создание клиента
----------------

Expand Down Expand Up @@ -300,13 +321,14 @@ Debug-логи показывают handshake, login, входящие собы
Клиент собирает несколько API-направлений:

Сообщения
``send_message()``, ``forward_message()``, ``fetch_history()``,
``send_message()``, ``forward_message()``, ``fetch_history()``, ``vote_poll()``,
``delete_message()``, ``pin_message()``, ``read_message()``, реакции и
получение URL для входящих файлов/видео.

Чаты
``get_chat()``, ``fetch_chats()``, создание групп, invite-ссылки,
участники, настройки групп, удаление чатов и выход из групп/каналов.
участники, назначение администраторов, настройки групп, удаление чатов и
выход из групп/каналов.

Пользователи
``get_user()``, ``get_users()``, ``fetch_users()``, ``search_by_phone()``,
Expand Down
67 changes: 62 additions & 5 deletions docs/files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Files
Что это
-------

Для отправки вложений PyMax использует три класса:
Для отправки вложений PyMax использует пять основных классов:

``Photo``
Фото. Проверяет расширение и MIME-тип.
Expand All @@ -15,14 +15,22 @@ Files
``File``
Обычный файл. Тоже загружается чанками и ждет событие готовности.

``Voice``
Голосовое сообщение. Поддерживается только формат OGG; PyMax не
конвертирует другие аудиоформаты.

``VideoNote``
Круглое видеосообщение. Можно передать длительность вручную или установить
extra ``video`` для автоматического определения.

Как отправить файл
------------------

.. code-block:: python

import asyncio

from pymax import Client, File, Photo, Video
from pymax import Client, File, Photo, Video, VideoNote, Voice

client = Client(phone="+79990000000", work_dir="cache")

Expand All @@ -46,6 +54,11 @@ Files
attachments=[Video(path="clip.mp4")],
)

await chat.answer(attachments=[Voice(path="voice.ogg")])
await chat.answer(
attachments=[VideoNote(path="circle.mp4", duration=4200)]
)


asyncio.run(client.start())

Expand All @@ -59,17 +72,57 @@ Files
Photo(path="image.jpg")
File(url="https://example.com/report.pdf")
Video(raw=b"...", name="clip.mp4")
Voice(path="voice.ogg")
VideoNote(path="circle.mp4", duration=4200)

Для ``raw`` обязательно указывайте ``name``. Для ``File``, ``Video``,
``Voice`` и ``VideoNote`` имя берется из ``path`` или ``url``, если не
передано явно.

Если длительность ``VideoNote`` не передана, установите дополнительную
зависимость:

.. code-block:: console

Для ``raw`` обязательно указывайте ``name``. Для ``File`` и ``Video`` имя
берется из ``path`` или ``url``, если не передано явно.
uv add "maxapi-python[video]"

Формат Voice и VideoNote
------------------------

Для ``Voice`` используйте готовый OGG-файл. Простого переименования MP3, WAV
или другого аудиофайла в ``.ogg`` недостаточно: PyMax загружает исходные байты
без перекодирования.

``VideoNote`` также не перекодирует видео. Для совместимости с официальным
клиентом 26.21.1 рекомендуется следующий формат:

* контейнер MP4;
* видео H.264/AVC, 480x480, 30 FPS и bitrate около 1 024 000 bit/s;
* pixel format ``yuv420p`` при подготовке через FFmpeg;
* ключевой кадр примерно раз в секунду, то есть GOP около 30 кадров;
* аудио AAC в том же MP4-контейнере;
* длительность до 60 секунд.

Официальный клиент задает квадратное разрешение, фиксированные 30 FPS и
максимальную длительность через server config. Нижняя граница в одну секунду
не является подтвержденным ограничением upload API, поэтому PyMax ее не
проверяет.

Поворот лучше физически применить при перекодировании и убрать rotation
metadata: так файл меньше зависит от того, как конкретный клиент обработает
orientation hint. H.264 profile и level специально фиксировать не требуется.
``faststart`` официальный recorder явно не включает; при самостоятельной
подготовке файла его можно использовать, но для PyMax это не обязательное
условие.

Как работает upload
-------------------

1. PyMax запрашивает у Max временный upload URL.
2. Читает файл из ``path``, ``url`` или ``raw``.
3. Загружает данные HTTP-запросом.
4. Для ``File`` и ``Video`` ждет служебное событие готовности до 60 секунд.
4. Для ``File``, ``Video``, ``Voice`` и ``VideoNote`` ждет служебное событие
готовности до 60 секунд.
5. Подставляет token/file_id/video_id в отправляемое сообщение.

Фото проходит проще: после HTTP-upload PyMax сразу достает token из ответа.
Expand Down Expand Up @@ -115,3 +168,7 @@ Files
Upload-сервис не получил нужный ответ от Max. Включите ``DEBUG``-логи:
часто причина в недоступном URL, неверном размере файла, timeout или в том,
что событие готовности файла не пришло за 60 секунд.

``Automatic video duration detection requires the 'video' extra``
Передайте ``duration`` в миллисекундах или установите
``maxapi-python[video]``.
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-4-0
release-2-3-1
release-2-3-0
release-2-2-0
Expand Down
57 changes: 54 additions & 3 deletions docs/messages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,57 @@ Messages
source_chat_id=123456,
)

Опросы
------

Опрос можно отправить без текста. Настройки объединяются оператором ``|``:

.. code-block:: python

from pymax.types import Poll, PollAnswer, PollFlags

await client.send_message(
chat_id=123456,
attachments=[
Poll(
title="Какой вариант выбрать?",
answers=[
PollAnswer(text="Первый"),
PollAnswer(text="Второй"),
],
settings=(
PollFlags.FLAG_SETTINGS_ANONYMOUS
| PollFlags.FLAG_SETTINGS_REVOTE
),
)
],
)

Для голосования нужны ID сообщения, опроса и вариантов из входящего
``PollAttachment``:

.. code-block:: python

from pymax import Message
from pymax.types import PollAttachment

@client.on_message()
async def vote(message: Message, client: Client) -> None:
if message.chat_id is None:
return

for attach in message.attaches:
if isinstance(attach, PollAttachment):
answer_id = attach.answers[0].answer_id
if answer_id is not None:
state = await client.vote_poll(
chat_id=message.chat_id,
message_id=message.id,
poll_id=attach.poll_id,
answer_ids=[answer_id],
)
print(state.total)

Ответ, реакции, удаление и прочтение
----------------------------------------

Expand Down Expand Up @@ -147,7 +198,7 @@ Messages
.. code-block:: python

history = await client.fetch_history(chat_id=123456, backward=50)
for message in history or []:
for message in history:
print(message.id, message.text)

``fetch_history()`` принимает ``item_type``. По умолчанию используются обычные
Expand Down Expand Up @@ -178,8 +229,8 @@ Max присылает разные формы событий. Некоторы
--------

Входящие вложения лежат в ``message.attaches``. Тип вложения определяется по
полю ``type``: фото, видео, файл, стикер, аудио, контакт, звонок, share или
inline-клавиатура.
полю ``type``: фото, видео, файл, стикер, аудио, опрос, контакт, звонок, share
или inline-клавиатура.

.. code-block:: python

Expand Down
Loading