diff --git a/example.py b/example.py index dfc8d6f..7d5464a 100644 --- a/example.py +++ b/example.py @@ -1,62 +1,23 @@ +import asyncio +import logging from maxapi.bot import Bot from maxapi.dispatcher import Dispatcher from maxapi.types.updates.message_created import MessageCreated -from maxapi.types.updates.message_callback import MessageCallback -from maxapi.types.attachments.attachment import ButtonsPayload -from maxapi.types.attachments.buttons.callback_button import CallbackButton -from maxapi.types.attachments.attachment import Attachment -from maxapi.enums.attachment import AttachmentType -from maxapi.enums.button_type import ButtonType -from maxapi.enums.intent import Intent from maxapi.filters import F - +logging.basicConfig(level=logging.INFO) bot = Bot('токен') dp = Dispatcher() -# Отвечает только на текст "Привет" -@dp.message_created(F.message.body.text == 'q') -async def hello(obj: MessageCreated): - msg = await obj.message.answer('Привет 👋') - a = await obj.bot.get_video('f9LHodD0cOJ5BfLGZ81uXgypU1z7PNhJMkmIe_dtEcxfC3V8vxWk65mRJX8MFQ5F9OAs3yDgbUv6DS6X1p7P') - - ... - - -# Отвечает только на текст "Клавиатура" -@dp.message_created(F.message.body.text == 'Клавиатура') -async def hello(obj: MessageCreated): - button_1 = CallbackButton(type=ButtonType.CALLBACK, text='Кнопка 1', payload='1', intent=Intent.DEFAULT) - button_2 = CallbackButton(type=ButtonType.CALLBACK, text='Кнопка 2', payload='2', intent=Intent.DEFAULT) - - keyboard = ButtonsPayload(buttons=[[button_1], [button_2]]) - - attachments = [Attachment(type=AttachmentType.INLINE_KEYBOARD, payload=keyboard)] - - await obj.message.answer('Привет 👋', attachments=attachments) - -# Ответчает на коллбек с начинкой "1" -@dp.message_callback(F.callback.payload == '1') -async def _(obj: MessageCallback): - a = await obj.answer('test') - ... - -# Ответчает на коллбек с начинкой "2" -@dp.message_callback(F.callback.payload == '2') -async def _(obj: MessageCallback): - await obj.message.answer('Вы нажали на кнопку 2 🥳') - -# Отвечает на любое текстовое сообщение +# Отвечает на лю +# любое текстовое сообщение @dp.message_created(F.message.body.text) async def hello(obj: MessageCreated): await obj.message.answer(f'Повторяю за вами: {obj.message.body.text}') -@dp.message_created() -async def hello(obj: MessageCreated): - # await obj.message.answer(f'Повторяю за вами: {obj.message.body.text}') - pass +async def main(): + await dp.start_polling(bot) - -dp.handle_webhook(bot) \ No newline at end of file +asyncio.run(main()) \ No newline at end of file diff --git a/maxapi/bot.py b/maxapi/bot.py index a1a272b..056ba4c 100644 --- a/maxapi/bot.py +++ b/maxapi/bot.py @@ -1,21 +1,41 @@ from datetime import datetime from typing import Any, Dict, List, TYPE_CHECKING +from .methods.get_updates import GetUpdates +from .methods.remove_member_chat import RemoveMemberChat +from .methods.add_admin_chat import AddAdminChat +from .methods.add_members_chat import AddMembersChat +from .methods.get_members_chat import GetMembersChat +from .methods.remove_admin import RemoveAdmin +from .methods.get_list_admin_chat import GetListAdminChat +from .methods.delete_bot_from_chat import DeleteMeFromMessage +from .methods.get_me_from_chat import GetMeFromChat +from .methods.delete_pin_message import DeletePinMessage +from .methods.get_pinned_message import GetPinnedMessage +from .methods.pin_message import PinMessage +from .methods.delete_chat import DeleteChat +from .methods.send_action import SendAction +from .methods.edit_chat import EditChat +from .methods.get_chat_by_id import GetChatById +from .methods.get_chat_by_link import GetChatByLink from .methods.send_callback import SendCallback - from .methods.get_video import GetVideo - from .methods.delete_message import DeleteMessage from .methods.edit_message import EditMessage -from .enums.parse_mode import ParseMode -from .types.attachments.attachment import Attachment -from .types.message import NewMessageLink -from .types.users import BotCommand from .methods.change_info import ChangeInfo from .methods.get_me import GetMe from .methods.get_messages import GetMessages from .methods.get_chats import GetChats from .methods.send_message import SendMessage + +from .enums.parse_mode import ParseMode +from .enums.sender_action import SenderAction + +from .types.attachments.attachment import Attachment +from .types.attachments.image import PhotoAttachmentRequestPayload +from .types.message import NewMessageLink +from .types.users import BotCommand, ChatAdmin + from .connection.base import BaseConnection if TYPE_CHECKING: @@ -32,6 +52,7 @@ class Bot(BaseConnection): self.params = { 'access_token': self.__token } + self.marker_updates = None async def send_message( self, @@ -56,6 +77,17 @@ class Bot(BaseConnection): parse_mode=parse_mode ).request() + async def send_action( + self, + chat_id: int = None, + action: SenderAction = SenderAction.TYPING_ON + ): + return await SendAction( + bot=self, + chat_id=chat_id, + action=action + ).request() + async def edit_message( self, message_id: str, @@ -83,6 +115,15 @@ class Bot(BaseConnection): bot=self, message_id=message_id, ).request() + + async def delete_chat( + self, + chat_id: int + ): + return await DeleteChat( + bot=self, + chat_id=chat_id, + ).request() async def get_messages( self, @@ -107,6 +148,9 @@ class Bot(BaseConnection): async def get_me(self): return await GetMe(self).request() + async def get_pin_message(self, chat_id: int): + return await GetPinnedMessage(bot=self, chat_id=chat_id).request() + async def change_info( self, name: str = None, @@ -123,11 +167,43 @@ class Bot(BaseConnection): photo=photo ).request() - async def get_chats(self): - return await GetChats(self).request() + async def get_chats( + self, + count: int = 50, + marker: int = None + ): + return await GetChats( + bot=self, + count=count, + marker=marker + ).request() + + async def get_chat_by_link(self, link: str): + """под вопросом""" + return await GetChatByLink(bot=self, link=link).request() + + async def get_chat_by_id(self, id: int): + return await GetChatById(bot=self, id=id).request() + + async def edit_chat( + self, + chat_id: int, + icon: PhotoAttachmentRequestPayload = None, + title: str = None, + pin: str = None, + notify: bool = True, + ): + return await EditChat( + bot=self, + chat_id=chat_id, + icon=icon, + title=title, + pin=pin, + notify=notify + ).request() async def get_video(self, video_token: str): - return await GetVideo(self, video_token).request() + return await GetVideo(bot=self, video_token=video_token).request() async def send_callback( self, @@ -140,4 +216,123 @@ class Bot(BaseConnection): callback_id=callback_id, message=message, notification=notification + ).request() + + async def pin_message( + self, + chat_id: int, + message_id: str, + notify: bool = True + ): + return await PinMessage( + bot=self, + chat_id=chat_id, + message_id=message_id, + notify=notify + ).request() + + async def delete_pin_message( + self, + chat_id: int, + ): + return await DeletePinMessage( + bot=self, + chat_id=chat_id, + ).request() + + async def get_me_from_chat( + self, + chat_id: int, + ): + return await GetMeFromChat( + bot=self, + chat_id=chat_id, + ).request() + + async def delete_me_from_chat( + self, + chat_id: int, + ): + return await DeleteMeFromMessage( + bot=self, + chat_id=chat_id, + ).request() + + async def get_list_admin_chat( + self, + chat_id: int, + ): + return await GetListAdminChat( + bot=self, + chat_id=chat_id, + ).request() + + async def add_list_admin_chat( + self, + chat_id: int, + admins: List[ChatAdmin], + marker: int = None + ): + return await AddAdminChat( + bot=self, + chat_id=chat_id, + admins=admins, + marker=marker, + ).request() + + async def remove_admin( + self, + chat_id: int, + user_id: int + ): + return await RemoveAdmin( + bot=self, + chat_id=chat_id, + user_id=user_id, + ).request() + + async def get_chat_members( + self, + chat_id: int, + user_ids: List[int] = None, + marker: int = None, + count: int = None, + ): + return await GetMembersChat( + bot=self, + chat_id=chat_id, + user_ids=user_ids, + marker=marker, + count=count, + ).request() + + async def add_chat_members( + self, + chat_id: int, + user_ids: List[str], + ): + return await AddMembersChat( + bot=self, + chat_id=chat_id, + user_ids=user_ids, + ).request() + + async def kick_chat_member( + self, + chat_id: int, + user_id: int, + block: bool = False, + ): + return await RemoveMemberChat( + bot=self, + chat_id=chat_id, + user_id=user_id, + block=block, + ).request() + + async def get_updates( + self, + ): + return await GetUpdates( + bot=self, ).request() \ No newline at end of file diff --git a/maxapi/connection/base.py b/maxapi/connection/base.py index d42d5c6..cd572ef 100644 --- a/maxapi/connection/base.py +++ b/maxapi/connection/base.py @@ -12,6 +12,7 @@ class BaseConnection: def __init__(self): self.bot = None + self.session = None async def request( self, @@ -21,29 +22,29 @@ class BaseConnection: is_return_raw: bool = False, **kwargs ): - async with aiohttp.ClientSession(self.API_URL) as s: - r = await s.request( - method=method.value, - url=path.value if isinstance(path, ApiPath) else path, - **kwargs - ) + s = self.bot.session + r = await s.request( + method=method.value, + url=path.value if isinstance(path, ApiPath) else path, + **kwargs + ) - if not r.ok: - raw = await r.text() - return Error(code=r.status, text=raw) - - raw = await r.json() + if not r.ok: + raw = await r.text() + return Error(code=r.status, text=raw) + + raw = await r.json() - if is_return_raw: return raw + if is_return_raw: return raw - model = model(**raw) - - if hasattr(model, 'message'): - attr = getattr(model, 'message') - if hasattr(attr, 'bot'): - attr.bot = self.bot - - if hasattr(model, 'bot'): - model.bot = self.bot + model = model(**raw) + + if hasattr(model, 'message'): + attr = getattr(model, 'message') + if hasattr(attr, 'bot'): + attr.bot = self.bot + + if hasattr(model, 'bot'): + model.bot = self.bot - return model \ No newline at end of file + return model \ No newline at end of file diff --git a/maxapi/dispatcher.py b/maxapi/dispatcher.py index d2f6941..ba89d19 100644 --- a/maxapi/dispatcher.py +++ b/maxapi/dispatcher.py @@ -1,10 +1,14 @@ from typing import Callable, List +import aiohttp +from fastapi.responses import JSONResponse import uvicorn from fastapi import FastAPI, Request from magic_filter import MagicFilter +from .methods.types.getted_updates import process_update_webhook, process_update_request + from .filters import filter_m from .types.updates import Update @@ -24,6 +28,9 @@ from .types.updates.user_removed import UserRemoved from .loggers import logger +app = FastAPI() + + class Handler: def __init__( @@ -67,50 +74,51 @@ class Dispatcher: for event in router.event_handlers: self.event_handlers.append(event) + async def start_polling(self, bot: Bot): + self.bot = bot + self.bot.session = aiohttp.ClientSession(self.bot.API_URL) + + while True: + try: + events = await self.bot.get_updates() + + for event in events: + handlers: List[Handler] = self.event_handlers + for handler in handlers: + + if not handler.update_type == event.update_type: + continue + + if handler.filters: + if not filter_m(event, *handler.filters): + continue + + await handler.func_event(event) + break + except Exception as e: + print(e) + ... + + logger.info(f'{len(self.event_handlers)} event handlers started') + def handle_webhook(self, bot: Bot, host: str = 'localhost', port: int = 8080): self.bot = bot - - app = FastAPI() + self.bot.session = aiohttp.ClientSession(self.bot.API_URL) @app.post("/") async def _(request: Request): try: event_json = await request.json() - event = Update(**event_json) - event_object = None - match event.update_type: - case UpdateType.BOT_ADDED: - event_object = BotAdded(**event_json) - case UpdateType.BOT_REMOVED: - event_object = BotRemoved(**event_json) - case UpdateType.BOT_STARTED: - event_object = BotStarted(**event_json) - case UpdateType.CHAT_TITLE_CHANGED: - event_object = ChatTitleChanged(**event_json) - case UpdateType.MESSAGE_CALLBACK: - event_object = MessageCallback(**event_json) - event_object.message.bot = self.bot - event_object.bot = self.bot - case UpdateType.MESSAGE_CHAT_CREATED: - event_object = MessageChatCreated(**event_json) - case UpdateType.MESSAGE_CREATED: - event_object = MessageCreated(**event_json) - event_object.message.bot = self.bot - event_object.bot = self.bot - case UpdateType.MESSAGE_EDITED: - event_object = MessageEdited(**event_json) - case UpdateType.MESSAGE_REMOVED: - event_object = MessageRemoved(**event_json) - case UpdateType.USER_ADDED: - event_object = UserAdded(**event_json) - case UpdateType.USER_REMOVED: - event_object = UserRemoved(**event_json) + event_object = await process_update_webhook( + event_json=event_json, + bot=self.bot + ) handlers: List[Handler] = self.event_handlers for handler in handlers: - if not handler.update_type == event.update_type: + if not handler.update_type == event_object.update_type: continue if handler.filters: @@ -120,7 +128,7 @@ class Dispatcher: await handler.func_event(event_object) break - return True + return JSONResponse(content={'ok': True}, status_code=200) except Exception as e: print(e) ... diff --git a/maxapi/enums/api_path.py b/maxapi/enums/api_path.py index 45eae36..3b092b5 100644 --- a/maxapi/enums/api_path.py +++ b/maxapi/enums/api_path.py @@ -6,4 +6,8 @@ class ApiPath(str, Enum): MESSAGES = '/messages' UPDATES = '/updates' VIDEOS = '/videos' - ANSWERS = '/answers' \ No newline at end of file + ANSWERS = '/answers' + ACTIONS = '/actions' + PIN = '/pin' + MEMBERS = '/members' + ADMINS = '/admins' \ No newline at end of file diff --git a/maxapi/enums/chat_permission.py b/maxapi/enums/chat_permission.py new file mode 100644 index 0000000..3b8dc1c --- /dev/null +++ b/maxapi/enums/chat_permission.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class ChatPermission(str, Enum): + READ_ALL_MESSAGES = 'read_all_messages' + ADD_REMOVE_MEMBERS = 'add_remove_members' + ADD_ADMINS = 'add_admins' + CHANGE_CHAT_INFO = 'change_chat_info' + PIN_MESSAGE = 'pin_message' + WRITE = 'write' \ No newline at end of file diff --git a/maxapi/enums/sender_action.py b/maxapi/enums/sender_action.py new file mode 100644 index 0000000..f661ce3 --- /dev/null +++ b/maxapi/enums/sender_action.py @@ -0,0 +1,9 @@ +from enum import Enum + +class SenderAction(str, Enum): + TYPING_ON = 'typing_on' + SENDING_PHOTO = 'sending_photo' + SENDING_VIDEO = 'sending_video' + SENDING_AUDIO = 'sending_audio' + SENDING_FILE = 'sending_file' + MARK_SEEN = 'mark_seen' \ No newline at end of file diff --git a/maxapi/loggers.py b/maxapi/loggers.py index 2e976b2..c507ea6 100644 --- a/maxapi/loggers.py +++ b/maxapi/loggers.py @@ -1,4 +1,3 @@ import logging -logging.basicConfig(level=logging.INFO) logger = logging.getLogger('bot') \ No newline at end of file diff --git a/maxapi/methods/add_admin_chat.py b/maxapi/methods/add_admin_chat.py new file mode 100644 index 0000000..e245b15 --- /dev/null +++ b/maxapi/methods/add_admin_chat.py @@ -0,0 +1,46 @@ + + +from re import findall +from typing import TYPE_CHECKING, List + +from .types.added_admin_chat import AddedListAdminChat + +from ..types.users import ChatAdmin + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class AddAdminChat(BaseConnection): + + def __init__( + self, + bot: 'Bot', + chat_id: int, + admins: List[ChatAdmin], + marker: int = None + ): + self.bot = bot + self.chat_id = chat_id + self.admins = admins + self.marker = marker + + async def request(self) -> AddedListAdminChat: + json = {} + + json['admins'] = [admin.model_dump() for admin in self.admins] + json['marker'] = self.marker + + return await super().request( + method=HTTPMethod.POST, + path=ApiPath.CHATS.value + '/' + str(self.chat_id) + ApiPath.MEMBERS + ApiPath.ADMINS, + model=AddedListAdminChat, + params=self.bot.params, + json=json + ) \ No newline at end of file diff --git a/maxapi/methods/add_members_chat.py b/maxapi/methods/add_members_chat.py new file mode 100644 index 0000000..654b0fe --- /dev/null +++ b/maxapi/methods/add_members_chat.py @@ -0,0 +1,42 @@ + + +from re import findall +from typing import TYPE_CHECKING, List + +from ..methods.types.added_members_chat import AddedMembersChat + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class AddMembersChat(BaseConnection): + + def __init__( + self, + bot: 'Bot', + chat_id: int, + user_ids: List[int], + + ): + self.bot = bot + self.chat_id = chat_id + self.user_ids = user_ids + + async def request(self) -> AddedMembersChat: + json = {} + + json['user_ids'] = self.user_ids + + return await super().request( + method=HTTPMethod.POST, + path=ApiPath.CHATS.value + '/' + str(self.chat_id) + ApiPath.MEMBERS, + model=AddedMembersChat, + params=self.bot.params, + json=json + ) \ No newline at end of file diff --git a/maxapi/methods/delete_bot_from_chat.py b/maxapi/methods/delete_bot_from_chat.py new file mode 100644 index 0000000..3b09300 --- /dev/null +++ b/maxapi/methods/delete_bot_from_chat.py @@ -0,0 +1,30 @@ +from typing import TYPE_CHECKING + +from ..methods.types.deleted_bot_from_chat import DeletedBotFromChat +from ..methods.types.deleted_message import DeletedMessage + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class DeleteMeFromMessage(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int, + ): + self.bot = bot + self.chat_id = chat_id + + async def request(self) -> DeletedBotFromChat: + return await super().request( + method=HTTPMethod.DELETE, + path=ApiPath.CHATS + '/' + str(self.chat_id) + ApiPath.MEMBERS + ApiPath.ME, + model=DeletedBotFromChat, + params=self.bot.params, + ) \ No newline at end of file diff --git a/maxapi/methods/delete_chat.py b/maxapi/methods/delete_chat.py new file mode 100644 index 0000000..cd15cd9 --- /dev/null +++ b/maxapi/methods/delete_chat.py @@ -0,0 +1,29 @@ +from typing import TYPE_CHECKING + +from ..methods.types.deleted_chat import DeletedChat + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class DeleteChat(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int + ): + self.bot = bot + self.chat_id = chat_id + + async def request(self) -> DeletedChat: + return await super().request( + method=HTTPMethod.DELETE, + path=ApiPath.CHATS.value + '/' + str(self.chat_id), + model=DeletedChat, + params=self.bot.params + ) \ No newline at end of file diff --git a/maxapi/methods/delete_pin_message.py b/maxapi/methods/delete_pin_message.py new file mode 100644 index 0000000..04c8b96 --- /dev/null +++ b/maxapi/methods/delete_pin_message.py @@ -0,0 +1,29 @@ +from typing import TYPE_CHECKING + +from ..methods.types.deleted_pin_message import DeletedPinMessage + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class DeletePinMessage(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: str, + ): + self.bot = bot + self.chat_id = chat_id + + async def request(self) -> DeletedPinMessage: + return await super().request( + method=HTTPMethod.DELETE, + path=ApiPath.CHATS + '/' + str(self.chat_id) + ApiPath.PIN, + model=DeletedPinMessage, + params=self.bot.params, + ) \ No newline at end of file diff --git a/maxapi/methods/edit_chat.py b/maxapi/methods/edit_chat.py new file mode 100644 index 0000000..40be72a --- /dev/null +++ b/maxapi/methods/edit_chat.py @@ -0,0 +1,68 @@ + + +from logging import getLogger +from typing import Any, Dict, List, TYPE_CHECKING +from collections import Counter + +from ..types.attachments.image import PhotoAttachmentRequestPayload + +from ..types.chats import Chat + +from ..types.users import BotCommand, User + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + +logger = getLogger(__name__) + + +if TYPE_CHECKING: + from ..bot import Bot + + +class EditChat(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int, + icon: PhotoAttachmentRequestPayload = None, + title: str = None, + pin: str = None, + notify: bool = True, + ): + self.bot = bot + self.chat_id = chat_id + self.icon = icon + self.title = title + self.pin = pin + self.notify = notify + + async def request(self) -> Chat: + json = {} + + if self.icon: + dump = self.icon.model_dump() + counter = Counter(dump.values()) + + if not None in counter or \ + not counter[None] == 2: + return logger.error( + 'Все атрибуты модели Icon являются взаимоисключающими | ' + 'https://dev.max.ru/docs-api/methods/PATCH/chats/-chatId-' + ) + + json['icon'] = dump + + if self.title: json['title'] = self.title + if self.pin: json['pin'] = self.pin + if self.notify: json['notify'] = self.notify + + return await super().request( + method=HTTPMethod.PATCH, + path=ApiPath.CHATS.value + '/' + str(self.chat_id), + model=Chat, + params=self.bot.params, + json=json + ) \ No newline at end of file diff --git a/maxapi/methods/get_chat_by_id.py b/maxapi/methods/get_chat_by_id.py new file mode 100644 index 0000000..8aa776b --- /dev/null +++ b/maxapi/methods/get_chat_by_id.py @@ -0,0 +1,36 @@ + + +from re import findall +from typing import TYPE_CHECKING + +from ..types.chats import Chat + +from ..types.users import User + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetChatById(BaseConnection): + + def __init__( + self, + bot: 'Bot', + id: int + ): + self.bot = bot + self.id = id + + async def request(self) -> Chat: + return await super().request( + method=HTTPMethod.GET, + path=ApiPath.CHATS.value + '/' + str(self.id), + model=Chat, + params=self.bot.params + ) \ No newline at end of file diff --git a/maxapi/methods/get_chat_by_link.py b/maxapi/methods/get_chat_by_link.py new file mode 100644 index 0000000..5e959e5 --- /dev/null +++ b/maxapi/methods/get_chat_by_link.py @@ -0,0 +1,41 @@ + + +from re import findall +from typing import TYPE_CHECKING + +from ..types.chats import Chat + +from ..types.users import User + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetChatByLink(BaseConnection): + + PATTERN_LINK = r'@?[a-zA-Z]+[a-zA-Z0-9-_]*' + + def __init__( + self, + bot: 'Bot', + link: str + ): + self.bot = bot + self.link = findall(self.PATTERN_LINK, link) + + if not self.link: + return + + async def request(self) -> Chat: + return await super().request( + method=HTTPMethod.GET, + path=ApiPath.CHATS.value + '/' + self.link[-1], + model=Chat, + params=self.bot.params + ) \ No newline at end of file diff --git a/maxapi/methods/get_chats.py b/maxapi/methods/get_chats.py index 35616f4..2f84b2c 100644 --- a/maxapi/methods/get_chats.py +++ b/maxapi/methods/get_chats.py @@ -17,13 +17,27 @@ if TYPE_CHECKING: class GetChats(BaseConnection): - def __init__(self, bot: 'Bot'): + def __init__( + self, + bot: 'Bot', + count: int = 50, + marker: int = None + ): self.bot = bot + self.count = count + self.marker = marker async def request(self) -> Chats: + params = self.bot.params.copy() + + params['count'] = self.count + + if self.marker: + params['marker'] = self.marker + return await super().request( method=HTTPMethod.GET, path=ApiPath.CHATS, model=Chats, - params=self.bot.params + params=params ) \ No newline at end of file diff --git a/maxapi/methods/get_list_admin_chat.py b/maxapi/methods/get_list_admin_chat.py new file mode 100644 index 0000000..8cf471d --- /dev/null +++ b/maxapi/methods/get_list_admin_chat.py @@ -0,0 +1,34 @@ + + +from re import findall +from typing import TYPE_CHECKING + +from maxapi.methods.types.getted_list_admin_chat import GettedListAdminChat + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetListAdminChat(BaseConnection): + + def __init__( + self, + bot: 'Bot', + chat_id: int + ): + self.bot = bot + self.chat_id = chat_id + + async def request(self) -> GettedListAdminChat: + return await super().request( + method=HTTPMethod.GET, + path=ApiPath.CHATS.value + '/' + str(self.chat_id) + ApiPath.MEMBERS + ApiPath.ADMINS, + model=GettedListAdminChat, + params=self.bot.params + ) \ No newline at end of file diff --git a/maxapi/methods/get_me_from_chat.py b/maxapi/methods/get_me_from_chat.py new file mode 100644 index 0000000..3c49ad5 --- /dev/null +++ b/maxapi/methods/get_me_from_chat.py @@ -0,0 +1,34 @@ + + +from typing import TYPE_CHECKING + +from ..types.chats import ChatMember, Chats + +from ..types.users import User + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetMeFromChat(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int + ): + self.bot = bot + self.chat_id = chat_id + + async def request(self) -> ChatMember: + return await super().request( + method=HTTPMethod.GET, + path=ApiPath.CHATS + '/' + str(self.chat_id) + ApiPath.MEMBERS + ApiPath.ME, + model=ChatMember, + params=self.bot.params + ) \ No newline at end of file diff --git a/maxapi/methods/get_members_chat.py b/maxapi/methods/get_members_chat.py new file mode 100644 index 0000000..5bce729 --- /dev/null +++ b/maxapi/methods/get_members_chat.py @@ -0,0 +1,47 @@ + + +from re import findall +from typing import TYPE_CHECKING, List + +from maxapi.methods.types.getted_members_chat import GettedMembersChat + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetMembersChat(BaseConnection): + + def __init__( + self, + bot: 'Bot', + chat_id: int, + user_ids: List[str] = None, + marker: int = None, + count: int = None, + + ): + self.bot = bot + self.chat_id = chat_id + self.user_ids = user_ids + self.marker = marker + self.count = count + + async def request(self) -> GettedMembersChat: + params = self.bot.params.copy() + + if self.user_ids: params['user_ids'] = ','.join(self.user_ids) + if self.marker: params['marker'] = self.marker + if self.count: params['marker'] = self.count + + return await super().request( + method=HTTPMethod.GET, + path=ApiPath.CHATS.value + '/' + str(self.chat_id) + ApiPath.MEMBERS, + model=GettedMembersChat, + params=params + ) \ No newline at end of file diff --git a/maxapi/methods/get_pinned_message.py b/maxapi/methods/get_pinned_message.py new file mode 100644 index 0000000..3fbb292 --- /dev/null +++ b/maxapi/methods/get_pinned_message.py @@ -0,0 +1,32 @@ + + +from datetime import datetime +from typing import TYPE_CHECKING, List + +from .types.getted_pineed_message import GettedPin + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetPinnedMessage(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int, + ): + self.bot = bot + self.chat_id = chat_id + + async def request(self) -> GettedPin: + return await super().request( + method=HTTPMethod.GET, + path=ApiPath.CHATS + '/' + str(self.chat_id) + ApiPath.PIN, + model=GettedPin, + params=self.bot.params + ) \ No newline at end of file diff --git a/maxapi/methods/get_updates.py b/maxapi/methods/get_updates.py new file mode 100644 index 0000000..f6400fc --- /dev/null +++ b/maxapi/methods/get_updates.py @@ -0,0 +1,50 @@ + + +from datetime import datetime +from typing import TYPE_CHECKING, List + +from ..methods.types.getted_updates import process_update_request + +from ..enums.update import UpdateType + +from ..types.updates import Update + +from ..types.message import Messages +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class GetUpdates(BaseConnection): + def __init__( + self, + bot: 'Bot', + limit: int = 100, + ): + self.bot = bot + self.limit = limit + + async def request(self) -> Messages: + params = self.bot.params.copy() + + params['limit'] = self.limit + + if self.bot.marker_updates: + params['marker'] = self.bot.marker_updates + + event_json = await super().request( + method=HTTPMethod.GET, + path=ApiPath.UPDATES, + model=Messages, + params=params, + is_return_raw=True + ) + + return await process_update_request( + event_json=event_json, + bot=self.bot + ) \ No newline at end of file diff --git a/maxapi/methods/pin_message.py b/maxapi/methods/pin_message.py new file mode 100644 index 0000000..cb181f5 --- /dev/null +++ b/maxapi/methods/pin_message.py @@ -0,0 +1,42 @@ + + +from datetime import datetime +from typing import TYPE_CHECKING, List + +from .types.pinned_message import PinnedMessage + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class PinMessage(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int, + message_id: str, + notify: bool = True + ): + self.bot = bot + self.chat_id = chat_id + self.message_id = message_id + self.notify = notify + + async def request(self) -> PinnedMessage: + json = {} + + json['message_id'] = self.message_id + json['notify'] = self.notify + + return await super().request( + method=HTTPMethod.PUT, + path=ApiPath.CHATS + '/' + str(self.chat_id) + ApiPath.PIN, + model=PinnedMessage, + params=self.bot.params, + json=json + ) \ No newline at end of file diff --git a/maxapi/methods/remove_admin.py b/maxapi/methods/remove_admin.py new file mode 100644 index 0000000..9cbbd9c --- /dev/null +++ b/maxapi/methods/remove_admin.py @@ -0,0 +1,36 @@ + + +from typing import TYPE_CHECKING + +from .types.removed_admin import RemovedAdmin + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class RemoveAdmin(BaseConnection): + + def __init__( + self, + bot: 'Bot', + chat_id: int, + user_id: int + ): + self.bot = bot + self.chat_id = chat_id + self.user_id = user_id + + async def request(self) -> RemovedAdmin: + return await super().request( + method=HTTPMethod.DELETE, + path=ApiPath.CHATS.value + '/' + str(self.chat_id) + \ + ApiPath.MEMBERS + ApiPath.ADMINS + '/' + str(self.user_id), + model=RemovedAdmin, + params=self.bot.params, + ) \ No newline at end of file diff --git a/maxapi/methods/remove_member_chat.py b/maxapi/methods/remove_member_chat.py new file mode 100644 index 0000000..1033bc4 --- /dev/null +++ b/maxapi/methods/remove_member_chat.py @@ -0,0 +1,42 @@ +from typing import TYPE_CHECKING, List + +from .types.removed_member_chat import RemovedMemberChat + +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath + +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class RemoveMemberChat(BaseConnection): + + def __init__( + self, + bot: 'Bot', + chat_id: int, + user_id: int, + block: bool = False, + + ): + self.bot = bot + self.chat_id = chat_id + self.user_id = user_id + self.block = block + + async def request(self) -> RemovedMemberChat: + params = self.bot.params.copy() + + params['chat_id'] = self.chat_id + params['user_id'] = self.user_id + params['block'] = str(self.block).lower() + + return await super().request( + method=HTTPMethod.DELETE, + path=ApiPath.CHATS.value + '/' + str(self.chat_id) + ApiPath.MEMBERS, + model=RemovedMemberChat, + params=params, + ) \ No newline at end of file diff --git a/maxapi/methods/send_action.py b/maxapi/methods/send_action.py new file mode 100644 index 0000000..28ec38b --- /dev/null +++ b/maxapi/methods/send_action.py @@ -0,0 +1,43 @@ + + +from typing import List, TYPE_CHECKING + +from maxapi.enums.sender_action import SenderAction +from maxapi.methods.types.sended_action import SendedAction + +from .types.sended_message import SendedMessage +from ..types.message import NewMessageLink +from ..types.attachments.attachment import Attachment +from ..enums.parse_mode import ParseMode +from ..enums.http_method import HTTPMethod +from ..enums.api_path import ApiPath +from ..connection.base import BaseConnection + + +if TYPE_CHECKING: + from ..bot import Bot + + +class SendAction(BaseConnection): + def __init__( + self, + bot: 'Bot', + chat_id: int = None, + action: SenderAction = SenderAction.TYPING_ON + ): + self.bot = bot + self.chat_id = chat_id + self.action = action + + async def request(self) -> SendedAction: + json = {} + + json['action'] = self.action.value + + return await super().request( + method=HTTPMethod.POST, + path=ApiPath.CHATS + '/' + str(self.chat_id) + ApiPath.ACTIONS, + model=SendedAction, + params=self.bot.params, + json=json + ) \ No newline at end of file diff --git a/maxapi/methods/send_message.py b/maxapi/methods/send_message.py index de67e68..65b2a23 100644 --- a/maxapi/methods/send_message.py +++ b/maxapi/methods/send_message.py @@ -39,31 +39,27 @@ class SendMessage(BaseConnection): self.parse_mode = parse_mode async def request(self) -> SendedMessage: - try: - params = self.bot.params.copy() + params = self.bot.params.copy() - json = {} + json = {} - if self.chat_id: params['chat_id'] = self.chat_id - elif self.user_id: params['user_id'] = self.user_id + if self.chat_id: params['chat_id'] = self.chat_id + elif self.user_id: params['user_id'] = self.user_id - json['text'] = self.text - json['disable_link_preview'] = str(self.disable_link_preview).lower() - - if self.attachments: json['attachments'] = \ - [att.model_dump() for att in self.attachments] - - if not self.link is None: json['link'] = self.link.model_dump() - if not self.notify is None: json['notify'] = self.notify - if not self.parse_mode is None: json['format'] = self.parse_mode.value + json['text'] = self.text + json['disable_link_preview'] = str(self.disable_link_preview).lower() + + if self.attachments: json['attachments'] = \ + [att.model_dump() for att in self.attachments] + + if not self.link is None: json['link'] = self.link.model_dump() + if not self.notify is None: json['notify'] = self.notify + if not self.parse_mode is None: json['format'] = self.parse_mode.value - return await super().request( - method=HTTPMethod.POST, - path=ApiPath.MESSAGES, - model=SendedMessage, - params=params, - json=json - ) - except Exception as e: - print(e) - ... \ No newline at end of file + return await super().request( + method=HTTPMethod.POST, + path=ApiPath.MESSAGES, + model=SendedMessage, + params=params, + json=json + ) \ No newline at end of file diff --git a/maxapi/methods/types/added_admin_chat.py b/maxapi/methods/types/added_admin_chat.py new file mode 100644 index 0000000..0329d67 --- /dev/null +++ b/maxapi/methods/types/added_admin_chat.py @@ -0,0 +1,9 @@ +from typing import List, Optional +from pydantic import BaseModel + +from ...types.chats import ChatMember + + +class AddedListAdminChat(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/added_members_chat.py b/maxapi/methods/types/added_members_chat.py new file mode 100644 index 0000000..2fa52c8 --- /dev/null +++ b/maxapi/methods/types/added_members_chat.py @@ -0,0 +1,9 @@ +from typing import List, Optional +from pydantic import BaseModel + +from ...types.chats import ChatMember + + +class AddedMembersChat(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/deleted_bot_from_chat.py b/maxapi/methods/types/deleted_bot_from_chat.py new file mode 100644 index 0000000..19b8f09 --- /dev/null +++ b/maxapi/methods/types/deleted_bot_from_chat.py @@ -0,0 +1,7 @@ +from typing import Optional +from pydantic import BaseModel + + +class DeletedBotFromChat(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/deleted_chat.py b/maxapi/methods/types/deleted_chat.py new file mode 100644 index 0000000..0855cd0 --- /dev/null +++ b/maxapi/methods/types/deleted_chat.py @@ -0,0 +1,7 @@ +from typing import Optional +from pydantic import BaseModel + + +class DeletedChat(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/deleted_pin_message.py b/maxapi/methods/types/deleted_pin_message.py new file mode 100644 index 0000000..c781714 --- /dev/null +++ b/maxapi/methods/types/deleted_pin_message.py @@ -0,0 +1,7 @@ +from typing import Optional +from pydantic import BaseModel + + +class DeletedPinMessage(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/getted_list_admin_chat.py b/maxapi/methods/types/getted_list_admin_chat.py new file mode 100644 index 0000000..f71bb32 --- /dev/null +++ b/maxapi/methods/types/getted_list_admin_chat.py @@ -0,0 +1,9 @@ +from typing import List, Optional +from pydantic import BaseModel + +from ...types.chats import ChatMember + + +class GettedListAdminChat(BaseModel): + members: List[ChatMember] + marker: Optional[int] = None \ No newline at end of file diff --git a/maxapi/methods/types/getted_members_chat.py b/maxapi/methods/types/getted_members_chat.py new file mode 100644 index 0000000..d11fc9b --- /dev/null +++ b/maxapi/methods/types/getted_members_chat.py @@ -0,0 +1,9 @@ +from typing import List, Optional +from pydantic import BaseModel + +from ...types.chats import ChatMember + + +class GettedMembersChat(BaseModel): + members: List[ChatMember] + marker: Optional[int] = None \ No newline at end of file diff --git a/maxapi/methods/types/getted_pineed_message.py b/maxapi/methods/types/getted_pineed_message.py new file mode 100644 index 0000000..1061acd --- /dev/null +++ b/maxapi/methods/types/getted_pineed_message.py @@ -0,0 +1,8 @@ +from typing import Optional +from pydantic import BaseModel + +from maxapi.types.message import Message + + +class GettedPin(BaseModel): + message: Optional[Message] = None \ No newline at end of file diff --git a/maxapi/methods/types/getted_updates.py b/maxapi/methods/types/getted_updates.py new file mode 100644 index 0000000..79162be --- /dev/null +++ b/maxapi/methods/types/getted_updates.py @@ -0,0 +1,97 @@ +from typing import TYPE_CHECKING + +from maxapi.enums.update import UpdateType +from ...types.updates import Update +from ...enums.update import UpdateType +from ...types.updates.bot_added import BotAdded +from ...types.updates.bot_removed import BotRemoved +from ...types.updates.bot_started import BotStarted +from ...types.updates.chat_title_changed import ChatTitleChanged +from ...types.updates.message_callback import MessageCallback +from ...types.updates.message_chat_created import MessageChatCreated +from ...types.updates.message_created import MessageCreated +from ...types.updates.message_edited import MessageEdited +from ...types.updates.message_removed import MessageRemoved +from ...types.updates.user_added import UserAdded +from ...types.updates.user_removed import UserRemoved + +if TYPE_CHECKING: + from maxapi.bot import Bot + + +async def process_update_request(event_json: dict, bot: 'Bot'): + events = [event for event in event_json['updates']] + + bot.marker_updates = event_json.get('marker') + + objects = [] + + for event in events: + + event_object = None + match event['update_type']: + case UpdateType.BOT_ADDED: + event_object = BotAdded(**event) + case UpdateType.BOT_REMOVED: + event_object = BotRemoved(**event) + case UpdateType.BOT_STARTED: + event_object = BotStarted(**event) + case UpdateType.CHAT_TITLE_CHANGED: + event_object = ChatTitleChanged(**event) + case UpdateType.MESSAGE_CALLBACK: + event_object = MessageCallback(**event) + event_object.message.bot = bot + event_object.bot = bot + case UpdateType.MESSAGE_CHAT_CREATED: + event_object = MessageChatCreated(**event) + case UpdateType.MESSAGE_CREATED: + event_object = MessageCreated(**event) + event_object.message.bot = bot + event_object.bot = bot + case UpdateType.MESSAGE_EDITED: + event_object = MessageEdited(**event) + case UpdateType.MESSAGE_REMOVED: + event_object = MessageRemoved(**event) + case UpdateType.USER_ADDED: + event_object = UserAdded(**event) + case UpdateType.USER_REMOVED: + event_object = UserRemoved(**event) + + objects.append(event_object) + + return objects + + +async def process_update_webhook(event_json: dict, bot: 'Bot'): + event = Update(**event_json) + + event_object = None + match event.update_type: + case UpdateType.BOT_ADDED: + event_object = BotAdded(**event_json) + case UpdateType.BOT_REMOVED: + event_object = BotRemoved(**event_json) + case UpdateType.BOT_STARTED: + event_object = BotStarted(**event_json) + case UpdateType.CHAT_TITLE_CHANGED: + event_object = ChatTitleChanged(**event_json) + case UpdateType.MESSAGE_CALLBACK: + event_object = MessageCallback(**event_json) + event_object.message.bot = bot + event_object.bot = bot + case UpdateType.MESSAGE_CHAT_CREATED: + event_object = MessageChatCreated(**event_json) + case UpdateType.MESSAGE_CREATED: + event_object = MessageCreated(**event_json) + event_object.message.bot = bot + event_object.bot = bot + case UpdateType.MESSAGE_EDITED: + event_object = MessageEdited(**event_json) + case UpdateType.MESSAGE_REMOVED: + event_object = MessageRemoved(**event_json) + case UpdateType.USER_ADDED: + event_object = UserAdded(**event_json) + case UpdateType.USER_REMOVED: + event_object = UserRemoved(**event_json) + + return event_object \ No newline at end of file diff --git a/maxapi/methods/types/pinned_message.py b/maxapi/methods/types/pinned_message.py new file mode 100644 index 0000000..e8859cb --- /dev/null +++ b/maxapi/methods/types/pinned_message.py @@ -0,0 +1,7 @@ +from typing import Optional +from pydantic import BaseModel + + +class PinnedMessage(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/removed_admin.py b/maxapi/methods/types/removed_admin.py new file mode 100644 index 0000000..44fe8c3 --- /dev/null +++ b/maxapi/methods/types/removed_admin.py @@ -0,0 +1,7 @@ +from typing import List, Optional +from pydantic import BaseModel + + +class RemovedAdmin(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/removed_member_chat.py b/maxapi/methods/types/removed_member_chat.py new file mode 100644 index 0000000..5dd4d59 --- /dev/null +++ b/maxapi/methods/types/removed_member_chat.py @@ -0,0 +1,9 @@ +from typing import List, Optional +from pydantic import BaseModel + +from ...types.chats import ChatMember + + +class RemovedMemberChat(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/methods/types/sended_action.py b/maxapi/methods/types/sended_action.py new file mode 100644 index 0000000..4d4c2c8 --- /dev/null +++ b/maxapi/methods/types/sended_action.py @@ -0,0 +1,7 @@ +from typing import Optional +from pydantic import BaseModel + + +class SendedAction(BaseModel): + success: bool + message: Optional[str] = None \ No newline at end of file diff --git a/maxapi/types/attachments/image.py b/maxapi/types/attachments/image.py index 2d49a94..ce7475c 100644 --- a/maxapi/types/attachments/image.py +++ b/maxapi/types/attachments/image.py @@ -1,6 +1,14 @@ -from typing import Literal +from typing import Literal, Optional + +from pydantic import BaseModel from .attachment import Attachment +class PhotoAttachmentRequestPayload(BaseModel): + url: Optional[str] = None + token: Optional[str] = None + photos: Optional[str] = None + + class Image(Attachment): type: Literal['image'] = 'image' \ No newline at end of file diff --git a/maxapi/types/chats.py b/maxapi/types/chats.py index 0c60f6a..03a830c 100644 --- a/maxapi/types/chats.py +++ b/maxapi/types/chats.py @@ -1,8 +1,10 @@ -from pydantic import BaseModel -from typing import List, Optional +from pydantic import BaseModel, field_validator +from typing import Dict, List, Optional from enum import Enum from datetime import datetime +from ..enums.chat_permission import ChatPermission + from ..types.users import User from ..types.message import Message @@ -20,6 +22,7 @@ class ChatStatus(str, Enum): class Icon(BaseModel): url: str + class Chat(BaseModel): chat_id: int type: ChatType @@ -29,7 +32,7 @@ class Chat(BaseModel): last_event_time: int participants_count: int owner_id: Optional[int] = None - participants: None = None + participants: Optional[Dict[str, datetime]] = None is_public: bool link: Optional[str] = None description: Optional[str] = None @@ -38,9 +41,26 @@ class Chat(BaseModel): chat_message_id: Optional[str] = None pinned_message: Optional[Message] = None + @field_validator('participants', mode='before') + @classmethod + def convert_timestamps(cls, value: Dict[str, int]) -> Dict[str, datetime]: + return { + key: datetime.fromtimestamp(ts / 1000) + for key, ts in value.items() + } + class Config: arbitrary_types_allowed=True class Chats(BaseModel): - chats: List[Chat] = [] \ No newline at end of file + chats: List[Chat] = [] + marker: Optional[int] = None + + +class ChatMember(User): + last_access_time: Optional[int] = None + is_owner: Optional[bool] = None + is_admin: Optional[bool] = None + join_time: Optional[int] = None + permissions: Optional[List[ChatPermission]] = None \ No newline at end of file diff --git a/maxapi/types/message.py b/maxapi/types/message.py index a0a72fc..11584a6 100644 --- a/maxapi/types/message.py +++ b/maxapi/types/message.py @@ -132,6 +132,13 @@ class Message(BaseModel): return await self.bot.delete_message( message_id=self.body.mid, ) + + async def pin(self, notify: bool = True): + return await self.bot.pin_message( + chat_id=self.recipient.chat_id, + message_id=self.body.mid, + notify=notify + ) class Messages(BaseModel): diff --git a/maxapi/types/updates/message_callback.py b/maxapi/types/updates/message_callback.py index e1da1b1..9c1939c 100644 --- a/maxapi/types/updates/message_callback.py +++ b/maxapi/types/updates/message_callback.py @@ -51,16 +51,16 @@ class MessageCallback(Update): bot: Optional[Bot] async def answer( - self, - text: str, + self, + notification: str, + new_text: str = None, link: NewMessageLink = None, notify: bool = True, format: ParseMode = None, - notification: str = None ): message = MessageForCallback() - message.text = text + message.text = new_text message.attachments = self.message.body.attachments message.link = link message.notify = notify diff --git a/maxapi/types/users.py b/maxapi/types/users.py index 57f1553..5cfa87c 100644 --- a/maxapi/types/users.py +++ b/maxapi/types/users.py @@ -2,6 +2,8 @@ from pydantic import BaseModel from typing import List, Optional from datetime import datetime +from ..enums.chat_permission import ChatPermission + class BotCommand(BaseModel): name: str @@ -22,5 +24,10 @@ class User(BaseModel): class Config: json_encoders = { - datetime: lambda v: int(v.timestamp() * 1000) # Конвертация datetime в Unix-время (ms) - } \ No newline at end of file + datetime: lambda v: int(v.timestamp() * 1000) + } + + +class ChatAdmin(BaseModel): + user_id: int + permissions: List[ChatPermission] \ No newline at end of file