Создание меню/кнопок в pyTelegramBotAPI на основе SQL запроса

от автора

В данной статье планирую поделиться с вами своей наработкой, которая позволяет создавать меню и кнопки вашего Telegram бота на основе данных хранящихся в БД.

Реализовывать все это будем на Python и нам потребуются библиотеки:

  • sqlite3 — для работы с БД (устанавливать не нужно, поставляется в коробке с Питоном)

  • pyTelegramBotAPI — для создания Telegram бота (предварительно необходимо установить)

Представьте, что мы имеем файл базы данных «database.db» с таблицей, которая называется «create_menu«. Эта таблица хранит следующую информацию:

type_menu

order_num

btn_name

btn_callback

buy

1

?Яблоки

apple

buy

2

?Лимоны

lemon

buy

3

?Бананы

banana

buy

4

?Назад

back

help

1

?Назад

back

main

1

?Купить

buy

main

2

?Продать

sell

main

3

?Помощь

help

sell

1

?Томаты

tomato

sell

2

?Кокосы

coconut

sell

3

?Манго

mango

sell

4

?Назад

back

type_menu — название меню, к которому будет относиться данная кнопка

order_num — порядковый номер кнопки в меню (сверху вниз)

btn_name текст, который будет отображаться на кнопке

btn_callback данные, которые будут возвращены при нажатии на кнопку. Их будет отлавливать обработчик событий.

Ну что, теперь поехали!

Создадим класс CreateMenu и сразу инициализируем в нём путь к нашему файлу базы данных.

import os import sqlite3 from telebot import types  class CreateMenu:     def __init__(self):         '''Конструктор класса. Определяет файл базы данных'''         self.__db_path = os.getcwd()         self.db_name = os.path.join(self.__db_path, 'database.db')

По умолчанию считаем, что файл БД находиться в одном каталоге с файлом программы, поэтому строим путь к нему используя os.getcwd() и os.path.join().

Теперь сделаем внутренний метод __connect() который будет отвечать за подключение к нашей БД с использованием библиотеки sqlite3.

def __connect(self):         '''Функция подключения к базе данных'''         connect = sqlite3.connect(self.db_name)         return connect

Так же сделаем внутренний метод __select_button() который будет бегать в БД с SQL запросом и возвращать нам словарь (dict), где ключ (key) = название кнопки и значение (value) = callback data этой кнопки. Что бы этот метод не тащил абы чего, он будет принимать аргумент type_menu. Это позволит нам набирать кнопки под конкретное меню.

def __select_button(self, type_menu: str) -> dict:         '''В качестве аргумента принимает "тип меню". Возвращает словарь где ключ = текст кнопки, значение = callbackdata кнопки'''         with self.__connect() as connect:             cursor = connect.cursor()             sql = """SELECT btn_name, btn_callback FROM create_menu WHERE type_menu = (?) ORDER BY order_num"""             select_db = cursor.execute(sql, (type_menu,))             result = dict()             for btn_name, btn_callback in select_db.fetchall():                 result[btn_name] = btn_callback             return result

Думаю стоит объяснить, что за магия тут твориться.-

  • Подключаемся к БД (используя ранее заготовленный __connect())

  • Выполняем SQL запрос, который дословно можно перевести :

    «Покажи мне «название кнопки» и «её callback» из таблицы create_menu где тип меню равен type_menu и отсортируй все это по order_num в порядке возрастания.

  • Создаем словарик в который будем записывать результат SQL запроса.

  • Пробегаемся в цикле по результату SQL запроса и добавляем новые записи в словарь.

  • Возвращаем из функции словарь.

    Например если мы передадим в качестве аргумента «main» функция вернёт словарь: {‘?Купить’: ‘buy’, ‘?Продать’: ‘sell’, ‘?Помощь’: ‘help’})

Ну и давайте напишем единственный метод в классе, который будет вызываться программистом. Назовем его create_menu() и как вы догадались он будет создавать меню.

def create_menu(self, type_menu: str) -> types.InlineKeyboardMarkup:         '''Создаём меню для TG бота'''         markup = types.InlineKeyboardMarkup()         btn_list = self.__select_button(type_menu)         for element in btn_list.items():             btn = types.InlineKeyboardButton(text= element[0], callback_data= element[1])             markup.add(btn)         return markup

Метод в качестве аргумента принимает тип меню (type_menu), которое мы хотим создать. Далее он с этим аргументам дёргает выше рассмотренный внутренний метод __select_button() и получает в свое распоряжение словарик из которого будет лепить меню.

По классике жанра, пробегает словарик в цикле и добавляет в меню (markup) кнопки, где текст кнопки — ключ словаря, а её обратный данные — значение из словаря.

Итоговый код выглядит следующим образом:

import os import sqlite3 from telebot import types  class CreateMenu:     def __init__(self):         '''Конструктор класса. Определяет файл базы данных'''         self.__db_path = os.getcwd()         self.db_name = os.path.join(self.__db_path, 'database.db')       def __connect(self):         '''Функция подключения к базе данных'''         connect = sqlite3.connect(self.db_name)         return connect       def __select_button(self, type_menu: str) -> dict:         '''В качестве аргумента принимает "тип меню". Возвращает словарь где ключ = текст кнопки, значение = callbackdata кнопки'''         with self.__connect() as connect:             cursor = connect.cursor()             sql = """SELECT btn_name, btn_callback FROM create_menu WHERE type_menu = (?) ORDER BY order_num"""             select_db = cursor.execute(sql, (type_menu,))             result = dict()             for btn_name, btn_callback in select_db.fetchall():                 result[btn_name] = btn_callback             return result           def create_menu(self, type_menu: str) -> types.InlineKeyboardMarkup:         '''Создаём меню для TG бота'''         markup = types.InlineKeyboardMarkup()         btn_list = self.__select_button(type_menu)         for element in btn_list.items():             btn = types.InlineKeyboardButton(text= element[0], callback_data= element[1])             markup.add(btn)         return markup

Теперь мы можем импортировать написанный нами класс в свой проект. Создать экземпляр класса CreateMenu и использовать его метод create_menu() для создания разного рода меню.

Примечание класс CreateMenu описывался мной в файле с именем db.py

Пример использования:

import telebot  from db import CreateMenu  bot = telebot.TeleBot("ТОКЕН ВАШЕГО БОТА") cm = CreateMenu()  @bot.message_handler(commands=["start"]) def start(message):     bot.send_message(message.chat.id, "Добро пожаловать", reply_markup= cm.create_menu('main'))  @bot.callback_query_handler(func=lambda call: True) def callback_inline(call):     if call.data == 'buy':         bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="Что покупаем?", reply_markup= cm.create_menu('buy'))     if call.data == 'sell':         bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="Что продаём?", reply_markup= cm.create_menu('sell'))     if call.data == 'back':         bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="Добро пожаловать", reply_markup= cm.create_menu('main'))     if call.data == 'help':         bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="Ничем не могу помочь тебе", reply_markup= cm.create_menu('help'))  if __name__ == "__main__":     bot.polling(none_stop=True)

Исходник проекта и файл базы данных из примера, вы найдёте у меня на GitHub

Надеюсь данный материал был полезен для вас!

Спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/832342/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *