Обработка файлов и медиа в Telegram ботах

В этой статье мы рассмотрим, как работать с различными типами файлов и медиа в Telegram ботах: изображения, документы, аудио, видео и другие форматы.

Содержание

Типы медиа в Telegram

Поддерживаемые форматы

# Основные типы медиа в Telegram
MEDIA_TYPES = {
    'photo': {
        'extensions': ['.jpg', '.jpeg', '.png', '.webp'],
        'max_size': '10MB',
        'description': 'Изображения'
    },
    'document': {
        'extensions': ['.pdf', '.doc', '.docx', '.txt', '.zip', '.rar'],
        'max_size': '2GB',
        'description': 'Документы'
    },
    'video': {
        'extensions': ['.mp4', '.avi', '.mov', '.mkv'],
        'max_size': '2GB',
        'description': 'Видео файлы'
    },
    'audio': {
        'extensions': ['.mp3', '.wav', '.ogg', '.m4a'],
        'max_size': '50MB',
        'description': 'Аудио файлы'
    },
    'voice': {
        'extensions': ['.ogg'],
        'max_size': '20MB',
        'description': 'Голосовые сообщения'
    },
    'video_note': {
        'extensions': ['.mp4'],
        'max_size': '1MB',
        'description': 'Видеосообщения'
    },
    'sticker': {
        'extensions': ['.webp', '.tgs'],
        'max_size': '512KB',
        'description': 'Стикеры'
    },
    'animation': {
        'extensions': ['.gif', '.mp4'],
        'max_size': '50MB',
        'description': 'GIF анимации'
    }
}

Обработка изображений

Базовый обработчик фотографий

# handlers/media_handlers.py
import os
import requests
from PIL import Image
from telegram import Update
from telegram.ext import ContextTypes
import logging

logger = logging.getLogger(__name__)

async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка фотографий"""
    photo = update.message.photo[-1]  # Берем фото наивысшего качества
    user = update.effective_user
    
    try:
        # Получение информации о файле
        file_info = await context.bot.get_file(photo.file_id)
        
        # Создание директории для сохранения
        os.makedirs('downloads/photos', exist_ok=True)
        
        # Путь для сохранения
        file_path = f"downloads/photos/{photo.file_id}.jpg"
        
        # Скачивание файла
        await file_info.download_to_drive(file_path)
        
        # Получение метаданных изображения
        with Image.open(file_path) as img:
            width, height = img.size
            format_type = img.format
            mode = img.mode
        
        # Отправка информации о фото
        info_text = f"""
📸 Фото получено!

📏 Размер: {width}x{height} пикселей
📁 Формат: {format_type}
🎨 Режим: {mode}
💾 Размер файла: {os.path.getsize(file_path)} байт
🆔 File ID: {photo.file_id}
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
        # Отправка обработанного изображения
        await send_processed_image(update, context, file_path)
        
    except Exception as e:
        logger.error(f"Ошибка при обработке фото: {e}")
        await update.message.reply_text("❌ Произошла ошибка при обработке фото.")

async def send_processed_image(update: Update, context: ContextTypes.DEFAULT_TYPE, image_path: str):
    """Отправка обработанного изображения"""
    try:
        # Создание миниатюры
        thumbnail_path = create_thumbnail(image_path)
        
        # Отправка оригинального изображения с подписью
        with open(image_path, 'rb') as photo_file:
            await context.bot.send_photo(
                chat_id=update.effective_chat.id,
                photo=photo_file,
                caption="🖼️ Обработанное изображение"
            )
        
        # Отправка миниатюры
        with open(thumbnail_path, 'rb') as thumb_file:
            await context.bot.send_photo(
                chat_id=update.effective_chat.id,
                photo=thumb_file,
                caption="🔍 Миниатюра"
            )
        
        # Удаление временных файлов
        os.remove(thumbnail_path)
        
    except Exception as e:
        logger.error(f"Ошибка при отправке обработанного изображения: {e}")

def create_thumbnail(image_path: str, size: tuple = (200, 200)) -> str:
    """Создание миниатюры изображения"""
    thumbnail_path = image_path.replace('.jpg', '_thumb.jpg')
    
    with Image.open(image_path) as img:
        img.thumbnail(size, Image.Resampling.LANCZOS)
        img.save(thumbnail_path, 'JPEG', quality=85)
    
    return thumbnail_path

Обработка множественных фотографий

async def handle_photo_album(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка альбома фотографий"""
    if update.message.media_group_id:
        # Это часть медиа-группы (альбом)
        media_group_id = update.message.media_group_id
        
        # Проверяем, обрабатывали ли мы уже этот альбом
        if hasattr(context, 'processed_albums'):
            if media_group_id in context.processed_albums:
                return
        else:
            context.processed_albums = set()
        
        context.processed_albums.add(media_group_id)
        
        # Обрабатываем только первое фото из альбома
        photo = update.message.photo[-1]
        
        try:
            file_info = await context.bot.get_file(photo.file_id)
            file_path = f"downloads/albums/{media_group_id}_{photo.file_id}.jpg"
            
            os.makedirs('downloads/albums', exist_ok=True)
            await file_info.download_to_drive(file_path)
            
            await update.message.reply_text(
                f"📸 Альбом получен!\n"
                f"🆔 Media Group ID: {media_group_id}\n"
                f"📁 Файл сохранен: {file_path}",
                parse_mode='Markdown'
            )
            
        except Exception as e:
            logger.error(f"Ошибка при обработке альбома: {e}")

Работа с документами

Обработчик документов

``python async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработка документов""" document = update.message.document user = update.effective_user try: # Получение информации о документе file_info = await context.bot.get_file(document.file_id) # Проверка типа файла file_extension = os.path.splitext(document.file_name)[1].lower() allowed_extensions = ['.pdf', '.doc', '.docx', '.txt', '.py', '.js', '.html', '.css'] if file_extension not in allowed_extensions: await update.message.reply_text( f"❌ Неподдерживаемый тип файла: {file_extension}\n" f"📋 Поддерживаемые форматы: {', '.join(allowed_extensions)}" ) return # Создание директории для сохранения os.makedirs('downloads/documents', exist_ok=True) # Путь для сохранения file_path = f"downloads/documents/{document.file_name}" # Скачивание файла await file_info.download_to_drive(file_path) # Информация о документе file_size = os.path.getsize(file_path) file_size_mb = file_size / (1024 * 1024) info_text = f""" 📄 Документ получен! 📝 Название: {document.file_name} 📏 Размер: {file_size_mb:.2f} MB 🆔 File ID: {document.file_id} 📁 Путь: {file_path} """ await update.message.reply_text(info_text, parse_mode='Markdown') # Обработка в зависимости от типа файла await process_document_by_type(update, context, file_path, file_extension) except Exception as e: logger.error(f"Ошибка при обработке документа: {e}") await update.message.reply_text("❌ Произошла ошибка при обработке документа.") async def process_document_by_type(update: Update, context: ContextTypes.DEFAULT_TYPE, file_path: str, extension: str): """Обработка документа в зависимости от типа""" try: if extension == '.txt': # Чтение текстового файла with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # Отправка первых 1000 символов preview = content[:1000] + "..." if len(content) > 1000 else content await update.message.reply_text(f"📖 Содержимое файла:\n\n`\n{preview}\n`", parse_mode='Markdown') elif extension == '.py': # Анализ Python файла with open(file_path, 'r', encoding='utf-8') as f: lines = f.readlines() # Подсчет строк кода code_lines = [line for line in lines if line.strip() and not line.strip().startswith('#')] comment_lines = [line for line in lines if line.strip().startswith('#')] analysis_text = f""" 🐍 Анализ Python файла 📊 Всего строк: {len(lines)} 💻 Строк кода: {len(code_lines)} 💬 Комментариев: {len(comment_lines)} 📏 Пустых строк: {len(lines) - len(code_lines) - len(comment_lines)} """ await update.message.reply_text(analysis_text, parse_mode='Markdown') elif extension == '.pdf': # Информация о PDF (требует дополнительных библиотек) await update.message.reply_text("📄 PDF файл получен. Для полного анализа требуется установка дополнительных библиотек.") except Exception as e: logger.error(f"Ошибка при обработке документа по типу: {e}") `

Аудио и видео

Обработка аудио файлов

async def handle_audio(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка аудио файлов"""
    audio = update.message.audio
    
    try:
        file_info = await context.bot.get_file(audio.file_id)
        
        # Создание директории для аудио
        os.makedirs('downloads/audio', exist_ok=True)
        
        # Определение расширения файла
        file_extension = '.mp3'  # По умолчанию
        if audio.mime_type:
            if 'ogg' in audio.mime_type:
                file_extension = '.ogg'
            elif 'm4a' in audio.mime_type:
                file_extension = '.m4a'
        
        file_path = f"downloads/audio/{audio.file_id}{file_extension}"
        
        # Скачивание файла
        await file_info.download_to_drive(file_path)
        
        # Информация об аудио
        duration_minutes = audio.duration // 60
        duration_seconds = audio.duration % 60
        
        info_text = f"""
🎵 Аудио файл получен!

🎤 Исполнитель: {audio.performer or 'Не указан'}
🎼 Название: {audio.title or 'Не указано'}
⏱️ Длительность: {duration_minutes}:{duration_seconds:02d}
📏 Размер: {audio.file_size / (1024 * 1024):.2f} MB
🆔 File ID: {audio.file_id}
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
    except Exception as e:
        logger.error(f"Ошибка при обработке аудио: {e}")
        await update.message.reply_text("❌ Произошла ошибка при обработке аудио.")

async def handle_video(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка видео файлов"""
    video = update.message.video
    
    try:
        file_info = await context.bot.get_file(video.file_id)
        
        # Создание директории для видео
        os.makedirs('downloads/video', exist_ok=True)
        
        file_path = f"downloads/video/{video.file_id}.mp4"
        
        # Скачивание файла
        await file_info.download_to_drive(file_path)
        
        # Информация о видео
        duration_minutes = video.duration // 60
        duration_seconds = video.duration % 60
        
        info_text = f"""
🎬 Видео файл получен!

📏 Разрешение: {video.width}x{video.height}
⏱️ Длительность: {duration_minutes}:{duration_seconds:02d}
📏 Размер: {video.file_size / (1024 * 1024):.2f} MB
🆔 File ID: {video.file_id}
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
    except Exception as e:
        logger.error(f"Ошибка при обработке видео: {e}")
        await update.message.reply_text("❌ Произошла ошибка при обработке видео.")

Стикеры и анимации

Обработка стикеров

async def handle_sticker(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка стикеров"""
    sticker = update.message.sticker
    
    try:
        # Информация о стикере
        info_text = f"""
😀 Стикер получен!

📦 Набор: {sticker.set_name or 'Не указан'}
🎨 Тип: {sticker.type}
📏 Размер: {sticker.width}x{sticker.height}
🆔 File ID: {sticker.file_id}
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
        # Отправка информации о файле стикера
        file_info = await context.bot.get_file(sticker.file_id)
        
        # Создание директории для стикеров
        os.makedirs('downloads/stickers', exist_ok=True)
        
        file_path = f"downloads/stickers/{sticker.file_id}.webp"
        await file_info.download_to_drive(file_path)
        
        await update.message.reply_text(f"💾 Стикер сохранен: {file_path}")
        
    except Exception as e:
        logger.error(f"Ошибка при обработке стикера: {e}")

async def handle_animation(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка GIF анимаций"""
    animation = update.message.animation
    
    try:
        file_info = await context.bot.get_file(animation.file_id)
        
        # Создание директории для анимаций
        os.makedirs('downloads/animations', exist_ok=True)
        
        file_path = f"downloads/animations/{animation.file_id}.gif"
        await file_info.download_to_drive(file_path)
        
        # Информация об анимации
        duration_minutes = animation.duration // 60
        duration_seconds = animation.duration % 60
        
        info_text = f"""
🎭 GIF анимация получена!

📏 Размер: {animation.width}x{animation.height}
⏱️ Длительность: {duration_minutes}:{duration_seconds:02d}
📏 Размер файла: {animation.file_size / (1024 * 1024):.2f} MB
🆔 File ID: {animation.file_id}
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
    except Exception as e:
        logger.error(f"Ошибка при обработке анимации: {e}")

Голосовые сообщения

Обработка голосовых сообщений

async def handle_voice(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка голосовых сообщений"""
    voice = update.message.voice
    
    try:
        file_info = await context.bot.get_file(voice.file_id)
        
        # Создание директории для голосовых сообщений
        os.makedirs('downloads/voice', exist_ok=True)
        
        file_path = f"downloads/voice/{voice.file_id}.ogg"
        await file_info.download_to_drive(file_path)
        
        # Информация о голосовом сообщении
        duration_minutes = voice.duration // 60
        duration_seconds = voice.duration % 60
        
        info_text = f"""
🎤 Голосовое сообщение получено!

⏱️ Длительность: {duration_minutes}:{duration_seconds:02d}
📏 Размер: {voice.file_size / 1024:.2f} KB
🆔 File ID: {voice.file_id}
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
        # Отправка обратно голосового сообщения
        with open(file_path, 'rb') as voice_file:
            await context.bot.send_voice(
                chat_id=update.effective_chat.id,
                voice=voice_file,
                caption="🔄 Ваше голосовое сообщение"
            )
        
    except Exception as e:
        logger.error(f"Ошибка при обработке голосового сообщения: {e}")

Видеосообщения

Обработка видеосообщений

async def handle_video_note(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Обработка видеосообщений"""
    video_note = update.message.video_note
    
    try:
        file_info = await context.bot.get_file(video_note.file_id)
        
        # Создание директории для видеосообщений
        os.makedirs('downloads/video_notes', exist_ok=True)
        
        file_path = f"downloads/video_notes/{video_note.file_id}.mp4"
        await file_info.download_to_drive(file_path)
        
        # Информация о видеосообщении
        duration_minutes = video_note.duration // 60
        duration_seconds = video_note.duration % 60
        
        info_text = f"""
📹 Видеосообщение получено!

📏 Размер: {video_note.length}x{video_note.length} (квадратное)
⏱️ Длительность: {duration_minutes}:{duration_seconds:02d}
📏 Размер файла: {video_note.file_size / (1024 * 1024):.2f} MB
🆔 File ID: {video_note.file_id}`
        """
        
        await update.message.reply_text(info_text, parse_mode='Markdown')
        
    except Exception as e:
        logger.error(f"Ошибка при обработке видеосообщения: {e}")

Обработка ошибок

Универсальный обработчик ошибок

# utils/error_handler.py
import logging
from telegram import Update
from telegram.ext import ContextTypes

logger = logging.getLogger(__name__)

async def handle_media_error(update: Update, context: ContextTypes.DEFAULT_TYPE, error: Exception):
    """Универсальный обработчик ошибок медиа"""
    error_messages = {
        'FileNotFoundError': '❌ Файл не найден. Возможно, он был удален.',
        'PermissionError': '❌ Нет прав доступа к файлу.',
        'OSError': '❌ Ошибка операционной системы при работе с файлом.',
        'ValueError': '❌ Некорректные данные файла.',
        'MemoryError': '❌ Недостаточно памяти для обработки файла.',
        'TimeoutError': '❌ Превышено время ожидания при загрузке файла.'
    }
    
    error_type = type(error).__name__
    user_message = error_messages.get(error_type, f'❌ Произошла ошибка: {error_type}')
    
    await update.message.reply_text(user_message)
    logger.error(f"Ошибка при обработке медиа: {error}", exc_info=True)

# Декоратор для обработки ошибок
def media_error_handler(func):
    """Декоратор для обработки ошибок в медиа-функциях"""
    async def wrapper(update: Update, context: ContextTypes.DEFAULT_TYPE):
        try:
            await func(update, context)
        except Exception as e:
            await handle_media_error(update, context, e)
    return wrapper

Интеграция с основным ботом

# bot_with_media.py
import logging
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
from handlers.media_handlers import *
from utils.error_handler import media_error_handler
import os
from dotenv import load_dotenv

load_dotenv()

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)
logger = logging.getLogger(__name__)

BOT_TOKEN = os.getenv('BOT_TOKEN')

@media_error_handler
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Команда /start"""
    await update.message.reply_text(
        "🤖 Добро пожаловать в бот для обработки медиа!\n\n"
        "Отправьте мне:\n"
        "📸 Фото\n"
        "📄 Документ\n"
        "🎵 Аудио\n"
        "🎬 Видео\n"
        "🎤 Голосовое сообщение\n"
        "📹 Видеосообщение\n"
        "😀 Стикер\n"
        "🎭 GIF анимацию"
    )

def main():
    """Основная функция запуска бота"""
    if not BOT_TOKEN:
        logger.error("BOT_TOKEN не найден!")
        return
    
    # Создание приложения
    application = Application.builder().token(BOT_TOKEN).build()
    
    # Добавление обработчиков команд
    application.add_handler(CommandHandler("start", start))
    
    # Добавление обработчиков медиа
    application.add_handler(MessageHandler(filters.PHOTO, handle_photo))
    application.add_handler(MessageHandler(filters.Document.ALL, handle_document))
    application.add_handler(MessageHandler(filters.AUDIO, handle_audio))
    application.add_handler(MessageHandler(filters.VIDEO, handle_video))
    application.add_handler(MessageHandler(filters.VOICE, handle_voice))
    application.add_handler(MessageHandler(filters.VIDEO_NOTE, handle_video_note))
    application.add_handler(MessageHandler(filters.STICKER, handle_sticker))
    application.add_handler(MessageHandler(filters.ANIMATION, handle_animation))
    
    # Запуск бота
    logger.info("Бот для обработки медиа запущен!")
    application.run_polling()

if __name__ == '__main__':
    main()

Заключение

В этой статье мы рассмотрели:
  • ✅ Типы медиа в Telegram и их ограничения
  • ✅ Обработку изображений с созданием миниатюр
  • ✅ Работу с документами различных форматов
  • ✅ Обработку аудио и видео файлов
  • ✅ Работу со стикерами и анимациями
  • ✅ Обработку голосовых и видеосообщений
  • ✅ Универсальную обработку ошибок
Теперь ваш бот может полноценно работать с любыми типами медиа-файлов!

Полезные ссылки

1475 просмотров
4 лайков
0 комментариев