Помогите с inline кнопками telegram bot python

Рейтинг: 0Ответов: 2Опубликовано: 19.02.2023

В общем простой поисковый бот по БД sqlite с выдачей в бот в виде списка записей. В связи с ограничениеми телеграм на длину текста, принято решение выдавать записи по 20 шт и кнопками пагинации перелистывать. Проблема в том что при повторном запросе пропадают кнопки пререлистывания. видимо бот не сбрасывает из памяти номер page. есть вариант сбрасывать состояние пользователя через await state.reset_state() но это не удобно перезапускать каждый раз бот при повторном поиске. Как можно решить проблему?

import sqlite3
from aiogram.dispatcher import Dispatcher
from aiogram.types import CallbackQuery
from aiogram.dispatcher.filters import Text
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery as CQ
from aiogram.utils.exceptions import MessageTextIsEmpty
from aiogram.dispatcher import FSMContext
from aiogram import executor
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage

# Создаем экземпляр бота
bot = Bot(token='', parse_mode=types.ParseMode.HTML)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)



# Обработчик нажатий на кнопки клавиатуры
@dp.callback_query_handler(Text('prev_page'), state='*')
@dp.callback_query_handler(Text('next_page'), state='*')
async def process_pagination_buttons(callback_query: CQ, state: FSMContext):
    # Получаем текущую страницу из состояния
    data = await state.get_data()
    page = data.get('page', 0)

    # Обновляем номер страницы в зависимости от нажатой кнопки
    if callback_query.data == 'prev_page':
        page -= 1
    elif callback_query.data == 'next_page':
        page += 1

    # Сохраняем номер страницы в состоянии
    await state.update_data(page=page)

    # Получаем список книг из базы данных
    conn = sqlite3.connect('ru.db')
    cursor = conn.cursor()

    search_text = data.get('search_text', '')
    offset = page * 20
    
    limit = 20

    cursor.execute(f"SELECT fullname FROM abooks WHERE fullname LIKE '%{search_text}%' LIMIT {limit} OFFSET {offset}")
    
    books = cursor.fetchall()
    results = cursor.fetchmany()
    num_rows = str(len(books))
    keyboard = InlineKeyboardMarkup(row_width=2)
    if page > 0:
        keyboard.row(InlineKeyboardButton('<< Назад', callback_data='prev_page'))
    if int(num_rows) == 20:
        keyboard.row(InlineKeyboardButton('Далее >>', callback_data='next_page'))

    print(num_rows)
    # Формируем сообщение
    message_text = 'Результаты поиска:\n\n'
    for book in books:
        message_text += f"{book}\n"

    # Обновляем сообщение с результатами поиска и клавиатурой
    try:
        await bot.edit_message_text(chat_id=callback_query.message.chat.id, message_id=callback_query.message.message_id, text=message_text, reply_markup=keyboard)
    except MessageTextIsEmpty:
        pass

    # Закрываем соединение с базой данных
    cursor.close()
    conn.close()

# Обработчик текстовых сообщений
# Обработчик текстовых сообщений
@dp.message_handler(content_types=types.ContentTypes.TEXT)
async def process_text_message(message: types.Message, state: FSMContext):
    # Сохраняем текст запроса в состоянии
    await state.update_data(search_text=message.text)

    # Получаем список книг из базы данных
    conn = sqlite3.connect('ru.db')
    cursor = conn.cursor()

    search_text = message.text
    offset = 0
    limit = 20

    cursor.execute(f"SELECT fullname FROM abooks WHERE fullname LIKE '%{search_text}%' LIMIT {limit} OFFSET {offset}")
    results = cursor.fetchmany()
    
    books = cursor.fetchall()
    num_rows = str(len(search_text))
    
    
    # Формируем сообщение
    message_text = 'Результаты поиска:\n\n'

    for book in books:
        message_text += f"{book}\n"

    # Создаем клавиатуру для пагинации
   
    data = await state.get_data()
    page = data.get('page', 0)
    
        
    keyboard = InlineKeyboardMarkup(row_width=2)
    if page == 0:
        
        
   
        keyboard.row(InlineKeyboardButton('Далее >>', callback_data='next_page'))
    print(page)
         
    
    # Удаляем предыдущее сообщение, если оно было отправлено
    if 'last_message_id' in data:
        try:
            await bot.delete_message(chat_id=message.chat.id, message_id=data['last_message_id'])
        except Exception as e:
            print(f"Failed to delete message: {e}")

    # Создаем новое сообщение и клавиатуру
    try:
        sent_message = await bot.send_message(chat_id=message.chat.id, text=message_text, reply_markup=keyboard)
        await state.update_data(last_message_id=sent_message.message_id)
    except MessageTextIsEmpty:
        pass

    # Закрываем соединение с базой данных
    cursor.close()
    conn.close()
   
if __name__ == '__main__':
    
    executor.start_polling(dp, skip_updates=True)

Ответы

▲ 0

Для сохранения в памяти можно юзать этот метод - await state.reset_state()и передавать в этот параметр уникальный идентификатор пользователя, связанный с текущим поиском Пример выглядит это так

# Обработчик команды поиска
@dp.message_handler(commands=['search'])
async def cmd_search(message: types.Message):
    # Получаем запрос пользователя
    query = message.text.split(' ', 1)[1]

    # Ищем записи в БД
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute(f"SELECT * FROM records WHERE title LIKE '%{query}%'")
    records = cursor.fetchall()
    conn.close()

    # Разбиваем записи на страницы по 20 штук
    pages = [records[i:i+20] for i in range(0, len(records), 20)]

    # Сохраняем данные в состоянии пользователя
    await state.update_data(records=records, pages=pages)

    # Отправляем первую страницу записей
    await send_records_page(message.chat.id, 0)

# Функция отправки страницы записей
async def send_records_page(chat_id, page):
    # Получаем записи и количество страниц из состояния пользователя
    data = await state.get_data(user_id=chat_id)
    records = data.get('records', [])
    pages = data.get('pages', [])

    # Отправляем записи текущей страницы и клавиатуру с кнопками пагинации
    keyboard = InlineKeyboardMarkup()
    if page > 0:
        keyboard.row(InlineKeyboardButton('<<', callback_data='prev_page'))
    if page < len(pages) - 1:
        keyboard.row(InlineKeyboardButton('>>', callback_data='next_page'))
    records_str = '\n\n'.join([f"{i}. {record[1]}" for i, record in enumerate(pages[page], start=1)])
    await bot.send_message(chat_id, records_str, reply_markup=keyboard)

# Обработчик нажатий на кнопки клавиатуры
@dp.callback_query_handler(Text('prev_page'), state='*')
@dp.callback_query_handler(Text('next_page'), state='*')
async def process_pagination_buttons(callback_query: CQ, state: FSMContext):
    # Получаем текущую страницу из состояния пользователя
    user_id = callback_query.message.chat.id
    data = await state.get_data(user_id=user_id)
    page = data.get('page', 0)

    # Обновляем номер страницы в зависимости от нажатой кнопки
    if callback_query.data == 'prev_page':
        page -= 1
    elif callback_query.data == 'next_page':
        page += 1

    # Сохраняем номер страницы в состоянии пользователя
    await state.update_data(user_id=user_id, page=page)

    # Отправляем новую страницу записей
    await send_records_page(callback_query.message.chat.id, page)
▲ 0

Переделал код на основании примера предложенного Aleksandr Fetisov , код работает ошибок не выдает в консоль, и даже выгружает записи, но проблема в том что при нажатии кнопки далее ничего не происходит. в чем может быть проблема.

import sqlite3
import aiogram
from aiogram import Bot, Dispatcher, types
from aiogram.dispatcher import FSMContext
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from aiogram.dispatcher.filters import Text
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage

from aiogram import executor

bot = Bot(token="")

storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)

class MyStates(StatesGroup):
    waiting_for_search_query = State()
    waiting_for_record_number = State()

@dp.message_handler(commands=['start'])
async def cmd_start(message: types.Message):
    await message.reply("Привет! Я бот для записей. Чтобы начать, отправьте /search, чтобы искать записи.")

# Обработчик команды поиска
@dp.message_handler(commands=['search'])
async def cmd_search(message: types.Message, state: FSMContext):
    # Получаем запрос пользователя
    query = message.text.split(' ', 1)[1]

    # Ищем записи в БД
    conn = sqlite3.connect('ru.db')
    cursor = conn.cursor()
    cursor.execute(f"SELECT fullname FROM abooks WHERE fullname LIKE '%{query}%'")
    records = cursor.fetchall()
    conn.close()

    # Разбиваем записи на страницы по 20 штук
    pages = [records[i:i+20] for i in range(0, len(records), 20)]

    # Сохраняем данные в состоянии пользователя
    await state.update_data(records=records, pages=pages)

    # Отправляем первую страницу записей
    await send_records_page(message.chat.id, 0, state)


# Функция отправки страницы записей
async def send_records_page(chat_id, page, state):
    # Получаем записи и количество страниц из состояния пользователя
    data = await state.get_data()
    records = data.get('records', [])
    pages = data.get('pages', [])

    # Если страницы записей нет, выводим сообщение об этом
    if not pages:
        await bot.send_message(chat_id, "Нет записей")
        return

    # Отправляем записи текущей страницы и клавиатуру с кнопками пагинации
    keyboard = InlineKeyboardMarkup()
    if page > 0:
        keyboard.row(InlineKeyboardButton('<<', callback_data='prev_page'))
    if page < len(pages) - 1:
        keyboard.row(InlineKeyboardButton('>>', callback_data='next_page'))
    records_str = '\n\n'.join([f"{i}. {record}" for i, record in enumerate(pages[page], start=1)])
    await bot.send_message(chat_id, records_str, reply_markup=keyboard)

# Обработчик нажатий на кнопки клавиатуры
@dp.callback_query_handler(Text('prev_page'), state=MyStates.waiting_for_record_number)
@dp.callback_query_handler(Text('next_page'), state=MyStates.waiting_for_record_number)
async def process_pagination_buttons(callback_query: CallbackQuery, state: FSMContext):
    # Получаем текущую страницу из состояния пользователя
    data = await state.get_data()
    page = data.get('page', 0)

    # Обновляем номер страницы в зависимости от нажатой кнопки
    if callback_query.data == 'prev_page':
        page -= 1
    elif callback_query.data == 'next_page':
        page += 1

    # Сохраняем номер страницы в состоянии пользователя
    await state.update_data(data={"page": page})

    # Отправляем новую страницу записей
    await send_records_page(callback_query.message.chat.id, page, state)
   
if __name__ == '__main__':
    
    executor.start_polling(dp, skip_updates=True)