Задать вопрос
@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("Произошла ошибка, попробуйте позже")
  • Вопрос задан
  • 72 просмотра
Подписаться 1 Средний 2 комментария
Решения вопроса 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, нежели каждый раз загружать файлы на сервера телеги.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы