WebApp реализован в HTML и JavaScript. При выборе картинки js код должен отправлять выбранный вариант при помощи метода Telegram.WebApp.sendData(data) в JSON-формате и WebApp должен закрывать.
webapp.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Выбор варианта</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
<h3>Выберите один из предложенных вариантов:</h3>
<div id="variants">
<script type="text/javascript">
Telegram.WebApp.ready();
const path = `${window.location.origin}/userData/`;
const parsedUrl = new URL(document.URL);
const userId = Telegram.WebApp.initDataUnsafe.user.id;
const previewImages = parsedUrl.searchParams.get("images");
const imagesArray = previewImages ? JSON.parse(previewImages) : [];
imagesArray.forEach((image, index) => {
const imgElement = document.createElement("img");
imgElement.src = path + userId + "/" + image;
imgElement.alt = `Вариант ${index + 1}`;
imgElement.style.width = "100px";
imgElement.style.margin = "5px";
imgElement.onclick = () => selectOption(index);
document.getElementById("variants").appendChild(imgElement);
});
function selectOption(index) {
const data = JSON.stringify({ selected_variant: index });
Telegram.WebApp.sendData(data);
Telegram.WebApp.close();
}
</script>
</div>
</body>
</html>
Бот в Telegram должен принимать данные WebApp с помощью обработчика web_app_data, определённого в коде боат следующим образом:
run.py:
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton, WebAppInfo
from telegram.ext import CommandHandler, MessageHandler, filters, CallbackContext, CallbackQueryHandler, Application
import os
import logging
import time
import json
from urllib.parse import urlencode
from config import *
from utils.image_processing import *
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
def message_format(lines):
return "\n".join(lines)
async def start(update: Update, context: CallbackContext) -> None:
welcome_message_lines = [
"Добро пожаловать! ",
"",
"Чтобы начать обработку, нажмите кнопку ниже и отправьте фото.",
"Рекомендации к фото:",
"1. Фото должно быть четким.",
"2. Предпочтительно использовать горизонтальную ориентацию.",
"3. Избегайте слишком маленьких объектов на фото."
]
keyboard = [[InlineKeyboardButton("/preview", callback_data='preview')]]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(message_format(welcome_message_lines), reply_markup=reply_markup)
async def preview(update: Update, context: CallbackContext) -> None:
await update.callback_query.answer()
await update.callback_query.message.reply_text("Пожалуйста, отправьте фото для обработки.")
async def handle_photo(update: Update, context: CallbackContext) -> None:
user_id = update.effective_user.id
timestamp = int(time.time())
user_dir = f"/var/www/www-root/data/www/***/userData/{user_id}"
if not os.path.exists(user_dir):
os.makedirs(user_dir)
photo_file = await update.message.photo[-1].get_file()
photo_path = f"photo_{timestamp}.jpg"
await photo_file.download_to_drive(user_dir + "/" + photo_path)
notify_message_lines = [
"Фото получено! ️",
"Начинаю обработку...",
"Пожалуйста, подождите несколько секунд."
]
await update.message.reply_text(message_format(notify_message_lines))
preview_images = process_image(user_dir, photo_path, PALETTE, PREVIEW_SIZE, MAX_PIXELS_PER_COLOR)
context.user_data['preview_images'] = preview_images
images_param = json.dumps(preview_images)
query_params = urlencode({'images': images_param})
web_app_url = f"https://***?{query_params}"
keyboard = InlineKeyboardMarkup(
[[InlineKeyboardButton(text="Выбрать вариант", web_app=WebAppInfo(url=web_app_url))]]
)
await update.message.reply_text("Обработка завершена! Нажмите кнопку ниже, чтобы выбрать вариант.", reply_markup=keyboard)
async def web_app_data(update: Update, context: CallbackContext) -> None:
data = json.loads(update.effective_message.web_app_data.data)
variant_index = int(data.get("selected_variant"))
print(variant_index)
def main():
application = Application.builder().token(BOT_TOKEN).build()
application.add_handler(CommandHandler("start", start))
application.add_handler(MessageHandler(filters.PHOTO, handle_photo))
application.add_handler(CallbackQueryHandler(preview, pattern='^preview$'))
application.add_handler(MessageHandler(filters.StatusUpdate.WEB_APP_DATA, web_app_data))
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == '__main__':
main()
В main() добавлен MessageHandler с фильтром filters.StatusUpdate.WEB_APP_DATA для обработки данных WebApp, но не понятно по какой причине данный обработчик не срабатывает. Проведены отладочные проверки с использованием фильтров ALL и логирования всех типов обновлений, но обновление с типом WEB_APP_DATA не приходит в бота, несмотря на то, что Telegram.WebApp.sendData() корректно отправляет данные и WebApp закрывается.