Skip to content
This repository was archived by the owner on Feb 11, 2026. It is now read-only.

Commit defa27f

Browse files
committed
фиксы апи, добавли typing контекст менеджер ну и по мелочи
1 parent d6cbc69 commit defa27f

File tree

8 files changed

+81
-4
lines changed

8 files changed

+81
-4
lines changed

src/pymax/interfaces.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ async def _handle_file_upload(self, data: dict[str, Any]) -> None:
310310
self.logger.debug("Fulfilled file upload waiter for %s=%s", key, id_)
311311

312312
async def _send_notification_response(self, chat_id: int, message_id: str) -> None:
313-
if self._socket is None or not self.is_connected:
313+
if self._socket is not None:
314314
return
315315
await self._queue_message(
316316
opcode=Opcode.NOTIF_MESSAGE,

src/pymax/mixins/socket.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,21 @@ async def _get_chat(self, chat_id: int) -> Chat | None:
481481
if chat.id == chat_id:
482482
return chat
483483
return None
484+
485+
async def _send_only(self, opcode: Opcode, payload: dict[str, Any], cmd: int = 0) -> None:
486+
async def send_task():
487+
async with self._sock_lock:
488+
if not self.is_connected or self._socket is None:
489+
return
490+
msg = self._make_message(opcode, payload, cmd)
491+
packet = self._pack_packet(
492+
msg["ver"],
493+
msg["cmd"],
494+
msg["seq"],
495+
msg["opcode"],
496+
msg["payload"],
497+
)
498+
loop = asyncio.get_running_loop()
499+
await loop.run_in_executor(None, lambda: self._socket.sendall(packet))
500+
501+
asyncio.create_task(send_task())

src/pymax/mixins/user.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from collections.abc import AsyncGenerator
2+
from contextlib import asynccontextmanager
13
from typing import Any, Literal
24

35
from pymax.exceptions import Error, ResponseError, ResponseStructureError
@@ -7,9 +9,10 @@
79
ContactPresencePayload,
810
FetchContactsPayload,
911
SearchByPhonePayload,
12+
SetTypingPayload,
1013
)
1114
from pymax.protocols import ClientProtocol
12-
from pymax.static.enum import ContactAction, Opcode
15+
from pymax.static.enum import ContactAction, Opcode, TypingType
1316
from pymax.types import Contact, Presence, Session, User
1417
from pymax.utils import MixinsUtils
1518

@@ -267,3 +270,28 @@ async def get_contact_presence(self, contact_ids: list[int]) -> list[Presence]:
267270
Presence(user_id=int(contact_id), last_seen=info.get("seen"))
268271
for contact_id, info in presence.items()
269272
]
273+
274+
@asynccontextmanager
275+
async def typing(
276+
self, chat_id: int, typing_type: TypingType = TypingType.TEXT
277+
) -> AsyncGenerator[None, Any]:
278+
"""
279+
Устанавливает состояние "печатает" для указанного чата.
280+
281+
:param chat_id: ID чата.
282+
:type chat_id: int
283+
:param typing_type: Тип состояния "печатает".
284+
:type typing_type: TypingType
285+
:yields: None
286+
:rtype: None
287+
"""
288+
self.logger.debug("Set typing for chat %s", chat_id)
289+
290+
payload = SetTypingPayload(
291+
chat_id=chat_id,
292+
type=typing_type,
293+
).model_dump(by_alias=True)
294+
await self._send_only(opcode=Opcode.MSG_TYPING, payload=payload)
295+
self.logger.debug("Set typing for chat %s success (maybe?)", chat_id)
296+
yield
297+
self.logger.debug("Set typing for chat %s done", chat_id)

src/pymax/mixins/websocket.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,9 @@ async def _get_chat(self, chat_id: int) -> Chat | None:
152152
if chat.id == chat_id:
153153
return chat
154154
return None
155+
156+
async def _send_only(self, opcode: Opcode, payload: dict[str, Any], cmd: int = 0) -> None:
157+
msg = self._make_message(opcode, payload, cmd)
158+
packet = json.dumps(msg)
159+
160+
asyncio.create_task(self.ws.send(packet))

src/pymax/payloads.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@
1515
DEFAULT_TIMEZONE,
1616
DEFAULT_USER_AGENT,
1717
)
18-
from pymax.static.enum import AttachType, AuthType, Capability, ContactAction, ReadAction
18+
from pymax.static.enum import (
19+
AttachType,
20+
AuthType,
21+
Capability,
22+
ContactAction,
23+
ReadAction,
24+
TypingType,
25+
)
1926

2027

2128
def to_camel(string: str) -> str:
@@ -413,3 +420,8 @@ class ContactPresencePayload(CamelModel):
413420

414421
class ApproveQrLoginPayload(CamelModel):
415422
qr_link: str
423+
424+
425+
class SetTypingPayload(CamelModel):
426+
chat_id: int
427+
typing_type: TypingType = Field(default=TypingType.TEXT, alias="type")

src/pymax/protocols.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,7 @@ def _create_safe_task(
126126
self, coro: Awaitable[Any], name: str | None = None
127127
) -> asyncio.Task[Any]:
128128
pass
129+
130+
@abstractmethod
131+
async def _send_only(self, opcode: Opcode, payload: dict[str, Any], cmd: int = 0) -> None:
132+
pass

src/pymax/static/constant.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
DEFAULT_SCREEN: Final[str] = choice(SCREEN_SIZES)
8181
DEFAULT_OS_VERSION: Final[str] = choice(OS_VERSIONS)
8282
DEFAULT_USER_AGENT: Final[str] = ua_generator.generate().text
83-
DEFAULT_BUILD_NUMBER: Final[int] = 0x9E2A
83+
DEFAULT_BUILD_NUMBER: Final[int] = 40490
8484
DEFAULT_CLIENT_SESSION_ID: Final[int] = randint(1, 15)
8585
DEFAULT_TIMEZONE: Final[str] = choice(TIMEZONES)
8686
DEFAULT_CHAT_MEMBERS_LIMIT: Final[int] = 50

src/pymax/static/enum.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,12 @@ class Capability(int, Enum):
231231
SECOND_FACTOR_PASSWORD_ENABLED = 2
232232
SECOND_FACTOR_HAS_EMAIL = 3
233233
SECOND_FACTOR_HAS_HINT = 4
234+
235+
236+
class TypingType(str, Enum):
237+
TEXT = "TEXT"
238+
AUDIO = "AUDIO"
239+
FILE = "FILE"
240+
PHOTO = "PHOTO"
241+
STICKER = "STICKER"
242+
VIDEO = "VIDEO"

0 commit comments

Comments
 (0)