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

Commit e86c453

Browse files
committed
фиксы сокета + добавил аппрув qr логина
1 parent 146630d commit e86c453

4 files changed

Lines changed: 51 additions & 11 deletions

File tree

src/pymax/mixins/auth.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from pymax.exceptions import Error
1010
from pymax.payloads import (
11+
ApproveQrLoginPayload,
1112
Capability,
1213
CheckPasswordChallengePayload,
1314
CreateTrackPayload,
@@ -222,8 +223,8 @@ def version_tuple(v: str) -> tuple[int, ...]:
222223
async def _login(self) -> None:
223224
self.logger.info("Starting login flow")
224225

225-
if self.user_agent.device_type == DeviceType.WEB.value and self._ws:
226-
if not self._validate_version(self.user_agent.app_version, "25.12.13"):
226+
if self.headers.device_type == DeviceType.WEB.value and self._ws:
227+
if not self._validate_version(self.headers.app_version, "25.12.13"):
227228
self.logger.error("Your app version is too old")
228229
raise ValueError("Your app version is too old")
229230

@@ -659,3 +660,33 @@ async def set_password(
659660
MixinsUtils.handle_error(data)
660661

661662
return True
663+
664+
async def approve_qr_login(self, qr_link: str) -> bool:
665+
"""
666+
Подтверждает вход по QR-коду, используя предоставленную ссылку.
667+
668+
:param qr_link: Ссылка на QR-код для подтверждения входа.
669+
:type qr_link: str
670+
:return: True, если вход успешно подтвержден, иначе выбрасывается исключение.
671+
:rtype: bool
672+
"""
673+
self.logger.info("Approving QR login")
674+
675+
payload = ApproveQrLoginPayload(
676+
qr_link=qr_link,
677+
).model_dump(by_alias=True)
678+
679+
data = await self._send_and_wait(
680+
opcode=Opcode.AUTH_QR_APPROVE,
681+
payload=payload,
682+
)
683+
payload = data.get("payload", {})
684+
if payload and payload.get("error"):
685+
MixinsUtils.handle_error(data)
686+
687+
self.logger.debug(
688+
"QR login approval response opcode=%s seq=%s",
689+
data.get("opcode"),
690+
data.get("seq"),
691+
)
692+
return True

src/pymax/mixins/socket.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def _unpack_packet(self, data: bytes) -> dict[str, Any] | None:
7070
"cmd": cmd,
7171
"seq": seq,
7272
"opcode": opcode,
73-
"payload": payload,
73+
"payload": payload, #
7474
}
7575

7676
def _pack_packet(
@@ -80,17 +80,20 @@ def _pack_packet(
8080
seq: int,
8181
opcode: int,
8282
payload: dict[str, Any],
83+
compress: bool = False,
8384
) -> bytes:
8485
ver_b = ver.to_bytes(1, "big")
8586
cmd_b = cmd.to_bytes(1, "big")
8687
seq_b = seq.to_bytes(2, "big")
8788
opcode_b = opcode.to_bytes(2, "big")
88-
payload_bytes: bytes | None = msgpack.packb(payload)
89-
if payload_bytes is None:
90-
payload_bytes = b""
89+
90+
payload_bytes = msgpack.packb(payload) or b""
91+
92+
comp_flag = 1 if compress else 0
9193
payload_len = len(payload_bytes) & 0xFFFFFF
92-
self.logger.debug("Packing message: payload size=%d bytes", len(payload_bytes))
93-
payload_len_b = payload_len.to_bytes(4, "big")
94+
packed_len = (comp_flag << 24) | payload_len
95+
payload_len_b = packed_len.to_bytes(4, "big")
96+
9497
return ver_b + cmd_b + seq_b + opcode_b + payload_len_b + payload_bytes
9598

9699
def _create_socket_with_proxy(self, proxy: str) -> socket.socket:
@@ -260,6 +263,7 @@ async def connect(self, user_agent: UserAgentPayload | None = None) -> dict[str,
260263
"recv_task done: cancelled=%s, exc=%r", t.cancelled(), t.exception()
261264
)
262265
)
266+
263267
self._outgoing_task = self._create_safe_task(
264268
self._outgoing_loop(), name="outgoing_loop socket task"
265269
)
@@ -383,7 +387,7 @@ async def _recv_loop(self) -> None:
383387
for data_item in datas:
384388
seq = data_item.get("seq")
385389

386-
if self._handle_pending(seq % 256 if seq is not None else None, data_item):
390+
if self._handle_pending(seq, data_item):
387391
continue
388392

389393
if self._incoming is not None:
@@ -469,7 +473,7 @@ async def _send_and_wait(
469473
msg = self._make_message(opcode, payload, cmd)
470474
loop = asyncio.get_running_loop()
471475
fut: asyncio.Future[dict[str, Any]] = loop.create_future()
472-
seq_key = msg["seq"] % 256
476+
seq_key = msg["seq"]
473477

474478
old_fut = self._pending.get(seq_key)
475479
if old_fut and not old_fut.done():
@@ -510,7 +514,7 @@ async def _send_and_wait(
510514
raise SocketSendError from exc
511515

512516
finally:
513-
self._pending.pop(msg["seq"] % 256, None)
517+
self._pending.pop(msg["seq"], None)
514518

515519
@override
516520
async def _get_chat(self, chat_id: int) -> Chat | None:

src/pymax/payloads.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,7 @@ class AddContactByPhonePayload(CamelModel):
409409

410410
class ContactPresencePayload(CamelModel):
411411
contact_ids: list[int]
412+
413+
414+
class ApproveQrLoginPayload(CamelModel):
415+
qr_link: str

src/pymax/static/enum.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class Opcode(int, Enum):
144144

145145
GET_QR = 288 # ✅
146146
GET_QR_STATUS = 289 # ✅
147+
AUTH_QR_APPROVE = 290 # ✅
147148
LOGIN_BY_QR = 291 # ✅
148149

149150

0 commit comments

Comments
 (0)