В этом коде есть баг. Когда пользователь генерирует текс бот ему отвечает 2 раза. исправь это
import html
import re
import markdown

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
import logging
from gradio_client import Client
from aiogram.enums import ParseMode
from collections import deque

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

# Ваши API ключи
# Файл с токеном Telegram
TELEGRAM_TOKEN_FILE = 'telegram_token.txt'
API_KEYS_FILE = 'api_keys.json'
LOG_FILE = 'all_messages.json'

# Массив фраз, которые будут появляться перед ответом
processing_phrases = [
    "Нейросеть размышляет...",
    "⌛",
    "📝",
    "💡",
    "Нейросеть колдует над вашим запросом...",
    "🔬",
    "🛷",
    "🔭",
    "🚀",
    "🪖",
    "⌨️",
]

# Функция для загрузки токена Telegram из файла
def load_telegram_token():
    try:
        with open(TELEGRAM_TOKEN_FILE, 'r') as f:
            token = f.read().strip()
            return token
    except FileNotFoundError:
        print(f"Ошибка: Файл с токеном Telegram '{TELEGRAM_TOKEN_FILE}' не найден.")
        return None

# Создание экземпляра бота и диспетчера с состоянием
telegram_token = load_telegram_token()
if telegram_token is None:
    exit(1)

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

# Словарь для хранения времени последнего сообщения от пользователя
last_message_time = {}

# Словарь для хранения пользователей, заблокированных за спам
spam_blocked_users = {}

model_queue = deque()

processing_users = {}  # Глобальный словарь для отслеживания пользователей в обработке

lock = asyncio.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):
    try:
        user_id = int(user_id)
    except ValueError:
        print(f"Ошибка: user_id '{user_id}' не является числом.")
        return

    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())
                    data = {int(k): v for k, v in data.items()}
                except json.JSONDecodeError:
                    print("Ошибка декодирования JSON. Файл будет очищен.")
                    data = {}

            if user_id not in data:
                data[user_id] = []
            data[user_id].append(log_entry)

            async with aiofiles.open(LOG_FILE, 'w', encoding='utf-8') as file:
                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:
                await file.write(json.dumps({user_id: [log_entry]}, indent=4, ensure_ascii=False))

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

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

# Конфигурация генеративной модели
async 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:
        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

# --- Функция для ChatGPT-4O ---
async def ask_chatgpt(message, history=None):
    client = Client("yuntian-deng/ChatGPT4")

    if history:
        context = "".join(
            [f"User: {entry['message']}\nAI: {entry['response']}\n"
             for entry in history]) + f"User: {message}\n"
    else:
        context = f"User: {message}\n"

    result = await asyncio.to_thread(client.predict,
                                    inputs=context,
                                    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 ---
async def ask_o1(message, history=None):
    client = Client("yuntian-deng/o1mini")

    if history:
        context = "".join(
            [f"User: {entry['message']}\nAI: {entry['response']}\n"
             for entry in history]) + f"User: {message}\n"
    else:
        context = f"User: {message}\n"

    result = await asyncio.to_thread(client.predict,
                                    inputs=context,
                                    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 ключа
@dp.message(Command("start"))
async def start_handler(message: types.Message, state: FSMContext):
    user_id = str(message.from_user.id)
    api_keys = load_api_keys()

    if user_id not in api_keys:
        api_keys[user_id] = {"selected_model": "gemini-1.5-flash"}
    elif "selected_model" not in api_keys[user_id]:
        api_keys[user_id]["selected_model"] = "gemini-1.5-flash"

    save_api_keys(api_keys)

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

# Обработчик для получения API ключа
@dp.message(Form.waiting_for_api_key)
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 ключа
@dp.message(Command("apir"))
async def apir_handler(message: types.Message, state: FSMContext):
    await message.answer("Пожалуйста, введите новый API ключ:")
    await state.set_state(Form.waiting_for_api_key)

# Команда /model для выбора модели с инлайн-кнопками
@dp.message(Command("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')]
        ]
    )

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

# Обработчик нажатий на инлайн-кнопки
@dp.callback_query()
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] = {}

    MODEL_DETAILS = {
        'model_gemini_1_5_pro': {
            'name': "gemini-1.5-pro",
            'display_name': "Gemini 1.5 Pro",
            'description': "Внимание! У вас следующие ограничения:\n2 об/мин (запросы в минуту)\n32 000 TPM (токенов в минуту)\n50 RPD (запросов в день)"
        },
        'model_gemini_1_5_flash': {
            'name': "gemini-1.5-flash",
            'display_name': "Gemini 1.5 Flash",
            'description': "Внимание! У вас следующие ограничения:\n15 об/мин (запросы в минуту)\n1 миллион TPM (токенов в минуту)\n1500 RPD (запросов в день)"
        },
        'model_chatgpt-4o': {
            'name': "chatgpt-4o",
            'display_name': "ChatGPT-4o",
            'description': "Внимание ChatGPT-4o находится в бета режиме. Возможны баги."
        },
        'model_o1': {
            'name': "o1",
            'display_name': "o1 mini",
            'description': "Внимание o1 mini находится в бета режиме. Возможны баги."
        }
    }

    if model in MODEL_DETAILS:
        selected_model_info = MODEL_DETAILS[model]
        selected_model = selected_model_info['name']
        user_model_display = selected_model_info['display_name']
        model_description = selected_model_info['description']

        if selected_model.startswith("gemini"):
            if "api_key" not in api_keys[user_id]:
                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)
                await callback_query.message.answer(model_description)
                return

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

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

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

    await callback_query.message.edit_reply_markup()

# Команда /clear для очистки чата
@dp.message(Command("clear"))
async def clear_chat_handler(message: types.Message):
    user_id = int(message.from_user.id)
    try:
        async with aiofiles.open(LOG_FILE, 'r+', encoding='utf-8') as file:
            try:
                data = json.loads(await file.read())
                data = {int(k): v for k, v in data.items()}
            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 для вывода настроек
@dp.message(Command("meny"))
async def meny_handler(message: types.Message):
    user_id = str(message.from_user.id)
    api_keys = load_api_keys()

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

    user_model = api_keys[user_id].get("model", "Не выбрана")
    api_key = api_keys[user_id].get("api_key", "Не установлен")

    api_key = api_key.replace("_", "\\_")

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

    try:
        async with aiofiles.open(LOG_FILE, 'r', encoding='utf-8') as file:
            data = json.loads(await file.read())
            message_count = len(data.get(str(user_id), []))
    except FileNotFoundError:
        message_count = 0

    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"
        f"**API ключ:** {api_key}\n"
        f"**Выбранная модель:** {user_model}\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 для вывода краткого содержания сайта
@dp.message(Command("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'
    async with aiofiles.open(file_path, 'w', encoding='utf-8') as file:
        await 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."""
    user_id = str(message.from_user.id)

    try:
        response = html.escape(response)

        html_response = markdown.markdown(response, extensions=['extra', 'codehilite'])

        html_response = html_response.strip()

        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

        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 message.answer_document(open('long_message.txt', 'rb'))

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

    except Exception as e:
        await message.answer("Произошла ошибка при обработке ответа.")
        logging.error(f"Ошибка в send_response: {e}")

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

# Новый обработчик для команды /always photo
@dp.message(Command("always"))
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}")

        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_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[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='')
            await asyncio.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_queued_message(model_name):
    """Обрабатывает сообщения из очереди для моделей ChatGPT-4o и o1."""
    global model_queue

    while True:
        if model_queue:
            user_id, message, state = model_queue.popleft()
            logging.info(f"Извлечен пользователь {user_id} из очереди. Осталось: {len(model_queue)}")
            await process_model_request(model_name, user_id, message, state)
        else:
            await asyncio.sleep(1)

async def process_model_request(model_name, user_id, message, state):
    """Обрабатывает запрос к модели в отдельной задаче."""
    user_position = len(model_queue) + 1

    if user_position > 1:
        processing_message = await message.answer(random.choice(processing_phrases))
        queue_message = await message.answer(f"Вы добавлены в очередь на обработку.")

    api_keys = load_api_keys()

    try:
        history = []
        if os.path.exists(LOG_FILE):
            try:
                async with aiofiles.open(LOG_FILE, 'r', encoding='utf-8') as file:
                    data = json.loads(await file.read())
                    data = {int(k): v for k, v in data.items()}
                    if isinstance(data, dict) and int(user_id) in data:
                        history = data[int(user_id)]
            except UnicodeDecodeError:
                try:
                    async with aiofiles.open(LOG_FILE, 'r', encoding='cp1251') as file:
                        data = json.loads(await file.read())
                        data = {int(k): v for k, v in data.items()}
                        if isinstance(data, dict) and int(user_id) in data:
                            history = data[int(user_id)]
                except UnicodeDecodeError:
                    print("Ошибка: не удалось декодировать файл истории сообщений.")

        if model_name == "chatgpt-4o":
            context = "".join(
                [f"User: {entry['message']}\nAI: {entry['response']}\n" for entry in history]
            ) + f"User: {message.text}\n"

            logging.info(f"Context for ChatGPT-4o: {context}")
            response_text = await ask_chatgpt(context, history=history)
            logging.info(f"ChatGPT-4o response: {response_text}")

            await log_user_message(user_id, message.text, response_text)
            await send_response(message, response_text)

        elif model_name == "o1":
            context = "".join(
                [f"User: {entry['message']}\nAI: {entry['response']}\n" for entry in history]
            ) + f"User: {message.text}\n"

            logging.info(f"Context for O1: {context}")
            response_text = await ask_o1(context, history=history)
            logging.info(f"O1 response: {response_text}")

            await log_user_message(user_id, message.text, response_text)
            await send_response(message, response_text)

    except Exception as e:
        await message.answer(f"Ошибка при обработке сообщения: {str(e)}")
    finally:
        if user_position > 1:
            await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)
            await bot.delete_message(chat_id=message.chat.id, message_id=queue_message.message_id)

        del processing_users[user_id]  # Удаляем пользователя из списка обрабатываемых после завершения


async def handle_message(message: types.Message, state: FSMContext):
    """
    Основной обработчик сообщений.
    Обрабатывает текстовые сообщения, фото, голосовые сообщения и видео.
    Выбирает модель для генерации текста в зависимости от настроек пользователя
    и отправляет запрос в API выбранной модели.
    """
    global model_queue, processing_users
    user_id = str(message.from_user.id)

    # Проверка на блокировку в начале функции
    current_time = time.time()
    if user_id in last_message_time and current_time - last_message_time[user_id] < 1:
        if user_id not in spam_blocked_users:
            spam_blocked_users[user_id] = current_time + 60
            await message.answer("Вы отправляете сообщения слишком часто. Пожалуйста, подождите 1 минуту.")
        else:
            time_left = int(spam_blocked_users[user_id] - current_time)
            await message.answer(f"Вы заблокированы на {time_left} секунд.")
        return

    last_message_time[user_id] = current_time

    # Разблокировка пользователя, если прошло время блокировки
    if user_id in spam_blocked_users and current_time > spam_blocked_users[user_id]:
        del spam_blocked_users[user_id]

    api_keys = load_api_keys()

    # Если у пользователя нет записи в api_keys, просим выбрать модель
    if user_id not in api_keys:
        await message.answer("Пожалуйста, выберите модель для генерации текста командой /model.")
        return

    selected_model = api_keys[user_id]["selected_model"]

    # Обработка ChatGPT-4o и o1 с очередью
    if selected_model in ["chatgpt-4o", "o1"]:
        if user_id in processing_users:  # Проверяем, обрабатывается ли уже запрос пользователя
            return

        processing_users[user_id] = True  # Помечаем пользователя как обрабатываемого
        user_position = len(model_queue) + 1
        model_queue.append((user_id, message, state))

        if user_position == 1:
            asyncio.create_task(process_model_request(selected_model, user_id, message, state))
        else:
            await message.answer(f"Вы добавлены в очередь на обработку. Ваша позиция: {user_position}")
        return

    if selected_model in ["chatgpt-4o", "o1"]: return

    # Если выбрана модель Gemini и нет API ключа, просим ввести ключ
    if selected_model.startswith("gemini") and "api_key" not in api_keys[user_id]:
        await message.answer("API ключ не установлен. Пожалуйста, выберите модель Gemini "
                             "и введите API ключ с помощью команды /apir.")
        return

    # Если выбрана модель Gemini, запускаем задачу для обработки запроса
    if selected_model.startswith("gemini"):
        if user_id in processing_users:
            return

        processing_users[user_id] = True
        asyncio.create_task(process_gemini_request(message, state, api_keys[user_id]["api_key"], selected_model))
        return


# Асинхронная функция для обработки запросов к Gemini
async def process_gemini_request(message, state, api_key, selected_model):
    processing_message = await message.answer(random.choice(processing_phrases))
    user_id = str(message.from_user.id)
    current_time = time.time()

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

        if message.content_type == types.ContentType.TEXT:
            current_state = await state.get_state()

            if current_state == Form.waiting_for_prompt.state:
                prompt = message.text

                # Проверка на блокировку перед запросом к Gemini
                if user_id in spam_blocked_users and current_time <= spam_blocked_users[user_id]:
                    time_left = int(spam_blocked_users[user_id] - current_time)
                    await message.answer(f"Вы заблокированы на {time_left} секунд.")
                    return

                if user_id in user_data:
                    file_processed = False

                    if "video_file" in user_data[user_id] and not file_processed:
                        video_file = user_data[user_id]["video_file"]
                        original_message = user_data[user_id].get("message")

                        logging.info(f"Context for Gemini (video): [video_file, {prompt}]")
                        response = model.generate_content(
                            [video_file, prompt],
                            request_options={"timeout": 600}
                        )
                        response_text = response.text.replace("AI:", "", 1).strip()
                        logging.info(f"Gemini response (video): {response_text}")

                        if original_message and original_message.video:
                            await log_user_message(user_id, original_message.video.file_id, response_text,
                                                    is_file=True)

                        await send_response(original_message or message, response_text)

                        await state.clear()
                        file_processed = True

                    elif "audio_file_path" in user_data[user_id] and not file_processed:
                        audio_file_path = user_data[user_id]["audio_file_path"]
                        original_message = user_data[user_id].get("message")

                        genai.configure(api_key=api_key)
                        audio_file = genai.upload_file(path=audio_file_path)

                        logging.info(f"Context for Gemini (audio): [audio_file, {prompt}]")
                        response = model.generate_content(
                            [audio_file, prompt],
                            request_options={"timeout": 600}
                        )
                        response_text = response.text.replace("AI:", "", 1).strip()
                        logging.info(f"Gemini response (audio): {response_text}")

                        if original_message and original_message.voice:
                            await log_user_message(user_id, original_message.voice.file_id, response_text,
                                                    is_file=True)

                        await send_response(original_message or message, response_text)

                        await state.clear()
                        file_processed = True

                    elif "photo_file_path" in user_data[user_id] and not file_processed:
                        photo_file_path = user_data[user_id]["photo_file_path"]

                        await process_file_and_text(model, photo_file_path, prompt, user_id, message)
                        await state.clear()
                        file_processed = True

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

            else:
                # Загружаем историю сообщений для контекста
                history = []
                if os.path.exists(LOG_FILE):
                    try:
                        async with aiofiles.open(LOG_FILE, 'r', encoding='utf-8') as file:
                            data = json.loads(await file.read())
                            data = {int(k): v for k, v in data.items()}
                            if isinstance(data, dict) and int(user_id) in data:
                                history = data[int(user_id)]
                    except UnicodeDecodeError:
                        try:
                            async with aiofiles.open(LOG_FILE, 'r', encoding='cp1251') as file:
                                data = json.loads(await file.read())
                                data = {int(k): v for k, v in data.items()}
                                if isinstance(data, dict) and int(user_id) in data:
                                    history = data[int(user_id)]
                        except UnicodeDecodeError:
                            print("Ошибка: не удалось декодировать файл истории сообщений.")

                logging.info(f"User ID: {user_id}")
                logging.info(f"Loaded history: {history}")

                # Формируем контекст для Gemini, используя историю
                context = "".join(
                    [f"User: {entry['message']}\nAI: {entry['response']}\n"
                     for entry in history]) + f"User: {message.text}\nAI:"

                logging.info(f"Context for Gemini (text): {context}")
                # Проверка на блокировку перед запросом к Gemini
                if user_id in spam_blocked_users and current_time <= spam_blocked_users[user_id]:
                    time_left = int(spam_blocked_users[user_id] - current_time)
                    await message.answer(f"Вы заблокированы на {time_left} секунд.")
                    return

                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()

                            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()

                        await process_file_and_text(model, always_photo_users[user_id]["file_path"],
                                                    message.text,
                                                    user_id, message)
                else:
                    response = model.generate_content(
                        [context],
                        request_options={"timeout": 600}
                    )
                    response_text = response.text.replace("AI:", "", 1).strip()
                    logging.info(f"Gemini response (text): {response_text}")
                    await log_user_message(user_id, message.text, response_text)
                    await send_response(message, response_text)


        elif message.content_type in [types.ContentType.VOICE, types.ContentType.PHOTO,
                                      types.ContentType.VIDEO]:

            current_state = await state.get_state()
            if user_id in always_photo_users and current_state != Form.waiting_for_prompt.state:
                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:
                # Проверка на блокировку перед запросом к Gemini
                if user_id in spam_blocked_users and current_time <= spam_blocked_users[user_id]:
                    time_left = int(spam_blocked_users[user_id] - current_time)
                    await message.answer(f"Вы заблокированы на {time_left} секунд.")
                    return

                await process_video_message(model, message, state)

        else:
            await message.answer(
                "Этот тип сообщения не поддерживается. Пожалуйста, отправьте текст, фото, голосовое сообщение или видео.")

    except Exception as e:
        if "500 An internal error has occurred" in str(e) or "503 Illegal metadata" in str(
                e):
            await message.answer(
                "Превышен доступ к нейросети или ошибка в метаданных. Пожалуйста, попробуйте позже или выберите другую модель."
            )
        if "Unknown field for Candidate: finish_message" in str(e) or "503 Illegal metadata" in str(e):
            await message.answer(
                "Произошла ошибка. Скорее всего нарушеный настроийки безопасности."
            )
        elif "TimeoutError" in str(e):
            await message.answer("Запрос к нейросети занял слишком много времени. Попробуйте позже.")
        elif "google.api_core.exceptions.InvalidArgument" in str(e):
            await message.answer("Ошибка в метаданных запроса к нейросети. Проверьте настройки.")
        else:
            await message.answer(f"Ошибка при обработке сообщения: {str(e)}")
    finally:
        await bot.delete_message(chat_id=message.chat.id, message_id=processing_message.message_id)

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

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"))

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

dp.message.register(handle_message)

dp.callback_query.register(process_model_selection)

dp.message.register(handle_message,
                    ~Command(commands=["start", "apir", "model", "clear", "meny", "always", "FAQ"]))

dp.callback_query.register(process_model_selection)

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"]
        genai.configure(api_key=api_key)

        if file_path is not None:
            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 main():
    print("Запуск бота...")
    asyncio.create_task(process_queued_message("chatgpt-4o"))
    asyncio.create_task(process_queued_message("o1"))
    await dp.start_polling(bot, timeout=60)

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