maxapi-python 1.1.7__tar.gz → 1.1.9__tar.gz
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.
- maxapi_python-1.1.9/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
- maxapi_python-1.1.9/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- maxapi_python-1.1.9/.github/ISSUE_TEMPLATE/refactor.md +25 -0
- maxapi_python-1.1.9/.github/pull_request_template.md +19 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/PKG-INFO +10 -4
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/README.md +9 -3
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/examples/example.py +25 -17
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/pyproject.toml +1 -1
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/files.py +1 -1
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/user.py +17 -1
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/static.py +1 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/types.py +94 -1
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/.github/FUNDING.yml +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/.github/workflows/publish.yml +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/.gitignore +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/LICENSE +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/assets/icon.svg +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/assets/logo.svg +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/api.md +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/assets/icon.svg +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/examples.md +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/index.md +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/mkdocs.yml +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/ruff.toml +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/scripts/build.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/__init__.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/core.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/crud.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/exceptions.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/filters.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/interfaces.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/__init__.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/auth.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/channel.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/group.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/handler.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/message.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/self.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/socket.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/telemetry.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/websocket.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/models.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/navigation.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/payloads.py +0 -0
- {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/utils.py +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Сообщить о баге
|
|
4
|
+
title: ''
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Описание**
|
|
11
|
+
Что пошло не так?
|
|
12
|
+
|
|
13
|
+
**Шаги для воспроизведения**
|
|
14
|
+
1.
|
|
15
|
+
2.
|
|
16
|
+
3.
|
|
17
|
+
|
|
18
|
+
**Ожидаемый результат**
|
|
19
|
+
Что должно было произойти
|
|
20
|
+
|
|
21
|
+
**Фактический результат**
|
|
22
|
+
Что произошло на самом деле
|
|
23
|
+
|
|
24
|
+
**Дополнительно**
|
|
25
|
+
Логи, скриншоты, версия Python/API
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
about: Предложить новую функциональность
|
|
4
|
+
title: ''
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Описание**
|
|
10
|
+
Опишите новую функцию или улучшение.
|
|
11
|
+
|
|
12
|
+
**Зачем это нужно**
|
|
13
|
+
Кратко объясните, какую проблему решает или что улучшает.
|
|
14
|
+
|
|
15
|
+
**Пример использования**
|
|
16
|
+
Как это будет использоваться в коде:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
# пример
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Refactor
|
|
3
|
+
about: Предложение по улучшению кода без изменения функционала
|
|
4
|
+
title: ''
|
|
5
|
+
labels: refactor
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Описание
|
|
10
|
+
Опишите часть кода, которую нужно улучшить или рефакторить, без изменения функциональности.
|
|
11
|
+
|
|
12
|
+
## Причина
|
|
13
|
+
Почему требуется рефакторинг? Например:
|
|
14
|
+
- Улучшение читаемости кода
|
|
15
|
+
- Повышение производительности
|
|
16
|
+
- Упрощение поддержки
|
|
17
|
+
|
|
18
|
+
## Предлагаемые изменения
|
|
19
|
+
Опишите, как вы планируете изменить код, чтобы улучшить его структуру или качество.
|
|
20
|
+
|
|
21
|
+
## Влияние
|
|
22
|
+
Какие модули/части библиотеки могут быть затронуты? Нужно убедиться, что функционал остаётся прежним.
|
|
23
|
+
|
|
24
|
+
## Дополнительно
|
|
25
|
+
Любая дополнительная информация, ссылки на документацию, примеры кода.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Описание
|
|
2
|
+
Кратко, что делает этот PR. Например, добавляет новый метод, исправляет баг, улучшает документацию.
|
|
3
|
+
|
|
4
|
+
## Тип изменений
|
|
5
|
+
- [ ] Исправление бага
|
|
6
|
+
- [ ] Новая функциональность
|
|
7
|
+
- [ ] Улучшение документации
|
|
8
|
+
- [ ] Рефакторинг
|
|
9
|
+
|
|
10
|
+
## Связанные задачи / Issue
|
|
11
|
+
Ссылка на issue, если есть: #
|
|
12
|
+
|
|
13
|
+
## Тестирование
|
|
14
|
+
Покажите пример кода, который проверяет изменения:
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import pymax
|
|
18
|
+
|
|
19
|
+
# пример использования нового функционала
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maxapi-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.9
|
|
4
4
|
Summary: Python wrapper для API мессенджера Max
|
|
5
5
|
Project-URL: Homepage, https://github.com/noxzion/PyMax
|
|
6
6
|
Project-URL: Repository, https://github.com/noxzion/PyMax
|
|
@@ -157,7 +157,13 @@ if __name__ == "__main__":
|
|
|
157
157
|
|
|
158
158
|
## Авторы
|
|
159
159
|
|
|
160
|
-
- **[noxzion](https://github.com/noxzion)** —
|
|
161
|
-
- **[ink](https://github.com/ink-developer)** —
|
|
162
|
-
- **[fresh-milkshake](https://github.com/fresh-milkshake)** — контрибьютор и автор лого
|
|
160
|
+
- **[noxzion](https://github.com/noxzion)** — Оригинальный автор проекта
|
|
161
|
+
- **[ink](https://github.com/ink-developer)** — Главный разработчик, исследование API и его документация
|
|
163
162
|
|
|
163
|
+
## Контрибьюторы
|
|
164
|
+
|
|
165
|
+
Спасибо всем за помощь в разработке!
|
|
166
|
+
|
|
167
|
+
<a href="https://github.com/ink-developer/PyMax/graphs/contributors">
|
|
168
|
+
<img src="https://contrib.rocks/image?repo=ink-developer/PyMax" />
|
|
169
|
+
</a>
|
|
@@ -135,7 +135,13 @@ if __name__ == "__main__":
|
|
|
135
135
|
|
|
136
136
|
## Авторы
|
|
137
137
|
|
|
138
|
-
- **[noxzion](https://github.com/noxzion)** —
|
|
139
|
-
- **[ink](https://github.com/ink-developer)** —
|
|
140
|
-
- **[fresh-milkshake](https://github.com/fresh-milkshake)** — контрибьютор и автор лого
|
|
138
|
+
- **[noxzion](https://github.com/noxzion)** — Оригинальный автор проекта
|
|
139
|
+
- **[ink](https://github.com/ink-developer)** — Главный разработчик, исследование API и его документация
|
|
141
140
|
|
|
141
|
+
## Контрибьюторы
|
|
142
|
+
|
|
143
|
+
Спасибо всем за помощь в разработке!
|
|
144
|
+
|
|
145
|
+
<a href="https://github.com/ink-developer/PyMax/graphs/contributors">
|
|
146
|
+
<img src="https://contrib.rocks/image?repo=ink-developer/PyMax" />
|
|
147
|
+
</a>
|
|
@@ -21,27 +21,35 @@ async def handle_message(message: Message) -> None:
|
|
|
21
21
|
@client.on_start
|
|
22
22
|
async def handle_start() -> None:
|
|
23
23
|
print("Client started successfully!")
|
|
24
|
+
sessions = await client.get_sessions()
|
|
25
|
+
for session in sessions:
|
|
26
|
+
print(session.client)
|
|
24
27
|
# print(client.dialogs)
|
|
25
28
|
# history = await client.fetch_history(chat_id=0)
|
|
26
29
|
# if history:
|
|
27
30
|
# for message in history:
|
|
28
|
-
# if message.
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
31
|
+
# if message.link:
|
|
32
|
+
# print(message.link.chat_id)
|
|
33
|
+
# print(message.link.message.text)
|
|
34
|
+
# for attach in message.attaches:
|
|
35
|
+
# if attach.type == AttachType.CONTROL:
|
|
36
|
+
# print(attach.event)
|
|
37
|
+
# print(attach.extra)
|
|
38
|
+
# if attach.type == AttachType.VIDEO:
|
|
39
|
+
# print(message)
|
|
40
|
+
# vid = await client.get_video_by_id(
|
|
41
|
+
# chat_id=0,
|
|
42
|
+
# video_id=attach.video_id,
|
|
43
|
+
# message_id=message.id,
|
|
44
|
+
# )
|
|
45
|
+
# print(vid.url)
|
|
46
|
+
# elif attach.type == AttachType.FILE:
|
|
47
|
+
# file = await client.get_file_by_id(
|
|
48
|
+
# chat_id=0,
|
|
49
|
+
# file_id=attach.file_id,
|
|
50
|
+
# message_id=message.id,
|
|
51
|
+
# )
|
|
52
|
+
# print(file.url)
|
|
45
53
|
# print(client.me.names[0].first_name)
|
|
46
54
|
# user = await client.get_user(client.me.id)
|
|
47
55
|
|
|
@@ -56,7 +56,7 @@ class Photo(BaseFile):
|
|
|
56
56
|
return (extension[1:], ("image/" + extension[1:]).lower())
|
|
57
57
|
elif self.url:
|
|
58
58
|
extension = Path(self.url).suffix.lower()
|
|
59
|
-
if extension in self.ALLOWED_EXTENSIONS:
|
|
59
|
+
if extension not in self.ALLOWED_EXTENSIONS:
|
|
60
60
|
raise ValueError(
|
|
61
61
|
f"Invalid photo extension in URL: {extension}. Allowed: {self.ALLOWED_EXTENSIONS}"
|
|
62
62
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pymax.interfaces import ClientProtocol
|
|
2
2
|
from pymax.payloads import FetchContactsPayload, SearchByPhonePayload
|
|
3
3
|
from pymax.static import Opcode
|
|
4
|
-
from pymax.types import User
|
|
4
|
+
from pymax.types import Session, User
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class UserMixin(ClientProtocol):
|
|
@@ -115,3 +115,19 @@ class UserMixin(ClientProtocol):
|
|
|
115
115
|
except Exception:
|
|
116
116
|
self.logger.exception("Search by phone failed")
|
|
117
117
|
return None
|
|
118
|
+
|
|
119
|
+
async def get_sessions(self) -> list[Session] | None:
|
|
120
|
+
try:
|
|
121
|
+
self.logger.info("Fetching sessions")
|
|
122
|
+
|
|
123
|
+
data = await self._send_and_wait(opcode=Opcode.SESSIONS_INFO, payload={})
|
|
124
|
+
|
|
125
|
+
if error := data.get("payload", {}).get("error"):
|
|
126
|
+
self.logger.error("Fetching sessions error: %s", error)
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
return [Session.from_dict(s) for s in data["payload"].get("sessions", [])]
|
|
130
|
+
|
|
131
|
+
except Exception:
|
|
132
|
+
self.logger.exception("Fetching sessions failed")
|
|
133
|
+
return None
|
|
@@ -39,6 +39,32 @@ class Names:
|
|
|
39
39
|
return self.name
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
class ControlAttach:
|
|
43
|
+
def __init__(self, type: AttachType, event: str, **kwargs: dict[str, Any]) -> None:
|
|
44
|
+
self.type = type
|
|
45
|
+
self.event = event
|
|
46
|
+
self.extra = kwargs
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_dict(cls, data: dict[str, Any]) -> "ControlAttach":
|
|
50
|
+
data = dict(data)
|
|
51
|
+
attach_type = AttachType(data.pop("_type"))
|
|
52
|
+
event = data.pop("event")
|
|
53
|
+
return cls(
|
|
54
|
+
type=attach_type,
|
|
55
|
+
event=event,
|
|
56
|
+
**data,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@override
|
|
60
|
+
def __repr__(self) -> str:
|
|
61
|
+
return f"ControlAttach(type={self.type!r}, event={self.event!r}, extra={self.extra!r})"
|
|
62
|
+
|
|
63
|
+
@override
|
|
64
|
+
def __str__(self) -> str:
|
|
65
|
+
return f"ControlAttach: {self.event}"
|
|
66
|
+
|
|
67
|
+
|
|
42
68
|
class PhotoAttach:
|
|
43
69
|
def __init__(
|
|
44
70
|
self,
|
|
@@ -268,6 +294,29 @@ class Element:
|
|
|
268
294
|
return f"{self.type}({self.length})"
|
|
269
295
|
|
|
270
296
|
|
|
297
|
+
class MessageLink:
|
|
298
|
+
def __init__(self, chat_id: int, message: "Message", type: str) -> None:
|
|
299
|
+
self.chat_id = chat_id
|
|
300
|
+
self.message = message
|
|
301
|
+
self.type = type
|
|
302
|
+
|
|
303
|
+
@classmethod
|
|
304
|
+
def from_dict(cls, data: dict[str, Any]) -> "MessageLink":
|
|
305
|
+
return cls(
|
|
306
|
+
chat_id=data["chatId"],
|
|
307
|
+
message=Message.from_dict(data["message"]),
|
|
308
|
+
type=data["type"],
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
@override
|
|
312
|
+
def __repr__(self) -> str:
|
|
313
|
+
return f"MessageLink(chat_id={self.chat_id!r}, message={self.message!r}, type={self.type!r})"
|
|
314
|
+
|
|
315
|
+
@override
|
|
316
|
+
def __str__(self) -> str:
|
|
317
|
+
return f"MessageLink: {self.chat_id}/{self.message.id}"
|
|
318
|
+
|
|
319
|
+
|
|
271
320
|
class Message:
|
|
272
321
|
def __init__(
|
|
273
322
|
self,
|
|
@@ -278,10 +327,11 @@ class Message:
|
|
|
278
327
|
options: int | None,
|
|
279
328
|
id: int,
|
|
280
329
|
time: int,
|
|
330
|
+
link: MessageLink | None,
|
|
281
331
|
text: str,
|
|
282
332
|
status: MessageStatus | str | None,
|
|
283
333
|
type: MessageType | str,
|
|
284
|
-
attaches: list[PhotoAttach | VideoAttach | FileAttach],
|
|
334
|
+
attaches: list[PhotoAttach | VideoAttach | FileAttach | ControlAttach] | None,
|
|
285
335
|
) -> None:
|
|
286
336
|
self.chat_id = chat_id
|
|
287
337
|
self.sender = sender
|
|
@@ -293,6 +343,7 @@ class Message:
|
|
|
293
343
|
self.type = type
|
|
294
344
|
self.attaches = attaches
|
|
295
345
|
self.status = status
|
|
346
|
+
self.link = link
|
|
296
347
|
self.reactionInfo = reaction_info
|
|
297
348
|
|
|
298
349
|
@classmethod
|
|
@@ -306,6 +357,8 @@ class Message:
|
|
|
306
357
|
attaches.append(VideoAttach.from_dict(a))
|
|
307
358
|
elif a["_type"] == AttachType.FILE:
|
|
308
359
|
attaches.append(FileAttach.from_dict(a))
|
|
360
|
+
elif a["_type"] == AttachType.CONTROL:
|
|
361
|
+
attaches.append(ControlAttach.from_dict(a))
|
|
309
362
|
return cls(
|
|
310
363
|
chat_id=data.get("chatId"),
|
|
311
364
|
sender=message.get("sender"),
|
|
@@ -317,6 +370,9 @@ class Message:
|
|
|
317
370
|
type=message["type"],
|
|
318
371
|
attaches=attaches,
|
|
319
372
|
status=message.get("status"),
|
|
373
|
+
link=MessageLink.from_dict(message.get("link"))
|
|
374
|
+
if message.get("link")
|
|
375
|
+
else None,
|
|
320
376
|
reaction_info=message.get("reactionInfo"),
|
|
321
377
|
)
|
|
322
378
|
|
|
@@ -614,3 +670,40 @@ class Attach: # УБРАТЬ ГАДА!!! или нет...
|
|
|
614
670
|
@override
|
|
615
671
|
def __str__(self) -> str:
|
|
616
672
|
return f"Attach: {self.type}"
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
class Session:
|
|
676
|
+
def __init__(
|
|
677
|
+
self,
|
|
678
|
+
client: str,
|
|
679
|
+
info: str,
|
|
680
|
+
location: str,
|
|
681
|
+
time: int,
|
|
682
|
+
current: bool | None = None,
|
|
683
|
+
) -> None:
|
|
684
|
+
self.client = client
|
|
685
|
+
self.info = info
|
|
686
|
+
self.location = location
|
|
687
|
+
self.time = time
|
|
688
|
+
self.current = current if current is not None else False
|
|
689
|
+
|
|
690
|
+
@classmethod
|
|
691
|
+
def from_dict(cls, data: dict[str, Any]) -> "Session":
|
|
692
|
+
return cls(
|
|
693
|
+
client=data["client"],
|
|
694
|
+
info=data["info"],
|
|
695
|
+
location=data["location"],
|
|
696
|
+
time=data["time"],
|
|
697
|
+
current=data.get("current"),
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
@override
|
|
701
|
+
def __repr__(self) -> str:
|
|
702
|
+
return (
|
|
703
|
+
f"Session(client={self.client!r}, info={self.info!r}, "
|
|
704
|
+
f"location={self.location!r}, time={self.time!r}, current={self.current!r})"
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
@override
|
|
708
|
+
def __str__(self) -> str:
|
|
709
|
+
return f"Session: {self.client} from {self.location} at {self.time} (current={self.current})"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|