maxapi-python 0.1.2__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ import asyncio
2
+ import json
3
+ import random
4
+ import time
5
+
6
+ from pymax.interfaces import ClientProtocol
7
+ from pymax.navigation import Navigation
8
+ from pymax.payloads import (
9
+ NavigationEventParams,
10
+ NavigationEventPayload,
11
+ NavigationPayload,
12
+ )
13
+ from pymax.static import Opcode
14
+
15
+
16
+ class TelemetryMixin(ClientProtocol):
17
+ async def _send_navigation_event(
18
+ self, events: list[NavigationEventPayload]
19
+ ) -> None:
20
+ try:
21
+ payload = NavigationPayload(events=events).model_dump(by_alias=True)
22
+ data = await self._send_and_wait(
23
+ opcode=Opcode.LOG,
24
+ payload=payload,
25
+ )
26
+
27
+ payload_data = data.get("payload")
28
+
29
+ if payload_data and (error := payload_data.get("error")):
30
+ self.logger.error("Failed to send navigation event: %s", error)
31
+ return
32
+ except Exception:
33
+ self.logger.warning("Failed to send navigation event", exc_info=True)
34
+ return
35
+
36
+ async def _send_cold_start(self) -> None:
37
+ if not self.me:
38
+ self.logger.error("Cannot send cold start, user not set")
39
+ return
40
+
41
+ payload = NavigationEventPayload(
42
+ event="COLD_START",
43
+ time=int(time.time() * 1000),
44
+ user_id=self.me.id,
45
+ params=NavigationEventParams(
46
+ action_id=self._action_id,
47
+ screen_to=Navigation.get_screen_id("chats_list_tab"),
48
+ screen_from=1,
49
+ source_id=1,
50
+ session_id=self._session_id,
51
+ ),
52
+ )
53
+
54
+ self._action_id += 1
55
+
56
+ await self._send_navigation_event([payload])
57
+
58
+ async def _send_random_navigation(self) -> None:
59
+ if not self.me:
60
+ self.logger.error("Cannot send navigation event, user not set")
61
+ return
62
+
63
+ screen_from = self._current_screen
64
+ screen_to = Navigation.get_random_navigation(screen_from)
65
+
66
+ self._action_id += 1
67
+ self._current_screen = screen_to
68
+
69
+ payload = NavigationEventPayload(
70
+ event="NAV",
71
+ time=int(time.time() * 1000),
72
+ user_id=self.me.id,
73
+ params=NavigationEventParams(
74
+ action_id=self._action_id,
75
+ screen_from=Navigation.get_screen_id(screen_from),
76
+ screen_to=Navigation.get_screen_id(screen_to),
77
+ source_id=1,
78
+ session_id=self._session_id,
79
+ ),
80
+ )
81
+
82
+ await self._send_navigation_event([payload])
83
+
84
+ def _get_random_sleep_time(self) -> int:
85
+ # TODO: вынести в статик
86
+ sleep_options = [
87
+ (1000, 3000),
88
+ (300, 1000),
89
+ (60, 300),
90
+ (5, 60),
91
+ (5, 20),
92
+ ]
93
+
94
+ weights = [0.05, 0.10, 0.15, 0.20, 0.50]
95
+
96
+ low, high = random.choices(sleep_options, weights=weights, k=1)[0]
97
+ return random.randint(low, high)
98
+
99
+ async def _start(self) -> None:
100
+ if not self.is_connected:
101
+ self.logger.error("Cannot start telemetry, client not connected")
102
+ return
103
+
104
+ await self._send_cold_start()
105
+
106
+ try:
107
+ while self.is_connected:
108
+ await self._send_random_navigation()
109
+ await asyncio.sleep(self._get_random_sleep_time())
110
+
111
+ except asyncio.CancelledError:
112
+ self.logger.debug("Telemetry task cancelled")
113
+ except Exception:
114
+ self.logger.warning("Telemetry task failed", exc_info=True)
pymax/mixins/user.py CHANGED
@@ -1,82 +1,82 @@
1
- from pymax.interfaces import ClientProtocol
2
- from pymax.payloads import FetchContactsPayload
3
- from pymax.static import Opcode
4
- from pymax.types import User
5
-
6
-
7
- class UserMixin(ClientProtocol):
8
- def get_cached_user(self, user_id: int) -> User | None:
9
- """
10
- Получает юзера из кеша по его ID
11
-
12
- Args:
13
- user_id (int): ID пользователя.
14
-
15
- Returns:
16
- User | None: Объект User или None при ошибке.
17
- """
18
- user = self._users.get(user_id)
19
- self.logger.debug("get_cached_user id=%s hit=%s", user_id, bool(user))
20
- return user
21
-
22
- async def get_users(self, user_ids: list[int]) -> list[User]:
23
- """
24
- Получает информацию о пользователях по их ID (с кешем).
25
- """
26
- self.logger.debug("get_users ids=%s", user_ids)
27
- cached = {uid: self._users[uid] for uid in user_ids if uid in self._users}
28
- missing_ids = [uid for uid in user_ids if uid not in self._users]
29
-
30
- if missing_ids:
31
- self.logger.debug("Fetching missing users: %s", missing_ids)
32
- fetched_users = await self.fetch_users(missing_ids)
33
- if fetched_users:
34
- for user in fetched_users:
35
- self._users[user.id] = user
36
- cached[user.id] = user
37
-
38
- ordered = [cached[uid] for uid in user_ids if uid in cached]
39
- self.logger.debug("get_users result_count=%d", len(ordered))
40
- return ordered
41
-
42
- async def get_user(self, user_id: int) -> User | None:
43
- """
44
- Получает информацию о пользователе по его ID (с кешем).
45
- """
46
- self.logger.debug("get_user id=%s", user_id)
47
- if user_id in self._users:
48
- return self._users[user_id]
49
-
50
- users = await self.fetch_users([user_id])
51
- if users:
52
- self._users[user_id] = users[0]
53
- return users[0]
54
- return None
55
-
56
- async def fetch_users(self, user_ids: list[int]) -> None | list[User]:
57
- """
58
- Получает информацию о пользователях по их ID.
59
- """
60
- try:
61
- self.logger.info("Fetching users count=%d", len(user_ids))
62
-
63
- payload = FetchContactsPayload(contact_ids=user_ids).model_dump(
64
- by_alias=True
65
- )
66
-
67
- data = await self._send_and_wait(
68
- opcode=Opcode.CONTACT_INFO, payload=payload
69
- )
70
- if error := data.get("payload", {}).get("error"):
71
- self.logger.error("Fetch users error: %s", error)
72
- return None
73
-
74
- users = [User.from_dict(u) for u in data["payload"].get("contacts", [])]
75
- for user in users:
76
- self._users[user.id] = user
77
-
78
- self.logger.debug("Fetched users: %d", len(users))
79
- return users
80
- except Exception:
81
- self.logger.exception("Fetch users failed")
82
- return []
1
+ from pymax.interfaces import ClientProtocol
2
+ from pymax.payloads import FetchContactsPayload
3
+ from pymax.static import Opcode
4
+ from pymax.types import User
5
+
6
+
7
+ class UserMixin(ClientProtocol):
8
+ def get_cached_user(self, user_id: int) -> User | None:
9
+ """
10
+ Получает юзера из кеша по его ID
11
+
12
+ Args:
13
+ user_id (int): ID пользователя.
14
+
15
+ Returns:
16
+ User | None: Объект User или None при ошибке.
17
+ """
18
+ user = self._users.get(user_id)
19
+ self.logger.debug("get_cached_user id=%s hit=%s", user_id, bool(user))
20
+ return user
21
+
22
+ async def get_users(self, user_ids: list[int]) -> list[User]:
23
+ """
24
+ Получает информацию о пользователях по их ID (с кешем).
25
+ """
26
+ self.logger.debug("get_users ids=%s", user_ids)
27
+ cached = {uid: self._users[uid] for uid in user_ids if uid in self._users}
28
+ missing_ids = [uid for uid in user_ids if uid not in self._users]
29
+
30
+ if missing_ids:
31
+ self.logger.debug("Fetching missing users: %s", missing_ids)
32
+ fetched_users = await self.fetch_users(missing_ids)
33
+ if fetched_users:
34
+ for user in fetched_users:
35
+ self._users[user.id] = user
36
+ cached[user.id] = user
37
+
38
+ ordered = [cached[uid] for uid in user_ids if uid in cached]
39
+ self.logger.debug("get_users result_count=%d", len(ordered))
40
+ return ordered
41
+
42
+ async def get_user(self, user_id: int) -> User | None:
43
+ """
44
+ Получает информацию о пользователе по его ID (с кешем).
45
+ """
46
+ self.logger.debug("get_user id=%s", user_id)
47
+ if user_id in self._users:
48
+ return self._users[user_id]
49
+
50
+ users = await self.fetch_users([user_id])
51
+ if users:
52
+ self._users[user_id] = users[0]
53
+ return users[0]
54
+ return None
55
+
56
+ async def fetch_users(self, user_ids: list[int]) -> None | list[User]:
57
+ """
58
+ Получает информацию о пользователях по их ID.
59
+ """
60
+ try:
61
+ self.logger.info("Fetching users count=%d", len(user_ids))
62
+
63
+ payload = FetchContactsPayload(contact_ids=user_ids).model_dump(
64
+ by_alias=True
65
+ )
66
+
67
+ data = await self._send_and_wait(
68
+ opcode=Opcode.CONTACT_INFO, payload=payload
69
+ )
70
+ if error := data.get("payload", {}).get("error"):
71
+ self.logger.error("Fetch users error: %s", error)
72
+ return None
73
+
74
+ users = [User.from_dict(u) for u in data["payload"].get("contacts", [])]
75
+ for user in users:
76
+ self._users[user.id] = user
77
+
78
+ self.logger.debug("Fetched users: %d", len(users))
79
+ return users
80
+ except Exception:
81
+ self.logger.exception("Fetch users failed")
82
+ return []