Compare commits
23 Commits
b60d8571d3
...
e8b7c71d25
| Author | SHA1 | Date | |
|---|---|---|---|
| e8b7c71d25 | |||
| aab87e16b2 | |||
| df383665dc | |||
| bd06b33343 | |||
| 7b70d1de18 | |||
| 5f2c908da4 | |||
| 1abbc16cc8 | |||
| 7b61ceaa58 | |||
| 37f7907398 | |||
| 9dab5f97fb | |||
| 0a3d1ca327 | |||
| 93043835d1 | |||
| 3548d0558f | |||
| 5ae4de6816 | |||
| d77288ea07 | |||
| 30cf778504 | |||
| dd1bdb5e37 | |||
| b20a46de24 | |||
| de05e7931a | |||
| a8727c71e9 | |||
| b6c11cd28a | |||
| 12f64f0805 | |||
| 3df4dd21b4 |
@@ -56,7 +56,7 @@ if __name__ == '__main__':
|
||||
|
||||
## 📚 Документация
|
||||
|
||||
В разработке...
|
||||
[Тут](https://github.com/love-apples/maxapi/wiki)
|
||||
|
||||
---
|
||||
|
||||
|
||||
153
examples/keyboard/main.py
Normal file
153
examples/keyboard/main.py
Normal file
@@ -0,0 +1,153 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from maxapi import Bot, Dispatcher
|
||||
|
||||
# Кнопки
|
||||
from maxapi.types import (
|
||||
ChatButton,
|
||||
LinkButton,
|
||||
CallbackButton,
|
||||
RequestGeoLocationButton,
|
||||
MessageButton,
|
||||
ButtonsPayload, # Для постройки клавиатуры без InlineKeyboardBuilder
|
||||
RequestContactButton,
|
||||
OpenAppButton,
|
||||
)
|
||||
|
||||
from maxapi.types import (
|
||||
MessageCreated,
|
||||
MessageCallback,
|
||||
MessageChatCreated,
|
||||
CommandStart,
|
||||
Command
|
||||
)
|
||||
|
||||
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
bot = Bot('тут_ваш_токен')
|
||||
dp = Dispatcher()
|
||||
|
||||
|
||||
@dp.message_created(CommandStart())
|
||||
async def echo(event: MessageCreated):
|
||||
await event.message.answer(
|
||||
(
|
||||
'Привет! Мои команды:\n\n'
|
||||
|
||||
'/builder - Клавиатура из InlineKeyboardBuilder\n'
|
||||
'/pyaload - Клавиатура из pydantic моделей\n'
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@dp.message_created(Command('builder'))
|
||||
async def echo(event: MessageCreated):
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.row(
|
||||
ChatButton(
|
||||
text="Создать чат",
|
||||
chat_title='Test',
|
||||
chat_description='Test desc'
|
||||
),
|
||||
LinkButton(
|
||||
text="Канал разработчика",
|
||||
url="https://t.me/loveapples_dev"
|
||||
),
|
||||
)
|
||||
|
||||
builder.row(
|
||||
RequestGeoLocationButton(text="Геолокация"),
|
||||
MessageButton(text="Сообщение"),
|
||||
)
|
||||
|
||||
builder.row(
|
||||
RequestContactButton(text="Контакт"),
|
||||
OpenAppButton(
|
||||
text="Приложение",
|
||||
web_app=event.bot.me.username,
|
||||
contact_id=event.bot.me.user_id
|
||||
),
|
||||
)
|
||||
|
||||
builder.row(
|
||||
CallbackButton(
|
||||
text='Callback',
|
||||
payload='test',
|
||||
)
|
||||
)
|
||||
|
||||
await event.message.answer(
|
||||
text='Клавиатура из InlineKeyboardBuilder',
|
||||
attachments=[
|
||||
builder.as_markup()
|
||||
])
|
||||
|
||||
|
||||
@dp.message_created(Command('payload'))
|
||||
async def echo(event: MessageCreated):
|
||||
buttons = [
|
||||
[
|
||||
# кнопку типа "chat" убрали из документации,
|
||||
# возможны баги
|
||||
ChatButton(
|
||||
text="Создать чат",
|
||||
chat_title='Test',
|
||||
chat_description='Test desc'
|
||||
),
|
||||
LinkButton(
|
||||
text="Канал разработчика",
|
||||
url="https://t.me/loveapples_dev"
|
||||
),
|
||||
],
|
||||
[
|
||||
RequestGeoLocationButton(text="Геолокация"),
|
||||
MessageButton(text="Сообщение"),
|
||||
],
|
||||
[
|
||||
RequestContactButton(text="Контакт"),
|
||||
OpenAppButton(
|
||||
text="Приложение",
|
||||
web_app=event.bot.me.username,
|
||||
contact_id=event.bot.me.user_id
|
||||
),
|
||||
],
|
||||
[
|
||||
CallbackButton(
|
||||
text='Callback',
|
||||
payload='test',
|
||||
)
|
||||
]
|
||||
]
|
||||
|
||||
buttons_payload = ButtonsPayload(buttons=buttons).pack()
|
||||
|
||||
await event.message.answer(
|
||||
text='Клавиатура из pydantic моделей',
|
||||
attachments=[
|
||||
buttons_payload
|
||||
])
|
||||
|
||||
|
||||
@dp.message_chat_created()
|
||||
async def callback(obj: MessageChatCreated):
|
||||
await obj.bot.send_message(
|
||||
chat_id=obj.chat.chat_id,
|
||||
text=f'Чат создан! Ссылка: {obj.chat.link}'
|
||||
)
|
||||
|
||||
|
||||
@dp.message_callback()
|
||||
async def callback(callback: MessageCallback):
|
||||
await callback.message.answer('Вы нажали на Callback!')
|
||||
|
||||
|
||||
async def main():
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
@@ -82,7 +82,8 @@ class Bot(BaseConnection):
|
||||
auto_requests: bool = True,
|
||||
):
|
||||
|
||||
"""Инициализирует экземпляр бота с указанным токеном.
|
||||
"""
|
||||
Инициализирует экземпляр бота с указанным токеном.
|
||||
|
||||
:param token: Токен доступа к API бота
|
||||
:param parse_mode: Форматирование по умолчанию
|
||||
@@ -103,6 +104,12 @@ class Bot(BaseConnection):
|
||||
self.notify = notify
|
||||
self.auto_requests = auto_requests
|
||||
|
||||
self._me: User = None
|
||||
|
||||
@property
|
||||
def me(self):
|
||||
return self._me
|
||||
|
||||
def _resolve_notify(self, notify: Optional[bool]) -> Optional[bool]:
|
||||
return notify if notify is not None else self.notify
|
||||
|
||||
@@ -120,7 +127,8 @@ class Bot(BaseConnection):
|
||||
parse_mode: Optional[ParseMode] = None
|
||||
) -> SendedMessage:
|
||||
|
||||
"""Отправляет сообщение в чат или пользователю.
|
||||
"""
|
||||
Отправляет сообщение в чат или пользователю.
|
||||
|
||||
:param chat_id: ID чата для отправки (обязателен, если не указан user_id)
|
||||
:param user_id: ID пользователя для отправки (обязателен, если не указан chat_id)
|
||||
@@ -150,7 +158,8 @@ class Bot(BaseConnection):
|
||||
action: SenderAction = SenderAction.TYPING_ON
|
||||
) -> SendedAction:
|
||||
|
||||
"""Отправляет действие в чат (например, "печатает").
|
||||
"""
|
||||
Отправляет действие в чат (например, "печатает").
|
||||
|
||||
:param chat_id: ID чата для отправки действия
|
||||
:param action: Тип действия (по умолчанию SenderAction.TYPING_ON)
|
||||
@@ -174,7 +183,8 @@ class Bot(BaseConnection):
|
||||
parse_mode: Optional[ParseMode] = None
|
||||
) -> EditedMessage:
|
||||
|
||||
"""Редактирует существующее сообщение.
|
||||
"""
|
||||
Редактирует существующее сообщение.
|
||||
|
||||
:param message_id: ID сообщения для редактирования
|
||||
:param text: Новый текст сообщения
|
||||
@@ -201,7 +211,8 @@ class Bot(BaseConnection):
|
||||
message_id: str
|
||||
) -> DeletedMessage:
|
||||
|
||||
"""Удаляет сообщение.
|
||||
"""
|
||||
Удаляет сообщение.
|
||||
|
||||
:param message_id: ID сообщения для удаления
|
||||
|
||||
@@ -218,7 +229,8 @@ class Bot(BaseConnection):
|
||||
chat_id: int
|
||||
) -> DeletedChat:
|
||||
|
||||
"""Удаляет чат.
|
||||
"""
|
||||
Удаляет чат.
|
||||
|
||||
:param chat_id: ID чата для удаления
|
||||
|
||||
@@ -239,7 +251,8 @@ class Bot(BaseConnection):
|
||||
count: int = 50,
|
||||
) -> Messages:
|
||||
|
||||
"""Получает сообщения из чата.
|
||||
"""
|
||||
Получает сообщения из чата.
|
||||
|
||||
:param chat_id: ID чата (обязателен, если не указаны message_ids)
|
||||
:param message_ids: Список ID сообщений для получения
|
||||
@@ -264,7 +277,8 @@ class Bot(BaseConnection):
|
||||
message_id: str
|
||||
) -> Messages:
|
||||
|
||||
"""Получает одно сообщение по ID.
|
||||
"""
|
||||
Получает одно сообщение по ID.
|
||||
|
||||
:param message_id: ID сообщения
|
||||
|
||||
@@ -277,7 +291,8 @@ class Bot(BaseConnection):
|
||||
|
||||
async def get_me(self) -> User:
|
||||
|
||||
"""Получает информацию о текущем боте.
|
||||
"""
|
||||
Получает информацию о текущем боте.
|
||||
|
||||
:return: Объект пользователя бота
|
||||
"""
|
||||
@@ -289,7 +304,8 @@ class Bot(BaseConnection):
|
||||
chat_id: int
|
||||
) -> GettedPin:
|
||||
|
||||
"""Получает закрепленное сообщение в чате.
|
||||
"""
|
||||
Получает закрепленное сообщение в чате.
|
||||
|
||||
:param chat_id: ID чата
|
||||
|
||||
@@ -309,7 +325,8 @@ class Bot(BaseConnection):
|
||||
photo: Dict[str, Any] = None
|
||||
) -> User:
|
||||
|
||||
"""Изменяет информацию о боте.
|
||||
"""
|
||||
Изменяет информацию о боте.
|
||||
|
||||
:param name: Новое имя бота
|
||||
:param description: Новое описание бота
|
||||
@@ -333,7 +350,8 @@ class Bot(BaseConnection):
|
||||
marker: int = None
|
||||
) -> Chats:
|
||||
|
||||
"""Получает список чатов бота.
|
||||
"""
|
||||
Получает список чатов бота.
|
||||
|
||||
:param count: Количество чатов (по умолчанию 50)
|
||||
:param marker: Маркер для пагинации
|
||||
@@ -352,7 +370,8 @@ class Bot(BaseConnection):
|
||||
link: str
|
||||
) -> Chat:
|
||||
|
||||
"""Получает чат по ссылке.
|
||||
"""
|
||||
Получает чат по ссылке.
|
||||
|
||||
:param link: Ссылка на чат
|
||||
|
||||
@@ -366,7 +385,8 @@ class Bot(BaseConnection):
|
||||
id: int
|
||||
) -> Chat:
|
||||
|
||||
"""Получает чат по ID.
|
||||
"""
|
||||
Получает чат по ID.
|
||||
|
||||
:param id: ID чата
|
||||
|
||||
@@ -384,7 +404,8 @@ class Bot(BaseConnection):
|
||||
notify: Optional[bool] = None,
|
||||
) -> Chat:
|
||||
|
||||
"""Редактирует параметры чата.
|
||||
"""
|
||||
Редактирует параметры чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param icon: Данные иконки чата
|
||||
@@ -409,7 +430,8 @@ class Bot(BaseConnection):
|
||||
video_token: str
|
||||
) -> Video:
|
||||
|
||||
"""Получает видео по токену.
|
||||
"""
|
||||
Получает видео по токену.
|
||||
|
||||
:param video_token: Токен видео
|
||||
|
||||
@@ -428,7 +450,8 @@ class Bot(BaseConnection):
|
||||
notification: str = None
|
||||
) -> SendedCallback:
|
||||
|
||||
"""Отправляет callback ответ.
|
||||
"""
|
||||
Отправляет callback ответ.
|
||||
|
||||
:param callback_id: ID callback
|
||||
:param message: Сообщение для отправки
|
||||
@@ -451,7 +474,8 @@ class Bot(BaseConnection):
|
||||
notify: Optional[bool] = None
|
||||
) -> PinnedMessage:
|
||||
|
||||
"""Закрепляет сообщение в чате.
|
||||
"""
|
||||
Закрепляет сообщение в чате.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param message_id: ID сообщения
|
||||
@@ -472,7 +496,8 @@ class Bot(BaseConnection):
|
||||
chat_id: int,
|
||||
) -> DeletedPinMessage:
|
||||
|
||||
"""Удаляет закрепленное сообщение в чате.
|
||||
"""
|
||||
Удаляет закрепленное сообщение в чате.
|
||||
|
||||
:param chat_id: ID чата
|
||||
|
||||
@@ -489,7 +514,8 @@ class Bot(BaseConnection):
|
||||
chat_id: int,
|
||||
) -> ChatMember:
|
||||
|
||||
"""Получает информацию о боте в конкретном чате.
|
||||
"""
|
||||
Получает информацию о боте в конкретном чате.
|
||||
|
||||
:param chat_id: ID чата
|
||||
|
||||
@@ -506,7 +532,8 @@ class Bot(BaseConnection):
|
||||
chat_id: int,
|
||||
) -> DeletedBotFromChat:
|
||||
|
||||
"""Удаляет бота из чата.
|
||||
"""
|
||||
Удаляет бота из чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
|
||||
@@ -523,7 +550,8 @@ class Bot(BaseConnection):
|
||||
chat_id: int,
|
||||
) -> GettedListAdminChat:
|
||||
|
||||
"""Получает список администраторов чата.
|
||||
"""
|
||||
Получает список администраторов чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
|
||||
@@ -542,7 +570,8 @@ class Bot(BaseConnection):
|
||||
marker: int = None
|
||||
) -> AddedListAdminChat:
|
||||
|
||||
"""Добавляет администраторов в чат.
|
||||
"""
|
||||
Добавляет администраторов в чат.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param admins: Список администраторов
|
||||
@@ -564,7 +593,8 @@ class Bot(BaseConnection):
|
||||
user_id: int
|
||||
) -> RemovedAdmin:
|
||||
|
||||
"""Удаляет администратора из чата.
|
||||
"""
|
||||
Удаляет администратора из чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param user_id: ID пользователя
|
||||
@@ -586,7 +616,8 @@ class Bot(BaseConnection):
|
||||
count: int = None,
|
||||
) -> GettedMembersChat:
|
||||
|
||||
"""Получает участников чата.
|
||||
"""
|
||||
Получает участников чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param user_ids: Список ID участников
|
||||
@@ -610,7 +641,8 @@ class Bot(BaseConnection):
|
||||
user_id: int,
|
||||
) -> GettedMembersChat:
|
||||
|
||||
"""Получает участника чата.
|
||||
"""
|
||||
Получает участника чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param user_id: ID участника
|
||||
@@ -632,7 +664,8 @@ class Bot(BaseConnection):
|
||||
user_ids: List[str],
|
||||
) -> AddedMembersChat:
|
||||
|
||||
"""Добавляет участников в чат.
|
||||
"""
|
||||
Добавляет участников в чат.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param user_ids: Список ID пользователей
|
||||
@@ -653,7 +686,8 @@ class Bot(BaseConnection):
|
||||
block: bool = False,
|
||||
) -> RemovedMemberChat:
|
||||
|
||||
"""Исключает участника из чата.
|
||||
"""
|
||||
Исключает участника из чата.
|
||||
|
||||
:param chat_id: ID чата
|
||||
:param user_id: ID пользователя
|
||||
@@ -673,7 +707,8 @@ class Bot(BaseConnection):
|
||||
self,
|
||||
) -> UpdateUnion:
|
||||
|
||||
"""Получает обновления для бота.
|
||||
"""
|
||||
Получает обновления для бота.
|
||||
|
||||
:return: Список обновлений
|
||||
"""
|
||||
@@ -687,7 +722,8 @@ class Bot(BaseConnection):
|
||||
type: UploadType
|
||||
) -> GettedUploadUrl:
|
||||
|
||||
"""Получает URL для загрузки файлов.
|
||||
"""
|
||||
Получает URL для загрузки файлов.
|
||||
|
||||
:param type: Тип загружаемого файла
|
||||
|
||||
@@ -704,7 +740,8 @@ class Bot(BaseConnection):
|
||||
*commands: BotCommand
|
||||
) -> User:
|
||||
|
||||
"""Устанавливает список команд бота.
|
||||
"""
|
||||
Устанавливает список команд бота.
|
||||
|
||||
:param commands: Список команд
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import os
|
||||
import mimetypes
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from uuid import uuid4
|
||||
|
||||
import aiofiles
|
||||
import aiohttp
|
||||
|
||||
import puremagic
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ..exceptions.invalid_token import InvalidToken
|
||||
@@ -135,6 +138,51 @@ class BaseConnection:
|
||||
|
||||
return await response.text()
|
||||
|
||||
async def upload_file_buffer(
|
||||
self,
|
||||
url: str,
|
||||
buffer: bytes,
|
||||
type: UploadType
|
||||
):
|
||||
"""
|
||||
Загружает файл из буфера.
|
||||
|
||||
:param url: Конечная точка загрузки файла
|
||||
:param buffer: Буфер (bytes)
|
||||
:param type: Тип файла (video, image, audio, file)
|
||||
|
||||
:return: Сырой .text() ответ от сервера после загрузки файла
|
||||
"""
|
||||
|
||||
try:
|
||||
matches = puremagic.magic_string(buffer[:4096])
|
||||
if matches:
|
||||
mime_type = matches[0][1]
|
||||
ext = mimetypes.guess_extension(mime_type) or ''
|
||||
else:
|
||||
mime_type = f"{type.value}/*"
|
||||
ext = ''
|
||||
except Exception:
|
||||
mime_type = f"{type.value}/*"
|
||||
ext = ''
|
||||
|
||||
basename = f'{uuid4()}{ext}'
|
||||
|
||||
form = aiohttp.FormData()
|
||||
form.add_field(
|
||||
name='data',
|
||||
value=buffer,
|
||||
filename=basename,
|
||||
content_type=mime_type
|
||||
)
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(
|
||||
url=url,
|
||||
data=form
|
||||
)
|
||||
return await response.text()
|
||||
|
||||
async def download_file(
|
||||
self,
|
||||
path: str,
|
||||
|
||||
@@ -36,12 +36,19 @@ GET_UPDATES_RETRY_DELAY = 5
|
||||
|
||||
class Dispatcher:
|
||||
|
||||
"""Основной класс для обработки событий бота.
|
||||
"""
|
||||
Основной класс для обработки событий бота.
|
||||
|
||||
Обеспечивает работу с вебхуком и поллингом, управляет обработчиками событий.
|
||||
Обеспечивает запуск поллинга и вебхука, маршрутизацию событий,
|
||||
применение middleware, фильтров и вызов соответствующих обработчиков.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
"""
|
||||
Инициализация диспетчера.
|
||||
"""
|
||||
|
||||
self.event_handlers: List[Handler] = []
|
||||
self.contexts: List[MemoryContext] = []
|
||||
self.routers: List[Router] = []
|
||||
@@ -66,22 +73,34 @@ class Dispatcher:
|
||||
|
||||
async def check_me(self):
|
||||
|
||||
"""Проверяет и логирует информацию о боте."""
|
||||
"""
|
||||
Проверяет и логирует информацию о боте.
|
||||
"""
|
||||
|
||||
me = await self.bot.get_me()
|
||||
|
||||
self.bot._me = me
|
||||
|
||||
logger_dp.info(f'Бот: @{me.username} first_name={me.first_name} id={me.user_id}')
|
||||
|
||||
def include_routers(self, *routers: 'Router'):
|
||||
|
||||
"""Добавляет обработчики из роутеров.
|
||||
|
||||
Args:
|
||||
*routers: Роутеры для включения
|
||||
"""
|
||||
Добавляет указанные роутеры в диспетчер.
|
||||
|
||||
:param routers: Роутеры для добавления.
|
||||
"""
|
||||
|
||||
self.routers += [r for r in routers]
|
||||
|
||||
async def __ready(self, bot: Bot):
|
||||
|
||||
"""
|
||||
Подготавливает диспетчер: сохраняет бота, регистрирует обработчики, вызывает on_started.
|
||||
|
||||
:param bot: Экземпляр бота.
|
||||
"""
|
||||
|
||||
self.bot = bot
|
||||
await self.check_me()
|
||||
|
||||
@@ -96,14 +115,12 @@ class Dispatcher:
|
||||
|
||||
def __get_memory_context(self, chat_id: int, user_id: int):
|
||||
|
||||
"""Возвращает или создает контекст для чата и пользователя.
|
||||
|
||||
Args:
|
||||
chat_id: ID чата
|
||||
user_id: ID пользователя
|
||||
|
||||
Returns:
|
||||
Существующий или новый контекст
|
||||
"""
|
||||
Возвращает существующий или создает новый контекст по chat_id и user_id.
|
||||
|
||||
:param chat_id: Идентификатор чата.
|
||||
:param user_id: Идентификатор пользователя.
|
||||
:return: Объект MemoryContext.
|
||||
"""
|
||||
|
||||
for ctx in self.contexts:
|
||||
@@ -121,6 +138,15 @@ class Dispatcher:
|
||||
result_data_kwargs: Dict[str, Any]
|
||||
):
|
||||
|
||||
"""
|
||||
Последовательно обрабатывает middleware цепочку.
|
||||
|
||||
:param middlewares: Список middleware.
|
||||
:param event_object: Объект события.
|
||||
:param result_data_kwargs: Аргументы, передаваемые обработчику.
|
||||
:return: Изменённые аргументы или None.
|
||||
"""
|
||||
|
||||
for middleware in middlewares:
|
||||
result = await middleware.process_middleware(
|
||||
event_object=event_object,
|
||||
@@ -139,11 +165,12 @@ class Dispatcher:
|
||||
|
||||
async def handle(self, event_object: UpdateUnion):
|
||||
|
||||
"""Обрабатывает событие.
|
||||
|
||||
Args:
|
||||
event_object: Объект события для обработки
|
||||
"""
|
||||
Основной обработчик события. Применяет фильтры, middleware и вызывает подходящий handler.
|
||||
|
||||
:param event_object: Событие, пришедшее в бот.
|
||||
"""
|
||||
|
||||
try:
|
||||
ids = event_object.get_ids()
|
||||
memory_context = self.__get_memory_context(*ids)
|
||||
@@ -209,11 +236,12 @@ class Dispatcher:
|
||||
|
||||
async def start_polling(self, bot: Bot):
|
||||
|
||||
"""Запускает поллинг обновлений.
|
||||
|
||||
Args:
|
||||
bot: Экземпляр бота
|
||||
"""
|
||||
Запускает цикл получения обновлений с сервера (long polling).
|
||||
|
||||
:param bot: Экземпляр бота.
|
||||
"""
|
||||
|
||||
await self.__ready(bot)
|
||||
|
||||
while True:
|
||||
@@ -243,12 +271,12 @@ class Dispatcher:
|
||||
|
||||
async def handle_webhook(self, bot: Bot, host: str = '0.0.0.0', port: int = 8080):
|
||||
|
||||
"""Запускает вебхук сервер.
|
||||
|
||||
Args:
|
||||
bot: Экземпляр бота
|
||||
host: Хост для сервера
|
||||
port: Порт для сервера
|
||||
"""
|
||||
Запускает FastAPI-приложение для приёма обновлений через вебхук.
|
||||
|
||||
:param bot: Экземпляр бота.
|
||||
:param host: Хост, на котором запускается сервер.
|
||||
:param port: Порт сервера.
|
||||
"""
|
||||
|
||||
await self.__ready(bot)
|
||||
@@ -277,7 +305,9 @@ class Dispatcher:
|
||||
|
||||
class Router(Dispatcher):
|
||||
|
||||
"""Роутер для группировки обработчиков событий."""
|
||||
"""
|
||||
Роутер для группировки обработчиков событий.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -285,13 +315,30 @@ class Router(Dispatcher):
|
||||
|
||||
class Event:
|
||||
|
||||
"""Декоратор для регистрации обработчиков событий."""
|
||||
"""
|
||||
Декоратор для регистрации обработчиков событий.
|
||||
"""
|
||||
|
||||
def __init__(self, update_type: UpdateType, router: Dispatcher | Router):
|
||||
|
||||
"""
|
||||
Инициализирует событие-декоратор.
|
||||
|
||||
:param update_type: Тип события (UpdateType).
|
||||
:param router: Роутер или диспетчер, в который регистрируется обработчик.
|
||||
"""
|
||||
|
||||
self.update_type = update_type
|
||||
self.router = router
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
|
||||
"""
|
||||
Регистрирует функцию как обработчик события.
|
||||
|
||||
:return: Исходная функция.
|
||||
"""
|
||||
|
||||
def decorator(func_event: Callable):
|
||||
|
||||
if self.update_type == UpdateType.ON_STARTED:
|
||||
|
||||
@@ -14,3 +14,5 @@ class ButtonType(str, Enum):
|
||||
LINK = 'link'
|
||||
REQUEST_GEO_LOCATION = 'request_geo_location'
|
||||
CHAT = 'chat'
|
||||
MESSAGE = 'message'
|
||||
OPEN_APP = 'open_app'
|
||||
@@ -4,7 +4,7 @@ from magic_filter import F, MagicFilter
|
||||
|
||||
from ..filters.middleware import BaseMiddleware
|
||||
|
||||
from ..types.command import Command
|
||||
from ..types.command import Command, CommandStart
|
||||
|
||||
from ..context.state_machine import State
|
||||
|
||||
@@ -52,7 +52,7 @@ class Handler:
|
||||
self.filters.append(arg)
|
||||
elif isinstance(arg, State):
|
||||
self.state = arg
|
||||
elif isinstance(arg, Command):
|
||||
elif isinstance(arg, (Command, CommandStart)):
|
||||
self.filters.insert(0, F.message.body.text.startswith(arg.command))
|
||||
elif isinstance(arg, BaseMiddleware):
|
||||
self.middlewares.append(arg)
|
||||
|
||||
@@ -9,7 +9,7 @@ from .types.sended_message import SendedMessage
|
||||
from ..types.attachments.upload import AttachmentPayload, AttachmentUpload
|
||||
from ..types.errors import Error
|
||||
from ..types.message import NewMessageLink
|
||||
from ..types.input_media import InputMedia
|
||||
from ..types.input_media import InputMedia, InputMediaBuffer
|
||||
from ..types.attachments.attachment import Attachment
|
||||
|
||||
from ..enums.upload_type import UploadType
|
||||
@@ -67,7 +67,7 @@ class SendMessage(BaseConnection):
|
||||
|
||||
async def __process_input_media(
|
||||
self,
|
||||
att: InputMedia
|
||||
att: InputMedia | InputMediaBuffer
|
||||
):
|
||||
|
||||
# очень нестабильный метод независящий от модуля
|
||||
@@ -85,11 +85,18 @@ class SendMessage(BaseConnection):
|
||||
|
||||
upload = await self.bot.get_upload_url(att.type)
|
||||
|
||||
upload_file_response = await self.upload_file(
|
||||
url=upload.url,
|
||||
path=att.path,
|
||||
type=att.type
|
||||
)
|
||||
if isinstance(att, InputMedia):
|
||||
upload_file_response = await self.upload_file(
|
||||
url=upload.url,
|
||||
path=att.path,
|
||||
type=att.type,
|
||||
)
|
||||
elif isinstance(att, InputMediaBuffer):
|
||||
upload_file_response = await self.upload_file_buffer(
|
||||
url=upload.url,
|
||||
buffer=att.buffer,
|
||||
type=att.type,
|
||||
)
|
||||
|
||||
if att.type in (UploadType.VIDEO, UploadType.AUDIO):
|
||||
token = upload.token
|
||||
@@ -134,7 +141,7 @@ class SendMessage(BaseConnection):
|
||||
|
||||
for att in self.attachments:
|
||||
|
||||
if isinstance(att, InputMedia):
|
||||
if isinstance(att, InputMedia) or isinstance(att, InputMediaBuffer):
|
||||
input_media = await self.__process_input_media(att)
|
||||
json['attachments'].append(
|
||||
input_media.model_dump()
|
||||
|
||||
@@ -11,6 +11,7 @@ from ..types.updates.user_added import UserAdded
|
||||
from ..types.updates.user_removed import UserRemoved
|
||||
from ..types.updates import UpdateUnion
|
||||
|
||||
from ..types.attachments.attachment import Attachment
|
||||
from ..types.attachments.attachment import PhotoAttachmentPayload
|
||||
from ..types.attachments.attachment import OtherAttachmentPayload
|
||||
from ..types.attachments.attachment import ContactAttachmentPayload
|
||||
@@ -19,22 +20,31 @@ from ..types.attachments.attachment import StickerAttachmentPayload
|
||||
from ..types.attachments.buttons.callback_button import CallbackButton
|
||||
from ..types.attachments.buttons.chat_button import ChatButton
|
||||
from ..types.attachments.buttons.link_button import LinkButton
|
||||
from ..types.attachments.buttons.request_contact import RequestContact
|
||||
from ..types.attachments.buttons.request_contact import RequestContactButton
|
||||
from ..types.attachments.buttons.open_app_button import OpenAppButton
|
||||
from ..types.attachments.buttons.request_geo_location_button import RequestGeoLocationButton
|
||||
from ..types.attachments.buttons.message_button import MessageButton
|
||||
from ..types.message import Message
|
||||
|
||||
from ..types.command import Command, BotCommand
|
||||
from ..types.command import Command, BotCommand, CommandStart
|
||||
|
||||
from .input_media import InputMedia
|
||||
from .input_media import InputMediaBuffer
|
||||
|
||||
__all__ = [
|
||||
CommandStart,
|
||||
OpenAppButton,
|
||||
Message,
|
||||
Attachment,
|
||||
InputMediaBuffer,
|
||||
MessageButton,
|
||||
UpdateUnion,
|
||||
InputMedia,
|
||||
BotCommand,
|
||||
CallbackButton,
|
||||
ChatButton,
|
||||
LinkButton,
|
||||
RequestContact,
|
||||
RequestContactButton,
|
||||
RequestGeoLocationButton,
|
||||
Command,
|
||||
PhotoAttachmentPayload,
|
||||
|
||||
@@ -82,6 +82,12 @@ class ButtonsPayload(BaseModel):
|
||||
"""
|
||||
|
||||
buttons: List[List[InlineButtonUnion]]
|
||||
|
||||
def pack(self):
|
||||
return Attachment(
|
||||
type=AttachmentType.INLINE_KEYBOARD,
|
||||
payload=self
|
||||
)
|
||||
|
||||
|
||||
class Attachment(BaseModel):
|
||||
|
||||
@@ -3,13 +3,17 @@ from typing import Union
|
||||
from .callback_button import CallbackButton
|
||||
from .chat_button import ChatButton
|
||||
from .link_button import LinkButton
|
||||
from .request_contact import RequestContact
|
||||
from .request_contact import RequestContactButton
|
||||
from .request_geo_location_button import RequestGeoLocationButton
|
||||
from .message_button import MessageButton
|
||||
from .open_app_button import OpenAppButton
|
||||
|
||||
InlineButtonUnion = Union[
|
||||
CallbackButton,
|
||||
ChatButton,
|
||||
LinkButton,
|
||||
RequestContact,
|
||||
RequestGeoLocationButton
|
||||
RequestContactButton,
|
||||
RequestGeoLocationButton,
|
||||
MessageButton,
|
||||
OpenAppButton
|
||||
]
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from ....enums.button_type import ButtonType
|
||||
|
||||
from .button import Button
|
||||
|
||||
|
||||
@@ -12,4 +14,5 @@ class LinkButton(Button):
|
||||
url: Ссылка для перехода (должна содержать http/https)
|
||||
"""
|
||||
|
||||
type: ButtonType = ButtonType.LINK
|
||||
url: Optional[str] = None
|
||||
19
maxapi/types/attachments/buttons/message_button.py
Normal file
19
maxapi/types/attachments/buttons/message_button.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ....enums.button_type import ButtonType
|
||||
|
||||
from .button import Button
|
||||
|
||||
|
||||
class MessageButton(Button):
|
||||
|
||||
"""
|
||||
Кнопка для отправки текста
|
||||
|
||||
Attributes:
|
||||
type: Тип кнопки (определяет её поведение и функционал)
|
||||
text: Отправляемый текст
|
||||
"""
|
||||
|
||||
type: ButtonType = ButtonType.MESSAGE
|
||||
text: str
|
||||
22
maxapi/types/attachments/buttons/open_app_button.py
Normal file
22
maxapi/types/attachments/buttons/open_app_button.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import Optional
|
||||
|
||||
from ....enums.button_type import ButtonType
|
||||
|
||||
from .button import Button
|
||||
|
||||
|
||||
class OpenAppButton(Button):
|
||||
|
||||
"""
|
||||
Кнопка для открытия приложения
|
||||
|
||||
Attributes:
|
||||
text: Видимый текст кнопки
|
||||
web_app: Публичное имя (username) бота или ссылка на него, чьё мини-приложение надо запустить
|
||||
contact_id: Идентификатор бота, чьё мини-приложение надо запустить
|
||||
"""
|
||||
|
||||
type: ButtonType = ButtonType.OPEN_APP
|
||||
text: str
|
||||
web_app: Optional[str] = None
|
||||
contact_id: Optional[int] = None
|
||||
@@ -1,8 +1,18 @@
|
||||
from typing import Optional
|
||||
|
||||
from ....enums.button_type import ButtonType
|
||||
|
||||
from .button import Button
|
||||
|
||||
|
||||
class RequestContact(Button):
|
||||
class RequestContactButton(Button):
|
||||
|
||||
"""
|
||||
Кнопка с контактом.
|
||||
Кнопка с контактом
|
||||
|
||||
Args:
|
||||
text: Текст кнопки
|
||||
"""
|
||||
...
|
||||
|
||||
type: ButtonType = ButtonType.REQUEST_CONTACT
|
||||
text: str
|
||||
@@ -1,3 +1,5 @@
|
||||
from ....enums.button_type import ButtonType
|
||||
|
||||
from .button import Button
|
||||
|
||||
|
||||
@@ -10,4 +12,5 @@ class RequestGeoLocationButton(Button):
|
||||
подтверждения пользователя (по умолчанию False)
|
||||
"""
|
||||
|
||||
type: ButtonType = ButtonType.REQUEST_GEO_LOCATION
|
||||
quick: bool = False
|
||||
@@ -40,4 +40,19 @@ class BotCommand(BaseModel):
|
||||
"""
|
||||
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class CommandStart(Command):
|
||||
|
||||
"""
|
||||
Класс для представления команды /start бота.
|
||||
|
||||
Attributes:
|
||||
prefix (str): Префикс команды. По умолчанию '/'.
|
||||
"""
|
||||
|
||||
text = 'start'
|
||||
|
||||
def __init__(self, prefix: str = '/'):
|
||||
self.prefix = prefix
|
||||
@@ -1,34 +1,38 @@
|
||||
import mimetypes
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import puremagic
|
||||
|
||||
from ..enums.upload_type import UploadType
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
class InputMedia:
|
||||
|
||||
"""
|
||||
Класс для представления медиафайла.
|
||||
|
||||
Attributes:
|
||||
path (str): Путь к файлу.
|
||||
type (UploadType): Тип файла, определенный на основе MIME-типа.
|
||||
type (UploadType): Тип файла, определенный на основе содержимого (MIME-типа).
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, path: str):
|
||||
|
||||
"""
|
||||
Инициализирует объект медиафайла.
|
||||
|
||||
Args:
|
||||
path (str): Путь к файлу.
|
||||
"""
|
||||
|
||||
self.path = path
|
||||
self.type = self.__detect_file_type(path)
|
||||
|
||||
def __detect_file_type(self, path: str) -> UploadType:
|
||||
|
||||
"""
|
||||
Определяет тип файла на основе его MIME-типа.
|
||||
Определяет тип файла на основе его содержимого (MIME-типа).
|
||||
|
||||
Args:
|
||||
path (str): Путь к файлу.
|
||||
@@ -36,12 +40,62 @@ class InputMedia:
|
||||
Returns:
|
||||
UploadType: Тип файла (VIDEO, IMAGE, AUDIO или FILE).
|
||||
"""
|
||||
|
||||
mime_type, _ = mimetypes.guess_type(path)
|
||||
with open(path, 'rb') as f:
|
||||
sample = f.read(4096)
|
||||
|
||||
try:
|
||||
matches = puremagic.magic_string(sample)
|
||||
if matches:
|
||||
mime_type = matches[0].mime_type
|
||||
else:
|
||||
mime_type = None
|
||||
except Exception:
|
||||
mime_type = None
|
||||
|
||||
if mime_type is None:
|
||||
return UploadType.FILE
|
||||
|
||||
if mime_type.startswith('video/'):
|
||||
return UploadType.VIDEO
|
||||
elif mime_type.startswith('image/'):
|
||||
return UploadType.IMAGE
|
||||
elif mime_type.startswith('audio/'):
|
||||
return UploadType.AUDIO
|
||||
else:
|
||||
return UploadType.FILE
|
||||
|
||||
|
||||
class InputMediaBuffer:
|
||||
"""
|
||||
Класс для представления медиафайла из буфера.
|
||||
|
||||
Attributes:
|
||||
buffer (BytesIO): Буфер с содержимым файла.
|
||||
type (UploadType): Тип файла, определенный по содержимому.
|
||||
"""
|
||||
|
||||
def __init__(self, buffer: BytesIO):
|
||||
"""
|
||||
Инициализирует объект медиафайла из буфера.
|
||||
|
||||
Args:
|
||||
buffer (IO): Буфер с содержимым файла.
|
||||
"""
|
||||
self.buffer = buffer
|
||||
self.type = self.__detect_file_type(buffer)
|
||||
|
||||
def __detect_file_type(self, buffer: BytesIO) -> UploadType:
|
||||
try:
|
||||
matches = puremagic.magic_string(buffer)
|
||||
if matches:
|
||||
mime_type = matches[0].mime_type
|
||||
else:
|
||||
mime_type = None
|
||||
except Exception:
|
||||
mime_type = None
|
||||
|
||||
if mime_type is None:
|
||||
return UploadType.FILE
|
||||
if mime_type.startswith('video/'):
|
||||
return UploadType.VIDEO
|
||||
elif mime_type.startswith('image/'):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "maxapi"
|
||||
version = "0.8.7"
|
||||
version = "0.8.9"
|
||||
description = "Библиотека для разработки чат-ботов с помощью API мессенджера MAX"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
@@ -19,6 +19,7 @@ dependencies = [
|
||||
"pydantic>=1.8.0",
|
||||
"uvicorn>=0.15.0",
|
||||
"aiofiles==24.1.0",
|
||||
"puremagic==1.30"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -29,4 +30,7 @@ license-files = []
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=68.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["maxapi*", "wiki*", "examples*"]
|
||||
284
wiki/bot_methods.md
Normal file
284
wiki/bot_methods.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Методы класса Bot
|
||||
|
||||
## 💬 Работа с сообщениями
|
||||
|
||||
### `send_message(...)`
|
||||
|
||||
**Описание:** Отправить сообщение в чат или пользователю.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id` *(int)* — ID чата. Обязателен, если не указан `user_id`.
|
||||
* `user_id` *(int)* — ID пользователя. Обязателен, если не указан `chat_id`.
|
||||
* `text` *(str)* — текст сообщения.
|
||||
* `attachments` *(List\[Attachment])* — вложения (фото, видео и т.д.).
|
||||
* `link` *(NewMessageLink)* — объект для создания ссылочного сообщения.
|
||||
* `notify` *(bool)* — отправлять ли уведомление (по умолчанию берётся из настроек бота).
|
||||
* `parse_mode` *(ParseMode)* — форматирование текста (например, `ParseMode.HTML`).
|
||||
|
||||
**Возвращает:** `SendedMessage` — объект отправленного сообщения.
|
||||
|
||||
---
|
||||
|
||||
### `edit_message(...)`
|
||||
|
||||
**Описание:** Редактировать существующее сообщение.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `message_id` *(str)* — ID сообщения, полученное ранее в `SendedMessage.id`.
|
||||
* `text`, `attachments`, `link`, `notify`, `parse_mode` — см. `send_message`.
|
||||
|
||||
**Возвращает:** `EditedMessage` — объект изменённого сообщения.
|
||||
|
||||
---
|
||||
|
||||
### `delete_message(message_id)`
|
||||
|
||||
**Описание:** Удалить сообщение по его ID.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `message_id` *(str)* — ID сообщения.
|
||||
|
||||
**Возвращает:** `DeletedMessage` — результат удаления.
|
||||
|
||||
---
|
||||
|
||||
### `get_messages(...)`
|
||||
|
||||
**Описание:** Получить список сообщений.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id` *(int)* — ID чата.
|
||||
* `message_ids` *(List\[str])* — список ID сообщений.
|
||||
* `from_time` / `to_time` *(datetime | int)* — диапазон по времени.
|
||||
* `count` *(int)* — сколько сообщений вернуть (по умолчанию 50).
|
||||
|
||||
**Возвращает:** `Messages` — список объектов сообщений.
|
||||
|
||||
---
|
||||
|
||||
### `get_message(message_id)`
|
||||
|
||||
**Описание:** Получить одно сообщение по ID.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `message_id` *(str)* — ID сообщения.
|
||||
|
||||
**Возвращает:** `Messages` — содержит одно сообщение в списке.
|
||||
|
||||
---
|
||||
|
||||
### `pin_message(...)`
|
||||
|
||||
**Описание:** Закрепить сообщение в чате.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id` *(int)* — ID чата.
|
||||
* `message_id` *(str)* — ID сообщения.
|
||||
* `notify` *(bool)* — уведомление.
|
||||
|
||||
**Возвращает:** `PinnedMessage`
|
||||
|
||||
---
|
||||
|
||||
### `delete_pin_message(chat_id)`
|
||||
|
||||
**Описание:** Удалить закреплённое сообщение.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id` *(int)* — ID чата.
|
||||
|
||||
**Возвращает:** `DeletedPinMessage`
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Информация о боте
|
||||
|
||||
### `get_me()`
|
||||
|
||||
**Описание:** Получить объект бота.
|
||||
|
||||
**Возвращает:** `User` — текущий бот.
|
||||
|
||||
---
|
||||
|
||||
### `change_info(...)`
|
||||
|
||||
**Описание:** Изменить профиль бота.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `name` *(str)* — новое имя.
|
||||
* `description` *(str)* — описание.
|
||||
* `commands` *(List\[BotCommand])* — команды (name + description).
|
||||
* `photo` *(Dict)* — `{ "url": ..., "token": ... }` — загруженное изображение. URL можно получить через `get_upload_url(...)`.
|
||||
|
||||
**Возвращает:** `User`
|
||||
|
||||
---
|
||||
|
||||
### `set_my_commands(*commands)`
|
||||
|
||||
**Описание:** Установить команды бота.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `commands` *(BotCommand)* — команды, например `BotCommand(name="help", description="Справка")`
|
||||
|
||||
**Возвращает:** `User`
|
||||
|
||||
---
|
||||
|
||||
## 👥 Работа с чатами
|
||||
|
||||
### `get_chats(...)`
|
||||
|
||||
**Описание:** Получить список чатов.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `count` *(int)* — количество (по умолчанию 50).
|
||||
* `marker` *(int)* — маркер страницы.
|
||||
|
||||
**Возвращает:** `Chats`
|
||||
|
||||
---
|
||||
|
||||
### `get_chat_by_id(id)` / `get_chat_by_link(link)`
|
||||
|
||||
**Описание:** Получить объект чата по ID или публичной ссылке.
|
||||
|
||||
**Возвращает:** `Chat`
|
||||
|
||||
---
|
||||
|
||||
### `edit_chat(...)`
|
||||
|
||||
**Описание:** Изменить чат.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id`, `title`, `pin`, `notify` — как выше.
|
||||
* `icon` *(PhotoAttachmentRequestPayload)* — вложение фото, загруженное через `get_upload_url(...)` и `download_file(...)`.
|
||||
|
||||
**Возвращает:** `Chat`
|
||||
|
||||
---
|
||||
|
||||
### `delete_chat(chat_id)`
|
||||
|
||||
Удаляет чат.
|
||||
|
||||
**Возвращает:** `DeletedChat`
|
||||
|
||||
---
|
||||
|
||||
## 👤 Работа с участниками чатов
|
||||
|
||||
### `get_chat_members(...)` / `get_chat_member(...)`
|
||||
|
||||
**Описание:** Получить одного или нескольких участников.
|
||||
|
||||
**Возвращает:** `GettedMembersChat` (у него есть `.members`)
|
||||
|
||||
---
|
||||
|
||||
### `add_chat_members(...)`
|
||||
|
||||
**Описание:** Добавить участников.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id`, `user_ids` *(List\[str])* — список строковых ID.
|
||||
|
||||
**Возвращает:** `AddedMembersChat`
|
||||
|
||||
---
|
||||
|
||||
### `kick_chat_member(...)`
|
||||
|
||||
**Описание:** Исключить и опционально заблокировать.
|
||||
|
||||
**Возвращает:** `RemovedMemberChat`
|
||||
|
||||
---
|
||||
|
||||
### `get_list_admin_chat(...)` / `add_list_admin_chat(...)` / `remove_admin(...)`
|
||||
|
||||
**Описание:** Управление администраторами.
|
||||
|
||||
**Возвращают:** `GettedListAdminChat`, `AddedListAdminChat`, `RemovedAdmin`
|
||||
|
||||
---
|
||||
|
||||
### `get_me_from_chat(...)`
|
||||
|
||||
**Описание:** Получить, кем является бот в чате.
|
||||
|
||||
**Возвращает:** `ChatMember`
|
||||
|
||||
### `delete_me_from_chat(...)`
|
||||
|
||||
**Удаляет бота из чата.**
|
||||
|
||||
**Возвращает:** `DeletedBotFromChat`
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Обновления и действия
|
||||
|
||||
### `get_updates()`
|
||||
|
||||
**Описание:** Получить события (новости, сообщения и т.д.).
|
||||
|
||||
**Возвращает:** `UpdateUnion`
|
||||
|
||||
---
|
||||
|
||||
### `send_action(...)`
|
||||
|
||||
**Описание:** Отправить "печатает..." и т.д.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `chat_id`, `action` *(SenderAction)* — например, `SenderAction.TYPING_ON`
|
||||
|
||||
**Возвращает:** `SendedAction`
|
||||
|
||||
---
|
||||
|
||||
### `send_callback(...)`
|
||||
|
||||
**Описание:** Ответ на callback-кнопку.
|
||||
|
||||
**Аргументы:**
|
||||
|
||||
* `callback_id`, `message`, `notification`
|
||||
|
||||
**Возвращает:** `SendedCallback`
|
||||
|
||||
---
|
||||
|
||||
## 📎 Медиа и файлы
|
||||
|
||||
### `get_video(video_token)`
|
||||
|
||||
**Возвращает:** `Video`
|
||||
|
||||
### `get_upload_url(type)`
|
||||
|
||||
**Аргументы:** `type` *(UploadType)* — например, `UploadType.IMAGE`
|
||||
|
||||
**Возвращает:** `GettedUploadUrl` (у него есть `.url`)
|
||||
|
||||
### `download_file(path, url, token)` (НЕАКТУАЛЬНО)
|
||||
|
||||
**Описание:** Скачивает файл, используя URL и токен.
|
||||
|
||||
**Возвращает:** статус загрузки
|
||||
35
wiki/events.md
Normal file
35
wiki/events.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# События
|
||||
|
||||
Для обработки разных типов обновлений используются события (Event). Ниже перечислены основные события и их назначение.
|
||||
|
||||
| Событие | Описание |
|
||||
|-----------------------|----------------------------------------------------------------------------------------------|
|
||||
| `message_created` | Создание нового сообщения (пользователь отправил сообщение) |
|
||||
| `bot_added` | Бот добавлен в чат |
|
||||
| `bot_removed` | Бот удалён из чата |
|
||||
| `bot_started` | Пользователь запустил бота |
|
||||
| `chat_title_changed` | Изменено название чата |
|
||||
| `message_callback` | Пользователь нажал на callback-кнопку (inline button) |
|
||||
| `message_chat_created`| Срабатывает когда пользователь нажал на кнопку с действием "Создать чат" (работает некорректно со стороны API MAX, ждем исправлений) |
|
||||
| `message_edited` | Сообщение было отредактировано |
|
||||
| `message_removed` | Сообщение было удалено |
|
||||
| `user_added` | Пользователь добавлен в чат |
|
||||
| `user_removed` | Пользователь удалён из чата |
|
||||
| `on_started` | Бот запущен (**внутреннее** событие библиотеки) |
|
||||
|
||||
---
|
||||
|
||||
## Пример использования
|
||||
|
||||
```python
|
||||
@dp.message_created()
|
||||
async def on_message(event: MessageCreated):
|
||||
... # обработка нового сообщения
|
||||
|
||||
@dp.bot_added()
|
||||
async def on_bot_added(event: BotAdded):
|
||||
... # логика при добавлении бота
|
||||
|
||||
@dp.message_callback()
|
||||
async def on_callback(event: MessageCallback):
|
||||
... # обработка нажатия на callback-кнопку
|
||||
65
wiki/handlers.md
Normal file
65
wiki/handlers.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Философия хендлеров или как задается хендлер в maxapi
|
||||
|
||||
Для регистрации хендлера в maxapi используется объект `Dispatcher` или `Router` и декоратор с указанием типа события и фильтра.
|
||||
|
||||
## Общий синтаксис
|
||||
|
||||
```python
|
||||
@dp.<тип_события>(<фильтры>)
|
||||
async def <имя_функции>(event: <тип_события>):
|
||||
...
|
||||
```
|
||||
|
||||
* `dp` — экземпляр `Dispatcher`
|
||||
|
||||
* `<тип_события>` — тип события (например, `message_created`)
|
||||
|
||||
* `<фильтр>` — условие `MagicFilter`, по которому срабатывает хендлер (например, наличие текста в сообщении)
|
||||
|
||||
* `event` — объект события с данными (например, `MessageCreated`)
|
||||
|
||||
## Пример
|
||||
|
||||
```python
|
||||
@dp.message_created(F.message.body.text)
|
||||
async def echo(event: MessageCreated):
|
||||
await event.message.answer(f"Повторяю за вами: {event.message.body.text}")
|
||||
```
|
||||
|
||||
* `@dp.message_created` — хендлер на событие создания сообщения
|
||||
|
||||
* `F.message.body.text` — фильтр: сработает только если в сообщении есть текст
|
||||
|
||||
* `echo` — асинхронная функция-обработчик, которая принимает событие `MessageCreated`
|
||||
|
||||
* В теле функции вызывается метод `answer` для отправки ответа с повтором текста
|
||||
|
||||
|
||||
## Полный код
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from maxapi import Bot, Dispatcher
|
||||
from maxapi.filters import F
|
||||
from maxapi.types import MessageCreated
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
bot = Bot('тут_ваш_токен')
|
||||
dp = Dispatcher()
|
||||
|
||||
|
||||
@dp.message_created(F.message.body.text)
|
||||
async def echo(event: MessageCreated):
|
||||
await event.message.answer(f"Повторяю за вами: {event.message.body.text}")
|
||||
|
||||
|
||||
async def main():
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
```
|
||||
99
wiki/memory_context.md
Normal file
99
wiki/memory_context.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# MemoryContext
|
||||
|
||||
Контекст данных пользователя с поддержкой асинхронных блокировок. Используется для хранения и управления состоянием пользователя в рамках сессии.
|
||||
|
||||
При передаче в хендлер события `message_chat_created` в качестве атрибута `chat_id` идёт идентификатор созданного чата, а `user_id` - идентификатора бота!
|
||||
|
||||
## Класс: `MemoryContext`
|
||||
|
||||
```python
|
||||
MemoryContext(chat_id: int, user_id: int)
|
||||
````
|
||||
|
||||
### Аргументы:
|
||||
|
||||
* `chat_id` (`int`): Идентификатор чата.
|
||||
* `user_id` (`int`): Идентификатор пользователя.
|
||||
|
||||
|
||||
## Методы
|
||||
|
||||
### `async def get_data() -> dict[str, Any]`
|
||||
|
||||
Возвращает текущие данные контекста.
|
||||
|
||||
#### Возвращает:
|
||||
|
||||
* `dict[str, Any]`: Словарь с текущими данными пользователя.
|
||||
|
||||
---
|
||||
|
||||
### `async def set_data(data: dict[str, Any])`
|
||||
|
||||
Полностью заменяет контекст данных.
|
||||
|
||||
#### Аргументы:
|
||||
|
||||
* `data` (`dict[str, Any]`): Новый словарь данных, заменяющий текущий.
|
||||
|
||||
---
|
||||
|
||||
### `async def update_data(**kwargs)`
|
||||
|
||||
Обновляет текущий контекст, добавляя или изменяя переданные пары ключ-значение.
|
||||
|
||||
#### Аргументы:
|
||||
|
||||
* `**kwargs`: Ключи и значения для обновления контекста.
|
||||
|
||||
---
|
||||
|
||||
### `async def set_state(state: State | str = None)`
|
||||
|
||||
Устанавливает новое состояние пользователя или сбрасывает его.
|
||||
|
||||
#### Аргументы:
|
||||
|
||||
* `state` (`State | str | None`): Новое состояние. Если `None` — состояние будет сброшено.
|
||||
|
||||
---
|
||||
|
||||
### `async def get_state() -> State | None`
|
||||
|
||||
Возвращает текущее состояние пользователя.
|
||||
|
||||
#### Возвращает:
|
||||
|
||||
* `State | None`: Текущее состояние или `None`, если не установлено.
|
||||
|
||||
---
|
||||
|
||||
### `async def clear()`
|
||||
|
||||
Очищает все данные контекста и сбрасывает состояние.
|
||||
|
||||
---
|
||||
|
||||
## Пример использования
|
||||
|
||||
[Полный пример](https://github.com/love-apples/maxapi/tree/main/examples/router_with_input_media)
|
||||
|
||||
```python
|
||||
@dp.message_created(Command('clear'))
|
||||
async def hello(event: MessageCreated, context: MemoryContext):
|
||||
await context.clear()
|
||||
await event.message.answer(f"Ваш контекст был очищен!")
|
||||
|
||||
|
||||
@dp.message_created(Command('data'))
|
||||
async def hello(event: MessageCreated, context: MemoryContext):
|
||||
data = await context.get_data()
|
||||
await event.message.answer(f"Ваша контекстная память: {str(data)}")
|
||||
|
||||
|
||||
@dp.message_created(Command('context'))
|
||||
@dp.message_created(Command('state'))
|
||||
async def hello(event: MessageCreated, context: MemoryContext):
|
||||
data = await context.get_state()
|
||||
await event.message.answer(f"Ваше контекстное состояние: {str(data)}")
|
||||
```
|
||||
Reference in New Issue
Block a user