Welcome to vk_maria’s documentation!#
vk_maria очень простой фреймворк для создания ботов сообществ Vk, написанный на Python 3.8.
Официальные ресурсы vk_maria#
Новости: @vk_maria
Чат комьюнити: @vk_maria_ru
Pip: vk_maria
Docs: ReadTheDocs
Source: Github репозиторий
Issues/Bug tracker: Github issues tracker
Сильные стороны#
Простота и удобство
Наличие конечных автоматов (FSM)
Типизированная
Contents#
Установка#
Установка с помощью pip:#
(.venv) $ pip install vk_maria
Установка с github:#
(.venv) $ git clone https://github.com/lxstvayne/vk_maria
(.venv) $ cd vk_maria
(.venv) $ python setup.py install
Обычно рекомендуется использовать первый способ.
Хотя библиотека и готова к использованию, она всё ещё находится на стадии разработки, поэтому не забывайте регулярно её обновлять:
(.venv) $ pip install vk_maria --upgrade
Быстрый старт#
Прежде всего вы должны получить ключ доступа вашего сообщества.
Простейший пример#
Класс Vk
инкапсулирует все методы работы с токеном сообщества.
Создайте файл echo_bot.py. Откройте его и создайте экземпляр класса Vk
:
from vk_maria import Vk, types
from vk_maria.dispatcher import Dispatcher
vk = Vk(access_token='token')
Примечание
Обязательно замените token ключом доступа вашего сообщества.
Затем создайте экземпляр класса Dispatcher
передав ему в качестве аргумента vk
:
dp = Dispatcher(vk)
После этого нам необходимо зарегистрировать обработчик событий. Обработчики событий определяют фильтры, которые должно пройти событие. Если событие проходит фильтры, вызывается декорированная функция и входящее событие передаётся в качестве аргумента.
Давайте определим обработчик событий, который будет обрабатывать все входящие сообщения от пользователя в личные сообщения сообщества и отвечать на команду Начать:
@dp.message_handler(text='Начать')
def send_welcome(event: types.Message):
vk.messages_send(user_id=event.message.from_id, message='Добро пожаловать!')
Добавим ещё один обработчик:
@dp.message_handler()
def echo(event: types.Message):
event.answer(event.message.text)
Как вы могли заметить, event.answer
является удобным аналогом vk.messages_send
.
Декорированная функция может иметь произвольное имя, однако она должна принимать минимум 1 параметр (event).
Примечание
Все обработчики тестируются в том порядке, в котором они были объявлены.
Отлично, теперь у нас есть простой бот, который отвечает на сообщение Начать приветствием и повторяет остальные отправленные сообщения. Чтобы запустить бота добавьте в исходный код следующее:
dp.start_polling(debug=True)
Примечание
Параметр debug отвечает за вывод в консоль всех происходящих событий
Вот и всё! Наш исходный файл теперь выглядит так:
from vk_maria import Vk, types
from vk_maria.dispatcher import Dispatcher
vk = Vk(access_token='token')
dp = Dispatcher(vk)
@dp.message_handler(text='Начать')
def send_welcome(event: types.Message):
vk.messages_send(user_id=event.message.from_id, message='Добро пожаловать!')
@dp.message_handler()
def echo(event: types.Message):
event.answer(event.message.text)
if __name__ == '__main__':
dp.start_polling(debug=True)
Чтобы запустить бота, просто откройте терминал, введите python echo_bot.py
и протестируйте его.
Обработка событий#
Лонгполлинг#
События можно обрабатывать двумя способами
С помощью цикла#
from vk_maria import Vk
from vk_maria.longpoll import LongPoll
vk = Vk(access_token='token')
lp = LongPoll(vk)
for event in lp.listen():
...
С помощью диспетчера событий#
from vk_maria import Vk
from vk_maria.dispatcher import Dispatcher
vk = Vk(access_token='token')
dp = Dispatcher(vk)
@dp.message_handler()
def handler(event: types.Message)
...
if __name__ == '__main__':
dp.start_polling()
Обработчики событий#
Обработчик событий это функция с декоратором event_handler()
. Он определяет фильтры для обрабатываемых событий.
@dp.event_handler(event_type, *filters, **bound_filters)
def handler(event):
...
Для удобной работы с сообщениями, существует декоратор message_handler(*filters, **bound_filters)
, в который по стандарту передаётся types.EventType.MESSAGE_NEW
.
Связанные фильтры устанавливаются следующим образом: name=argument
Название |
Аргументы |
Условие |
---|---|---|
event_type |
types.EventType |
True, если типы событий совпадают. |
text |
Строка |
True, если текст сообщения совпадает с аргументом text |
regexp |
Регулярное выражение или подстрока |
True, если подстрока находится в сообщении или строка проходит проверку на наличие шаблона регулярного выражения (Подробнее Python Regular Expressions). |
commands |
Список строк |
True, если текст сообщения совпадает с одной из команд |
frm |
От кого обрабатывать события (user, chat, group) по умолчанию user |
True, если поле from_(user, chat, group) соответственно равно аргументу frm |
state |
Состояние автомата |
True, если текущее состояние равно состаянию аргумента state |
Чтобы начать обрабатывать события, необходимо запустить start_polling()
. Для удобства разработки можно передать параметр debug=True
. Тогда все происходящие события будут красиво выводиться в консоль.
Собственные фильтры#
Чтобы определить собственный фильтр, необходимо написать класс и переопределить функцию check
.
from vk_maria.dispatcher.filters import AbstractFilter
class AdminFilter(AbstractFilter):
def check(self, event: types.Message):
return event.message.peer_id == 1234567890
И передать его в обработчик события:
@dp.message_handler(AdminFilter, commands=['/start'])
def cmd_start(event: types.Message):
event.reply("Hi there! What's your name?")
Количество пользовательских фильтров неограничено, их необходимо передавать первым аргументом через запятую.
Загрузка файлов#
Класс Upload
реализует готовые функции для загрузки файлов на сервера Вконтакте.
Доступные методы:
photo(photo)
set_chat_photo(file, chat_id, **kwargs)
set_group_cover_photo(photo)
document(document, peer_id, **kwargs)
Параметры photo и document могут быть как строкой относительного пути к файлу, так и файлом открытым с помощью open() на бинарное чтение rb стандартной библиотеки Python.
Клавиатуры#
Клавиатуры можно создавать двумя способами
KeyboardModel#
С помощью определения класса
from vk_maria.types import KeyboardModel, Button, Color
class TestKeyboard(KeyboardModel):
one_time = True
row1 = [
Button.Text(Color.PRIMARY, 'Кнопка 1'),
Button.Text(Color.PRIMARY, 'Кнопка 2')
]
row2 = [
Button.Text(Color.PRIMARY, 'Кнопка 3'),
Button.Text(Color.PRIMARY, 'Кнопка 4')
]
KeyboardMarkup#
С помощью генерации через код
from vk_maria.types import KeyboardMarkup, Button, Color
markup = KeyboardMarkup(one_time=True)
markup.add_button(Button.Text(Color.PRIMARY, 'Кнопка 1'))
markup.add_button(Button.Text(Color.PRIMARY, 'Кнопка 2'))
markup.add_row()
markup.add_button(Button.Text(Color.PRIMARY, 'Кнопка 3'))
markup.add_button(Button.Text(Color.PRIMARY, 'Кнопка 4'))
Конечные автоматы (FSM)#
Если вашему боту необходима система диалогов, то в дело вступают конечные автоматы. Это некая математическая модель, которая представляет собой набор состояний, которые переключаются в определённых условиях. В библиотеке это реализовано с помощью классов
from vk_maria.dispatcher.fsm import StatesGroup, State, MemoryStorage, FSMContext
class Form(StatesGroup):
waiting_for_name: State
waiting_for_age: State
waiting_for_gender: State
Необходимо описать в классе переменные состояний.
Чтобы имелась возможность запоминать текущие состояния для диалогов всех пользователей, в класс Dispatcher
необходимо передать экземпляр хранилища состояний.
dp = Dispatcher(vk, MemoryStorage())
В библиотеке на данный момент реализованы следующие хранилища состояний: MemoryStorage
, JSONStorage
, PickleStorage
.
Пример реализации#
from vk_maria import Vk, types
from vk_maria.dispatcher import Dispatcher
from vk_maria.dispatcher.fsm import StatesGroup, State, MemoryStorage, FSMContext
from vk_maria.types import KeyboardMarkup, Button, Color, RemoveReplyMarkup
vk = Vk(access_token='token')
dp = Dispatcher(vk, MemoryStorage())
GENDERS = ('Male', 'Female', 'Other')
class Form(StatesGroup):
waiting_for_name: State
waiting_for_age: State
waiting_for_gender: State
@dp.message_handler(commands=['/start'])
def cmd_start(event: types.Message):
event.reply("Hi there! What's your name?")
Form.waiting_for_name.set()
@dp.message_handler(state=Form.waiting_for_name)
def process_name(event: types.Message, state: FSMContext):
state.update_data(name=event.message.text)
event.reply("How old are you?")
Form.next()
@dp.message_handler(state=Form.waiting_for_age)
def process_age(event: types.Message, state: FSMContext):
state.update_data(age=event.message.text)
markup = KeyboardMarkup(one_time=False)
for gender in GENDERS:
markup.add_button(Button.Text(Color.PRIMARY, gender))
event.reply('What is your gender?', keyboard=markup)
Form.next()
@dp.message_handler(state=Form.waiting_for_gender)
def process_gender(event: types.Message, state: FSMContext):
if event.message.text not in GENDERS:
return event.reply('Bad gender name. Choose your gender from the keyboard.')
state.update_data(gender=event.message.text)
user_data = state.get_data()
event.answer(f'Hi! Nice to meet you, {user_data["name"]}\n'
f'Age: {user_data["age"]}\n'
f'Gender: {user_data["gender"]}', keyboard=RemoveReplyMarkup)
Form.finish()
if __name__ == '__main__':
dp.start_polling(debug=True)
Примечание
Чтобы каждый раз не писать FSMContext.get_current(), можно передать аргумент state: FSMContext в функцию-обработчик.
Чтобы отлавливать состояние, передаётся аргумент state
для message_handler