Вітаю всіх! У цій статті ми продовжимо досліджувати можливості бібліотеки Aiogram 3 та розглянемо тему інлайн кнопок та CallBack даних. Після ретельних роздумів я дійшов висновку, що логічним продовженням буде вивчення CallBack хендлерів та CallBack даних. У цій статті ми розглянемо: Приступимо до поглибленого вивчення цих аспектів і намагатимемося максимально використовувати можливості, які надає нам бібліотека Aiogram 3. CallBack в Aiogram 3 - це спосіб обробки взаємодій користувачів з роботом, коли вони натискають на інлайн кнопки. Простіше кажучи, CallBack дозволяє роботі реагувати на натискання інлайн кнопок, виконуючи певні дії у відповідь на це. Це дуже зручно для створення інтерактивних і динамічних ботів, які можуть змінювати свою поведінку в залежності від вибору користувача. CallBack кнопки можуть працювати, як у форматі текстових кнопок. Це коли текст на кнопці дорівнює тій інформації (CallBack даті), яку користувач передає боту. є тільки InlineKeyboard. У такому випадку можна, наприклад, в інлайн кнопку Home передати CallData home, але зазвичай на практиці зазвичай. CallBack дата кардинально відрізняється від напису на кнопці. Зараз я дам пару текстових прикладів з моєї реальної практики, щоб стало зрозуміліше, а потім приступимо до написання коду. Наприклад, у вас бот служби підтримки (близько 5-6 проектів різної складності робив у форматі «support bot»). гайди з об'єкта message). Далі він відправляє це повідомлення менеджерам служби підтримки з інлайн кнопкою «Відповісти». Менеджер натискає на цю кнопку, і бот чекає повідомлення у відповідь (докладніше розглянемо в темі FSM). не просто запускає сценарій відповіді на повідомлення, але й відразу вказує боту, що ця відповідь має полетіти конкретному користувачеві (хочете статтю про те, як написати просту службу підтримки?). Інший приклад (теж із служби підтримки): У ботах періодично бувають так звані FAQ (розділи з запитаннями, що часто задаються).Був досвід, коли я в адмін панелі прописував функціонал, що дозволяє захопити питання і відповідь на нього (FSM), далі все це записувалося в базу даних під певним айдішником. Після того, як користувач заходить у розділ FAQ, бот відправляє запит до бази даних і за допомогою генератора інлайн кнопок (InlineKeyboardBuilder за типом такого ж як для текстових кнопок) відбувається генерація клавіатури з питанням та відповіддю. Далі боту достатньо всього одного хендлера для того, щоб масово обробляти відразу всі відповіді на будь-які питання. Нижче приклад реалізації: Сильно не вдавайтеся зараз у подробиці коду, у майбутніх статтях я навчу вас робити кожну реалізацію. Тут просто сенс у демонстрації потужності CallBackData - буквально кілька рядків коду здатні закрити величезний блок FAQ (масштабування, власне, необмежене). Ну гаразд, я можу дуже довго говорити про CallBack, тому що технологія, на мою думку, шикарна. Якщо зараз поки не зрозуміло, що до чого – не переживайте. Дочитавши цю статтю ви точно розберетеся з темою CallBack. Код я писатиму в тому ж проекті, що й писав у минулих статтях (якщо хочете такий же шаблон як у мене – читайте першу статтю на тему aiogram – там я докладно розписав свій каркас бота стартового). Давайте у нашому пакеті keyboards створимо новий файл під Inline клавіатури і дамо йому назву inline_kbs.py : У нього відразу імпортуємо такі модулі: Цей приклад демонструє, як створювати інлайн клавіатуру з різними типами посилань, включаючи звичайні URL-адреси, посилання на акаунти в Telegram та веб-додатки. Давайте тестувати. Для тестів я пропоную створити новий message handler, який буде викликатись текстом «Давай інлайн!». До нього прикрутимо нашу інлайн клавіатуру та поклацаємо її. Думаю, що зараз ви вже розумієте, що тут ми використовували магічний фільтр F.text, який спрацьовуватиме на відправку тексту 'Давай інлайн!', а щоб було ще цікавіше давайте ми створимо текстову кнопку з текстом 'Давай інлайн!', а саму кнопку прив'яжемо до головної клавіатури (це вже ви вмієте робити, якщо ні, то читайте минулу статтю). Давайте тепер вивчати кожну кнопку-посилання: Після кліку на звичайну кнопку-посилання з'являється вікно, яке запитує, чи хочемо ми перейти за посиланням. Після кліка на посилання з моїм профілем Telegram відбувається перехід без вікна у профіль. Донедавна лише посилання формату «tg://resolve?domain=yakvenalexx» дозволяли переходити у профіль без вікна, але, при написанні цього тексту виявив що і при форматі посилання «https://t.me/yakvenalexx» вікно не з'являлося . Тепер до цікавого моменту ВЕБ-ДОДАТОК: Після кліку по інлайн кнопці з «квадратиком» телеграм надішле нам таке повідомлення: А потім відкриється той ВЕБ-ДОДАТОК, який я створив. З ПК версії, можливо, не так наочно як з телефону. Нині продемонструю. Зверніть увагу на інтерактивність. Веб-додаток стає, як би, частиною телеграм бота, тим самим відкриваючи необмежені можливості. Ось так, наприклад, я прописував спеціальну анкету в одному зі своїх роботів: Як я говорив вище, все це тема окремої статті. Може якось обговоримо. Отже, до проміжних висновків. Ми розібрали всі варіанти інлайн-клавіатур посилань, а це означає, що можемо переходити до більш цікавої частини – CallBack Data! Почнемо з простої клавіатури. Нехай у ній буде 2 інлайн-кнопки. Одна кнопка повинна переводити користувача на стартовий екран, а друга запускає певну дію, наприклад, виводить інформацію про випадкового користувача (перше, що спало на думку). Для цього нам потрібно підготуватись. Спочатку напишемо функцію, яка генеруватиме інформацію про випадкового користувача. Для цього скористаємось цікавою бібліотекою Faker (візьміть її на замітку, часто знадобиться). Пишемо функцію, яка генеруватиме випадкового користувача (якраз наш пакет із утилітами тут буде доречним). Тут усе досить просто. Імпортуємо модуль. При ініціалізації об'єкта fake зазначимо, що нас цікавлять російські дані. Далі створимо простий словник і захопимо до нього такі дані: ім'я, адреса, email, телефон, дата народження, компанія та робота. Далі ми напишемо спеціальний хендлер, який при отриманні CallData "get_person" повертатиме добре оформлене повідомлення з інформацією про користувача (якраз трохи поглибимо свої знання в темі форматування тексту в aiogram 3). Спочатку імпортуємо функцію для генерації випадкового користувача з пакета utils та CallbackQuery для зручності анотацій. Тепер напишемо сам хендлер. Я його пропишу повністю, а далі поясню. Тут у нас помінявся декоратор. Тепер це не @start_router.message, а @start_router.callback_query. Також змінився магічний фільтр. Тепер ми обробляємо не F.text, а F.data. Також ми вказуємо, що будемо працювати з об'єктом CallbackQuery, що дозволить нам отримувати підказки від IDE, в якому ми ведемо розробку бота (у мене це Pycharm). Зауважте, що я закоментував один рядок. Це зроблено навмисно, і далі ви зрозумієте навіщо. Після цього ми згенерували нашого користувача через функцію, яку писали раніше, а потім приступили до простого форматування тексту. Якщо ви користуєтеся моєю структурою робота, то у вас також при ініціалізації вказувалося: Тим самим ми навчили робота сприймати HTML теги в повідомленнях. Форматування у нас досить просте. З тегів ми використовували тільки те, що робить текст жирним. Далі ми дістаємо з об'єкта user дані та за допомогою \n робимо перенесення на новий рядок. За допомогою await call.message.answer(formatted_message) ми надсилаємо повідомлення користувачу (не плутати з await call.answer!). Тепер напишемо нашу інлайн-клавіатуру з call_data та приступимо до тестування. Ви можете звернути увагу, що клавіатура не дуже відрізняється від клавіатури з посиланнями.Єдина відмінність у тому, що замість url ми передаємо callback_data , на які буде реагувати наш бот (обробник під callback_data='get_person' ми вже написали). Давайте цю клавіатуру прив'яжемо замість клавіатури із посиланнями. Чудово. Запускаємо бота і дивимося, що в нас вийшло: Ми бачимо, що інформація про користувача згенерована, але кнопка не перестає блимати (знаю, що на скрині це погано видно і зрозуміло, але, повірте, вона блимає, а на смартфоні буде годинник біля кнопки). Справа в тому, що інлайн-клавіатури в Telegram влаштовані таким чином, що вони завжди чекають на відповідь від хендлера, що той виконаний. Чекають близько 30 секунд, після чого заспокоюються. Давайте ми дамо відповідь серверам Telegram, що все ОК і за планом: show_alert=False йде за замовчуванням, але я залишу це в коді, щоб вас не плутати. Перезапускаємо бота і дивимося: Ми бачимо, що з'явився білий напис на чорній плашці (висить 2-3 секунди) і, при цьому, у нас кнопка не блимає більше. Чудово. Якщо вам нічого повідомляти користувачеві, можете просто вказувати await call.answer() , тоді кнопка миттєво тухнутиме. Тепер змінимо прапор show_alert на True і подивимося, що в нас вийде: Тепер у нас з'являється вікно alert і для продовження потрібно буде натиснути на «Ок». У деяких випадках це зручно. Тепер, для закріплення матеріалу, пропишемо хендлер для обробки callback_data='back_home' . Виконайте самостійно, впевнений, що у вас все вийде. Зараз ми не просто розберемо генератор інлайн-клавіатур (це, насправді, не важко), але спробуємо написати свій перший більш-менш серйозний обробник. Сенс усієї витівки буде таким: Я знаю, що вже давав вище приклад такої функції із «бойового» бота. Тут ми пропишемо її спрощену версію. Почнемо з масиву запитань та відповідей. Напишемо таких питань 10 штук. Ось приклад: Цей масив я прописав у файлі create_bot.py Тепер напишемо функцію, яка прийматиме словник питань та повертатиме інлайн-клавіатуру: Як ви бачите функція генератор інлайн клавіатури не сильно відрізняється від свого аналога у генерації текстових клавіатур. Вона приймає пітонівський словник з питаннями, а потім пробігається по кожному, генеруючи потрібні нам кнопки. Зверніть увагу, що в кінці цієї клавіатури я додав callback_data='back_home' (сподіваюся, що ви написали обробник). Давайте тепер прив'яжемо цю клавіатуру, наприклад, до нового хендлера, який буде реагувати на команду /faq (для закріплення даних з минулих моїх статей можете закріпити цю команду в командному меню). У мене вийшов такий результат. Пишемо обробник. Ось простий обробник. Запитання я імпортував із файлу create_bot: Запускаємо і дивимося, що в нас вийшло: Бачимо, що питання благополучно підвантажились. Тепер нам залишається написати універсальний обробник, який відповідатиме на обране запитання. Давайте до кожної відповіді прикрутимо інлайн-клавіатуру з питаннями. Не хвилюйтесь, зараз з усім розберемося. Для початку виконаємо імпорти: asyncio нам тут потрібен для однієї мети – встановимо асинхронну паузу за 2 секунди. Сама пауза нам необхідна щоб ми могли імітувати набір роботом тексту. Для цього ми використовували конструкцію async with ChatActionSender(bot=bot, chat_id=call.from_user.id, action="typing"). Сильно загострювати тут увагу не буду, але загальний сенс у тому, що бот, протягом 2 секунд, імітує набір тексту. Давайте до розбору коду. F.data.startswith('qst_')) – нововведення почалися з цієї частини. Докладно розберемо всі подібні конструкції в окремій статті про фільтри, відразу зазначимо, що вказана конструкція виконала перевірку на те, чи починається CallBack data з «qst_» (магічні фільтри у справі). Далі, рядком await call.answer() ми дали зрозуміти серверу телеграм, що все у нас добре і все за планом (кнопка згасне відразу). А ось на цьому рядку qst_id = int(call.data.replace('qst_', '')) трохи загостримо увагу, тому що на цьому трюку можна вибудовувати неймовірно складні сценарії інтерактивної взаємодії користувача та телеграм бота. Даним трюком ми забираємо значення call_data і трансформуємо рядок в айдишник питання, тим самим даючи боту зрозуміти відповідь на яке питання ми хочемо отримати. Технічно call.data - це звичайнісінький рядок, а значить з ним можна робити все що зі звичайними рядками. Розумієте, до чого я? Тобто в одній call_data ви можете передати багато інформації, наприклад, айді користувача, сума оплати та ідентифікатор товару, який користувач купує у вашому боті (це реальний приклад одного з моїх ботів). Виглядає так: «order_112344_232_1245» Далі спеціальний хендлер спрацьовує на F.data.startswith(order_')), а далі з рядка забирає потрібні йому дані. Уявляєте, які це можливості відкриває? Ну і поки що ви під враженням (сподіваюся) ми продовжимо розбирати наш код. Далі ми дістаємо необхідне нам питання, через його гайді, а потім просто формуємо повідомлення, яке бот відправить користувачеві через f-рядок, забираючи необхідні нам дані. Ну, а далі ви вже знаєте. Зробили імітацію набору тексту на 2 секунди і відправили повідомлення з клавіатурою. Дивимося на прикладі: Як ви бачите бот почав імітувати набір тексту. А ось і відповідь: Друзі, сьогодні ми розібрали дуже важливу тему, яка відкриє вам двері до створення телеграм-ботів будь-якої складності. Якщо якісь моменти залишилися неясними, не соромтеся ставити запитання у коментарях. Чесно кажучи, коли я тільки починав, теж не одразу все зрозумів, але згодом відчув усі переваги роботи з CallBack Data. В іншому все як завжди. Якщо цей текст був корисним і завдяки ньому ви дізналися щось нове, не забудьте про це повідомити в коментарях, своїм лайком або підпискою. Написання такого роду туторіол вимагає багато часу і зусиль, а без вашої підтримки все це втратить сенс. Продовжуємо писати чат-бота для Telegram – додаємо кнопки та інтерактив. Ілюстрація: Катя Павловська для Skillbox Media У першій частині уроку чат-ботів для Telegram ми створили на Python ехо-бота за допомогою бібліотеки aiogram. Сам луна працює просто, а його функція очевидна з назви: у відповідь на повідомлення користувача він надсилає той же текст. Якщо ви ще не читали першу частину, почніть із неї. У другій частині уроку попрацюємо над меню: додамо для користувачів клавіатуру зі швидкими відповідями та інлайн-кнопки для переходу на сайт Skillbox. Почнемо зі створення меню, але спочатку розберемося із видами можливих клавіатур. Бібліотека aiogram дозволяє створити на Python клавіатури двох видів, що відрізняються один від одного розташуванням кнопок: Наш відлуння для Telegram зараз дозволяє тільки відправляти текстові повідомлення і отримувати їх назад. Давайте проапгрейді його і додамо кнопки з готовими повідомленнями, які не потрібно вводити самому. Це будуть reply-кнопки. Нам знадобиться клас ReplyKeyboardMarkup — для початку імпортуємо його та додаткові необхідні класи: ReplyKeybordRemove і ReplyKeyboardMarkup дозволяють створювати та видаляти клавіатуру, а клас KeyboardButton використовується для додавання кнопок. Тепер створимо кнопки із готовими фразами. Напишемо код і розберемося в ньому: Спочатку створимо у нашому першому декораторі список kb, який зберігатиме кнопки. Кнопка в aiogram створюється за допомогою types.KeyboardButton(text=" "), де у параметрі text ми передаємо відображувану назву кнопки. Після цього необхідно створити клавіатуру та розповісти їй про наші кнопки. Робиться це за допомогою методу types.ReplyKeyboardMarkup(keyboard=list), де замість list записується назва списку з кнопками - у нашому випадку це список kb. Тепер залишається показати клавіатуру у Telegram-чаті. Для цього додаємо у відповідь рядок reply_markup=keyboard, яка відображає клавіатуру після команди /start. Тепер при запуску бота ми бачимо, що в чаті з'явилися обидві кнопки зі списку kb: Якщо натиснути на будь-яку кнопку, текст кнопки відправиться в чат, а Telegram-бот надішле у відповідь цю ж фразу: Можна створювати багато шаблонів, а також зв'язувати кнопку з новими діями. Спробуємо це з прикладу інлайн-клавіатур. Інлайн-кнопки відрізняються від звичайних тим, що пов'язані не з областю клавіатури в месенджері, а з повідомленням в Telegram-чаті. Найпростіший приклад інлайн-кнопки це меню в каналі @BotFather, за допомогою якого ми створювали токен для доступу до API Telegram. Наприклад, ось так у ньому виглядає інлайн-меню із вже створеними ботами: Створимо на Python для нашого бота інлайн-кнопки з посиланнями на Skillbox Media та курси програмування. Для цього повернемося до розділу з імпортами коду і додамо ще один рядок, щоб можна було використовувати необхідні класи: InlineKeyboardMarkup стане в нагоді для ініціалізації інлайн-кнопок, а InlineKeyboardButton - Для їх створення. Тепер створимо самі кнопки: Розберемося в коді рядково. Спочатку ми ініціалізуємо клавіатуру за допомогою InlineKeyboardMarkup, передавши як аргумент row_width число 1. Воно визначає, скільки кнопок буде в одному ряду. Так як у нас написи на кнопках довгі, краще помістити їх один під одним. Після цього створюємо для кожної кнопки окрему змінну та ініціалізуємо клас Button. У параметрах вказуємо: За допомогою методу add додаємо дві кнопки до вже створеної клавіатури. Тепер потрібно написати message_handler, який викликатиме в Telegram ці інлайн-кнопки. Робимо це за аналогією зі звичайними кнопками - з тією лише різницею, що як команда виклику вкажемо посилання, а в параметрі reply_markup передамо назву нашої клавіатури urlkb. Повторно запустимо нашого бота і подивимося на результат: Все вийшло. Тепер інлайн-клавіатура з'являється під час відправлення команди /посилання у бот. У нашій луні для Telegram з'явилося два види меню, написаних на Python: reply-кнопки для швидких повідомлень та інлайн-кнопки для переходу на блог та сайт Skillbox. Для створення складних ботів - наприклад, ботів онлайн-магазинів - можна самостійно вивчити документацію до бібліотеки aiogram: спробувати нові класи, методи та об'єкти. Читайте також:Telegram Боти на Aiogram 3.x: Інлайн кнопки та CallBack Дата
Що таке CallBack в Aiogram 3
@qst_router.callback_query(F.data.startswith('qst_')) async def cmd_start(call: CallbackQuery, state: FSMContext): await state.clear() await call.answer() qst_id = int(call.data.replace ('qst_', '')) async with ChatActionSender(bot=bot, chat_id=call.from_user.id, action="typing"): info = await pg_db.select_data('questions', ]>) await call.message.answer(info.get('answer') , reply_markup=main_kb(call.from_user.id))
з aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo from aiogram.utils.keyboard import InlineKeyboardBuilderІмпортовані модулі у файлі inline_kbs.py
Інлайн клавіатура з посиланнями
def ease_link_kb(): inline_kb_list = [ [InlineKeyboardButton(text="Мій хабр", url='https://habr.com/uk/users/yakvenalex/')], [InlineKeyboardButton(text="Мій Telegram", url ='tg://resolve?domain=yakvenalexx')], [InlineKeyboardButton(text="Веб-додаток", web_app=WebAppInfo(url="https://tg-promo-bot.ru/questions"))] ] return InlineKeyboardMarkup(inline_keyboard=inline_kb_list)
@start_router.message(F.text == 'Давай інлайн!') async def get_inline_btn_link(message: Message): await message.answer('Ось тобі інлайн клавіатура з посиланнями!', reply_markup=ease_link_kb())from faker import Faker def get_random_person(): # Створюємо об'єкт Faker з російською локалізацією fake = Faker('ru_RU') # Генеруємо випадкові дані користувача user = < 'name': fake.name(), 'address': fake.address( ), 'email': fake.email(), 'phone_number': fake.phone_number(), 'birth_date': fake.date_of_birth(), 'company': fake.company(), 'job': fake.job() >return user
from utils.utils import get_random_person from aiogram.types import CallbackQuery@start_router.callback_query(F.data == 'get_person') async def send_random_person(call: CallbackQuery): # await call.answer('Генерую випадкового користувача') user = get_random_person() Ім'я: \n"f"🏠 Адреса: \n"f"📧 Email: \n"f"📞 Телефон: \n"f"🎂 Дата народження: \n"f"🏢 Компанія: \n"f"💼 Посада: \n" ) await call.message.answer(formatted_message)bot = Bot(token=config('TOKEN'), default=DefaultBotProperties(parse_mode=ParseMode.HTML))def get_inline_kb(): inline_kb_list = [ [InlineKeyboardButton(text="Генерувати користувача", callback_data='get_person')], [InlineKeyboardButton(text="На головну", callback_data='back_home')] ] return InlineKeyboard )await call.answer('Генерую випадкового користувача', show_alert=False)Генератор інлайн-клавіатур - InlineKeyboardBuilder
questions = < 1: , 2: , 3: , 4: , 5: , 6: , 7: , 8: , 9: , 10: , >з aiogram.utils.keyboard import InlineKeyboardBuilder def create_qst_inline_kb(questions: dict) -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() # Додаємо кнопки запитань для question_id, question_data in questions.items(): builder. text=question_data.get('qst'), callback_data=f'qst_' ) ) # Додаємо кнопку "На головну" builder.row( InlineKeyboardButton( text='На головну', callback_data='back_home' ) ) ) # Налаштовуємо розмір клавіатури builder.adjust(1) return builder.as_markup()async def set_commands(): commands = [BotCommand(command='start', description='Старт'), BotCommand(command='start_2', description='Старт 2'), BotCommand(command='faq', description= 'Часті питання')] await bot.set_my_commands(commands, BotCommandScopeDefault())
@start_router.message(Command('faq')) async def cmd_start_2(message: Message): await message.answer('Повідомлення з онлайн клавіатурою з питаннями', reply_markup=create_qst_inline_kb(questions))from create_bot import questions@start_router.callback_query(F.data.startswith('qst_')) async def cmd_start(call: CallbackQuery): await call.answer() qst_id = int(call.data.replace('qst_', '')) qst_data = questions[qst_id] msg_text = f'Відповідь на запитання \n\n' \ f' \n\n' \ f'Вибери інше питання:' async with ChatActionSender(bot=bot, chat_id=call.from_user.id, action="typing"): await asyncio.sleep(2 ) await call.message.answer(msg_text, reply_markup=create_qst_inline_kb(questions))
import asyncio from aiogram.utils.chat_action import ChatActionSender from create_bot import questions, botЧат-боти в Telegram на Python. Частина 2. Створюємо та налаштовуємо меню
Види клавіатур
Створення меню
from aiogram.types import ReplyKeyboardRemove, ReplyKeyboardMarkup, KeyboardButton
@dp.message_handler(commands=['start'])
async
def
send_welcome(message: types.Message): kb = [ [ types.KeyboardButton(text="Зможеш повторити це?"), types.KeyboardButton(text="А це?") ], ] keyboard = types.ReplyKeyboardMarkup(keyboard=kb) await message.reply("Привіт!\nЯ Ехобот від Skillbox!\nНадіслати мені будь-яке повідомлення, а я тобі обов'язково відповім.", reply_markup=keyboard)
Інлайн-кнопки на aiogram
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
urlkb = InlineKeyboardMarkup(row_width=1) urlButton = InlineKeyboardButton(text='Перейти до блогу Skillbox', url='https://skillbox.ru/media/code/') urlButton2 = InlineKeyboardButton(text='Перейти до курсів Skillbox', url='https://skillbox.ru/code/') urlkb.add(urlButton,urlButton2) @dp.message_handler(commands='посилання')
async
def
url_command(message: types.Message):
await message.answer('Корисні посилання:', reply_markup=urlkb)
Висновок