Урок 3. Машина состояний и то самое логгирование
Last updated
Was this helpful?
Last updated
Was this helpful?
Важно! Не забывайте обновлять библиотеку командой
python3.6 -m pip install -U aiogram
, так как разработчик постоянно обновляет её, избавляя от различных багов. Например в последнем релизе исправлены ошибкиFatal Python error: PyImport_GetModuleDict: no module dictionary!
и передача списков в фильтр состояний.
Урок проводится с использованием aiogram версии 1.2
машину состояний
встроенный
Традиционно код урока доступен
Так как мы сейчас будем разбирать машину состояний, эти самые состояния необходимо сначала создать. Сперва в голову приходит использование , однако я предпочел воспользоваться встроенным классом Helper, который подходит для данной задачи даже лучше.
Итак, запишем в файл наш демонстрационный класс с состояниями:
Если вы ещё не знакомы с классом Helper
, то советую позже ознакомиться, а сейчас можно просто посмотреть на значения всех элементов (или каждого по отдельности, если захотите):
К привычным с прошлых уроков импортам у нас добавляется ещё парочка, а именно:
И тут же применяем их:
На второй строчке подключаем логгирование. На нем долго останавливаться не буду - лучше проверьте его работу самостоятельно: например, можно добавить хэндлер только на текстовые сообщения, тогда при отправке стикера, фото (и т.п.), поста в канал, где бот является администратором, в логах увидите, что эти апдейты не отработаны никаким хэндлером.
Итак, по традиции добавляем обработчики команд start
и help
:
А так же "ловим" все сообщения, отправленные при "нулевом" состоянии:
Эти самые состояния нужно как-то устанавливать, поэтому сделаем так:
Не забываем, что хэндлеры обрабатываются в порядке их расположения в коде, поэтому описанная выше функция должна идти раньше приведенных ниже обработчиков.
В данном хэндлере мы передаем в состояние TestStates.TEST_STATE_1
, что эквивалентно ['test_state_1']
- отмечу дважды, тут передается именно массив.
Теперь добавим такой хэндлер:
Здесь мы уже указываем состояние 'test_state_2'
.
Библиотека сама понимает, когда мы передаем список состояний, а когда только одно состояние и под капотом обрабатывает их по-разному, но нам нет смысла об этом задумываться. В этом плане мы в плюсе, так как можем сделать вот так:
Выражение TestStates.TEST_STATE_3 | TestStates.TEST_STATE_4
возвращает массив: ['test_state_3', 'test_state_4']
. Грубо говоря, при проверке состояний библиотека проходится по списку и если встречает совпадение, хэндлер отрабатывается.
Ну и последний на сегодня хэндлер:
Он принимает в себя сообщения при всех состояниях из возможных в этом уроке, однако так как состояния с первого по четвертый ловятся хэндлерами выше, в этот попадают только нулевое и пятое.
Ещё в приведённом выше блоке кода используется контекстный менеджер (with as
) - многим с ним удобнее.
Для красоты ещё стоит закрывать соединение с хранилищем состояний, для этого объявляем функцию:
И в поллере указываем её:
Обращу внимание читателя на то, что здесь нам интересно использовать именно ListItem
, а не Item
, так как в таком случае мы сможем использовать разных состояний для передачи в handler (об этом дальше).
Так как в этот раз нам придется отправлять много сообщений, для разнообразия вынесем их в - для красоты.
Ещё не забываем добавить в токен своего бота и мы готовы писать логику!
На первой строчке мы указали хранилище состояний в оперативной памяти, так как потеря этих состояний нам не страшна (да и этот вариант больше всего подходит для демонстрационных целей, так как не требует настройки). Однако если у вас от состояний что-то зависит, рекомендуется ипользовать более надеждное хранилище. На данный момент можно подключить и .
В функции process_setstate_command
мы проверяем, идут ли с командой какие-то аргументы и запрашиваем текущее состояние пользователя. Если никаких аргументов с командой не идёт, сбрасываем состояние. Если же аргументы есть, то проверяем, чтобы те соответствовали нашему условию: не отрицательное число (минус в строке не пройдет валидацию .isdigit()
), которое меньше количества всех состояний. Если аргумент не подходит, сообщаем пользователю об этом. В ином случае устанавливаем новое текущее состояние и даем фидбек на действие сообщением. Обращу внимание читателя также на конструкцию message.reply('Some text', reply=False)
. Указав reply=False
мы используем шорткат, позволяющий нам ответить в тот же чат, не доставая айди пользователя / чата как, например, .