[Python] How to implement multi-select inline buttons in Telegram bot with checkmarks using...

Discussão em 'Python' iniciado por Stack, Agosto 26, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to create a Telegram bot using aiogram v3 where users can select multiple products from a list. I want the following behavior:


    1. Users see a list of products as inline buttons.


    2. When a user clicks on a product:

      • a checkmark ✅ appears next to it, indicating it is selected,

      • if clicked again, the checkmark disappears (toggle behavior).


    3. When the user selects at least one product, a “Save Selection” button appears. 4. Users can continue selecting/deselecting products and then press “Save Selection” to confirm.

    I tried implementing this myself, but I only managed to make single selection work. When I try to add the checkmarks or multiple selections, it either: • doesn’t update the checkmarks correctly, or • the “Save Selection” button does not appear, or • both.

    Here is my simplified code (aiogram v3):

    from aiogram import Bot, Dispatcher, types, Router, F
    from aiogram.filters import Command
    from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup
    import asyncio

    BOT_TOKEN = 'my-private-token'

    bot = Bot(token=BOT_TOKEN)
    dp = Dispatcher()
    router = Router()

    user_choice = set()

    @router.message(Command('shop'))
    async def menu_cmd(m: Message):
    choice = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text='Фрукти', callback_data='fruits'), InlineKeyboardButton(text='Шоколад', callback_data='chocolate')],
    [InlineKeyboardButton(text='Скасувати ❌', callback_data='cancel')],
    ])
    await m.answer('Привіт в бота. Вибери: ', reply_markup=choice)


    @router.callback_query(F.data == 'cancel')
    async def cancel_callback(query: types.CallbackQuery):
    await query.message.delete()
    await query.answer('')

    async def show_fruits():
    product_ft = ['Яблуко', 'Банан']
    buttons = []

    for product in product_ft:
    button_text = f'{product}✅' if product in user_choice else product
    button = types.InlineKeyboardButton(text=button_text, callback_data=product)
    buttons.append([button])

    if user_choice:
    buttons.append([types.InlineKeyboardButton(text='✔️Зберегти вибір', callback_data='save_choice')])
    else:
    None

    buttons.append([types.InlineKeyboardButton(text='↩️Назад', callback_data='back')])

    choice_fruits = types.InlineKeyboardMarkup(inline_keyboard=buttons)
    return choice_fruits

    @router.callback_query(F.data == 'fruits')
    async def select_fruits(query: types.CallbackQuery):
    fruits_keyboard = await show_fruits()
    await query.message.edit_text('Вибери фрукт: ', reply_markup=fruits_keyboard)
    await query.answer()

    @router.callback_query(F.data.in_(['Яблуко', 'Банан']))
    async def fruits_select(query: types.CallbackQuery):
    product_fruits = query.data

    if product_fruits in user_choice:
    user_choice.remove(product_fruits)
    else:
    user_choice.add(product_fruits)

    fruits_keyboard = await show_fruits()
    await query.message.edit_text('Вибери фрукт: ', reply_markup=fruits_keyboard)
    await query.answer()

    @router.callback_query(F.data == 'save_choice')
    async def save_choice_callback(query: types.CallbackQuery):
    user_choice = query.data
    await query.message.delete()
    await query.message.answer('Вибір збережено. Переглянути його ви можете завдяки команді - /my_choice')
    print(f'Вибір користувача {user_choice} збережено.')

    @router.callback_query(F.data == 'back')
    async def back_callback(query: types.CallbackQuery):
    await query.message.delete()
    choice = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text='Фрукти', callback_data='fruits'), InlineKeyboardButton(text='Шоколад', callback_data='chocolate')],
    [InlineKeyboardButton(text='Скасувати ❌', callback_data='cancel')],
    ])
    await query.message.answer('Привіт в бота. Вибери: ', reply_markup=choice)

    async def select_choco():
    product_choco = ['Мілка', 'Снікерс']
    buttons = []

    for products_choco in product_choco:
    button_text = f'{products_choco}✅' if products_choco in user_choice else products_choco
    button = types.InlineKeyboardButton(text=button_text, callback_data=product_choco)
    buttons.append([button])

    if user_choice:
    buttons.append([types.InlineKeyboardButton(text='✔️Зберегти вибір', callback_data='save_choice')])
    else:
    None

    buttons.append([types.InlineKeyboardButton(text='↩️Назад', callback_data='back')])

    choice_choco = types.InlineKeyboardMarkup(inline_keyboard=buttons)
    return choice_choco

    @router.callback_query(F.data == 'choco')
    async def choco_review(query: types.CallbackQuery):
    choco_review = await select_choco()
    await query.message.edit_text('Вибери шоколадку: ', reply_markup=choco_review)

    @router.callback_query(F.data.in_(['Мілка', 'Снікерс']))
    async def choco_select(query: types.CallbackQuery):
    product_choco = query.data

    if product_choco in user_choice:
    user_choice.remove(product_choco)
    else:
    user_choice.add(product_choco)

    choco_keyboard = await select_choco()
    await query.message.edit_text('Виберіть шоколадку: ', reply_markup=choco_keyboard)
    await query.answer()

    def go_to_start():
    kb_start = InlineKeyboardMarkup(
    inline_keyboard=
    [
    [InlineKeyboardButton(text='На головне меню', callback_data='go_start')]
    ])
    return kb_start

    @router.callback_query(F.data == 'go_start')
    async def go_start_callback(query: types.CallbackQuery):
    choice = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text='Фрукти', callback_data='fruits'), InlineKeyboardButton(text='Шоколад', callback_data='chocolate')],
    [InlineKeyboardButton(text='Скасувати ❌', callback_data='cancel')],
    ])
    await query.message.edit_text('Привіт в бота. Вибери: ', reply_markup=choice)

    @router.message(Command('my_choice'))
    async def mychoice_cmd(m: types.Message):
    choices = list(user_choice)
    show_kb_start = go_to_start()
    if user_choice:
    await m.reply(f'Ось список товарів у вашому кошику: {', '.join(choices)}', reply_markup=show_kb_start)
    else:
    await m.reply('❌Ваш кошик ще пустий. Добавити товари можете завдяки команді /shop або просто натисніть на кнопку "Перейти на головне меню"')

    async def main():
    dp.include_router(router)
    print("Bot is running...")
    await dp.start_polling(bot)

    if __name__ == '__main__':

    asyncio.run(main())


    I would really appreciate it if someone could show me how to implement multi-secret inline buttons with toggle checkmarks in aiogram.

    Continue reading...

Compartilhe esta Página