добавлен блок chats из документации и start_polling к Dispatcher

This commit is contained in:
Денис Семёнов 2025-06-18 17:00:03 +03:00
parent ff19f99704
commit 9a39dce1a6
45 changed files with 1262 additions and 150 deletions

View File

@ -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)
asyncio.run(main())

View File

@ -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()

View File

@ -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
return model

View File

@ -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)
...

View File

@ -6,4 +6,8 @@ class ApiPath(str, Enum):
MESSAGES = '/messages'
UPDATES = '/updates'
VIDEOS = '/videos'
ANSWERS = '/answers'
ANSWERS = '/answers'
ACTIONS = '/actions'
PIN = '/pin'
MEMBERS = '/members'
ADMINS = '/admins'

View File

@ -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'

View File

@ -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'

View File

@ -1,4 +1,3 @@
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('bot')

View File

@ -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
)

View File

@ -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
)

View File

@ -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,
)

View File

@ -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
)

View File

@ -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,
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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,
)

View File

@ -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,
)

View File

@ -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
)

View File

@ -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)
...
return await super().request(
method=HTTPMethod.POST,
path=ApiPath.MESSAGES,
model=SendedMessage,
params=params,
json=json
)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel
class DeletedBotFromChat(BaseModel):
success: bool
message: Optional[str] = None

View File

@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel
class DeletedChat(BaseModel):
success: bool
message: Optional[str] = None

View File

@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel
class DeletedPinMessage(BaseModel):
success: bool
message: Optional[str] = None

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel
class PinnedMessage(BaseModel):
success: bool
message: Optional[str] = None

View File

@ -0,0 +1,7 @@
from typing import List, Optional
from pydantic import BaseModel
class RemovedAdmin(BaseModel):
success: bool
message: Optional[str] = None

View File

@ -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

View File

@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel
class SendedAction(BaseModel):
success: bool
message: Optional[str] = None

View File

@ -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'

View File

@ -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] = []
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

View File

@ -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):

View File

@ -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

View File

@ -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)
}
datetime: lambda v: int(v.timestamp() * 1000)
}
class ChatAdmin(BaseModel):
user_id: int
permissions: List[ChatPermission]