Вот код
import html

from aiogram import Bot, Dispatcher, types
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InputFile
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import StatesGroup, State
from aiogram.fsm.storage.memory import MemoryStorage
import asyncio
import json
import os
import google.generativeai as genai
import random
import time
import requests
import aiofiles  # Импортируйте aiofiles
import logging  # Импорт для логирования
from gradio_client import Client  # Импорт для ChatGPT4
from aiogram.enums import ParseMode
from html import escape


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

# Ваши API ключи
TELEGRAM_TOKEN = '<TRUFFLEHOG_REDACTED_TELEGRAMBOTTOKEN>'  # Замените на ваш токен!
API_KEYS_FILE = 'api_keys.json'
LOG_FILE = 'all_messages.json'

# Массив фраз, которые будут появляться перед ответом
processing_phrases = [
    "Нейросеть размышляет...",
    "Идет обработка запроса...",
    "Пожалуйста, подождите...",
    "Генерируется ответ...",
    "Нейросеть колдует над вашим запросом..."
]

# Объявляем always_photo_users как пустой словарь
always_photo_users = {}

lock = asyncio.Lock()  # Инициализируйте lock

# Создание экземпляра бота и диспетчера с состоянием
bot = Bot(token=TELEGRAM_TOKEN)
dp = Dispatcher(storage=MemoryStorage())

# Инициализация словарей для хранения данных пользователя
user_data = {}
user_states = {}


# Определяем состояние пользователя
class Form(StatesGroup):
    waiting_for_api_key = State()
    waiting_for_model_selection = State()
    waiting_for_prompt = State()  # Состояние для ожидания промта после отправки файла


# --- Функции работы с файлами ---

# Загрузка API ключей из файла
def load_api_keys():
    print(f"Пытаюсь загрузить ключи из файла: {API_KEYS_FILE}")  # Отладочная информация
    try:
        with open(API_KEYS_FILE, 'r') as file:
            print("Файл успешно открыт")  # Отладочная информация
            data = json.load(file)
            print(f"Данные из файла: {data}")  # Отладочная информация
            if isinstance(data, dict):
                return data
            else:
                print(f"Файл {API_KEYS_FILE} имеет неверный формат. Будет создан новый файл.")
                return {}
    except FileNotFoundError:
        print(f"Файл {API_KEYS_FILE} не найден. Будет создан новый файл.")
        with open(API_KEYS_FILE, 'w') as file:
            json.dump({}, file)
        return {}
    except (json.JSONDecodeError, IOError) as e:
        print(f"Ошибка при загрузке файла: {e}")
        return {}


# Сохранение API ключей в файл
def save_api_keys(api_keys):
    try:
        with open(API_KEYS_FILE, 'w') as file:
            json.dump(api_keys, file, indent=4)
        print(f"Файл {API_KEYS_FILE} успешно обновлён.")
    except IOError as e:
        print(f"Ошибка при сохранении ключей в файл: {e}")


# Запись сообщений и ответов в JSON файл
async def log_user_message(user_id, message_text, response_text, is_file=False):
    if is_file:
        log_entry = {
            "message": message_text,
            "response": response_text
        }
    else:
        log_entry = {
            "message": message_text,
            "response": response_text
        }

    async with lock:  # Блокируем доступ к файлу
        try:
            async with aiofiles.open(LOG_FILE, 'r+', encoding='utf-8') as file:
                try:
                    data = json.loads(await file.read())
                except json.JSONDecodeError:
                    print("Ошибка декодирования JSON в файле all_messages.json. Файл будет очищен.")
                    data = {}

                # --- ПРОВЕРЯЕМ НАЛИЧИЕ КЛЮЧА ДО ПРЕОБРАЗОВАНИЯ В INT ---
                if user_id not in data:
                    user_id = int(user_id)  # Преобразуем user_id в int, только если ключа нет
                    data[user_id] = []  # Создаем пустой список

                data[user_id].append(log_entry)  # Добавляем запись

                await file.seek(0)
                await file.truncate()  # Очищаем файл перед записью
                await file.write(json.dumps(data, indent=4, ensure_ascii=False))

        except FileNotFoundError:
            # --- ЕСЛИ ФАЙЛ НЕ НАЙДЕН, СОЗДАЕМ ЕГО ---
            async with aiofiles.open(LOG_FILE, 'w', encoding='utf-8') as file:
                data = {int(user_id): [log_entry]}  # Создаем словарь с начальной записью
                await file.write(json.dumps(data, indent=4, ensure_ascii=False))

        except Exception as e:
            print(f"Ошибка при записи в файл журнала: {e}")


# --- Функции работы с Google Gemini ---

# Конфигурация генеративной модели
def configure_model(api_key, model_name="gemini-1.5-flash"):
    if model_name.startswith("gemini"):
        try:
            genai.configure(api_key=api_key)
            generation_config = {
                "temperature": 0.5,
                "top_p": 0.95,
                "top_k": 40,
                "max_output_tokens": 8192,
            }
            model = genai.GenerativeModel(model_name=model_name, generation_config=generation_config)
            return model
        except Exception as e:
            print(f"Ошибка конфигурации модели Gemini: {e}")
            return None
    else:  # Для ChatGPT и других моделей, которые не требуют настройки через genai
        return None


# Проверка ключа API
def test_api_key(api_key):
    try:
        genai.configure(api_key=api_key)
        return True
    except Exception as e:
        print(f"Ошибка при проверке API ключа: {e}")
        return False


# Функция для загрузки файла и генерации ответа (обновленная)
async def process_file_and_text(model, file_path, text, user_id, message):
    try:
        api_keys = load_api_keys()
        api_key = api_keys[user_id]["api_key"]  # Получаем Gemini API ключ пользователя
        genai.configure(api_key=api_key)

        if file_path is not None:
            # Если есть файл, загружаем его в Gemini
            file_to_process = genai.upload_file(path=file_path)
            response = model.generate_content(
                [file_to_process, text],
                request_options={"timeout": 600}
            )
        else:
            response = model.generate_content(
                [text],
                request_options={"timeout": 600}
            )

        response_text = response.text
        response_text = response_text.replace("AI:", "", 1).strip()

        # Логируем сообщение в зависимости от наличия файла
        if file_path:
            await log_user_message(user_id, file_path, response_text, is_file=True)
        else:
            await log_user_message(user_id, text, response_text)

        await send_response(message, response_text)  # Отправляем ответ

    except Exception as e:
        await message.answer(f"Ошибка при обработке файла и текста: {str(e)}")


# --- Функция для ChatGPT-4O (измененная) ---
def ask_chatgpt(message, history=None):
    client = Client("yuntian-deng/ChatGPT4")
    result = client.predict(
        inputs=message,
        top_p=1,
        temperature=1,
        chat_counter=0,
        chatbot=[],
        api_name="/predict"
    )

    if isinstance(result, tuple) and len(result) > 0 and isinstance(result[0], list):
        main_result = result[0]
        if isinstance(main_result, list) and len(main_result) > 0 and isinstance(main_result[0],
                                                                               list) and len(
                main_result[0]) > 1:
            response = main_result[0][1]
            return response
        else:
            return "Ошибка: неожиданный формат главного результата"
    else:
        return "Ошибка: неожиданный формат ответа"

# --- Функция для O1 ---
def ask_o1(message, history=None):
    client = Client("yuntian-deng/o1mini")  # Изменено имя модели
    result = client.predict(
        inputs=message,
        top_p=1,
        temperature=1,
        chat_counter=0,
        chatbot=[],
        api_name="/predict"
    )

    if isinstance(result, tuple) and len(result) > 0 and isinstance(result[0], list):
        main_result = result[0]
        if isinstance(main_result, list) and len(main_result) > 0 and isinstance(main_result[0],
                                                                               list) and len(
                main_result[0]) > 1:
            response = main_result[0][1]
            return response
        else:
            return "Ошибка: неожиданный формат главного результата"
    else:
        return "Ошибка: неожиданный формат ответа"

# --- Обработчики команд ---

# Команда /start для приветствия и запроса API ключа
async def start_handler(message: types.Message, state: FSMContext):
    await message.answer(
        "Добро пожаловать! Для начала работы выберите модель для генерации текста командой /model.\n"
        "Я могу сгенерировать текст по вашему запросу, а ещё могу просматривать фото, видео и голосовые сообщения. Если что-то непонятно, перейдите на сайт: https://clck.ru/3DPujE")


# Обработчик для получения API ключа
async def api_key_handler(message: types.Message, state: FSMContext):
    user_id = str(message.from_user.id)
    api_keys = load_api_keys()
    api_key = message.text.strip()

    print(f"Получен API ключ от пользователя {user_id}: {api_key}")  # Отладочная информация

    if not test_api_key(api_key):
        await message.answer("Неверный API ключ. Пожалуйста, попробуйте снова.")
        return

    api_keys[user_id] = {"api_key": api_key, "model": "gemini-1.5-flash"}
    print(f"API keys перед сохранением: {api_keys}")  # Отладочная информация
    save_api_keys(api_keys)

    await message.answer(
        "Ваш API ключ сохранен.\n"
        "Я могу сгенерировать текст по вашему запросу, а ещё могу просматривать фото, видео и голосовые сообщения. Если что-то непонятно, перейдите на сайт: https://clck.ru/3DPujE")
    await state.clear()  # Сбрасываем состояние


# Команда /apir для изменения API ключа
async def apir_handler(message: types.Message, state: FSMContext):
    await message.answer("Пожалуйста, введите новый API ключ:")
    await state.set_state(Form.waiting_for_api_key)


# Команда /model для выбора модели с инлайн-кнопками
async def model_handler(message: types.Message, state: FSMContext):
    keyboard = InlineKeyboardMarkup(
        inline_keyboard=[
            [InlineKeyboardButton(text="🧠 Gemini 1.5 Pro", callback_data='model_gemini_1_5_pro')],
            [InlineKeyboardButton(text="⚡️ Gemini 1.5 Flash", callback_data='model_gemini_1_5_flash')],
            [InlineKeyboardButton(text="🌍  ChatGPT-4o", callback_data='model_chatgpt-4o')],
            [InlineKeyboardButton(text="🤖 o1 mini", callback_data='model_o1')] # Добавлена кнопка для O1
        ]
    )

    await message.answer("Выберите модель для генерации текста:", reply_markup=keyboard)
    await state.set_state(Form.waiting_for_model_selection)


# Обработчик нажатий на инлайн-кнопки
async def process_model_selection(callback_query: types.CallbackQuery, state: FSMContext):
    model = callback_query.data
    user_id = str(callback_query.from_user.id)
    api_keys = load_api_keys()

    # --- ДОБАВЛЯЕМ ПРОВЕРКУ НАЛИЧИЯ ЗАПИСИ ДЛЯ ПОЛЬЗОВАТЕЛЯ ---
    if user_id not in api_keys:
        api_keys[user_id] = {}  # Создаем пустой словарь для пользователя

    if model.startswith('model_gemini'):
        # --- ЗАПРОС API КЛЮЧА, ЕСЛИ МОДЕЛЬ GEMINI ---
        if "api_key" not in api_keys[user_id]:
            if model == 'model_gemini_1_5_pro':
                selected_model = "gemini-1.5-pro"
                user_moder = "Gemini 1.5 Pro"
                await callback_query.message.answer(
                    f"Модель для генерации текста установлена на {user_moder}. Вы можете теперь отправлять текст для генерации.")
                model_description = "Внимание! У вас следующие ограничения:\n" \
                                     "2 об/мин (запросы в минуту)\n" \
                                     "32 000 TPM (токенов в минуту)\n" \
                                     "50 RPD (запросов в день)"
            elif model == 'model_gemini_1_5_flash':
                selected_model = "gemini-1.5-flash"
                user_moder = "Gemini 1.5 Flash"
                await callback_query.message.answer(
                    f"Модель для генерации текста установлена на {user_moder}. Вы можете теперь отправлять текст для генерации.")
                model_description = "Внимание! У вас следующие ограничения:\n" \
                                     "15 об/мин (запросы в минуту)\n" \
                                     "1 миллион TPM (токенов в минуту)\n" \
                                     "1500 RPD (запросов в день)"
            else:
                await callback_query.answer("Неизвестная модель")
                return

            # --- ВЫВОДИМ ОПИСАНИЕ МОДЕЛИ ПЕРЕД ЗАПРОСОМ API КЛЮЧА ---
            await callback_query.message.answer(model_description)

            await callback_query.message.answer(
                "Пожалуйста, введите ваш API ключ Google для начала работы:\n"
                "Получите ключ здесь: https://aistudio.google.com/app/apikey")
            await state.set_state(Form.waiting_for_api_key)  # Устанавливаем состояние ожидания API ключа
            return  # Прерываем выполнение обработчика, чтобы дождаться API ключа

        # --- ДАЛЬНЕЙШАЯ ОБРАБОТКА, ЕСЛИ КЛЮЧ УЖЕ ЕСТЬ ---
        if model == 'model_gemini_1_5_pro':
            selected_model = "gemini-1.5-pro"
            user_moder = "Gemini 1.5 Pro"
            model_description = "Внимание! У вас следующие ограничения:\n" \
                                 "2 об/мин (запросы в минуту)\n" \
                                 "32 000 TPM (токенов в минуту)\n" \
                                 "50 RPD (запросов в день)"
        elif model == 'model_gemini_1_5_flash':
            selected_model = "gemini-1.5-flash"
            user_moder = "Gemini 1.5 Flash"
            model_description = "Внимание! У вас следующие ограничения:\n" \
                                 "15 об/мин (запросы в минуту)\n" \
                                 "1 миллион TPM (токенов в минуту)\n" \
                                 "1500 RPD (запросов в день)"
        else:
            await callback_query.answer("Неизвестная модель")
            return

        api_keys[user_id]["model"] = selected_model
        save_api_keys(api_keys)

        await callback_query.message.answer(
            f"Модель для генерации текста установлена на {user_moder}. Вы можете теперь отправлять текст для генерации.")

    elif model == 'model_chatgpt-4o':
        api_keys[user_id]["model"] = "chatgpt-4o"  # Устанавливаем модель на chatgpt-4o
        save_api_keys(api_keys)
        await callback_query.message.answer(
            "Модель для генерации текста установлена на ChatGPT-4o. Вы можете теперь отправлять текст для генерации.")
        await callback_query.message.answer("Внимание ChatGPT-4o находиться в бета режиме. Возможны баги")

    elif model == 'model_o1': # Обработка выбора модели O1
        api_keys[user_id]["model"] = "o1"
        save_api_keys(api_keys)
        await callback_query.message.answer(
            "Модель для генерации текста установлена на o1 mini. Вы можете теперь отправлять текст для генерации.")
        await callback_query.message.answer("Внимание o1 mini находиться в бета режиме. Возможны баги")


    else:
        await callback_query.answer("Неизвестная модель")

    await callback_query.message.edit_reply_markup()  # Убираем inline-кнопки


# Команда /clear для очистки чата
async def clear_chat_handler(message: types.Message):
    user_id = str(message.from_user.id)
    try:
        async with aiofiles.open(LOG_FILE, 'r+', encoding='utf-8') as file:
            try:
                data = json.loads(await file.read())
            except json.JSONDecodeError:
                print("Ошибка декодирования JSON в файле all_messages.json. Файл будет очищен.")
                data = {}

            if user_id in data:
                del data[user_id]  # Удаляем историю только текущего пользователя

                await file.seek(0)
                await file.truncate()  # Очищаем файл перед записью
                await file.write(json.dumps(data, indent=4, ensure_ascii=False))
                await message.answer("Ваша история сообщений очищена.")
            else:
                await message.answer("Ваша история сообщений пуста.")
    except FileNotFoundError:
        await message.answer("Файл истории сообщений не найден.")
    except (json.JSONDecodeError, IOError) as e:
        print(f"Ошибка при очистке файла журнала: {e}")
        await message.answer("Произошла ошибка при очистке истории сообщений.")


# Команда /meny для вывода настроек
async def meny_handler(message: types.Message):
    user_id = str(message.from_user.id)
    api_keys = load_api_keys()
    user_moder = api_keys[user_id].get("model", "Не выбрана")

    if user_id not in api_keys:
        await message.answer("Ваш API ключ не найден. Пожалуйста, используйте команду /start для добавления ключа.")
        return

    api_key = api_keys[user_id].get("api_key", "Не установлен")  # Используем .get() для избежания KeyError

    # --- Экранируем символы подчеркивания в API ключе ---
    api_key = api_key.replace("_", "\_")

    selected_model = api_keys[user_id].get("model", "Не выбрана")

    try:
        with open(LOG_FILE, 'r', encoding='utf-8') as file:
            data = json.load(file)
            message_count = len(data.get(user_id, []))  # Используем .get() для избежания KeyError
    except FileNotFoundError:
        message_count = 0

    # --- Дополнительная информация для режима "Always photo" ---
    always_photo_status = "Включен" if user_id in always_photo_users else "Выключен"
    always_photo_info = ""
    if user_id in always_photo_users:
        file_path = always_photo_users[user_id].get("file_path")
        last_used_time = always_photo_users[user_id].get("last_used_time")
        if file_path:
            always_photo_info += f"\n - Путь к файлу: {file_path}"
        if last_used_time:
            time_diff = time.time() - last_used_time
            minutes_ago = int(time_diff // 60)
            always_photo_info += f"\n - Фото использовалось {minutes_ago} минут назад"

    response_text = (
        f"**Меню настроек:**\n\n"
        f"**Ваш ID:** {user_id}\n"
        # --- Используем экранированный API ключ ---
        f"**API ключ:** {api_key}\n"
        f"**Выбранная модель:** {user_moder}\n"
        f"**Количество сообщений в истории:** {message_count}\n\n"
        f"**Режим 'Always photo':** {always_photo_status}{always_photo_info}\n\n"
        f"**Доступные команды:**\n"
        f"/start - начать работу с ботом\n"
        f"/model - выбрать модель для генерации текста\n"
        f"/apir - изменить API ключ\n"
        f"/clear - очистить историю сообщений\n"
        f"/always - включить/выключить режим 'Always photo'\n"
        f"/FAQ - краткое содержание сайта"
    )
    await message.answer(response_text, parse_mode="Markdown")


# Команда /FAQ для вывода краткого содержания сайта
async def faq_handler(message: types.Message):
    faq_text = """
<b>Нейросеть Gemini - ваш интеллектуальный помощник в Telegram!</b>

<b>Что умеет бот?</b>

* <b>Генерация текста:</b> 
    * Пишет тексты на любую тему по вашему запросу.
    * Отвечает на вопросы, используя информацию из интернета.
    * Создает истории, стихи, сценарии и многое другое.
* <b>Анализ изображений:</b> 
    * Описывает содержимое фотографий.
    * Отвечает на вопросы, связанные с изображениями.
* <b>Обработка голосовых сообщений:</b> 
    * Преобразует голосовые сообщения в текст.
    * Отвечает на вопросы, заданные голосом.

<b>Дополнительные возможности:</b>

* <b>Поддержка различных форматов:</b> Работает с фото, видео и голосовыми сообщениями.
* <b>Режим "Always photo":</b> Позволяет прикреплять фото к каждому запросу.
* <b>История чата:</b> Использует историю для более контекстных ответов.

<b>Как начать?</b>

1. Нажмите кнопку "Start" ниже.
2. Введите команду /start в боте.
3. Следуйте инструкциям бота для настройки API ключа.

<b>Остались вопросы?</b>

Присоединяйтесь к нашему Telegram-каналу [ссылка на канал] для получения обновлений и информации.
"""
    await message.answer(faq_text, parse_mode="HTML")


# --- Функции для отправки сообщений ---

# Функция для отправки длинного ответа в виде файла
async def send_long_message_as_file(chat_id, text, bot):
    file_path = 'long_message.txt'
    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(text)

    input_file = InputFile(file_path)
    await bot.send_document(chat_id=chat_id, document=input_file, caption="Ответ от модели (файл)")

    os.remove(file_path)


# Функция для балансировки HTML тегов
def balance_html_tags(text):
    while text.count("<b>") > text.count("</b>"):
        text += "</b>"
    while text.count("<code>") > text.count("</code>"):
        text += "</code>"
    return text


# Функция для отправки ответа с проверкой режима "always photo"
async def send_response(message, response):
    """Отправляет ответ пользователю с поддержкой неограниченного форматирования Markdown в HTML.

    Args:
        message (types.Message): Объект сообщения Telegram.
        response (str): Текст ответа с разметкой Markdown.
    """
    user_id = str(message.from_user.id)

    try:
        # Экранирование специальных символов, которые не являются частью Markdown
        # Это предотвращает некорректное преобразование символов в теги
        response = html.escape(response)

        # Преобразование Markdown в HTML
        html_response = markdown.markdown(response, extensions=['extra', 'codehilite'])

        # Удаление лишних переносов строк и пробелов
        html_response = html_response.strip()

        # Балансировка HTML тегов (добавление закрывающих тегов, если необходимо)
        # Хотя библиотека markdown должна корректно закрывать теги, дополнительная проверка может помочь
        tag_pairs = {
            "<strong>": "</strong>",
            "<em>": "</em>",
            "<code>": "</code>",
            "<pre>": "</pre>",
            "<blockquote>": "</blockquote>",
            "<ul>": "</ul>",
            "<ol>": "</ol>",
            "<li>": "</li>",
            "<h1>": "</h1>",
            "<h2>": "</h2>",
            "<h3>": "</h3>",
            # Добавьте другие теги по необходимости
        }

        for open_tag, close_tag in tag_pairs.items():
            if html_response.count(open_tag) > html_response.count(close_tag):
                html_response += close_tag

        # Удаление пустых или неподдерживаемых тегов
        # Telegram поддерживает ограниченный набор HTML-тегов
        supported_tags = [
            'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike',
            'del', 'a', 'code', 'pre'
        ]

        # Регулярное выражение для поиска всех тегов
        all_tags = re.findall(r'</?[^>]+>', html_response)
        for tag in all_tags:
            tag_name = re.findall(r'</?(\w+)', tag)
            if tag_name and tag_name[0].lower() not in supported_tags:
                # Удаляем неподдерживаемый тег
                html_response = html_response.replace(tag, '')

        # Итоговая проверка длины сообщения
        if user_id in always_photo_users:
            try:
                await message.answer("Режим 'Always photo' включен.")
            except Exception as e:
                logging.error(f"Не удалось отправить уведомление о режиме 'Always photo': {e}")

        if len(html_response) > 4096:
            await send_long_message_as_file(message.chat.id, html_response, bot)
        else:
            try:
                await message.answer(html_response, parse_mode="HTML")
            except Exception as e:
                await message.answer("Не удалось обработать форматирование сообщения.")
                logging.error(f"Ошибка при отправке сообщения: {e}")
                logging.debug(f"Текст сообщения: {html_response}")



# --- Обработка сообщений ---

# Новый обработчик для команды /always photo
async def always_photo_handler(message: types.Message):
    user_id = str(message.from_user.id)
    print(f"always_photo_handler вызван для пользователя {user_id}")  # Отладочная информация

    if user_id in always_photo_users:
        # Удаляем фото с сервера, если оно существует
        if "file_path" in always_photo_users[user_id] and os.path.exists(
                always_photo_users[user_id]["file_path"]):
            os.remove(always_photo_users[user_id]["file_path"])

        del always_photo_users[user_id]
        await message.answer("Режим 'Always photo' деактивирован.")
    else:
        always_photo_users[user_id] = {}  # Создаем пустой словарь для хранения данных
        await message.answer(
            "Режим 'Always photo' активирован. Последнее фото или голосовое сообщение будет включено во все будущие запросы.")


async def process_photo_message(message: types.Message, state: FSMContext):
    """Обрабатывает фото сообщение, сохраняет фото и устанавливает состояние ожидания промта."""
    print(f"process_photo_message вызван для пользователя {message.from_user.id}")  # Отладочная информация

    user_id = str(message.from_user.id)
    try:
        file_id = message.photo[-1].file_id
        file = await bot.get_file(file_id)
        file_path = f"photo_{file_id}.jpg"
        await bot.download_file(file.file_path, file_path)

        print(f"always_photo_users: {always_photo_users}")  # Отладочная информация

        # Добавляем пользователя в always_photo_users ТОЛЬКО если режим Always Photo включен
        if user_id in always_photo_users:
            always_photo_users[user_id] = {
                "file_path": file_path,
                "last_used_time": time.time()
            }
            print(f"always_photo_users после добавления: {always_photo_users}")  # Отладочная информация

        # --- СОХРАНЯЕМ ИНФОРМАЦИЮ О ФОТО В user_data ---
        user_data[user_id] = {"photo_file_path": file_path}
        print(f"user_data: {user_data}")  # Отладочная информация

        logging.info(f"User {user_id}: photo processed, setting state to waiting_for_prompt") # Логирование
        await message.answer("Фото получено! Что вы хотите узнать о нем?")
        await state.set_state(Form.waiting_for_prompt)

    except Exception as e:
        logging.error(f"User {user_id}: error processing photo: {str(e)}") # Логирование ошибки
        await message.answer(f"Ошибка при обработке фото: {str(e)}")


async def process_voice_message(message: types.Message, state: FSMContext):
    """Обрабатывает голосовое сообщение, сохраняет его и запрашивает промт."""
    user_id = str(message.from_user.id)
    try:
        file_id = message.voice.file_id
        file = await bot.get_file(file_id)
        file_path = f"voice_{file_id}.ogg"
        await bot.download_file(file.file_path, file_path)

        # Сохраняем информацию о голосовом сообщении в user_data вместо always_photo_users
        user_data[user_id] = {
            "audio_file_path": file_path,
            "message": message
        }

        await message.answer("Голосовое сообщение получено! Что вы хотите узнать о нем?")
        await state.set_state(Form.waiting_for_prompt)

    except Exception as e:
        await message.answer(f"Ошибка при обработке голосового сообщения: {str(e)}")


async def process_video_message(model, message: types.Message, state: FSMContext):
    """Обрабатывает видео, загружая его в Gemini API через File API."""
    user_id = str(message.from_user.id)
    try:
        file_id = message.video.file_id
        file = await bot.get_file(file_id)
        file_path = f"video_{file_id}.mp4"
        await bot.download_file(file.file_path, file_path)

        processing_message = await message.answer(random.choice(processing_phrases))

        print(f"Uploading video...")
        video_file = genai.upload_file(path=file_path)
        print(f"Completed upload: {video_file.uri}")

        while video_file.state.name == "PROCESSING":
            print('.', end='')
            time.sleep(1)
            video_file = genai.get_file(video_file.name)

        if video_file.state.name == "FAILED":
            raise ValueError(video_file.state.name)

        os.remove(file_path)

        user_data[user_id] = {
            "video_file": video_file,
            "message": message
        }

        await bot.send_message(user_id, "Видео загружено и обработано. Теперь напишите, что вы хотите узнать о нём.")
        await state.set_state(Form.waiting_for_prompt)

        await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)

    except Exception as e:
        if os.path.exists(file_path):
            os.remove(file_path)
        if user_id in user_data and "file_path" in user_data[user_id]:
            del user_data[user_id]
        await message.answer(f"Ошибка при обработке видео: {str(e)}")


# --- Основной обработчик сообщений ---

async def process_file_and_text(model, file_path, text, user_id, message):
    try:
        api_keys = load_api_keys()
        api_key = api_keys[user_id]["api_key"]  # Получаем Gemini API ключ пользователя
        genai.configure(api_key=api_key)

        if file_path is not None:
            # Если есть файл, загружаем его в Gemini
            file_to_process = genai.upload_file(path=file_path)
            response = model.generate_content(
                [file_to_process, text],
                request_options={"timeout": 600}
            )
        else:
            response = model.generate_content(
                [text],
                request_options={"timeout": 600}
            )

        response_text = response.text
        response_text = response_text.replace("AI:", "", 1).strip()

        # Логируем сообщение в зависимости от наличия файла
        if file_path:
            await log_user_message(user_id, file_path, response_text, is_file=True)
        else:
            await log_user_message(user_id, text, response_text)

        await send_response(message, response_text)  # Отправляем ответ

    except Exception as e:
        await message.answer(f"Ошибка при обработке файла и текста: {str(e)}")


async def handle_message(message: types.Message, state: FSMContext):
    if message is None:
        print("Ошибка: message равен None в handle_message")
        return

    user_id = str(message.from_user.id)
    api_keys = load_api_keys()

    if user_id not in api_keys:
        # --- ЕСЛИ ПОЛЬЗОВАТЕЛЬ НЕ ВЫБИРАЛ МОДЕЛЬ, ЗАПРОСИТЬ ВЫБОР ---
        await message.answer("Пожалуйста, выберите модель для генерации текста командой /model.")
        return

    model_name = api_keys[user_id]["model"]

    # --- ПРОВЕРКА API КЛЮЧА ТОЛЬКО ДЛЯ МОДЕЛЕЙ GEMINI ---
    if model_name.startswith("gemini") and "api_key" not in api_keys[user_id]:
        await message.answer(
            "API ключ не установлен. Пожалуйста, выберите модель Gemini и введите API ключ."
        )
        return

    # --- ЕСЛИ МОДЕЛЬ GEMINI, ПОЛУЧАЕМ API КЛЮЧ ---
    if model_name.startswith("gemini"):
        api_key = api_keys[user_id]["api_key"]

    # --- Загрузка истории для ChatGPT-4O и Gemini ---
    history = []
    if os.path.exists(LOG_FILE):
        try:
            with open(LOG_FILE, 'r', encoding='utf-8') as file:
                data = json.load(file)
                if isinstance(data, dict) and user_id in data:
                    # --- Формат истории одинаковый для ChatGPT-4O и Gemini ---
                    history = data[user_id]
        except UnicodeDecodeError:
            try:
                with open(LOG_FILE, 'r', encoding='cp1251') as file:
                    data = json.load(file)
                    if isinstance(data, dict) and user_id in data:
                        history = data[user_id]
            except UnicodeDecodeError:
                print("Ошибка: не удалось декодировать файл истории сообщений.")

    # --- Обработка ChatGPT-4o ---
    if api_keys[user_id]["model"] == "chatgpt-4o":
        processing_message = await message.answer(random.choice(processing_phrases))
        try:
            # --- Формируем контекст с историей перед сообщением пользователя ---
            context = "".join(
                [f"User: {entry['message']}\nAI: {entry['response']}\n"
                 for entry in history[-5:]]) + f"User: {message.text}\n"

            # --- Передаем контекст в ask_chatgpt ---
            response_text = ask_chatgpt(context)
            await log_user_message(user_id, message.text, response_text)
            await send_response(message, response_text)
            await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)

        except Exception as e:
            await message.answer(f"Ошибка при обработке сообщения: {str(e)}")
        return

    # --- Обработка o1 ---
    elif api_keys[user_id]["model"] == "o1":
        processing_message = await message.answer(random.choice(processing_phrases))
        try:
            # --- Формируем контекст с историей перед сообщением пользователя ---
            context = "".join(
                [f"User: {entry['message']}\nAI: {entry['response']}\n"
                 for entry in history[-5:]]) + f"User: {message.text}\n"

            # --- Передаем контекст в ask_o1 ---
            response_text = ask_o1(context)
            await log_user_message(user_id, message.text, response_text)
            await send_response(message, response_text)
            await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)

        except Exception as e:
            await message.answer(f"Ошибка при обработке сообщения: {str(e)}")
        return

    # --- Обработка Gemini ---
    processing_message = await message.answer(random.choice(processing_phrases))
    try:
        if message.content_type == types.ContentType.TEXT:
            current_state = await state.get_state()
            print(f"Состояние: {current_state}")  # Отладочная информация
            print(f"user_data: {user_data}")  # Отладочная информация

            if current_state == Form.waiting_for_prompt:
                # Пользователь отправил промпт после отправки файла
                prompt = message.text

                # --- ОБРАБОТКА ВИДЕО ---
                if user_id in user_data and "video_file" in user_data[user_id]:
                    video_file = user_data[user_id]["video_file"]
                    original_message = user_data[user_id]["message"]

                    # --- ИСПОЛЬЗУЕМ video_file И prompt ДЛЯ ГЕНЕРАЦИИ ОТВЕТА ---
                    model = configure_model(api_key=api_key, model_name=model_name)
                    if model is None:
                        await message.answer(
                            "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                        )
                        return
                    response = model.generate_content(
                        [video_file, prompt],
                        request_options={"timeout": 600}
                    )
                    response_text = response.text
                    response_text = response_text.replace("AI:", "", 1).strip()

                    if original_message and original_message.video:
                        await log_user_message(user_id, original_message.video.file_id, response_text, is_file=True)
                    else:
                        print("Ошибка: original_message не является видео сообщением или не содержит видео.")

                    await send_response(original_message, response_text)

                    # --- СБРАСЫВАЕМ СОСТОЯНИЕ ---
                    await state.clear()
                    del user_data[user_id]

                # --- ОБРАБОТКА АУДИО ---
                elif user_id in user_data and "audio_file_path" in user_data[user_id]:
                    audio_file_path = user_data[user_id]["audio_file_path"]
                    original_message = user_data[user_id].get("message")

                    # Загружаем аудио в Gemini
                    genai.configure(api_key=api_key)
                    audio_file = genai.upload_file(path=audio_file_path)

                    # Используем audio_file и prompt для генерации ответа
                    model = configure_model(api_key=api_key, model_name=model_name)
                    if model is None:
                        await message.answer(
                            "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                        )
                        return

                    response = model.generate_content(
                        [audio_file, prompt],
                        request_options={"timeout": 600}
                    )
                    response_text = response.text
                    response_text = response_text.replace("AI:", "", 1).strip()

                    if original_message and original_message.voice:
                        await log_user_message(user_id, original_message.voice.file_id, response_text, is_file=True)
                    else:
                        print("Ошибка: original_message не является голосовым сообщением.")

                    await send_response(original_message, response_text)

                    # Сбрасываем состояние
                    await state.clear()
                    del user_data[user_id]

                # --- ОБРАБОТКА ФОТО ---
                elif user_id in user_data and "photo_file_path" in user_data[user_id]:
                    photo_file_path = user_data[user_id]["photo_file_path"]

                    # --- ИНИЦИАЛИЗИРУЕМ model ---
                    model = configure_model(api_key=api_key, model_name=model_name)
                    if model is None:
                        await message.answer(
                            "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                        )
                        return
                    # ---

                    await process_file_and_text(model, photo_file_path, prompt, user_id, message)
                    await state.clear()
                else:
                    await message.answer("Нет загруженного файла для обработки с этим промтом.")

                # --- УДАЛЕНИЕ ФАЙЛОВ ПОСЛЕ ОБРАБОТКИ ПРОМПТА ---
                if user_id in user_data:
                    if "audio_file_path" in user_data[user_id]:
                        audio_file_path = user_data[user_id]["audio_file_path"]
                        os.remove(audio_file_path)
                        del user_data[user_id]["audio_file_path"]
                    if "photo_file_path" in user_data[user_id]:
                        photo_file_path = user_data[user_id]["photo_file_path"]
                        os.remove(photo_file_path)
                        del user_data[user_id]["photo_file_path"]

            else:
                # Обработка обычного текстового запроса (с always photo и историей)
                context = "".join(
                    [f"User: {entry['message']}\nAI: {entry['response']}\n"
                     for entry in history[-5:]]) + f"User: {message.text}\nAI:"

                if user_id in always_photo_users:
                    last_used_time = always_photo_users[user_id].get("last_used_time")
                    if last_used_time:
                        time_diff = time.time() - last_used_time
                        if time_diff > 1800:
                            file_path = always_photo_users[user_id]["file_path"]
                            if os.path.exists(file_path):
                                os.remove(file_path)
                            del always_photo_users[user_id]
                            await message.answer(
                                "Фото в режиме 'Always photo' не использовалось более 30 минут и было удалено.")
                        else:
                            always_photo_users[user_id]["last_used_time"] = time.time()

                            # --- ИНИЦИАЛИЗИРУЕМ model ---
                            model = configure_model(api_key=api_key, model_name=model_name)
                            if model is None:
                                await message.answer(
                                    "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                                )
                                return
                            # ---

                            await process_file_and_text(model, always_photo_users[user_id]["file_path"], message.text,
                                                        user_id, message)
                    else:
                        always_photo_users[user_id]["last_used_time"] = time.time()

                        # --- ИНИЦИАЛИЗИРУЕМ model ---
                        model = configure_model(api_key=api_key, model_name=model_name)
                        if model is None:
                            await message.answer(
                                "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                            )
                            return
                        # ---

                        await process_file_and_text(model, always_photo_users[user_id]["file_path"], message.text,
                                                    user_id, message)
                else:
                    model = configure_model(api_key=api_key, model_name=model_name)
                    if model is None:
                        await message.answer(
                            "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                        )
                        return

                    response = model.generate_content(
                        [context],
                        request_options={"timeout": 600}
                    )
                    response_text = response.text
                    response_text = response_text.replace("AI:", "", 1).strip()
                    await log_user_message(user_id, message.text, response_text)
                    await send_response(message, response_text)

                await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)

        elif message.content_type in [types.ContentType.VOICE, types.ContentType.PHOTO,
                                      types.ContentType.VIDEO]:
            # --- ОБРАБОТКА ФАЙЛОВ (аудио, фото, видео) ---

            # Если режим Always Photo включен, не вызываем обработчики файлов повторно
            if user_id in always_photo_users and current_state != Form.waiting_for_prompt:
                await message.answer("Режим 'Always Photo' уже включен. Отправьте текстовый запрос.")
                return

            if message.content_type == types.ContentType.VOICE:
                await process_voice_message(message, state)

            elif message.content_type == types.ContentType.PHOTO:
                await process_photo_message(message, state)

            elif message.content_type == types.ContentType.VIDEO:
                model = configure_model(api_key=api_key, model_name=model_name)
                if model is None:
                    await message.answer(
                        "Ошибка инициализации модели. Проверьте ваш API ключ и имя модели."
                    )
                    return
                await process_video_message(model, message, state)

        else:
            response = "Этот тип сообщения не поддерживается. Пожалуйста, отправьте текст, фото, голосовое сообщение или видео."
            await send_response(message, response)
            await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)

    except Exception as e:
        await message.answer(f"Ошибка при обработке сообщения: {str(e)}")

# --- Регистрация обработчиков и запуск бота ---

dp.message.register(start_handler, Command("start"))
dp.message.register(apir_handler, Command("apir"))
dp.message.register(model_handler, Command("model"))
dp.message.register(clear_chat_handler, Command("clear"))
dp.message.register(meny_handler, Command("meny"))
dp.message.register(always_photo_handler, Command("always"))
dp.message.register(faq_handler, Command("FAQ"))  # Регистрация обработчика для /FAQ

dp.message.register(api_key_handler, Form.waiting_for_api_key)

# Регистрируем handle_message только для сообщений, которые не являются командами
dp.message.register(handle_message,
                    ~Command(commands=["start", "apir", "model", "clear", "meny", "always", "FAQ"]))

dp.callback_query.register(process_model_selection)


async def main():
    print("Запуск бота...")
    # Увеличиваем таймаут для получения обновлений
    await dp.start_polling(bot, timeout=60)


if __name__ == "__main__":
    asyncio.run(main())