Если погуглить, то решений пагинации не так то и много.
Есть библиотека telegram_bot_pagination, но там пагинация выглядит следующим образом:
Внизу кнопки, каждая из которых отвечает за определенную страницу
а хотелось бы что-то такое:
Внизу кнопки перелистывания страниц
Есть похожее решение, только там скрываются кнопки Вправо и Влево на последней и Первой страницах соответственно.
Но смысл? Пусть на первой странице кнопка Влево перелистывает на последнюю страницу, а кнопка Вправо на последней странице — на первую. Да и зачем юзеру кнопка Скрыть?
Для начала создадим бота
Находим БотаФазера, пишем ему /newbot — команду для создания бота. Отвечаем на его вопросы: Название бота; Username бота. Забираем от него токен.
В коде импортируем telebot — botAPI, types из telebot‘а для создания кнопок и sqlite3 для работы с Базой данных
import telebot from telebot import types import sqlite3
Далее создаем экземпляр класса TeleBot для дальнейшей работы. Проще говоря, создаем бота:
bot = telebot.TeleBot("TOKEN", parse_mode="MARKDOWN")
parse_mode — это способ форматирования — MarkDown, MarkDownV2 или HTML
Добавим обработчик команды /start
Для проверки, сделаем так, чтобы бот приветствовал нас при запуске бота.
Для этого воспользуемся методом reply_to()
Для запуска воспользуемся методом polling() c параметром none_stop в значении True чтобы бот не выключался при ошибке.
# команда /start @bot.message_handler(commands=['start']) def start(message): bot.reply_to(message, "приветствую тебя!") bot.polling(none_stop=True)
Проверяем…
Работает! Продолжаем.
Теперь добавим под сообщение кнопки
Воспользуемся нашими types‘ами: InlineKeyboardMarkup(), types.InlineKeyboardButton() и методом add()
buttons = types.InlineKeyboardMarkup() button = types.InlineKeyboardButton("Button", callback_data="Button") buttons.add(button)
Через callback_data потом будем отлавливать клик
Теперь присоединим кнопки к сообщению с помощью параметра reply_markup.
bot.reply_to(message, "Приветствую тебя!", reply_markup=buttons)
Запускаем, проверяем..
Работает! Теперь надо отследить нажатие на кнопку.
Для этого надо создать обработчик
@bot.callback_query_handler(func=lambda c: True) def callback(c): pass
наш callback_data приходит в c.data .Проверим. Если в нем «button» то отправим сообщение через bot.send_message()
@bot.callback_query_handler(func=lambda c: True) def callback(c): if c.data == 'Button': bot.send_message(c.message.chat.id, "Вы нажали на кнопку!")
Опять перезапускаем, проверяем…
Рабооотает!
Теперь создадим кнопки управления
С начала наметим наши кнопки:
Пусть будет так:
Сверху кнопки перелистывания, а внизу кнопки действия с сообщением, если требуется (например, купить товар, если это магазин)
left_button = types.InlineKeyboardButton("←", callback_data="None") page_button = types.InlineKeyboardButton("1/4", callback_data="None") right_button = types.InlineKeyboardButton("→", callback_data="None") buy_button = types.InlineKeyboardButton("КУПИТЬ", callback_data="None") buttons.add(left_button, page_button, right_button) buttons.add(buy_button)
Окей, кнопки есть. Теперь займемся содержимым
Получение данных из Базы Данных
для начала создадим колонку page)
Создаем базу данных
Для управления базой данных я использую SQLiteStudio
В программе добавляем БД
ыбираем/создаем файл db.db в папке с ботом
Ниже пишем для удобства название которое будет отображаться в программе
жмем ОК
Слева появится название то которое вы указали во втором поле выше.
Жмете на > и внизу выбираете Tables чтобы просмотреть таблицы
и видим … ничего не видим. БД то пустая!
Жмем на Creat Table
После чего появится такая вкладка.
В поле 1 задаем имя таблицы — users
Далее создаем колонку (2)
В открывшемся окне в поле Column name (1) пишем название столбца — id
Далее задаем тип данных (2) — INTEGER — целое число (в телеграме все id это цифры)
Задаём Primary Key (4) — это значит что данный столбец будет содержать только уникальные значения
Задаем not NULL — (5) — не ничего — это значит что эта колонка обязательно должна быть заполнена.
Жмем ОК
Прекрасно! Мы создали колонку id! Далее создадим еще одну колонку.
Создаем колонку (фото 15 2).
-
Название (фото 16 1):
page -
Тип данных (фото 16 2):
INTEGER -
Значение по умолчанию:
1-
Устанавливаем галочку возле
Dafault(фото 16 6) -
Жмем
Configure(фото 16 1) -
В открывшемся окне пишем
1 -
Жмем
Apply
-
-
Жмем
ОК
Отлично! Теперь сохраним. Жмем на кнопку Commit structure changes (фото 15 3) или жмем сочетание клавиш Ctrl + C. Появится новое окно с кодом. Не пугайтесь, так надо, жмем ОК
Таблица users есть, теперь нужна таблица с информацией которую будем пагинировать
Жмем на Creat Table (фото 14).Задаем имя (фото 15 1). В моем случае это store — магазин. Создаем 4 колонки (фото 15 2).
-
Название (фото 16 1):
page -
Тип данных (фото 16 2):
INTEGER -
Задаем
Primary Key(16 4) -
и
not NULL(фото 16 5) -
Название (фото 16 1):
title -
Тип данных (фото 16 2):
TEXT
-
Название (фото 16 1):
description -
Тип данных (фото 16 2):
TEXT -
Название (фото 16 1):
photo_path -
Тип данных (фото 16 2):
TEXT
Не забываем сохранить (фото 15 3)
Должно получится 2 таблицы
Итак, у нас есть БД. Теперь для теста создадим пару записей в store.
Жмем на вкладку Data (1), Далее на кнопку Insert rows (2), ниже появится строчка. Кликаем по синим ячейкам и заполняем их.
page — 1, 2, 3…
Title — тестирую название 1.
description — тестирую описание 2
photo_path — путь или URL к картинке.
Не забываем сохранить! Кнопка Commit (3)
У меня получилось так:
Итак, БД есть, Данные есть, Теперь подключимся к БД в пайтоне!
Будем использовать библиотеку sqlite3
connect = sqlite3.connect("db.db") # не забудьте поставить свое название БД, cursor = connect.cursor() # если оно у вас не такое!
с помощью cursor‘а будем обращаться к БД.
Для начала выведем первую страницу по команде /start
page_query = cursor.execute("SELECT `title`, `description`, `photo_path` FROM `store` WHERE `page` = 1;") title, description, photo_path = page_query.fetchone()
в переменных title, description и photo_path хранится соответственная информация из БД на странице 1 (!).
Теперь составим шаблон сообщения
# Название: *{title}* # Описание: *{description}* msg = f"Название: *{title}*\nОписание: *{description}*"
Отправим фото с описанием с помощью bot.send_photo(), и присобачим кнопки
bot.send_photo(message.chat.id, photo=photo_path, caption=msg, reply_markup=buttons)
итак, весь наш код сейчас выглядит так:
import telebot from telebot import types import sqlite3 bot = telebot.TeleBot("TOKEN", parse_mode="MARKDOWN") # команда /start @bot.message_handler(commands=['start']) def start(message): buttons = types.InlineKeyboardMarkup() left_button = types.InlineKeyboardButton("←", callback_data="None") page_button = types.InlineKeyboardButton("1/4", callback_data="None") right_button = types.InlineKeyboardButton("→", callback_data="None") buy_button = types.InlineKeyboardButton("КУПИТЬ", callback_data="None") buttons.add(left_button, page_button, right_button) buttons.add(buy_button) connect = sqlite3.connect("db.db") cursor = connect.cursor() page_query = cursor.execute("SELECT `title`, `description`, `photo_path` FROM `store` WHERE `page` = '1';") title, description, photo_path = page_query.fetchone() msg = f"Название: *{title}*\nОписание: *{description}*" bot.send_photo(message.chat.id, photo=photo_path, caption=msg) @bot.callback_query_handler(func=lambda c: True) def callback(c): if c.data == 'None': bot.send_message(c.message.chat.id, "Вы нажали на кнопку!") bot.polling(none_stop=True)
Запускаем, проверяем …
все работет, с БД все ок. Теперь присобачим кнопки
к bot.send_photo() на 27 строке добавим аргумент reply_markup с значением buttons:
bot.send_photo(message.chat.id, photo=photo_path, caption=msg, reply_markup=buttons)
проверяем..
Кнопки есть!
Сейчас раскажу саму суть работы кнопок.
При открытии какой-нибуть страницы в БД идет номер этой страницы (колонка page).
А в колбэк кнопок мы просто будем засовывать страницу на которую надо перейти.
То есть Делаем функцию которая показывает страницу (у нас она уже есть — start). Добавляем к ней атрибут page — номер страницы которую надо вывести. В колбэк кнопки «→» записываем номер данной страницы + 1. А в колбэк кнопки «←» номер страницы — 1.
При нажатии кнопки обработчик будет забирать номер страницы которую нужно вывести и вызывает функцию показа страницы (start) с параметром из колбэка.
Так-же в start надо добавить атрибут previous_message — в нем будет передаваться предидущее сообщение чтобы его удалить.
Итак. Сейчас создадим переменные right и left для колбэка.
НО! Помним, что кнопка Влево на первой странице кнопка должна перелистывать на последнюю страницу, а кнопка Вправо на последней странице — на первую.
По этому надо узнать количество страниц. Узнаем их по количеству строк в таблице store:
pages_count_query = cursor.execute(f"SELECT COUNT(*) FROM `store`") pages_count = int(pages_count_query.fetchone()[0])
Теперь создадим это переменные
left = page-1 if page != 1 else pages_count right = page+1 if page != pages_count else 1
Теперь добавим значения в кнопки. Колбєк будет типа команды: to {страница}
left_button = types.InlineKeyboardButton("←", callback_data=f'to {left}') page_button = types.InlineKeyboardButton(f"{str(page)}/{str(pages_count)}", callback_data='_') right_button = types.InlineKeyboardButton("→", callback_data=f'to {right}') buy_button = types.InlineKeyboardButton("КУПИТЬ", callback_data='buy')
Теперь изменим нашу функцию, добавим аргументы page и previous_messageПо-умолчаню page = 1
def start(message, page=1, previous_message=None): # code pass
Еще надо понимать, что сообщения могут быть как и с фотографией, так и без. При чём фотография может быть как и на компьютере, так и в интернете (ссылка).
Надо под это подстроится
try: try: photo = open(photo_path, 'rb') except: photo = photo_path msg = f"\[*{title}*]\nОписание: " msg += f"*{description}*\n" if description != None else '_нет_\n' bot.send_photo(message.chat.id, photo=photo, caption=msg, reply_markup=buttons) except: msg = f"\[*{title}*]\nОписание: " msg += f"*{description}*\n" if description != None else '_нет_\n' bot.send_message(message.chat.id, msg, reply_markup=buttons)
и на последок удаление сообщения:
try: bot.delete_message(message.chat.id, previous_message.id) except: pass
АГГГА! Чуть не забыл! Надо же еще доставать информацию из БД определенной страницы. Исправим.
product_query = cursor.execute(f"SELECT `title`, `description`, `photo_path` FROM `store` WHERE `page` = ?;", (page,)) title, description, photo_path = product_query.fetchone()
Вот так нормально. Переходим к обработчику.
Итак. В обработчике все просто. Вспоминаем план — «При нажатии кнопки обработчик будет забирать номер страницы которую нужно вывести и вызывает функцию показа страницы (start) с параметром из колбэка. «
То есть, проверяем, если есть в колбэке «to» то забираем то что после to и вызываем функцию старт с нужными параметрами.
# Обработчик callback @bot.callback_query_handler(func=lambda c: True) def callback(c): if 'to' in c.data: page = c.data.split(' ')[1] start(c.message, page=page, previous_message=c.message)
Вот и пагинация готова.
Вот результат:
Вот полный код:
# Импорты import telebot import sqlite3 from telebot import types # Создаем экземпляр бота (создаем бота) bot = telebot.TeleBot("ТОКЕН", parse_mode="MARKDOWN") # команда /start @bot.message_handler(commands=['start']) def start(message, page=1, previous_message=None): connect = sqlite3.connect("habr_db.db") cursor = connect.cursor() pages_count_query = cursor.execute(f"SELECT COUNT(*) FROM `store`") pages_count = int(pages_count_query.fetchone()[0]) product_query = cursor.execute(f"SELECT `title`, `description`, `photo_path` FROM `store` WHERE `page` = ?;", (page,)) title, description, photo_path = product_query.fetchone() cursor.execute(f"UPDATE `users` SET `page` = ? WHERE `id` = ?;", (page,message.chat.id)) connect.commit() buttons = types.InlineKeyboardMarkup() left = page-1 if page != 1 else pages_count right = page+1 if page != pages_count else 1 left_button = types.InlineKeyboardButton("←", callback_data=f'to {left}') page_button = types.InlineKeyboardButton(f"{str(page)}/{str(pages_count)}", callback_data='_') right_button = types.InlineKeyboardButton("→", callback_data=f'to {right}') buy_button = types.InlineKeyboardButton("КУПИТЬ", callback_data='buy') buttons.add(left_button, page_button, right_button) buttons.add(buy_button) try: try: photo = open(photo_path, 'rb') except: photo = photo_path msg = f"Название: *{title}*\nОписание: " msg += f"*{description}*\n" if description != None else '_нет_\n' bot.send_photo(message.chat.id, photo=photo, caption=msg, reply_markup=buttons) except: msg = f"Название: *{title}*\nОписание: " msg += f"*{description}*\n" if description != None else '_нет_\n' bot.send_message(message.chat.id, msg, reply_markup=buttons) try: bot.delete_message(message.chat.id, previous_message.id) except: pass # Обработчик callback @bot.callback_query_handler(func=lambda c: True) def callback(c): if 'to' in c.data: page = int(c.data.split(' ')[1]) start(c.message, page=page, previous_message=c.message) # Запуск бота bot.polling(none_stop=True)
Так-же хотел бы поблагодарить seeklay1337 за небольшую помощь в написании статьи
Если нашли ошибку — ctrl + Enter
ссылка на оригинал статьи https://habr.com/ru/articles/721516/
Добавить комментарий