привет! вот код телеграм бота.
исправь ошибку: когда пользователь выбирает модель для генерации изображений, бот не отвечает. кнопка просто бесконечно остаётся нажатой. в консоли пусто.
скинь полный код
import logging
import requests
import json
import os
import re
import html
from datetime import datetime
from telegram import (
    Update,
    InlineKeyboardMarkup,
    InlineKeyboardButton,
    ReplyKeyboardMarkup,
    KeyboardButton,
    InputFile
)
from telegram.constants import ParseMode
from telegram.ext import (
    ApplicationBuilder,
    CommandHandler,
    MessageHandler,
    ContextTypes,
    filters,
    CallbackQueryHandler,
    Defaults,
)
from telegram.error import TelegramError

# Функция для загрузки конфигурации
def load_config():
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, 'config.json')
    
    if not os.path.exists(config_path):
        raise FileNotFoundError(f"файл config.json не найден по пути: {config_path}")
    
    with open(config_path, 'r', encoding='utf-8') as f:
        return json.load(f)

# Загрузка конфигурации
try:
    config = load_config()
except FileNotFoundError as e:
    print(e)
    exit(1)

# Настройка логирования
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)
logger = logging.getLogger(__name__)

# Ваши API-эндпоинты
API_URL_TEXT = 'https://cablyai.com/v1/chat/completions'
API_URL_IMAGE = 'https://cablyai.com/v1/images/generations'  # Используем ваш актуальный URL для генерации изображений
API_HEADERS = {
    'Authorization': f"Bearer {config['api_key']}",
    'Content-Type': 'application/json'
}

# Инструкция для текстовых моделей
SYSTEM_INSTRUCTION_TEXT = config.get('system_instruction', "ты ведёшь себя как котик")

# Хранение контекста для каждой сессии и модели
context_store = {}
model_store = {}
MAX_MESSAGE_LENGTH = 4096
AUTHORIZED_USER_IDS = config.get('authorized_user_ids', [])

# Хранение режима пользователя ('message' или 'file')
user_mode_store = {}

# Создание маппинга model_id -> model_info для быстрого доступа
model_id_to_info = {}
model_id_to_name = {}
for model_name, model_info in config.get('models', {}).items():
    model_id = model_info.get('id')
    if model_id:
        model_id_to_info[model_id] = model_info
        model_id_to_name[model_id] = model_name
    else:
        logger.warning(f"Модель '{model_name}' не имеет 'id'.")

# Функция для разбивки длинного сообщения на части
def split_message(message, max_length):
    return [message[i:i + max_length] for i in range(0, len(message), max_length)]

# Функция для создания кнопок выбора модели
def get_model_buttons():
    buttons = []
    for name, model_info in config.get('models', {}).items():
        buttons.append([InlineKeyboardButton(html.escape(name), callback_data=model_info['id'])])
    return InlineKeyboardMarkup(buttons)

# Функция для создания основных кнопок: 'Сменить модель' и 'Продолжить'
def get_main_buttons():
    keyboard = [
        [
            InlineKeyboardButton("Сменить модель", callback_data='change_model'),
            InlineKeyboardButton("Продолжить", callback_data='continue_response')
        ]
    ]
    return InlineKeyboardMarkup(keyboard)

# Функция для создания кнопок главного меню (без инструкции)
def get_menu_buttons():
    buttons = [
        [InlineKeyboardButton("Сменить модель", callback_data='change_model'),
         InlineKeyboardButton("Продолжить", callback_data='continue_response')]
    ]
    return InlineKeyboardMarkup(buttons)

# Функция для определения, является ли текст русским
def is_russian(text):
    return bool(re.search('[а-яА-Я]', text))

# Функция для перевода текста с помощью модели GPT-4
def translate_to_english(original_text):
    translation_prompt = f"Переведи следующий текст на английский язык и верни только переведенный текст:\n\n{original_text}"
    
    translation_data = {
        "model": "chatgpt-4o-latest",
        "messages": [
            {"role": "system", "content": "You are a helpful assistant that translates Russian text to English."},
            {"role": "user", "content": translation_prompt}
        ]
    }
    
    try:
        response = requests.post(API_URL_TEXT, headers=API_HEADERS, json=translation_data)
        response.raise_for_status()
        response_data = response.json()
        
        if "choices" in response_data and len(response_data["choices"]) > 0:
            translated_text = response_data["choices"][0].get('message', {}).get('content', '').strip()
            return translated_text
        else:
            logger.error("неверная структура ответа от модели при переводе.")
            return None
    except requests.exceptions.RequestException as e:
        logger.error(f"ошибка при переводе текста: {e}")
        return None

# Функция для преобразования Markdown в HTML
def markdown_to_html(text):
    """
    Простой конвертер Markdown в HTML.
    Обрабатывает заголовки, **жирный**, *курсив*, __подчёркивание__, ~~зачёркивание~~.
    """
    # Экранирование HTML-символов
    text = html.escape(text)
    
    # Замена заголовков: ### Заголовок -> <b>Заголовок</b>
    for i in range(6, 0, -1):  # Обрабатываем заголовки от h6 до h1
        pattern = r'^{0} (.+)$'.format('#' * i)
        replacement = r'<b>\1</b>'
        text = re.sub(pattern, replacement, text, flags=re.MULTILINE)
    
    # Замена **жирный** на <b>жирный</b>
    text = re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', text)
    
    # Замена *курсив* на <i>курсив</i>
    text = re.sub(r'\*(.*?)\*', r'<i>\1</i>', text)
    
    # Замена __подчёркивание__ на <u>подчёркивание</u>
    text = re.sub(r'__(.*?)__', r'<u>\1</u>', text)
    
    # Замена ~~зачёркивание~~ на 
зачёркивание

    text = re.sub(r'~~(.*?)~~', r'
\1
', text)
    
    return text

# Функция для сохранения ответа в .md файл
def save_md_file(content, user_id):
    """
    Сохраняет содержимое в .md файл с уникальным именем.
    Возвращает путь к файлу.
    """
    if not os.path.exists('responses'):
        os.makedirs('responses')
    
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    filename = f"responses/{user_id}_{timestamp}.md"
    
    try:
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(content)
        return filename
    except Exception as e:
        logger.error(f"Ошибка при сохранении файла .md: {e}")
        return None

# Обработчик команды /set_mode
async def set_mode(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Позволяет пользователю выбрать режим получения ответов:
    - message: получение текстовых сообщений
    - file: получение ответов в .md файлах
    """
    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await update.message.reply_text('тебе сюда мяукать нельзя.')
        return
    
    if len(context.args) != 1:
        await update.message.reply_text('использование: /set_mode [message|file]')
        return
    
    mode = context.args[0].lower()
    if mode not in ['message', 'file']:
        await update.message.reply_text('неверный режим. используй "message" или "file".')
        return
    
    user_mode_store[user_id] = mode
    await update.message.reply_text(f'режим получения ответов установлен на: <b>{mode}</b>', parse_mode=ParseMode.HTML)

# Обработчик команды /get_mode
async def get_mode(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Позволяет пользователю узнать текущий режим получения ответов.
    """
    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await update.message.reply_text('тебе сюда мяукать нельзя.')
        return
    
    mode = user_mode_store.get(user_id, 'message')  # По умолчанию 'message'
    await update.message.reply_text(f'текущий режим получения ответов: <b>{mode}</b>', parse_mode=ParseMode.HTML)

# Обработчик команды /instructions
async def instructions(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Отправляет пользователю инструкции по использованию бота.
    """
    menu_text = (
        "<b>Инструкция по использованию бота:</b>\n\n"
        "<b>/start</b> - Запустить бота и выбрать модель для взаимодействия.\n"
        "<b>/set_mode [message|file]</b> - Выбрать режим получения ответов (message - текстовые сообщения, file - .md файлы).\n"
        "<b>/get_mode</b> - Узнать текущий выбранный режим.\n"
        "<b>/instructions</b> - Получить инструкции по использованию бота.\n\n"
        "Также доступно использование кнопок ниже:\n"
        "<b>Сменить модель</b> - Выбрать другую модель для общения.\n"
        "<b>Продолжить</b> - Продолжить ответ модели."
    )
    
    # Отправляем инструкции как сообщение без кнопок
    await update.message.reply_text(menu_text, parse_mode=ParseMode.HTML)

# Обработчик команды /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await update.message.reply_text('тебе сюда мяукать нельзя.')
        return

    chat_id = update.effective_chat.id
    # Инициализация контекста для нового пользователя
    if chat_id not in context_store:
        context_store[chat_id] = {}
    
    # Если модель ещё не выбрана, устанавливаем модель по умолчанию
    if chat_id not in model_store:
        if model_id_to_info:
            default_model_id = next(iter(model_id_to_info))
            model_store[chat_id] = default_model_id
            model_info = model_id_to_info[default_model_id]
            if model_info.get('type') == 'text' or model_info.get('type') == 'multimodal':
                context_store[chat_id][default_model_id] = [{"role": "system", "content": SYSTEM_INSTRUCTION_TEXT}]
        else:
            await update.message.reply_text('нет доступных моделей для использования.', reply_markup=get_main_buttons())
            return
    
    # Кнопки для выбора модели
    if config.get('models'):
        await update.message.reply_text(
            'привет! выбери модель для общения:',
            reply_markup=get_model_buttons()
        )
    else:
        await update.message.reply_text(
            'привет!\n\nНет доступных моделей для выбора.',
            reply_markup=get_main_buttons()
        )

# Обработчик выбора модели через кнопки
async def select_model(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    query = update.callback_query
    await query.answer()

    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await query.edit_message_text('тебе сюда мяукать нельзя.')
        return

    selected_model_id = query.data
    model_info = model_id_to_info.get(selected_model_id)

    if not model_info:
        await query.edit_message_text('выбранная модель не найдена в конфигурации.')
        return

    chat_id = update.effective_chat.id
    model_store[chat_id] = selected_model_id

    model_type = model_info.get('type', 'text')

    # Инициализация контекста для выбранной модели, только если это текстовая или мультимодальная модель
    if model_type in ['text', 'multimodal']:
        if selected_model_id not in context_store.get(chat_id, {}):
            context_store[chat_id][selected_model_id] = [{"role": "system", "content": SYSTEM_INSTRUCTION_TEXT}]
    else:
        # Для других типов моделей контекст не инициализируется или очищается
        if chat_id in context_store and selected_model_id in context_store[chat_id]:
            del context_store[chat_id][selected_model_id]

    model_name = html.escape(model_id_to_name.get(selected_model_id, "Неизвестная модель"))
    await query.edit_message_text(f'модель изменена на: <b>{model_name}</b>', reply_markup=get_menu_buttons())

# Обработчик текстовых сообщений
# Обработчик текстовых сообщений
async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await update.message.reply_text('тебе сюда мяукать нельзя.')
        return

    chat_id = update.effective_chat.id
    user_message = update.message.text

    # Проверка, нажата ли кнопка "Меню"
    if user_message.lower() == "меню":
        await show_menu(update, context)
        return

    # Получаем режим пользователя, по умолчанию 'message'
    mode = user_mode_store.get(user_id, 'message')

    if chat_id not in model_store:
        await update.message.reply_text('модель не выбрана. используется модель по умолчанию.')
        if model_id_to_info:
            default_model_id = next(iter(model_id_to_info))
            model_store[chat_id] = default_model_id
            model_info = model_id_to_info[default_model_id]
            if model_info.get('type') in ['text', 'multimodal']:
                context_store[chat_id][default_model_id] = [{"role": "system", "content": SYSTEM_INSTRUCTION_TEXT}]
        else:
            await update.message.reply_text('нет доступных моделей для использования.', reply_markup=get_main_buttons())
            return

    selected_model_id = model_store.get(chat_id)
    model_info = model_id_to_info.get(selected_model_id)
    if not model_info:
        await update.message.reply_text('модель не найдена в конфигурации.', reply_markup=get_main_buttons())
        return

    selected_model_type = model_info.get('type', 'text')

    # Проверка поддержки текстового режима моделью
    if selected_model_type not in ['text', 'multimodal']:
        await update.message.reply_text('выбранная модель не поддерживает текстовый ввод.', reply_markup=get_menu_buttons())
        return

    # Инициализация контекста для текстовой или мультимодальной модели, если ещё не инициализирована
    if selected_model_id not in context_store.get(chat_id, {}):
        context_store[chat_id][selected_model_id] = [{"role": "system", "content": SYSTEM_INSTRUCTION_TEXT}]
    
    # Добавление сообщения пользователя в контекст
    context_store[chat_id][selected_model_id].append({"role": "user", "content": user_message})

    # Подготовка данных для API с учетом контекста
    data = {
        "model": selected_model_id,
        "messages": context_store[chat_id][selected_model_id]
    }
    api_url = API_URL_TEXT

    try:
        # Отправка запроса в API
        response = requests.post(api_url, headers=API_HEADERS, json=data)
        response.raise_for_status()  # Эта строка вызовет исключение для статусов ошибок
    except requests.exceptions.RequestException as e:
        logger.error(f"Ошибка запроса к API: {e}")
        await update.message.reply_text('API не отвечает или произошла ошибка при обработке запроса. попробуй потом.', reply_markup=get_menu_buttons())
        return

    if response.status_code == 200:
        response_data = response.json()
        logger.info(f"Ответ API: {response_data}")

        # Проверка структуры ответа для текстовых моделей
        if "choices" in response_data and len(response_data["choices"]) > 0:
            answer = response_data["choices"][0].get('message', {}).get('content', 'Что-то пошло не так. Мяу :(')
            
            if mode == 'message':
                # Преобразование Markdown в HTML
                answer_html = markdown_to_html(answer)
                
                # Добавление ответа ассистента в контекст
                context_store[chat_id][selected_model_id].append({"role": "assistant", "content": answer})
                
                # Разбивка длинного ответа на части и отправка
                messages = split_message(answer_html, MAX_MESSAGE_LENGTH)
                for msg in messages:
                    await update.message.reply_text(msg, reply_markup=get_menu_buttons())
            elif mode == 'file':
                # Сохранение ответа в .md файл
                file_path = save_md_file(answer, user_id)
                if file_path:
                    # Отправка файла пользователю
                    try:
                        with open(file_path, 'rb') as f:
                            await update.message.reply_document(
                                document=InputFile(f),
                                filename=os.path.basename(file_path),
                                caption="твой ответ модели в формате Markdown.",
                                reply_markup=get_menu_buttons()
                            )
                        
                        # Удаление файла после отправки
                        os.remove(file_path)
                    except Exception as e:
                        logger.error(f"Ошибка при отправке файла: {e}")
                        await update.message.reply_text('Не удалось отправить файл. Попробуйте позже.', reply_markup=get_menu_buttons())
                else:
                    await update.message.reply_text('Не удалось создать файл с ответом.', reply_markup=get_menu_buttons())
        else:
            await update.message.reply_text('Что-то пошло не так. Мяу :(', reply_markup=get_menu_buttons())
    else:
        logger.error(f"Непредвиденный статус ответа API: {response.status_code}")
        await update.message.reply_text('API вернул непредвиденный ответ. Попробуй позже.', reply_markup=get_menu_buttons())

# Обработчик фотографий
async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await update.message.reply_text('тебе сюда мяукать нельзя.')
        return

    chat_id = update.effective_chat.id
    selected_model_id = model_store.get(chat_id)
    model_info = model_id_to_info.get(selected_model_id)

    if not model_info:
        await update.message.reply_text('модель не выбрана или не найдена.', reply_markup=get_main_buttons())
        return

    selected_model_type = model_info.get('type', 'text')

    # Проверка поддержки обработки изображений моделью
    if selected_model_type not in ['multimodal', 'image']:
        await update.message.reply_text('выбранная модель не поддерживает обработку изображений.', reply_markup=get_menu_buttons())
        return

    photo = update.message.photo[-1]  # Высокое разрешение фото
    file = await photo.get_file()
    
    # Скачиваем фото
    try:
        file_path = os.path.join('downloads', f"{file.file_id}.jpg")
        if not os.path.exists('downloads'):
            os.makedirs('downloads')
        await file.download_to_drive(file_path)
    except Exception as e:
        logger.error(f"Ошибка при скачивании фото: {e}")
        await update.message.reply_text('не удалось скачать фото. попробуй позже.', reply_markup=get_menu_buttons())
        return

    # Определение API URL в зависимости от типа модели
    if selected_model_type == 'image':
        api_url = API_URL_IMAGE
    elif selected_model_type == 'multimodal':
        api_url = API_URL_TEXT
    else:
        await update.message.reply_text('эта модель не может обработать изображения.', reply_markup=get_menu_buttons())
        return

    # Подготовка данных для API
    try:
        with open(file_path, 'rb') as img_file:
            if selected_model_type == 'image':
                # Отправка изображения на image API
                files = {'file': img_file}
                data = {'model': selected_model_id}
                response = requests.post(api_url, headers={'Authorization': API_HEADERS['Authorization']}, files=files, data=data)
            elif selected_model_type == 'multimodal':
                # Отправка изображения на text API вместе с текстовым сообщением
                # Предполагается, что API принимает изображение вместе с сообщением
                # Например, используя multipart/form-data с полями 'image' и 'message'
                files = {'image': img_file}
                data = {
                    'model': selected_model_id,
                    'messages': json.dumps([
                        {"role": "user", "content": "Вот изображение для обработки."}
                    ])
                }
                response = requests.post(api_url, headers={'Authorization': API_HEADERS['Authorization']}, files=files, data=data)
        
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        logger.error(f"Ошибка запроса к API для изображения: {e}")
        await update.message.reply_text('API не отвечает или произошла ошибка при обработке запроса. попробуй позже.', reply_markup=get_menu_buttons())
        return
    finally:
        # Удаляем скачанное изображение
        os.remove(file_path)

    if response.status_code == 200:
        response_data = response.json()
        logger.info(f"Ответ API (изображение): {response_data}")

        if selected_model_type == 'image':
            # Обработка ответа для image модели
            generated_images = response_data.get('data')
            if isinstance(generated_images, list) and len(generated_images) > 0:
                for image in generated_images:
                    image_url = image.get('url')
                    if image_url:
                        await update.message.reply_photo(photo=image_url, reply_markup=get_menu_buttons())
                    else:
                        await update.message.reply_text('Не удалось получить URL изображения.', reply_markup=get_menu_buttons())
            else:
                await update.message.reply_text('Не удалось получить изображение. попробуй позже.', reply_markup=get_menu_buttons())
        
        elif selected_model_type == 'multimodal':
            # Обработка ответа для мультимодальной модели
            # Предполагается, что API возвращает как текст, так и изображения
            text_response = response_data.get('text')
            generated_images = response_data.get('images', [])
            
            if text_response:
                # Отправляем текстовый ответ
                await update.message.reply_text(text_response, reply_markup=get_menu_buttons())
                
            if isinstance(generated_images, list) and len(generated_images) > 0:
                for image_url in generated_images:
                    if image_url:
                        await update.message.reply_photo(photo=image_url, reply_markup=get_menu_buttons())
                    else:
                        await update.message.reply_text('Не удалось получить URL изображения.', reply_markup=get_menu_buttons())
            elif not text_response:
                await update.message.reply_text('Не удалось обработать изображение. Попробуй позже.', reply_markup=get_menu_buttons())
    else:
        logger.error(f"Непредвиденный статус ответа API (изображение): {response.status_code}")
        await update.message.reply_text('API вернул непредвиденный ответ. Попробуй позже.', reply_markup=get_menu_buttons())

# Обработчик документов (файлов)
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await update.message.reply_text('тебе сюда мяукать нельзя.')
        return

    chat_id = update.effective_chat.id
    document = update.message.document
    file = await document.get_file()
    filename = document.file_name.lower()  # Приводим к нижнему регистру для надёжной проверки

    # Проверка, является ли файл .py
    if filename.endswith('.py'):
        # Обработка как текстовый файл
        try:
            # Создаём директорию для скачивания, если её нет
            download_path = os.path.join('downloads', 'python_files')
            if not os.path.exists(download_path):
                os.makedirs(download_path)

            # Скачиваем файл
            file_path = os.path.join(download_path, document.file_name)
            await file.download_to_drive(file_path)

            # Читаем содержимое файла
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()

            # Удаляем файл после чтения
            os.remove(file_path)
        except Exception as e:
            logger.error(f"Ошибка при скачивании или чтении файла: {e}")
            await update.message.reply_text('Не удалось скачать или прочитать файл. Попробуй позже.', reply_markup=get_menu_buttons())
            return

        # Получаем информацию о выбранной модели
        model_info = model_id_to_info.get(model_store.get(chat_id))
        if not model_info:
            await update.message.reply_text('Модель не выбрана или не найдена.', reply_markup=get_main_buttons())
            return

        # Проверяем, поддерживает ли модель текстовый ввод
        if model_info.get('type') not in ['text', 'multimodal']:
            await update.message.reply_text('Выбранная модель не поддерживает обработку текстовых файлов.', reply_markup=get_menu_buttons())
            return

        # Добавляем сообщение пользователя в контекст
        context_store[chat_id][model_store[chat_id]].append({"role": "user", "content": content})

        # Подготовка данных для API
        data = {
            "model": model_store[chat_id],
            "messages": context_store[chat_id][model_store[chat_id]]
        }
        api_url = API_URL_TEXT

        try:
            # Отправка запроса в текстовый API
            response = requests.post(api_url, headers=API_HEADERS, json=data)
            response.raise_for_status()
        except requests.exceptions.RequestException as e:
            logger.error(f"Ошибка запроса к текстовому API: {e}")
            await update.message.reply_text('API не отвечает или произошла ошибка при обработке запроса. попробуй позже.', reply_markup=get_menu_buttons())
            return

        # Обработка ответа от API
        if response.status_code == 200:
            response_data = response.json()
            logger.info(f"Ответ текстового API: {response_data}")

            # Проверка структуры ответа для текстовых моделей
            if "choices" in response_data and len(response_data["choices"]) > 0:
                answer = response_data["choices"][0].get('message', {}).get('content', 'Что-то пошло не так. Мяу :(')

                # Получаем режим пользователя
                mode = user_mode_store.get(user_id, 'message')

                if mode == 'message':
                    # Преобразование Markdown в HTML, если необходимо
                    answer_html = markdown_to_html(answer)

                    # Добавление ответа ассистента в контекст
                    context_store[chat_id][model_store[chat_id]].append({"role": "assistant", "content": answer})

                    # Разбивка длинного ответа на части и отправка
                    messages = split_message(answer_html, MAX_MESSAGE_LENGTH)
                    for msg in messages:
                        await update.message.reply_text(msg, reply_markup=get_menu_buttons())

                elif mode == 'file':
                    # Сохранение ответа в .md файл
                    file_path = save_md_file(answer, user_id)
                    if file_path:
                        # Отправка файла пользователю
                        try:
                            with open(file_path, 'rb') as f:
                                await update.message.reply_document(
                                    document=InputFile(f),
                                    filename=os.path.basename(file_path),
                                    caption="твой ответ модели в формате Markdown.",
                                    reply_markup=get_menu_buttons()
                                )

                            # Удаление файла после отправки
                            os.remove(file_path)
                        except Exception as e:
                            logger.error(f"Ошибка при отправке файла: {e}")
                            await update.message.reply_text('Не удалось отправить файл. Попробуйте позже.', reply_markup=get_menu_buttons())
                    else:
                        await update.message.reply_text('Не удалось создать файл с ответом.', reply_markup=get_menu_buttons())
            else:
                await update.message.reply_text('Что-то пошло не так. Мяу :(', reply_markup=get_menu_buttons())
        else:
            logger.error(f"Непредвиденный статус ответа API: {response.status_code}")
            await update.message.reply_text('API вернул непредвиденный ответ. Попробуй позже.', reply_markup=get_menu_buttons())

# Обработчик кнопки "Меню"
async def show_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """
    Отправляет пользователю кнопки меню.
    """
    menu_text = "ты в главном меню. Выбери действие ниже:"
    
    # Создаем кнопки в меню
    menu_buttons = [
        [InlineKeyboardButton("Сменить модель", callback_data='change_model'),
         InlineKeyboardButton("Продолжить", callback_data='continue_response')]
    ]
    reply_markup = InlineKeyboardMarkup(menu_buttons)

    await update.message.reply_text(menu_text, parse_mode=ParseMode.HTML, reply_markup=reply_markup)

# Обработчик нажатия кнопки "Сменить модель"
async def change_model_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Обрабатывает нажатие кнопки 'Сменить модель'."""
    query = update.callback_query
    await query.answer()

    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await query.edit_message_text('тебе сюда мяукать нельзя.')
        return

    # Отображаем кнопки выбора модели
    await query.edit_message_text('Выбери модель для взаимодействия:', reply_markup=get_model_buttons())

# Обработчик нажатия кнопки "Продолжить"
async def continue_response(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Обрабатывает нажатие кнопки 'Продолжить'."""
    query = update.callback_query
    await query.answer()

    user_id = update.effective_user.id
    if user_id not in AUTHORIZED_USER_IDS:
        await query.edit_message_text('тебе сюда мяукать нельзя.')
        return

    chat_id = update.effective_chat.id
    selected_model_id = model_store.get(chat_id)
    model_info = model_id_to_info.get(selected_model_id)

    if not model_info:
        await query.edit_message_text('Модель не найдена в конфигурации.')
        return

    selected_model_type = model_info.get('type', 'text')

    # Получаем режим пользователя
    mode = user_mode_store.get(user_id, 'message')

    if selected_model_type in ['text', 'multimodal']:
        # Добавление сообщения пользователя для продолжения
        context_store[chat_id][selected_model_id].append({"role": "user", "content": "продолжи."})

        # Подготовка данных для API с учетом контекста
        data = {
            "model": selected_model_id,
            "messages": context_store[chat_id][selected_model_id]
        }
        api_url = API_URL_TEXT

        try:
            # Отправка запроса в API
            response = requests.post(api_url, headers=API_HEADERS, json=data)
            response.raise_for_status()
        except requests.exceptions.RequestException as e:
            logger.error(f"Ошибка запроса к API: {e}")
            await query.message.reply_text('API не отвечает или произошла ошибка при обработке запроса. Попробуй позже.', reply_markup=get_menu_buttons())
            return

        if response.status_code == 200:
            response_data = response.json()
            logger.info(f"Ответ API: {response_data}")

            # Проверка структуры ответа для текстовых моделей
            if "choices" in response_data and len(response_data["choices"]) > 0:
                answer = response_data["choices"][0].get('message', {}).get('content', 'Что-то пошло не так. Мяу :(')
                
                if mode == 'message':
                    # Преобразование Markdown в HTML
                    answer_html = markdown_to_html(answer)
                    
                    # Добавление ответа ассистента в контекст
                    context_store[chat_id][selected_model_id].append({"role": "assistant", "content": answer})
                    
                    # Разбивка длинного ответа на части и отправка
                    messages = split_message(answer_html, MAX_MESSAGE_LENGTH)
                    for msg in messages:
                        await query.message.reply_text(msg, reply_markup=get_menu_buttons())
                elif mode == 'file':
                    # Сохранение ответа в .md файл
                    file_path = save_md_file(answer, user_id)
                    if file_path:
                        # Отправка файла пользователю
                        try:
                            with open(file_path, 'rb') as f:
                                await query.message.reply_document(
                                    document=InputFile(f),
                                    filename=os.path.basename(file_path),
                                    caption="твой ваш ответ модели в формате Markdown.",
                                    reply_markup=get_menu_buttons()
                                )
                            
                            # Удаление файла после отправки
                            os.remove(file_path)
                        except Exception as e:
                            logger.error(f"Ошибка при отправке файла: {e}")
                            await query.message.reply_text('Не удалось отправить файл. Попробуйте позже.', reply_markup=get_menu_buttons())
                    else:
                        await query.message.reply_text('Не удалось создать файл с ответом.', reply_markup=get_menu_buttons())
            else:
                await query.message.reply_text('Что-то пошло не так. Мяу :(', reply_markup=get_menu_buttons())
        else:
            logger.error(f"Непредвиденный статус ответа API: {response.status_code}")
            await query.message.reply_text('API вернул непредвиденный ответ. Попробуй позже.', reply_markup=get_menu_buttons())
    elif selected_model_type == 'image':
        await query.message.reply_text('кнопка "Продолжить" не поддерживается для выбранной модели.', reply_markup=get_menu_buttons())
    else:
        await query.message.reply_text('Неизвестный тип модели.', reply_markup=get_menu_buttons())

# Обработчик ошибок
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Ловит все необработанные ошибки."""
    logger.error(msg="Exception while handling an update:", exc_info=context.error)
    if isinstance(update, Update) and update.effective_user:
        try:
            await update.effective_message.reply_text('Произошла ошибка. Пожалуйста, попробуйте позже.', reply_markup=get_menu_buttons())
        except TelegramError as e:
            logger.error(f"Не удалось отправить сообщение об ошибке пользователю: {e}")

# Основная функция запуска бота
def main():
    # Загрузка конфигурации уже произведена выше
    TELEGRAM_TOKEN = config.get('telegram_token')
    if not TELEGRAM_TOKEN:
        logger.error("токен бота не указан в config.json.")
        exit(1)
    
    # Создание экземпляра приложения с установкой ParseMode.HTML по умолчанию
    application = ApplicationBuilder().token(TELEGRAM_TOKEN).defaults(Defaults(parse_mode=ParseMode.HTML)).build()

    # Обработчики команд
    application.add_handler(CommandHandler("start", start))
    application.add_handler(CommandHandler("set_mode", set_mode))
    application.add_handler(CommandHandler("get_mode", get_mode))
    application.add_handler(CommandHandler("instructions", instructions))
    
    # Специфичные обработчики CallbackQuery сначала
    application.add_handler(CallbackQueryHandler(change_model_callback, pattern='^change_model$'))
    application.add_handler(CallbackQueryHandler(continue_response, pattern='^continue_response$'))
    
    # Общий обработчик для выбора модели (модельные ID)
    application.add_handler(CallbackQueryHandler(select_model, pattern='^[a-zA-Z0-9_-]+$'))
    
    # Обработчики сообщений
    # Обработка текстовых сообщений
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text))
    
    # Обработка фотографий
    application.add_handler(MessageHandler(filters.PHOTO, handle_photo))
    
    # Обработка документов (файлов)
    application.add_handler(MessageHandler(filters.Document.ALL, handle_document))
    
    # Обработчик ошибок
    application.add_error_handler(error_handler)

    # Запуск бота
    application.run_polling()

if __name__ == '__main__':
    main()