Задать вопрос
@Kiwarek

Бот долго отвечает сообщением, где фото+текст. Как исправить?

Здравствуйте.
Впервые пишу бота на питоне и появилась такая проблема:
Бот хорошо и быстро отвечает текстом на команду/кнопку, но когда я пытаюсь перейти на какой-то из своих товаров (фото+текст), то появляется задержка на 6-15 секунд. Фото в формате jpg (от 5мб до 10мб), пытался (для теста) конвертировать в webp (вес до 250байт) - ситуация абсолютно не изменилась и бот все также долго выдаёт сообщение, где фото+текст.

Какие есть варианты решения такой проблемы?

# Просмотр товара
@dp.callback_query(F.data.startswith("product_"))
async def show_product_detail(callback: types.CallbackQuery):
    try:
        product_id = int(callback.data.split("_")[1])
        product = products[product_id]
        
        kb = types.InlineKeyboardMarkup(inline_keyboard=[
            [
                types.InlineKeyboardButton(text="❤️ В избранное", callback_data=f"fav_{product_id}"),
                types.InlineKeyboardButton(text=" Купить", callback_data=f"buy_{product_id}")
            ],
            [types.InlineKeyboardButton(text="◀️ Назад", callback_data="back_products")]
        ])
        
        photo_path = os.path.join(BASE_DIR, "images", product['photo'])
        caption = (
            f"<b>{product['name']}</b>\n\n"
            f" <s>{product['old_price']}₽</s> → {product['price']}₽\n"
            f" {product['short_desc']}\n\n"
            f"<b>Что ты получишь:</b>\n"
            f"{product['details']}"
        )
        
        if os.path.exists(photo_path):
            try:
                await callback.message.answer_photo(
                    photo=types.FSInputFile(photo_path),
                    caption=caption,
                    reply_markup=kb,
                    parse_mode="HTML"
                )
            except Exception as e:
                logger.error(f"Ошибка при отправке фото: {e}")
                await callback.message.answer(
                    caption + "\n\n⚠️ Не удалось загрузить изображение",
                    reply_markup=kb,
                    parse_mode="HTML"
                )
        else:
            logger.error(f"Файл не найден: {photo_path}")
            await callback.message.answer(
                caption + "\n\n⚠️ Изображение временно недоступно",
                reply_markup=kb,
                parse_mode="HTML"
            )
        
        await callback.answer()
            
    except TelegramRetryAfter as e:
        await asyncio.sleep(e.retry_after)
        return await show_product_detail(callback)
    except Exception as e:
        logger.error(f"Error in show_product_detail: {e}")
        await callback.answer("Произошла ошибка, попробуйте позже")

# Просмотр услуги
@dp.callback_query(F.data.startswith("service_"))
async def show_service_detail(callback: types.CallbackQuery):
    try:
        service_id = int(callback.data.split("_")[1])
        service = services[service_id]
        
        kb = types.InlineKeyboardMarkup(inline_keyboard=[
            [types.InlineKeyboardButton(text="✍️ Заказать", callback_data=f"order_{service_id}")],
            [types.InlineKeyboardButton(text="◀️ Назад", callback_data="back_services")]
        ])
        
        caption = (
            f"<b>{service['name']}</b>\n\n"
            f" <s>{service['old_price']}₽/1k</s> → {service['price_per_1k']}₽/1k\n"
            f" {service['short_desc']}\n"
            f" Минимум: {service['min_symbols']} симв.\n\n"
            f"<b>Что ты получишь:</b>\n"
            f"{service['details']}"
        )
        
        photo_path = os.path.join(BASE_DIR, "images", service['photo'])
        if os.path.exists(photo_path):
            try:
                await callback.message.answer_photo(
                    photo=types.FSInputFile(photo_path),
                    caption=caption,
                    reply_markup=kb,
                    parse_mode="HTML"
                )
            except Exception as e:
                logger.error(f"Ошибка при отправке фото: {e}")
                await callback.message.answer(
                    caption + "\n\n⚠️ Не удалось загрузить изображение",
                    reply_markup=kb,
                    parse_mode="HTML"
                )
        else:
            logger.error(f"Файл не найден: {photo_path}")
            await callback.message.answer(
                caption + "\n\n⚠️ Изображение временно недоступно",
                reply_markup=kb,
                parse_mode="HTML"
            )
        
        await callback.answer()
            
    except TelegramRetryAfter as e:
        await asyncio.sleep(e.retry_after)
        return await show_service_detail(callback)
    except Exception as e:
        logger.error(f"Error in show_service_detail: {e}")
        await callback.answer("Произошла ошибка, попробуйте позже")
  • Вопрос задан
  • 367 просмотров
Подписаться 1 Средний 4 комментария
Помогут разобраться в теме Все курсы
  • Нетология
    Python-разработчик: расширенный курс + нейросети
    12 месяцев
    Далее
  • Академия Эдюсон
    Python-разработчик
    9 месяцев
    Далее
  • ProductStar × РБК
    Профессия: Python-разработчик + ИИ
    8 месяцев
    Далее
Решения вопроса 1
@twistfire92
Python backend developer
По-хорошему нужно фото на сервер загрузить один раз и сохранить его file_id. И все дальнейшие ответы с этим фото реализовывать через этот file_id, а не загружая каждый раз одно и то же фото. Вполне возможно это ускорит ответ.

Как и где хранить этот file_id для фото нужного товара - тема отдельного вопроса. Обычно используется БД для подобных вещей, но у вас уже есть какой-то словарь products со словарями внутри. Предлагаю каждый элемент этого словаря расширить ключом file_id.
Далее логика следующая:

Когда нужно отправить фото товара, смотрите сначала на значение в file_id.
- Если оно пустое - отправляете по-старому сценарию, только сначала дождитесь ответа от метода message.answer_photo(). Результатом этого метода будет объект Message, в котором можно найти file_id для вашего загруженного файла (поиски возложу на вас). Просто прописываете его в ваш словарь products[product_id][file_id].

- Если значение file_id заполнено, то делаете все то же самое, что и сейчас, только в методе message.answer_photo() в параметре photo передаете file_id.

получается, что сам файл будет загружен на сервер только один раз, а все остальные разы будет браться только с сервера. Кстати в документации Bot API так и сказано, что предпочтительнее использовать file_id, нежели каждый раз загружать файлы на сервера телеги.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
ITK academy Краснодар
от 220 000 до 300 000 ₽
ITK academy Краснодар
от 75 000 ₽
DimaTech Ltd Краснодар
от 140 000 до 140 000 ₽