Дозированная выдача контента пользователю в боте Telegram
Создаю бота для телеграм. Концепция такая: пользователь вбивает ингредиенты блюда и мы по ключевым словам выдаем ссылку на рецепт, если он совпадает с запросом. Пока родил такой код и он работает:
@bot.message_handler(content_types=['text'])
def search_recept(message):
bot.send_message(message.chat.id, "Начинаю поиск")
user_query = message.text.lower().replace(',', '').split()
counter = 0
for key, value in dict.items():
for i in user_query:
fuzzy = process.extractOne(i, value)
if fuzzy[1] > 90:
counter += 1
else:
counter = 0
if counter == len(user_query):
bot.send_message(message.chat.id, key + '\n Полный рецепт здесь ☝️ ')
Но имеет огромный недостаток. Он выдает все совпадения с нашей базой (Она в виде словаря. Где key - это ссылка на рецепт, а value - список с ключевыми словами рецепта. По ним и идет сравнение с запросом пользователя) А этих совпадений может быть десятки и пользователя бомбит этими рецептами.
Задача - сделать так, чтобы выдавало небольшое количество рецептов сразу (2-3) и дальше уже по запросу пользователя. Я еще новичок и сейчас в тупике. Подскажите пожалуйста, хотя бы направление куда двигаться. У меня есть идея создавать походу цикла отдельный словарь с совпавшими запросами и потом с него отправлять только три первых ключа. И удалять после отправки. А дальше как? Как принять от пользователя условный запрос 'хочу еще рецепты' и продолжить отправку. Да и кажется, это сильно замороченный вариант. Буду благодарен за любую помощь.
Ответы (1 шт):
Реализовать можно разными способами, одним из самых удобных и максимально предназначенных для этой задачи будет оператор yield.
Алгоритм таков - сохранить результат поиска в генератор и "выталкивать" итерируемые объекты из генератора. Сколько выводить за один раз для пользователя - решать вам, главное предусмотреть исключение StopIteration. Более подробнее об операторе yield можете посмотреть здесь, автор прекрасно объясняет как работает этот оператор.
Так как я использую inline-клавиатуру, то приходится где-то сохранять промежуточный результат. Имейте ввиду, что после перезапуска бота - словарь очистится.
(Может и можно обойтись без словаря, используя метод bot.register_next_step_handler; не тестировал, поэтому сказать точно не смогу)
Теперь сам пример:
from telebot import types
import telebot
ram = {}
bot = telebot.TeleBot('token')
@bot.message_handler(content_types=['text'])
def search_recept(message):
k_yet = types.InlineKeyboardMarkup(row_width=1) # вывод кнопок в 1 колонку
yet = types.InlineKeyboardButton('Хочу еще!', callback_data='yet')
k_yet.add(yet)
bot.send_message(message.chat.id, "Начинаю поиск")
user_query = message.text.lower().replace(',', '').split()
# не могу понять из примера, как у вас работает поиск поэтому этот момент опускаю
# например получаем результаты поиска, сохраняем в список find
# на основе списка создаем генератор функцией output_data
# для каждого пользователя результат отправляем в словарь raw
find = ['url1', 'url2', 'url3']
ram.update({message.chat.id: output_data(find)})
print(ram) # {123456789: <generator object output_data at 0x0297C404>}
result1 = next(ram[message.chat.id])
result2 = next(ram[message.chat.id])
bot.send_message(message.chat.id, f'Вот список найденных результатов: \n{result1}\n{result2}', reply_markup=k_yet)
def output_data(my_list):
for i in my_list:
yield i
# обработка callback клавиатуры
@bot.callback_query_handler(func=lambda message: True)
def logic_inline(call):
keyboard_yet = types.InlineKeyboardMarkup()
in_yet = types.InlineKeyboardButton('ЕЩЕ!', callback_data='yet!')
keyboard_yet.add(in_yet)
if call.data == 'yet' or call.data == 'yet!':
try:
bot.send_message(call.message.chat.id, f'Вот тебе еще результат: \n{next(ram[call.message.chat.id])}',
reply_markup=keyboard_yet)
except StopIteration:
bot.send_message(call.message.chat.id, 'Больше нет результатов?')
bot.polling()
Результат:
