Обработка команды telegram bot python

Футбольный телеграм бот на Python (2/4): Функциональность бота

Во второй части серии статей по написанию телеграм бота на python, мы добавим функциональность. Бот будет приветствовать новых юзеров, предлагать выбрать и сохранять лиги. Добавим возможность получить результаты по выбранным лигам.

«Рыба» кода бота

Сразу запишем функции в «bot.py», которые понадобятся. Предварительно удалите test_message :

 
# fonlinebot/app/bot.py # . dp.middleware.setup(LoggingMiddleware()) @dp.message_handler(commands=['start']) async def start_handler(message: types.Message): """Обработка команды start. Вывод текста и меню""" . @dp.message_handler(commands=['help']) async def help_handler(message: types.Message): """Обработка команды help. Вывод текста и меню""" . @dp.callback_query_handler(lambda c: c.data == 'main_window') async def show_main_window(callback_query: types.CallbackQuery): """Главный экран""" . @dp.message_handler(lambda message: message.text == msg.btn_online) @dp.message_handler(commands=['online']) async def get_results(message: types.Message): """Обработка команды online и кнопки Онлайн. Запрос матчей. Вывод результатов""" . @dp.callback_query_handler(lambda c: c.data.startswith('update_results')) async def update_results(): """Обновление сообщения результатов""" . @dp.message_handler(lambda message: message.text == msg.btn_config) async def get_config(message: types.Message): """Обработка кнопки Настройки. Проверка выбора лиг. Вывод меню изменений настроек""" . @dp.callback_query_handler(lambda c: c.data.startswith('edit_config')) async def set_or_update_config(user_id: str): """Получение или обновление выбранных лиг""" . @dp.callback_query_handler(lambda c: c.data[:6] in ['del_le', 'add_le']) async def update_leagues_info(callback_query: types.CallbackQuery): """Добавление/удаление лиги из кеша, обновление сообщения""" . @dp.callback_query_handler(lambda c: c.data == 'save_config') async def save_config(callback_query: types.CallbackQuery): """Сохранение пользователя в базу данных""" . @dp.callback_query_handler(lambda c: c.data == 'delete_config') async def delete_config(user_id: str): """Удаление пользователя из базы данных""" . @dp.message_handler() async def unknown_message(message: types.Message): """Ответ на любое неожидаемое сообщение""" . async def on_shutdown(dp): # .

Это не окончательная версия, я мог что-то упустить. В процессе добавим недостающие.

Каждая функция обернута декоратором, так мы общаемся с Телегармом:

  • @dp.message_handler(commands=['start']) — декоратор ожидает сообщения-команды (которые начинаются с / ). В этом примере он ожидает команду /start .
  • @dp.callback_query_handler(lambda c: c.data == 'main_window') — ожидает callback и принимает lambda-функцию для его фильтрации. Callback отправляется inline-кнопками. В примере мы ожидаем callback со значением 'main_window' .
  • @dp.message_handler(lambda message: message.text == msg.btn_config) — этот декоратор похож на предыдущий, но ожидает сообщение от пользователя. В примере мы будем обрабатывать сообщение с текстом из msg.btn_config .

Итак. Пользователь нажимает команду старт, получает приветственное сообщение. В нем мы предлагаем выбрать 3 лиги и мониторить результаты по ним. Получить результаты можно командой или кнопкой меню. Так же мы даем возможность изменить выбранные соревнования или удалить свои данные из бота.

Добавление команд в бота

Изначально команды не настроены. Пользователи могут вводить их, но специально меню нет. Для добавления нужно снова написать https://t.me/botfather команду /setcommands . Выберите своего бота и добавьте этот текст:

start - Запуск и перезапуск бота
help - Возможности бота
online - Результаты матчей

В ответ получите «Success! Command list updated. /help». Теперь можно перейти в своего бота и проверить:

 Добавление команд в бота

Ответы на команды

Взаимодействие с ботом начинается с команды /start . Нужно поприветствовать и предложить следующий шаг. Эта команда будет возвращать текст с клавиатурой. Точно так же работает и /help .

Добавим обработку этих команд в «bot.py», обновите start_handler help_handler :

 
# fonlinebot/app/bot.py # . from config import TOKEN, YEAR, MINUTE import app.service as s # . @dp.message_handler(commands=['start']) async def start_handler(message: types.Message): """Обработка команды start. Вывод текста и меню""" # проверка, есть ли пользователь в базе user_league_ids = await s.get_league_ids(message.from_user.id) if not user_league_ids: await message.answer(msg.start_new_user) # добавление id сообщения настроек cache.setex(f"last_msg_", YEAR, message.message_id+2) await set_or_update_config(user_id=message.from_user.id) else: await message.answer(msg.start_current_user, reply_markup=s.MAIN_KB) @dp.message_handler(commands=['help']) async def help_handler(message: types.Message): """Обработка команды help. Вывод текста и меню""" await message.answer(msg.help, reply_markup=s.MAIN_KB) # .

Я добавил импорт import app.service as s . В этом модуле клавиатура и функция проверки пользователя. start_handler проверяет есть ли пользователь в кеше или базе данных, и отправляет ему соответствующий текст.

Перед отправкой текста для выбора лиг, я сохранил его будущий id. Получил номер последнего сообщения (это сама команда «start») и добавил 2 пункта: +1 за наш ответ на команду и +1 за само сообщения выбора лиг. Зная id сообщения, его можно редактирвать.

Теперь напишем клавиатуру и get_league_ids в модуль «service».

 
# fonlinebot/app/service.py from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, \ InlineKeyboardMarkup, InlineKeyboardButton from emoji import emojize from config import BOT_LEAGUES, BOT_LEAGUE_FLAGS from database import cache, database as db from app.dialogs import msg MAIN_KB = ReplyKeyboardMarkup( resize_keyboard=True, one_time_keyboard=True ).row( KeyboardButton(msg.btn_online), KeyboardButton(msg.btn_config) ) async def get_league_ids(user_id: str) -> list: """Функция получает id лиг пользователя в базе данных""" leagues = cache.lrange(f"u", 0, -1) if leagues is None: leagues = await db.select_users(user_id) if leagues is not None: leagues = leagues.split(',') [cache.lpush(f"u", lg_id) for lg_id in leagues] else: return [] return leagues

Ответы на команды

MAIN_KB — основная клавиатура, как на скриншоте выше. Разберем подробнее:

  • ReplyKeyboardMarkup — объект, который создает клавиатуру.
  • Параметр resize_keyboard=True уменьшает ее размер.
  • А с one_time_keyboard=True клавиатура будет скрываться, после использования.
  • .row — метод для группировки кнопок в строку.
  • KeyboardButton(msg.btn_online) и KeyboardButton(msg.btn_config) — кнопки с заданным текстом.

Осталось только добавить текста сообщений в dialogs. Вставьте этот код в класс Messages .

 
# fonlinebot/app/dialogs.py # . start_new_user: str = "Привет. Я могу сообщать тебе результаты матчей online." start_current_user: str = "Привет. С возвращением! " \ "Используй команды или меню внизу для продолжения." help: str = """ Этот бот получает результаты матчей за последние 48 часов. Включая режим LIVE. - Что бы выбрать/изменить лиги нажмите "Настройки". - Для проверки результатов нажмите "Онлайн". Бот создан в учебных целях, для сайта pythonru.com """

Выбор, изменение и удаление лиг

Сначала внесем правки в наши вспомогательные модули.

В «dababase» в класс Database добавим новый метод insert_or_update_users .

 
# fonlinebot/database.py #. async def insert_or_update_users(self, user_id: int, leagues: str): user_leagues = await self.select_users(user_id) if user_leagues is not None: await self.update_users(user_id, leagues) else: await self.insert_users(user_id, leagues) #.

В настройки добавим переменные метрик времени:

Источник

Создаем бота в Telegram

В этом статье я покажу как создать Telegram бота с помощью Python, поскольку не нашел хорошей русскоязычной статьи по этой теме.

Создание бота

Бот создается с помощью BotFather через Telegram. После команды /newbot надо просто следовать инструкции.

В конце мы получаем токен для управления ботом и работы с Telegram API.

pyTelegramBotApi

Ссылки на документации всех библиотек будут в конце.

Создадим простого бота, отвечающего на команду /start , с помощью этой библиотеки:

import telebot bot = telebot.TeleBot('1408700689:AAGVqcqscWWK7DnuNHahd0w1eNklfjPEVxE') @bot.message_handler(commands=['start']) def start(message): bot.send_message(message.chat.id, 'It works!') bot.polling()

pyTelegramBotApi является просто обёрткой для всего Telegram Bot API, но здесь разберутся только основные составляющие.
Взаимодействие с ботом происходит через переменную bot (токен надо вставить свой).
Декоратор @message_handler реагирует на входящие сообщение.
Message – это объект из Bot API, содержащий в себе информацию о сообщении. Полезные поля:
message.chat.id – идентификатор чата
message.from.id – идентификатор пользователя
message.text – текст сообщения
Функция send_message принимает идентификатор чата (берем его из сообщения) и текст для отправки.

Примеры функций

Отправка изображений

Можно отправлять фото из локального хранилища, но удобнее это делать по ссылке. Код аналогичен предыдущему:

@bot.message_handler(commands=['start']) def start(message): bot.send_photo(message.chat.id, photo=photo_url, caption='It works!')

Замена клавиатуры

У ботов есть функция замены стандартной клавиатуры на кнопочную. Для этого у всех функций есть опциональный аргумент reply_markup:

from telebot import types @bot.message_handler(commands=['start']) def start(message): markup = types.ReplyKeyboardMarkup() buttonA = types.KeyboardButton('A') buttonB = types.KeyboardButton('B') buttonC = types.KeyboardButton('C') markup.row(buttonA, buttonB) markup.row(buttonC) bot.send_message(message.chat.id, 'It works!', reply_markup=markup)

ReplyKeyboardMarkup – и есть та самая клавиатура. Метод row() создает ряд (максимум 12) из кнопок, передаваемых в качестве аргумента.
Также есть особенная клавиатура types.ReplyMarkupRemove(), которая меняет клавиатуру на стандартную.

Клавиатура для сообщений

Можно создавать клавиатуру для отдельного сообщения. Передавать его нужно так же в аргумент reply_markup:

from telebot import types @bot.message_handler(commands=['start']) def start(message): markup = types.InlineKeyboardMarkup() buttonA = types.InlineKeyboardButton('A', callback_data='a') buttonB = types.InlineKeyboardButton('B', callback_data='b') buttonC = types.InlineKeyboardButton('C', callback_data='c') markup.row(buttonA, buttonB) markup.row(buttonC) bot.send_message(message.chat.id, 'It works!', reply_markup=markup)

У кнопок есть несколько режимов, в зависимости от второго аргумента. Подробнее можно прочитать в официальной документации, но я остановлюсь только на callback_data.
При нажатии на такую кнопку боту придет отдельный CallbackQuery, который нужно обрабатывать подобно сообщению:

@bot.callback_query_handler(func=lambda call: True) def handle(call): bot.send_message(call.message.chat.id, 'Data: <>'.format(str(call.data))) bot.answer_callback_query(call.id)

Для обработки обязательно указать аргумент func для "отсеивания" Callback запросов.
После обработки каждого запроса нужно выполнить команду answer_callback_query, чтобы Telegram понял, что запрос обработан. В поле callback.data хранится информация из callback_data нажатой кнопки.

Изменение сообщений

У ботов есть функция изменения своих сообщений (можно использовать, чтобы сделать перелистывание страниц, например). Для этого нужно воспользоваться методом edit_message_text (edit_message_caption для картинок):

@bot.callback_query_handler(lambda call: True) def handle(call): bot.send_message(chat_id=call.message.chat.id, message_id=call.message.id, text='It works!') bot.answer_callback_query(call.id)

Смысл аргументов понятен из их названия.

Flask

Если запустить бота, то через какое-то время он упадет с ошибкой Connection to api.telegram.org timed out. Чтобы это исправить нужно использовать вебхук:

from flask import Flask, request import telebot token = '1408700689:AAGVqcqscWWK7DnuNHahd0w1eNklfjPEVxE' bot = telebot.TeleBot(token) app = Flask(__name__) @bot.message_handler(commands=['start']) def start(message): bot.send_message(message.chat.id, 'It works!') @app.route("/" + token, methods=['POST']) def getMessage(): bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]) return "!", 200 bot.remove_webhook() bot.set_webhook('https://test.com/' + token) app.run()

Этот код при запуске сначала удалит вебхук, если такой был, и установит его на желаемый. Все запросы, которые приходят в функцию getMessage будут направляться в bot с помощью метода process_new_updates. Этот код уже можно использовать для запуска, например, на Heroku.

P.S. Чтобы работать с длинными диалогами, я хранил для каждого пользователя в базе данных его текущее состояние.

Источник

Читайте также:  Python static variable in static method
Оцените статью