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.
Files changed (45) hide show
  1. maxapi_python-1.1.9/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
  2. maxapi_python-1.1.9/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  3. maxapi_python-1.1.9/.github/ISSUE_TEMPLATE/refactor.md +25 -0
  4. maxapi_python-1.1.9/.github/pull_request_template.md +19 -0
  5. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/PKG-INFO +10 -4
  6. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/README.md +9 -3
  7. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/examples/example.py +25 -17
  8. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/pyproject.toml +1 -1
  9. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/files.py +1 -1
  10. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/user.py +17 -1
  11. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/static.py +1 -0
  12. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/types.py +94 -1
  13. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/.github/FUNDING.yml +0 -0
  14. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/.github/workflows/publish.yml +0 -0
  15. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/.gitignore +0 -0
  16. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/LICENSE +0 -0
  17. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/assets/icon.svg +0 -0
  18. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/assets/logo.svg +0 -0
  19. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/api.md +0 -0
  20. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/assets/icon.svg +0 -0
  21. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/examples.md +0 -0
  22. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/docs/index.md +0 -0
  23. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/mkdocs.yml +0 -0
  24. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/ruff.toml +0 -0
  25. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/scripts/build.py +0 -0
  26. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/__init__.py +0 -0
  27. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/core.py +0 -0
  28. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/crud.py +0 -0
  29. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/exceptions.py +0 -0
  30. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/filters.py +0 -0
  31. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/interfaces.py +0 -0
  32. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/__init__.py +0 -0
  33. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/auth.py +0 -0
  34. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/channel.py +0 -0
  35. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/group.py +0 -0
  36. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/handler.py +0 -0
  37. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/message.py +0 -0
  38. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/self.py +0 -0
  39. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/socket.py +0 -0
  40. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/telemetry.py +0 -0
  41. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/mixins/websocket.py +0 -0
  42. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/models.py +0 -0
  43. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/navigation.py +0 -0
  44. {maxapi_python-1.1.7 → maxapi_python-1.1.9}/src/pymax/payloads.py +0 -0
  45. {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.7
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)** — второй разработчик, исследование API и его документация
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)** — второй разработчик, исследование API и его документация
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.attaches:
29
- # for attach in message.attaches:
30
- # if attach.type == AttachType.VIDEO:
31
- # print(message)
32
- # vid = await client.get_video_by_id(
33
- # chat_id=0,
34
- # video_id=attach.video_id,
35
- # message_id=message.id,
36
- # )
37
- # print(vid.url)
38
- # elif attach.type == AttachType.FILE:
39
- # file = await client.get_file_by_id(
40
- # chat_id=0,
41
- # file_id=attach.file_id,
42
- # message_id=message.id,
43
- # )
44
- # print(file.url)
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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "maxapi-python"
3
- version = "1.1.7"
3
+ version = "1.1.9"
4
4
  description = "Python wrapper для API мессенджера Max"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -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
@@ -191,6 +191,7 @@ class AttachType(str, Enum):
191
191
  VIDEO = "VIDEO"
192
192
  FILE = "FILE"
193
193
  STICKER = "STICKER"
194
+ CONTROL = "CONTROL"
194
195
 
195
196
 
196
197
  class Constants(Enum):
@@ -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