From 2f382214f0b36bc34575ac192a5638f32d65d9c9 Mon Sep 17 00:00:00 2001 From: Boris Talovikov Date: Sat, 1 Nov 2025 11:59:17 +0500 Subject: [PATCH 1/2] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F,?= =?UTF-8?q?=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D0=B0=D0=BA=D1=82=D0=B0=20=D0=B2=20=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pymax/mixins/user.py | 73 ++++++++++++++++++++++++++++++++++++++-- src/pymax/payloads.py | 7 +++- src/pymax/static/enum.py | 5 +++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/pymax/mixins/user.py b/src/pymax/mixins/user.py index 32d777c..fd591d3 100644 --- a/src/pymax/mixins/user.py +++ b/src/pymax/mixins/user.py @@ -1,7 +1,14 @@ +from typing import Any, Literal + +from pymax.exceptions import ResponseError, ResponseStructureError from pymax.interfaces import ClientProtocol -from pymax.payloads import FetchContactsPayload, SearchByPhonePayload -from pymax.static.enum import Opcode -from pymax.types import Session, User +from pymax.payloads import ( + ContactActionPayload, + FetchContactsPayload, + SearchByPhonePayload, +) +from pymax.static.enum import ContactAction, Opcode +from pymax.types import Contact, Session, User class UserMixin(ClientProtocol): @@ -142,3 +149,63 @@ async def get_sessions(self) -> list[Session] | None: except Exception: self.logger.exception("Fetching sessions failed") return None + + async def _contact_action( + self, payload: ContactActionPayload + ) -> dict[str, Any]: + """ + Действия с контактом + + Args: + payload (ContactActionPayload): Полезная нагрузка + + Return: + Полезная нагрузка ответа + """ + data = await self._send_and_wait( + opcode=Opcode.CONTACT_UPDATE, # 34 + payload=payload.model_dump(by_alias=True), + ) + response_payload = data.get("payload") + if not isinstance(response_payload, dict): + raise ResponseStructureError("Invalid response structure") + if error := response_payload.get("error"): + raise ResponseError(error) + return response_payload + + async def add_contact(self, contact_id: int) -> Contact: + """ + Добавляет контакт в список контактов + + Args: + contact_id (int): ID контакта + + Returns: + Contact: Объект контакта, иначе будут выброшены исключения + """ + payload = await self._contact_action( + ContactActionPayload( + contact_id=contact_id, action=ContactAction.ADD + ) + ) + contact_dict = payload.get("contact") + if isinstance(contact_dict, dict): + return Contact.from_dict(contact_dict) + raise ResponseStructureError("Wrong contact structure in response") + + async def remove_contact(self, contact_id: int) -> Literal[True]: + """ + Удаляет контакт из списка контактов + + Args: + contact_id (int): ID контакта + + Returns: + True если успешно, иначе будут выброшены исключения + """ + await self._contact_action( + ContactActionPayload( + contact_id=contact_id, action=ContactAction.REMOVE + ) + ) + return True diff --git a/src/pymax/payloads.py b/src/pymax/payloads.py index c4dfc6f..be97b3e 100644 --- a/src/pymax/payloads.py +++ b/src/pymax/payloads.py @@ -13,7 +13,7 @@ DEFAULT_TIMEZONE, DEFAULT_USER_AGENT, ) -from pymax.static.enum import AttachType, AuthType +from pymax.static.enum import AttachType, AuthType, ContactAction def to_camel(string: str) -> str: @@ -278,3 +278,8 @@ class UserAgentPayload(BaseModel): class ReworkInviteLinkPayload(CamelModel): revoke_private_link: bool = True chat_id: int + + +class ContactActionPayload(CamelModel): + contact_id: int + action: ContactAction diff --git a/src/pymax/static/enum.py b/src/pymax/static/enum.py index 70d29b4..8eb7d53 100644 --- a/src/pymax/static/enum.py +++ b/src/pymax/static/enum.py @@ -204,3 +204,8 @@ class MarkupType(StrEnum): ITALIC = "*" UNDERLINE = "__" STRIKETHROUGH = "~~" + + +class ContactAction(StrEnum): + ADD = "ADD" + REMOVE = "REMOVE" From ba66411c657d1f4c556ee385a7b6ce91db8827e6 Mon Sep 17 00:00:00 2001 From: Boris Talovikov Date: Sat, 1 Nov 2025 12:24:23 +0500 Subject: [PATCH 2/2] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=B2=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=20=D0=B1=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pymax/__init__.py | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/pymax/__init__.py b/src/pymax/__init__.py index 37c7bb4..419e87f 100644 --- a/src/pymax/__init__.py +++ b/src/pymax/__init__.py @@ -9,14 +9,22 @@ from .exceptions import ( InvalidPhoneError, LoginError, + ResponseError, + ResponseStructureError, + SocketNotConnectedError, + SocketSendError, WebSocketNotConnectedError, ) from .static.enum import ( AccessType, + AttachType, AuthType, ChatType, + ContactAction, DeviceType, ElementType, + FormattingType, + MarkupType, MessageStatus, MessageType, Opcode, @@ -24,10 +32,26 @@ from .types import ( Channel, Chat, + Contact, + ControlAttach, Dialog, Element, + FileAttach, + FileRequest, + Me, + Member, Message, + MessageLink, + Name, + Names, + PhotoAttach, + Presence, + ReactionCounter, + ReactionInfo, + Session, User, + VideoAttach, + VideoRequest, ) __author__ = "ink-developer" @@ -35,19 +59,43 @@ __all__ = [ # Перечисления и константы "AccessType", + "AttachType", "AuthType", + "ContactAction", + "FormattingType", + "MarkupType", # Типы данных "Channel", "Chat", "ChatType", + "Contact", + "ControlAttach", "DeviceType", "Dialog", "Element", "ElementType", + "FileAttach", + "FileRequest", + "Me", + "Member", + "MessageLink", + "Name", + "Names", + "PhotoAttach", + "Presence", + "ReactionCounter", + "ReactionInfo", + "Session", + "VideoAttach", + "VideoRequest", # Исключения "InvalidPhoneError", "LoginError", "WebSocketNotConnectedError", + "ResponseError", + "ResponseStructureError", + "SocketNotConnectedError", + "SocketSendError", # Клиент "MaxClient", "Message",