В Telegram‑ботах есть несколько способов сделать интерактивную карточку: пользователь нажимает кнопку,
бот получает callback_query и меняет содержимое сообщения. В результате карта выглядит
так, будто её перевернули. В этой статье мы разберём пошаговый процесс создания такого бота
на языке Python (библиотека aiogram), но принципы одинаковы и для других
языков/фреймворков.
Содержание
- Что понадобится
- Создаём бота в BotFather
- Подготовка изображений карт
- Inline‑клавиатура и callback‑данные
- Обработчик нажатий и «переворот»
- Хранение состояния (какая карта открыта)
- Развёртывание и тестирование
- Часто задаваемые вопросы
Что понадобится
- Учётная запись Telegram и доступ к BotFather (для создания токена).
- Сервер или локальная машина с установленным Python 3.9+.
- Библиотеки:
aiogram==3.*– асинхронный фреймворк для Telegram Bot API.python-dotenv– удобная работа с переменными окружения.
- Набор изображений карт Таро (закрытая сторона + открытая сторона).
Создаём бота в BotFather
- Откройте чат с @BotFather.
- Отправьте команду
/newbotи следуйте инструкциям. - Сохраните полученный токен – он понадобится в коде.
Подготовка изображений карт
Для каждой карты нам нужны два файла:
back.jpg– общая «закрытая» сторона (можно использовать одну картинку для всех).card_XX.jpg– изображение раскрытой карты (гдеXX– номер или название, например
card_01.jpgдля «Шут»).
Разместите их в папке assets/ вашего проекта.
Inline‑клавиатура и callback‑данные
В Telegram нет «тапа» по изображению, но можно прикрепить к сообщению inline‑кнопку.
При нажатии бот получает callback_query с данными, которые мы задаём сами.
Для «переворачивания» удобно хранить в callback‑данных идентификатор карты и её текущее состояние:
callback_data = f"card|{card_id}|{state}"
Обработчик нажатий и «переворот»
Ниже – минимальный рабочий пример на aiogram (версия 3).
import os
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, InputFile
from aiogram.utils.keyboard import InlineKeyboardBuilder
from dotenv import load_dotenv
load_dotenv
TOKEN = os.getenv("BOT_TOKEN")
bot = Bot(token=TOKEN)
dp = Dispatcher
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
# выбираем случайную карту (пример: 1)
card_id = 1
# создаём кнопку с данными «закрытая карта»
kb = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(
text="🔹 Открыть карту",
callback_data=f"card|{card_id}|0"
)]
])
# отправляем изображение закрытой стороны
await message.answer_photo(
photo=InputFile("assets/back.jpg"),
caption="Тяните карту — она раскроется!",
reply_markup=kb
)
@dp.callback_query
async def card_flip(callback: types.CallbackQuery):
# парсим данные: card||
try:
_, card_id, state = callback.data.split("|")
state = int(state)
except Exception:
await callback.answer("❗ Неправильные данные.")
return
# переключаем состояние
new_state = 1 if state == 0 else 0
# выбираем нужное изображение
if new_state == 1:
photo_path = f"assets/card_{card_id}.jpg"
button_text = "🔹 Скрыть карту"
else:
photo_path = "assets/back.jpg"
button_text = "🔹 Открыть карту"
# создаём новую кнопку с обновлённым state
new_kb = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(
text=button_text,
callback_data=f"card|{card_id}|{new_state}"
)]
])
# заменяем фото сообщения (edit_message_media)
await bot.edit_message_media(
chat_id=callback.message.chat.id,
message_id=callback.message.message_id,
media=types.InputMediaPhoto(media=InputFile(photo_path)),
reply_markup=new_kb
)
# отвечаем, чтобы убрать «часики» у кнопки
await callback.answer
if __name__ == "__main__":
import asyncio
asyncio.run(dp.start_polling(bot))
Что происходит?
- При
/startбот отправляет картинкуback.jpgс кнопкой «Открыть карту». - Нажатие кнопки генерирует
callback_queryс даннымиcard|1|0. - Обработчик меняет
stateна противоположный, подбирает нужный файл
(card_1.jpgилиback.jpg) и
с помощьюedit_message_mediaзаменяет изображение в том же сообщении; - Кнопка также меняет текст и callback‑данные, поэтому пользователь может «переворачивать»
карту сколько угодно раз.
Хранение состояния (массив карт)
Если вам нужно показывать сразу несколько карт (например, расклад из трёх‑пяти карт),
рекомендовано хранить их состояние в контексте пользователя или в базе данных;
Ниже пример с использованием aiogram.fsm.storage.memory.MemoryStorage:
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
storage = MemoryStorage
dp = Dispatcher(storage=storage)
class TarotState(StatesGroup):
cards = State # будет хранить dict {card_id: state}
@dp.message(Command("spread"))
async def cmd_spread(message: types.Message, state: FSMContext):
# Предположим, расклад из 3 карт с id 1,2,3
await state.update_data(cards={1: 0, 2: 0, 3: 0})
kb = InlineKeyboardBuilder
for cid in (1, 2, 3):
kb.button(
text=f"Карта {cid}",
callback_data=f"card|{cid}|0"
)
kb.adjust(3) # разместить в одну строку
await message.answer_photo(
photo=InputFile("assets/back.jpg"),
caption="Тарологический расклад. Тапайте, чтобы открыть.",
reply_markup=kb.as_markup
)
В обработчике card_flip можно дополнительно обновлять словарь
state["cards"], чтобы в любой момент знать, какие карты открыты.
Развёртывание и тестирование
- Локальное тестирование – запустите скрипт, откройте чат с ботом и
проверьте, что карта меняет изображение без задержек. - Хостинг – для постоянной работы удобно использовать
Heroku, Render или любой VPS.
Главное – установить переменную окруженияBOT_TOKENи запустить процесс
python bot.py. - Webhooks vs Polling – в продакшене рекомендуеться
переключиться на webhook (меньше нагрузки, быстрее реакция). Вaiogramэто выглядит так:await dp.start_webhook( webhook_path="/webhook", skip_updates=True, secret_token="your_secret" )Не забудьте настроить HTTPS‑сервер (например,
nginx+certbot).
Часто задаваемые вопросы
Почему карта «не меняет» изображение, а появляется новое сообщение?
Для замены изображения необходимо использовать метод edit_message_media.
Если вместо него вызвать answer_photo, Telegram просто отправит новое сообщение.
Можно ли использовать анимацию (GIF) вместо статичной картинки?
Да. Вместо InputFile("assets/card_1.jpg") укажите путь к .gif.
Telegram покажет анимированную «перевёрнутую» карту.
Как добавить эффект «переворота» (flip) без смены сообщения?
Telegram API не поддерживает CSS‑анимацию, но можно имитировать её с помощью
Web Apps:
запускаете мини‑веб‑страницу, где на клиенте реализуется CSS‑flip, а в боте просто
отдаёте кнопку web_app. Это более сложный путь, но выглядит красиво.
Как обработать несколько одновременно открытых карт?
Храните словарь {card_id: state} в FSMContext (см. пункт 6). При
каждом callback_query обновляйте только нужный элемент, а остальные оставляйте без изменений.
Что делать, если пользователь нажал кнопку, но бот не отвечает (таймаут)?
Telegram ждёт callback_query.answer не более 5 секунд. Если в обработчике
выполняется длительная работа (например, запрос к базе), вынесите её в отдельную задачу
или используйте await callback.answer(cache_time=1) сразу, а потом уже
обновляйте сообщение.
Создать «переворачиваемую» карту Таро в Telegram довольно просто:
- Подготовьте две картинки (закрытая и открытая сторона).
- Отправляйте их с inline‑кнопкой и храните в
callback_dataинформацию о состоянии. - При получении
callback_queryменяйте изображение черезedit_message_media
и обновляйте кнопку. - При необходимости храните состояние в FSM или базе данных, чтобы поддерживать
расклады из нескольких карт.
Эти шаги позволяют реализовать интерактивный таро‑расклад, который будет радовать
пользователей плавным «переворотом» карт прямо в чате.
Удачной разработки! 🚀