maxapi 0.6__tar.gz → 0.8__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 (133) hide show
  1. {maxapi-0.6/maxapi.egg-info → maxapi-0.8}/PKG-INFO +13 -1
  2. {maxapi-0.6 → maxapi-0.8}/README.md +12 -0
  3. {maxapi-0.6 → maxapi-0.8}/maxapi/bot.py +71 -24
  4. {maxapi-0.6 → maxapi-0.8}/maxapi/connection/base.py +39 -1
  5. {maxapi-0.6 → maxapi-0.8}/maxapi/context/__init__.py +1 -1
  6. {maxapi-0.6 → maxapi-0.8}/maxapi/dispatcher.py +58 -39
  7. maxapi-0.8/maxapi/exceptions/download_file.py +4 -0
  8. maxapi-0.8/maxapi/exceptions/invalid_token.py +4 -0
  9. maxapi-0.8/maxapi/filters/__init__.py +20 -0
  10. {maxapi-0.6 → maxapi-0.8}/maxapi/filters/handler.py +11 -3
  11. maxapi-0.8/maxapi/filters/middleware.py +6 -0
  12. maxapi-0.8/maxapi/methods/download_media.py +52 -0
  13. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_members_chat.py +3 -1
  14. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/getted_updates.py +43 -0
  15. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/attachment.py +40 -5
  16. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/bot_added.py +0 -4
  17. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/bot_removed.py +0 -4
  18. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/bot_started.py +1 -4
  19. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/chat_title_changed.py +0 -4
  20. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/message_callback.py +5 -8
  21. maxapi-0.8/maxapi/types/updates/message_chat_created.py +15 -0
  22. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/message_created.py +1 -11
  23. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/message_edited.py +0 -12
  24. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/message_removed.py +1 -11
  25. maxapi-0.8/maxapi/types/updates/update.py +36 -0
  26. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/user_added.py +1 -12
  27. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/user_removed.py +1 -11
  28. {maxapi-0.6 → maxapi-0.8/maxapi.egg-info}/PKG-INFO +13 -1
  29. {maxapi-0.6 → maxapi-0.8}/maxapi.egg-info/SOURCES.txt +4 -1
  30. {maxapi-0.6 → maxapi-0.8}/pyproject.toml +2 -2
  31. maxapi-0.6/LICENSE.md +0 -21
  32. maxapi-0.6/maxapi/filters/__init__.py +0 -53
  33. maxapi-0.6/maxapi/types/updates/message_chat_created.py +0 -24
  34. maxapi-0.6/maxapi/types/updates/update.py +0 -20
  35. {maxapi-0.6 → maxapi-0.8}/MANIFEST.in +0 -0
  36. {maxapi-0.6 → maxapi-0.8}/maxapi/__init__.py +0 -0
  37. {maxapi-0.6 → maxapi-0.8}/maxapi/connection/__init__.py +0 -0
  38. {maxapi-0.6 → maxapi-0.8}/maxapi/context/state_machine.py +0 -0
  39. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/__init__.py +0 -0
  40. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/api_path.py +0 -0
  41. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/attachment.py +0 -0
  42. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/button_type.py +0 -0
  43. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/chat_permission.py +0 -0
  44. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/chat_status.py +0 -0
  45. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/chat_type.py +0 -0
  46. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/http_method.py +0 -0
  47. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/intent.py +0 -0
  48. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/message_link_type.py +0 -0
  49. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/parse_mode.py +0 -0
  50. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/sender_action.py +0 -0
  51. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/text_style.py +0 -0
  52. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/update.py +0 -0
  53. {maxapi-0.6 → maxapi-0.8}/maxapi/enums/upload_type.py +0 -0
  54. {maxapi-0.6 → maxapi-0.8}/maxapi/loggers.py +0 -0
  55. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/__init__.py +0 -0
  56. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/add_admin_chat.py +0 -0
  57. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/add_members_chat.py +0 -0
  58. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/change_info.py +0 -0
  59. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/delete_bot_from_chat.py +0 -0
  60. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/delete_chat.py +0 -0
  61. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/delete_message.py +0 -0
  62. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/delete_pin_message.py +0 -0
  63. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/edit_chat.py +0 -0
  64. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/edit_message.py +0 -0
  65. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_chat_by_id.py +0 -0
  66. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_chat_by_link.py +0 -0
  67. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_chats.py +0 -0
  68. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_list_admin_chat.py +0 -0
  69. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_me.py +0 -0
  70. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_me_from_chat.py +0 -0
  71. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_messages.py +0 -0
  72. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_pinned_message.py +0 -0
  73. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_updates.py +0 -0
  74. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_upload_url.py +0 -0
  75. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/get_video.py +0 -0
  76. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/pin_message.py +0 -0
  77. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/remove_admin.py +0 -0
  78. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/remove_member_chat.py +0 -0
  79. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/send_action.py +0 -0
  80. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/send_callback.py +0 -0
  81. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/send_message.py +0 -0
  82. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/__init__.py +0 -0
  83. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/added_admin_chat.py +0 -0
  84. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/added_members_chat.py +0 -0
  85. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/deleted_bot_from_chat.py +0 -0
  86. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/deleted_chat.py +0 -0
  87. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/deleted_message.py +0 -0
  88. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/deleted_pin_message.py +0 -0
  89. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/edited_message.py +0 -0
  90. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/getted_list_admin_chat.py +0 -0
  91. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/getted_members_chat.py +0 -0
  92. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/getted_pineed_message.py +0 -0
  93. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/getted_upload_url.py +0 -0
  94. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/pinned_message.py +0 -0
  95. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/removed_admin.py +0 -0
  96. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/removed_member_chat.py +0 -0
  97. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/sended_action.py +0 -0
  98. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/sended_callback.py +0 -0
  99. {maxapi-0.6 → maxapi-0.8}/maxapi/methods/types/sended_message.py +0 -0
  100. {maxapi-0.6 → maxapi-0.8}/maxapi/types/__init__.py +0 -0
  101. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/__init__.py +0 -0
  102. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/audio.py +0 -0
  103. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/__init__.py +0 -0
  104. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/attachment_button.py +0 -0
  105. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/button.py +0 -0
  106. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/callback_button.py +0 -0
  107. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/chat_button.py +0 -0
  108. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/link_button.py +0 -0
  109. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/request_contact.py +0 -0
  110. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/buttons/request_geo_location_button.py +0 -0
  111. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/contact.py +0 -0
  112. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/file.py +0 -0
  113. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/image.py +0 -0
  114. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/location.py +0 -0
  115. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/share.py +0 -0
  116. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/sticker.py +0 -0
  117. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/upload.py +0 -0
  118. {maxapi-0.6 → maxapi-0.8}/maxapi/types/attachments/video.py +0 -0
  119. {maxapi-0.6 → maxapi-0.8}/maxapi/types/callback.py +0 -0
  120. {maxapi-0.6 → maxapi-0.8}/maxapi/types/chats.py +0 -0
  121. {maxapi-0.6 → maxapi-0.8}/maxapi/types/command.py +0 -0
  122. {maxapi-0.6 → maxapi-0.8}/maxapi/types/errors.py +0 -0
  123. {maxapi-0.6 → maxapi-0.8}/maxapi/types/input_media.py +0 -0
  124. {maxapi-0.6 → maxapi-0.8}/maxapi/types/message.py +0 -0
  125. {maxapi-0.6 → maxapi-0.8}/maxapi/types/updates/__init__.py +0 -0
  126. {maxapi-0.6 → maxapi-0.8}/maxapi/types/users.py +0 -0
  127. {maxapi-0.6 → maxapi-0.8}/maxapi/utils/__init__.py +0 -0
  128. {maxapi-0.6 → maxapi-0.8}/maxapi/utils/inline_keyboard.py +0 -0
  129. {maxapi-0.6 → maxapi-0.8}/maxapi.egg-info/dependency_links.txt +0 -0
  130. {maxapi-0.6 → maxapi-0.8}/maxapi.egg-info/requires.txt +0 -0
  131. {maxapi-0.6 → maxapi-0.8}/maxapi.egg-info/top_level.txt +0 -0
  132. {maxapi-0.6 → maxapi-0.8}/setup.cfg +0 -0
  133. {maxapi-0.6 → maxapi-0.8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxapi
3
- Version: 0.6
3
+ Version: 0.8
4
4
  Summary: Библиотека для разработки чат-ботов с помощью API мессенджера MAX
5
5
  Author-email: Denis <your-email@example.com>
6
6
  Project-URL: Homepage, https://github.com/love-apples/maxapi
@@ -33,6 +33,8 @@ pip install maxapi
33
33
 
34
34
  ## 🚀 Быстрый старт
35
35
 
36
+ Если вы тестируете бота в чате - не забудьте дать ему права администратора!
37
+
36
38
  ```python
37
39
  import asyncio
38
40
  import logging
@@ -75,6 +77,16 @@ if __name__ == '__main__':
75
77
 
76
78
  ---
77
79
 
80
+ ## ⭐️ Примеры
81
+
82
+ - [Эхо бот](https://love-apples/maxapi/blob/main/examples/echo/main.py)
83
+ - [Обработчик доступных событий](https://love-apples/maxapi/blob/main/examples/events/main.py)
84
+ - [Обработчики с MagicFilter](https://love-apples/maxapi/blob/main/examples/magic_filters/main.py)
85
+ - [Демонстрация роутинга, InputMedia и механика контекста](https://love-apples/maxapi/blob/main/examples/router_with_input_media/main.py) (audio.mp3 для команды /media)
86
+
87
+ ---
88
+
89
+
78
90
  ## 🧩 Возможности
79
91
 
80
92
  - ✅ Роутеры
@@ -16,6 +16,8 @@ pip install maxapi
16
16
 
17
17
  ## 🚀 Быстрый старт
18
18
 
19
+ Если вы тестируете бота в чате - не забудьте дать ему права администратора!
20
+
19
21
  ```python
20
22
  import asyncio
21
23
  import logging
@@ -58,6 +60,16 @@ if __name__ == '__main__':
58
60
 
59
61
  ---
60
62
 
63
+ ## ⭐️ Примеры
64
+
65
+ - [Эхо бот](https://love-apples/maxapi/blob/main/examples/echo/main.py)
66
+ - [Обработчик доступных событий](https://love-apples/maxapi/blob/main/examples/events/main.py)
67
+ - [Обработчики с MagicFilter](https://love-apples/maxapi/blob/main/examples/magic_filters/main.py)
68
+ - [Демонстрация роутинга, InputMedia и механика контекста](https://love-apples/maxapi/blob/main/examples/router_with_input_media/main.py) (audio.mp3 для команды /media)
69
+
70
+ ---
71
+
72
+
61
73
  ## 🧩 Возможности
62
74
 
63
75
  - ✅ Роутеры
@@ -1,6 +1,8 @@
1
1
  from datetime import datetime
2
2
  from typing import Any, Dict, List, TYPE_CHECKING
3
3
 
4
+ from maxapi.methods.download_media import DownloadMedia
5
+
4
6
  from .methods.get_upload_url import GetUploadURL
5
7
  from .methods.get_updates import GetUpdates
6
8
  from .methods.remove_member_chat import RemoveMemberChat
@@ -33,6 +35,7 @@ from .enums.parse_mode import ParseMode
33
35
  from .enums.sender_action import SenderAction
34
36
  from .enums.upload_type import UploadType
35
37
 
38
+ from .types.message import Message
36
39
  from .types.attachments.attachment import Attachment
37
40
  from .types.attachments.image import PhotoAttachmentRequestPayload
38
41
  from .types.message import Messages, NewMessageLink
@@ -41,29 +44,27 @@ from .types.command import BotCommand
41
44
 
42
45
  from .connection.base import BaseConnection
43
46
 
44
- if TYPE_CHECKING:
45
- from .types.message import Message
46
- from .methods.types.added_admin_chat import AddedListAdminChat
47
- from .methods.types.added_members_chat import AddedMembersChat
48
- from .methods.types.deleted_bot_from_chat import DeletedBotFromChat
49
- from .methods.types.deleted_chat import DeletedChat
50
- from .methods.types.deleted_message import DeletedMessage
51
- from .methods.types.deleted_pin_message import DeletedPinMessage
52
- from .methods.types.edited_message import EditedMessage
53
- from .methods.types.getted_list_admin_chat import GettedListAdminChat
54
- from .methods.types.getted_members_chat import GettedMembersChat
55
- from .methods.types.getted_pineed_message import GettedPin
56
- from .methods.types.getted_upload_url import GettedUploadUrl
57
- from .methods.types.pinned_message import PinnedMessage
58
- from .methods.types.removed_admin import RemovedAdmin
59
- from .methods.types.removed_member_chat import RemovedMemberChat
60
- from .methods.types.sended_action import SendedAction
61
- from .methods.types.sended_callback import SendedCallback
62
- from .methods.types.sended_message import SendedMessage
63
- from maxapi.types.attachments.video import Video
64
- from maxapi.types.chats import Chat, ChatMember, Chats
65
- from maxapi.types.updates import UpdateUnion
66
-
47
+ from .methods.types.added_admin_chat import AddedListAdminChat
48
+ from .methods.types.added_members_chat import AddedMembersChat
49
+ from .methods.types.deleted_bot_from_chat import DeletedBotFromChat
50
+ from .methods.types.deleted_chat import DeletedChat
51
+ from .methods.types.deleted_message import DeletedMessage
52
+ from .methods.types.deleted_pin_message import DeletedPinMessage
53
+ from .methods.types.edited_message import EditedMessage
54
+ from .methods.types.getted_list_admin_chat import GettedListAdminChat
55
+ from .methods.types.getted_members_chat import GettedMembersChat
56
+ from .methods.types.getted_pineed_message import GettedPin
57
+ from .methods.types.getted_upload_url import GettedUploadUrl
58
+ from .methods.types.pinned_message import PinnedMessage
59
+ from .methods.types.removed_admin import RemovedAdmin
60
+ from .methods.types.removed_member_chat import RemovedMemberChat
61
+ from .methods.types.sended_action import SendedAction
62
+ from .methods.types.sended_callback import SendedCallback
63
+ from .methods.types.sended_message import SendedMessage
64
+ from .types.attachments.video import Video
65
+ from .types.chats import Chat, ChatMember, Chats
66
+ from .types.updates import UpdateUnion
67
+
67
68
 
68
69
  class Bot(BaseConnection):
69
70
 
@@ -571,7 +572,7 @@ class Bot(BaseConnection):
571
572
  """Получает участников чата.
572
573
 
573
574
  :param chat_id: ID чата
574
- :param user_ids: Фильтр по ID пользователей
575
+ :param user_ids: Список ID участников
575
576
  :param marker: Маркер для пагинации
576
577
  :param count: Количество участников
577
578
 
@@ -585,6 +586,28 @@ class Bot(BaseConnection):
585
586
  marker=marker,
586
587
  count=count,
587
588
  ).request()
589
+
590
+ async def get_chat_member(
591
+ self,
592
+ chat_id: int,
593
+ user_id: int,
594
+ ) -> GettedMembersChat:
595
+
596
+ """Получает участника чата.
597
+
598
+ :param chat_id: ID чата
599
+ :param user_id: ID участника
600
+
601
+ :return: Участник
602
+ """
603
+
604
+ members = await self.get_chat_members(
605
+ chat_id=chat_id,
606
+ user_ids=[user_id]
607
+ )
608
+
609
+ if members.members:
610
+ return members.members[0]
588
611
 
589
612
  async def add_chat_members(
590
613
  self,
@@ -674,4 +697,28 @@ class Bot(BaseConnection):
674
697
  return await ChangeInfo(
675
698
  bot=self,
676
699
  commands=list(commands)
700
+ ).request()
701
+
702
+ async def download_file(
703
+ self,
704
+ path: str,
705
+ url: str,
706
+ token: str
707
+ ):
708
+
709
+ """
710
+ Скачивает медиа с указанной ссылки по токену, сохраняя по определенному пути
711
+
712
+ :param path: Путь сохранения медиа
713
+ :param url: Ссылка на медиа
714
+ :param token: Токен медиа
715
+
716
+ :return: Числовой статус
717
+ """
718
+
719
+ return await DownloadMedia(
720
+ bot=self,
721
+ path=path,
722
+ media_url=url,
723
+ media_token=token
677
724
  ).request()
@@ -1,13 +1,19 @@
1
1
  import os
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
5
+ import aiofiles
4
6
  import aiohttp
7
+
5
8
  from pydantic import BaseModel
6
9
 
10
+ from ..exceptions.invalid_token import InvalidToken
11
+
7
12
  from ..types.errors import Error
8
13
  from ..enums.http_method import HTTPMethod
9
14
  from ..enums.api_path import ApiPath
10
15
  from ..enums.upload_type import UploadType
16
+
11
17
  from ..loggers import logger_bot, logger_connection
12
18
 
13
19
  if TYPE_CHECKING:
@@ -65,6 +71,9 @@ class BaseConnection:
65
71
  )
66
72
  except aiohttp.ClientConnectorDNSError as e:
67
73
  return logger_connection.error(f'Ошибка при отправке запроса: {e}')
74
+
75
+ if r.status == 401:
76
+ raise InvalidToken('Неверный токен!')
68
77
 
69
78
  if not r.ok:
70
79
  raw = await r.json()
@@ -124,4 +133,33 @@ class BaseConnection:
124
133
  data=form
125
134
  )
126
135
 
127
- return await response.text()
136
+ return await response.text()
137
+
138
+ async def download_file(
139
+ self,
140
+ path: str,
141
+ url: str,
142
+ token: str,
143
+ ):
144
+ """
145
+ Скачивает медиа с указанной ссылки по токену, сохраняя по определенному пути
146
+
147
+ :param path: Путь сохранения медиа
148
+ :param url: Ссылка на медиа
149
+ :param token: Токен медиа
150
+
151
+ :return: Числовой статус
152
+ """
153
+
154
+ headers = {
155
+ 'Authorization': f'Bearer {token}'
156
+ }
157
+
158
+ async with aiohttp.ClientSession() as session:
159
+ async with session.get(url, headers=headers) as response:
160
+
161
+ if response.status == 200:
162
+ async with aiofiles.open(path, 'wb') as f:
163
+ await f.write(await response.read())
164
+
165
+ return response.status
@@ -2,7 +2,7 @@ import asyncio
2
2
 
3
3
  from typing import Any, Dict
4
4
 
5
- from ..context.state_machine import State
5
+ from ..context.state_machine import State, StatesGroup
6
6
 
7
7
 
8
8
  class MemoryContext:
@@ -2,6 +2,7 @@ from typing import Callable, List
2
2
 
3
3
  from fastapi import FastAPI, Request
4
4
  from fastapi.responses import JSONResponse
5
+ from magic_filter import MagicFilter
5
6
  from uvicorn import Config, Server
6
7
  from aiohttp import ClientConnectorError
7
8
 
@@ -33,6 +34,8 @@ class Dispatcher:
33
34
  def __init__(self):
34
35
  self.event_handlers: List[Handler] = []
35
36
  self.contexts: List[MemoryContext] = []
37
+ self.routers: List[Router] = []
38
+ self.filters: List[MagicFilter] = []
36
39
  self.bot = None
37
40
  self.on_started_func = None
38
41
 
@@ -65,9 +68,24 @@ class Dispatcher:
65
68
  """
66
69
 
67
70
  for router in routers:
68
- for event in router.event_handlers:
69
- self.event_handlers.append(event)
71
+ self.routers.append(router)
72
+
73
+ async def __ready(self, bot: Bot):
74
+ self.bot = bot
75
+ await self.check_me()
76
+
77
+ self.routers += [self]
78
+
79
+ handlers_count = 0
80
+ for router in self.routers:
81
+ for handler in router.event_handlers:
82
+ handlers_count += 1
70
83
 
84
+ logger_dp.info(f'{handlers_count} событий на обработку')
85
+
86
+ if self.on_started_func:
87
+ await self.on_started_func()
88
+
71
89
  def __get_memory_context(self, chat_id: int, user_id: int):
72
90
 
73
91
  """Возвращает или создает контекст для чата и пользователя.
@@ -95,40 +113,51 @@ class Dispatcher:
95
113
  Args:
96
114
  event_object: Объект события для обработки
97
115
  """
116
+ ids = event_object.get_ids()
98
117
 
99
118
  is_handled = False
119
+
120
+ for router in self.routers:
121
+
122
+ if is_handled:
123
+ break
124
+
125
+ if router.filters:
126
+ if not filter_attrs(event_object, *router.filters):
127
+ continue
128
+
129
+ for handler in router.event_handlers:
100
130
 
101
- for handler in self.event_handlers:
102
-
103
- if not handler.update_type == event_object.update_type:
104
- continue
105
-
106
- if handler.filters:
107
- if not filter_attrs(event_object, *handler.filters):
131
+ if not handler.update_type == event_object.update_type:
108
132
  continue
109
133
 
110
- ids = event_object.get_ids()
134
+ if handler.filters:
135
+ if not filter_attrs(event_object, *handler.filters):
136
+ continue
111
137
 
112
- memory_context = self.__get_memory_context(*ids)
113
-
114
- if not handler.state == await memory_context.get_state() \
115
- and handler.state:
116
- continue
117
-
118
- func_args = handler.func_event.__annotations__.keys()
138
+ memory_context = self.__get_memory_context(*ids)
139
+
140
+ if not handler.state == await memory_context.get_state() \
141
+ and handler.state:
142
+ continue
143
+
144
+ func_args = handler.func_event.__annotations__.keys()
119
145
 
120
- kwargs = {'context': memory_context}
146
+ kwargs = {'context': memory_context}
121
147
 
122
- for key in kwargs.copy().keys():
123
- if not key in func_args:
124
- del kwargs[key]
148
+ for key in kwargs.copy().keys():
149
+ if not key in func_args:
150
+ del kwargs[key]
151
+
152
+ if handler.middleware:
153
+ await handler.middleware()
125
154
 
126
- await handler.func_event(event_object, **kwargs)
155
+ await handler.func_event(event_object, **kwargs)
127
156
 
128
- logger_dp.info(f'Обработано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
157
+ logger_dp.info(f'Обработано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
129
158
 
130
- is_handled = True
131
- break
159
+ is_handled = True
160
+ break
132
161
 
133
162
  if not is_handled:
134
163
  logger_dp.info(f'Проигнорировано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
@@ -140,14 +169,7 @@ class Dispatcher:
140
169
  Args:
141
170
  bot: Экземпляр бота
142
171
  """
143
-
144
- self.bot = bot
145
- await self.check_me()
146
-
147
- logger_dp.info(f'{len(self.event_handlers)} событий на обработку')
148
-
149
- if self.on_started_func:
150
- await self.on_started_func()
172
+ await self.__ready(bot)
151
173
 
152
174
  while True:
153
175
  try:
@@ -184,11 +206,7 @@ class Dispatcher:
184
206
  port: Порт для сервера
185
207
  """
186
208
 
187
- self.bot = bot
188
- await self.check_me()
189
-
190
- if self.on_started_func:
191
- await self.on_started_func()
209
+ await self.__ready(bot)
192
210
 
193
211
  @app.post('/')
194
212
  async def _(request: Request):
@@ -206,7 +224,6 @@ class Dispatcher:
206
224
  except Exception as e:
207
225
  logger_dp.error(f"Ошибка при обработке события: {event_json['update_type']}: {e}")
208
226
 
209
- logger_dp.info(f'{len(self.event_handlers)} событий на обработку')
210
227
  config = Config(app=app, host=host, port=port, log_level="critical")
211
228
  server = Server(config)
212
229
 
@@ -231,8 +248,10 @@ class Event:
231
248
 
232
249
  def __call__(self, *args, **kwargs):
233
250
  def decorator(func_event: Callable):
251
+
234
252
  if self.update_type == UpdateType.ON_STARTED:
235
253
  self.router.on_started_func = func_event
254
+
236
255
  else:
237
256
  self.router.event_handlers.append(
238
257
  Handler(
@@ -0,0 +1,4 @@
1
+
2
+
3
+ class NotAvailableForDownload(BaseException):
4
+ ...
@@ -0,0 +1,4 @@
1
+
2
+
3
+ class InvalidToken(BaseException):
4
+ ...
@@ -0,0 +1,20 @@
1
+ from magic_filter import MagicFilter
2
+ from magic_filter.operations.call import CallOperation as mf_call
3
+ from magic_filter.operations.function import FunctionOperation as mf_func
4
+ from magic_filter.operations.comparator import ComparatorOperation as mf_comparator
5
+
6
+ F = MagicFilter()
7
+
8
+
9
+ def filter_attrs(obj: object, *filters: MagicFilter) -> bool:
10
+ """
11
+ Применяет один или несколько фильтров MagicFilter к объекту.
12
+
13
+ :param obj: Любой объект с атрибутами (например, event/message)
14
+ :param filters: Один или несколько MagicFilter выражений
15
+ :return: True, если все фильтры возвращают True, иначе False
16
+ """
17
+ try:
18
+ return all(f.resolve(obj) for f in filters)
19
+ except Exception:
20
+ return False
@@ -2,9 +2,14 @@ from typing import Callable
2
2
 
3
3
  from magic_filter import F, MagicFilter
4
4
 
5
+ from ..filters.middleware import BaseMiddleware
6
+
5
7
  from ..types.command import Command
8
+
6
9
  from ..context.state_machine import State
10
+
7
11
  from ..enums.update import UpdateType
12
+
8
13
  from ..loggers import logger_dp
9
14
 
10
15
 
@@ -36,10 +41,11 @@ class Handler:
36
41
  :param kwargs: Дополнительные параметры (не используются)
37
42
  """
38
43
 
39
- self.func_event = func_event
40
- self.update_type = update_type
44
+ self.func_event: Callable = func_event
45
+ self.update_type: UpdateType = update_type
41
46
  self.filters = []
42
- self.state = None
47
+ self.state: State = None
48
+ self.middleware: BaseMiddleware = None
43
49
 
44
50
  for arg in args:
45
51
  if isinstance(arg, MagicFilter):
@@ -48,6 +54,8 @@ class Handler:
48
54
  self.state = arg
49
55
  elif isinstance(arg, Command):
50
56
  self.filters.insert(0, F.message.body.text.startswith(arg.command))
57
+ elif isinstance(arg, BaseMiddleware):
58
+ self.middleware = arg
51
59
  else:
52
60
  logger_dp.info(f'Обнаружен неизвестный фильтр `{arg}` при '
53
61
  f'регистрации функции `{func_event.__name__}`')
@@ -0,0 +1,6 @@
1
+ from ..types.updates import UpdateUnion
2
+
3
+
4
+ class BaseMiddleware:
5
+ def __init__(self):
6
+ ...
@@ -0,0 +1,52 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ..methods.types.deleted_pin_message import DeletedPinMessage
4
+
5
+ from ..enums.http_method import HTTPMethod
6
+ from ..enums.api_path import ApiPath
7
+ from ..enums.upload_type import UploadType
8
+
9
+ from ..connection.base import BaseConnection
10
+
11
+
12
+ if TYPE_CHECKING:
13
+ from ..bot import Bot
14
+
15
+
16
+ class DownloadMedia(BaseConnection):
17
+
18
+ """
19
+ Класс для скачивания медиафайлов.
20
+
21
+ Args:
22
+ bot (Bot): Экземпляр бота для выполнения запроса.
23
+ media_url (str): Ссылка на медиа.
24
+ media_token (str): Токен медиа.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ bot: 'Bot',
30
+ path: str,
31
+ media_url: str,
32
+ media_token: str
33
+ ):
34
+ self.bot = bot
35
+ self.path = path
36
+ self.media_url = media_url
37
+ self.media_token = media_token
38
+
39
+ async def request(self) -> int:
40
+
41
+ """
42
+ Выполняет GET-запрос для скачивания медиафайла
43
+
44
+ Returns:
45
+ int: Код операции.
46
+ """
47
+
48
+ return await super().download_file(
49
+ path=self.path,
50
+ url=self.media_url,
51
+ token=self.media_token
52
+ )
@@ -60,7 +60,9 @@ class GetMembersChat(BaseConnection):
60
60
 
61
61
  params = self.bot.params.copy()
62
62
 
63
- if self.user_ids: params['user_ids'] = ','.join(self.user_ids)
63
+ if self.user_ids:
64
+ self.user_ids = [str(user_id) for user_id in self.user_ids]
65
+ params['user_ids'] = ','.join(self.user_ids)
64
66
  if self.marker: params['marker'] = self.marker
65
67
  if self.count: params['marker'] = self.count
66
68
 
@@ -19,34 +19,77 @@ if TYPE_CHECKING:
19
19
 
20
20
  async def get_update_model(event: dict, bot: 'Bot'):
21
21
  event_object = None
22
+
22
23
  match event['update_type']:
24
+
23
25
  case UpdateType.BOT_ADDED:
24
26
  event_object = BotAdded(**event)
27
+
25
28
  case UpdateType.BOT_REMOVED:
26
29
  event_object = BotRemoved(**event)
30
+
27
31
  case UpdateType.BOT_STARTED:
28
32
  event_object = BotStarted(**event)
33
+
29
34
  case UpdateType.CHAT_TITLE_CHANGED:
30
35
  event_object = ChatTitleChanged(**event)
36
+
31
37
  case UpdateType.MESSAGE_CALLBACK:
32
38
  event_object = MessageCallback(**event)
39
+ event_object.chat = await bot.get_chat_by_id(event_object.message.recipient.chat_id)
40
+ event_object.from_user = event_object.callback.user
41
+
33
42
  case UpdateType.MESSAGE_CHAT_CREATED:
34
43
  event_object = MessageChatCreated(**event)
44
+ event_object.chat = event_object.chat
45
+
35
46
  case UpdateType.MESSAGE_CREATED:
36
47
  event_object = MessageCreated(**event)
48
+ event_object.chat = await bot.get_chat_by_id(event_object.message.recipient.chat_id)
49
+ event_object.from_user = event_object.message.sender
50
+
37
51
  case UpdateType.MESSAGE_EDITED:
38
52
  event_object = MessageEdited(**event)
53
+ event_object.chat = await bot.get_chat_by_id(event_object.message.recipient.chat_id)
54
+ event_object.from_user = event_object.message.sender
55
+
39
56
  case UpdateType.MESSAGE_REMOVED:
40
57
  event_object = MessageRemoved(**event)
58
+ event_object.chat = await bot.get_chat_by_id(event_object.chat_id)
59
+ event_object.from_user = await bot.get_chat_member(
60
+ chat_id=event_object.chat_id,
61
+ user_id=event_object.user_id
62
+ )
63
+
41
64
  case UpdateType.USER_ADDED:
42
65
  event_object = UserAdded(**event)
66
+ event_object.chat = await bot.get_chat_by_id(event_object.chat_id)
67
+ event_object.from_user = event_object.user
68
+
43
69
  case UpdateType.USER_REMOVED:
44
70
  event_object = UserRemoved(**event)
71
+ event_object.chat = await bot.get_chat_by_id(event_object.chat_id)
72
+ event_object.from_user = await bot.get_chat_member(
73
+ chat_id=event_object.chat_id,
74
+ user_id=event_object.admin_id
75
+ ) if event_object.admin_id else None
76
+
77
+ if event['update_type'] in (UpdateType.BOT_ADDED,
78
+ UpdateType.BOT_REMOVED,
79
+ UpdateType.BOT_STARTED,
80
+ UpdateType.CHAT_TITLE_CHANGED):
81
+ event_object.chat = await bot.get_chat_by_id(event_object.chat_id)
82
+ event_object.from_user = event_object.user
45
83
 
46
84
  if hasattr(event_object, 'bot'):
47
85
  event_object.bot = bot
86
+
48
87
  if hasattr(event_object, 'message'):
49
88
  event_object.message.bot = bot
89
+
90
+ for attachment in event_object.message.body.attachments:
91
+ if hasattr(attachment, 'bot'):
92
+ attachment.bot = bot
50
93
 
51
94
  return event_object
52
95