-
Автор темы
- #1
Недавно столкнулся с нуждой реализовать вывод каталога. Делать это сообщениями слишком костыльно. А вот вариант вывода инлайн клавиатуры с 10 позициями и дальнейшем их пролистывании звучит довольно привлекательно.
Получение данных происходит с MongoDB. Код самой клавиатуры:
Функция где используется данная клавиатура выглядит так:
Эту функцию просто вызываем в нужном хэндлере.
Затем нужно реализовать кнопки вперед/назад/отмена:
Добавляем новый хэндлер:
Чтобы получить данные при нажатии на товар нужно реализовать еще один хэндлер:
Все, рабочий и относительно визуально плавный каталог работает
Получение данных происходит с MongoDB. Код самой клавиатуры:
offers keyboard:
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
def offers_kb(posts, n): #Передаем массив всех товаров и желаемый индекс последнего выводимого товара(по факту "строить массив" для желаемого индекса можно не в самой функции, но как по мне логически правильнее будет так)
offers_kb = InlineKeyboardMarkup()
for i in range(n-10, len(posts)):#Так как я решил выводить только 10 позиций за раз, то от конечно индекса отнимаю 10
if i >= n or i > len(posts):#Проверка на то когда нужно прекращать добавлять кнопки(когда индекс больше передаваемого, чтобы не выводилось больше позиций чем нужно. И проверка на то, если у последней страницы не хватает "добить" 10 позиций, оно не крашилось изза выхода за пределы массива)
break
else:
cur = InlineKeyboardButton(str(posts[i]["count"]), callback_data="offer_id:"+str(posts[i]["_id"]))#Создаю кнопку в тексте которой будет отображаться количество позиции(так как до этого человек уже выбрал что именно он хочет купить), а в callback_data закидываю id продукта из бд для дальнейшего взаимодействия
offers_kb.add(cur)#Добавляю в клавиатуру
if n <= 10 and n >= len(posts):#Здесь идут проверки для добавления кнопок назад/вперед чтобы в конце списка не появлялась кнопка вперед
cancel = InlineKeyboardButton("Cancel", callback_data="cancel_offers")
offers_kb.row(cancel)
elif n == 10:
forward = InlineKeyboardButton("Вперед", callback_data = "forward_offers" )
cancel = InlineKeyboardButton("Cancel", callback_data="cancel_offers")
offers_kb.row(forward)
offers_kb.row(cancel)
elif n>=len(posts):
back= InlineKeyboardButton("Назад", callback_data="back_offers" )
cancel = InlineKeyboardButton("Cancel", callback_data="cancel_offers")
offers_kb.row(back)
offers_kb.row(cancel)
else:
forward = InlineKeyboardButton("Вперед", callback_data = "forward_offers" )
back= InlineKeyboardButton("Назад", callback_data="back_offers" )
cancel = InlineKeyboardButton("Cancel", callback_data="cancel_offers")
offers_kb.row(back, forward)
offers_kb.row(cancel)
return offers_kb#Возвращаем созданную клавиатуру
buy function:
from aiogram.dispatcher import FSMContext
from aiogram import types
from pymongo.database import Database
from aiogram.dispatcher.filters.state import State, StatesGroup
from keyboards.offers import offers_kb
class buy_states(StatesGroup):
cur_list = State()
async def buy(call:types.CallbackQuery, state:FSMContext, db:Database, n = 10):#При первом вызове не указываем n
data = await state.get_data()
offers = []
for offer in db["some_pr"].find({"game":data.get("game")}).sort("cost_per_one"):#сортировка по цене
offers.append(offer)#прикол mongo, что он возвращает не массив а какую то залупу
await buy_states.cur_list.set()
await state.update_data(cur_list = n)#Для отслеживания конечной позиции
await call.message.answer("Вот все наши предложения:", reply_markup=offers_kb(offers, n))
Затем нужно реализовать кнопки вперед/назад/отмена:
Добавляем новый хэндлер:
bot.py:
@dp.callback_query_handler(lambda c: c.data.endswith("_offers"), state=buy_states.cur_list)#чтобы не писать отдельные хэндлеры для каждой функции, у каждой кнопке в каллбэк дате было дописано _offers, что мы здесь и проверяем. А также проверяем что state=cur_list(засетили его при первом вызове функции)
async def offers_process(call:types.CallbackQuery, state:FSMContext):
data = await state.get_data()
match call.data.replace("_offers", ""):
case "forward":
_cur_list = data.get("cur_list") + 10#нажата кнопка вперед, значит к желаемому индексу последнего товара прибавляем 10
case "back":
_cur_list = data.get("cur_list") - 10#аналогично, только теперь отнимаем
case "cancel":#если отмена, то завершаем state и удаляем сообщение с каталогом
await state.finish()
await call.message.delete()
return
offers = []
for offer in db[data.get("game_type").replace("cat_", "")].find({"game":data.get("game")}).sort("cost_per_one"):
offers.append(offer) #опять получаем массив
await state.update_data(cur_list = _cur_list)#вносим новый индекс
await call.message.edit_reply_markup(offers_kb(offers, _cur_list))#и для плавности работы не переотправляем сообщение, а просто изменяем уже отправленное сообщение
bot.py:
from bson.objectid import ObjectId
client = MongoClient(MONGO_API)
db = client["test_db"] #подключение к бд(просто написать две эти строчки в основном файле бота перед самой его инициализацией)
bot = Bot(token=BOT_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
@dp.callback_query_handler(lambda c: c.data.startswith("offer_id:"), state=buy_states.cur_list)
async def offer_process(call:types.CallbackQuery, state:FSMContext):
data = await state.get_data()
offer = db[data.get("game_type").replace("cat_", "")].find_one({"_id":ObjectId(call.data.replace("offer_id:", ""))})
await call.message.answer(f"Наименование: {offer["name"]}")#Ну а тут просто отправляем расширеннцю информацию о предложении, прикрепляя кнопки оплаты, чата и тд