Устранить ошибки в коде: import vk_api
from vk_api.longpoll import VkLongPoll, VkEventType
from vk_api.keyboard import VkKeyboard
import random
import datetime
import io
import requests
import logging
import os
import json
from license_generator import ImageProcessor
import concurrent.futures
import time
from threading import Lock
import functools

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

CONFIG_FILE = "config.json"
COMMANDS_FILE = "commands.json"

def load_config(config_path):
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
            return config
    except FileNotFoundError:
        logging.error(f"Файл конфигурации не найден: {config_path}")
        return None
    except json.JSONDecodeError:
        logging.error(f"Ошибка декодирования JSON в файле: {config_path}")
        return None

def load_commands(commands_path):
    try:
        with open(commands_path, 'r', encoding='utf-8') as f:
            commands = json.load(f)
            # Приводим ключи команд к нижнему регистру
            normalized_commands = {}
            for command in commands['commands']:
                normalized_commands[command.lower()] = commands['commands'][command]
            commands['commands'] = normalized_commands

            return commands
    except FileNotFoundError:
        logging.error(f"Файл команд не найден: {commands_path}")
        return None
    except json.JSONDecodeError:
        logging.error(f"Ошибка декодирования JSON в файле: {commands_path}")
        return None

CONFIG = load_config(CONFIG_FILE)
if not CONFIG:
    exit(1)
COMMANDS = load_commands(COMMANDS_FILE)
if not COMMANDS:
    exit(1)

if not isinstance(COMMANDS, dict):
    logging.error("Ошибка: COMMANDS не является словарем")
    exit(1)

logging.info(f"Загруженные команды: {COMMANDS}")

def retry(func, retries=3, delay=1):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        for attempt in range(retries):
            try:
                return func(*args, **kwargs)
            except (requests.exceptions.RequestException, vk_api.exceptions.ApiError) as e:
                logging.error(f"Ошибка в {func.__name__}, попытка {attempt + 1}: {e}. Повтор через {delay} секунд.")
                if attempt < retries - 1:
                   time.sleep(delay)
        logging.error(f"Сбой в {func.__name__} после {retries} попыток.")
        return None
    return wrapper

class VkBot:
    def get_group_name(self):
        try:
            group_info = self.vk.groups.getById(group_id=self.config["group_id"], fields='screen_name')
            return group_info[0]['screen_name']
        except vk_api.exceptions.ApiError as e:
            logging.error(f"Ошибка при получении имени группы: {e}")
            return None
    
    def __init__(self, config, image_processor, num_threads=5):
        self.config = config
        self.image_processor = image_processor
        self.vk_session = vk_api.VkApi(token=self.config["tokens"]["group"])
        self.vk = self.vk_session.get_api()
        self.vk_session_user = vk_api.VkApi(token=self.config["tokens"]["user"])
        self.vk_user = self.vk_session_user.get_api()
        self.longpoll = VkLongPoll(self.vk_session)
        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=num_threads)
        self.user_processing = {}
        self.cache_lock = Lock()
        self.user_state = {}

    def normalize_user_input(self, user_text):
        """Приводим текст в правильный вид для сравнения."""
        return user_text.strip().lower()

    def create_keyboard(self, keyboard_name):
        if keyboard_name not in COMMANDS["keyboards"]:
            return None
        keyboard = VkKeyboard(one_time=False)
        buttons = sorted(COMMANDS["keyboards"][keyboard_name], key=lambda x: x.get("layer", 1))
        current_layer = 1
        for button in buttons:
            if button.get("layer", 1) != current_layer:
                keyboard.add_line()
                current_layer = button.get("layer", 1)
            keyboard.add_button(button["text"], color=button["color"])
        return keyboard.get_keyboard()

    def load_all_albums(self):
        logging.info("Загрузка списка альбомов...")
        owner_id = self.config["group_id"]
        try:
            for album_name, album_id in self.config["albums_urls"].items():
                logging.info(f"Получение фотографий из альбома: {album_name} ({album_id})")
                try:
                    photos = self.vk_user.photos.get(owner_id=owner_id, album_id=album_id, count=1)['items']
                    if not photos:
                        logging.warning(f"В альбоме {album_name} ({album_id}) нет фотографий или доступ к альбому закрыт.")
                except vk_api.exceptions.ApiError as e:
                    logging.error(f"Ошибка при получении альбома {album_name}: {e}")

        except Exception as e:
            logging.error(f"Ошибка при загрузке списка альбомов: {e}")

    @retry
    def get_random_photo(self, album_id):
        owner_id = self.config["group_id"]
        logging.info(f"Запрос фото. Owner ID: {owner_id}, Album ID: {album_id}")
        photos = self.vk_user.photos.get(owner_id=owner_id, album_id=album_id, count=100)['items']
        if not photos:
            logging.warning(f"В альбоме {album_id} нет фотографий или доступ к альбому закрыт.")
            return None
        logging.info("Фото успешно получены.")
        return random.choice(photos)['sizes'][-1]['url']

    @retry
    def check_is_don(self, user_id):
        return self.vk.groups.isMember(group_id=self.config["donate_group_id"], user_id=user_id)

    @retry
    def send_vk_message(self, user_id, message, keyboard=None):
        self.vk.messages.send(peer_id=user_id, random_id=random.randint(0, 2 ** 20), message=message,
                              keyboard=keyboard)

    @retry
    def send_image_vk(self, image, user_id):
        image_bytes = io.BytesIO()
        image.save(image_bytes, format="PNG")
        image_bytes.seek(0)
        upload_url = self.vk.photos.getMessagesUploadServer(peer_id=user_id)["upload_url"]
        upload_response = requests.post(upload_url, files={'photo': ('image.png', image_bytes)}).json()
        if "photo" not in upload_response:
            logging.error(f"Ошибка загрузки: {upload_response}")
            return
        photo = self.vk.photos.saveMessagesPhoto(photo=upload_response['photo'], server=upload_response['server'],
                                                  hash=upload_response['hash'])[0]
        self.vk.messages.send(peer_id=user_id, random_id=random.randint(0, 2 ** 20),
                              attachment=f"photo{photo['owner_id']}_{photo['id']}")
        logging.info(f"Изображение отправлено: {user_id}.")

    def _get_license_data(self, user_id, selected_character, album_id) -> tuple:
        is_don = self.check_is_don(user_id)
        if is_don is None:
            raise ValueError("Не удалось проверить статус дона.")
        
        photo_url = self.get_random_photo(album_id)
        if not photo_url:
            raise ValueError("Не удалось получить фото")
    
        user_info = self.vk.users.get(user_ids=user_id)[0]
        user_name = f"{user_info.get('first_name', 'Noname')} {user_info.get('last_name', '')}"
        
        today = datetime.date.today()
        date_start = today.strftime("%d.%m.%Y")
        date_end = "Бессрочно" if is_don else (today + datetime.timedelta(days=30)).strftime("%d.%m.%Y")
        license_id = f"{'D' if is_don else 'F'}-{random.randint(10000000, 99999999)}"
        
        group_name = self.get_group_name()
        if group_name is None:
            raise ValueError("Не удалось получить имя группы")
        return user_name, date_start, date_end, license_id, photo_url, is_don, group_name
    
    def _create_qr_code(self, user_id: int, is_don: bool):
        if is_don:
            return self.image_processor.generate_qr_code(f"https://vk.com/id{user_id}")
        return self.image_processor.generate_qr_code(self.config["qr_default_url"])

    def _send_license_image(self, user_id: int, image):
        if image:
            self.send_image_vk(image, user_id)
            with self.cache_lock:
                self.user_processing[user_id] = False
        else:
            self.send_vk_message(user_id, "Ошибка создания лицензии")
            with self.cache_lock:
               self.user_processing[user_id] = False

    def process_license_async(self, user_id, selected_character, album_id):
        with self.cache_lock:
            if self.user_processing.get(user_id, False):
                self.send_vk_message(user_id, "Пожалуйста, дождитесь завершения предыдущего запроса.")
                return
            self.user_processing[user_id] = True
        self.executor.submit(self.process_license, user_id, selected_character, album_id)

    def process_license(self, user_id, selected_character, album_id):
        try:
            user_name, date_start, date_end, license_id, photo_url, is_don, group_name = self._get_license_data(user_id, selected_character, album_id)
            user_data = {
                'user_name': user_name,
                'date_start': date_start,
                'date_end': date_end,
                'license_id': license_id,
                'license_text': f"ЛИЦЕНЗИЯ НА {selected_character.upper()}",
                'group_name': group_name  
            }
            qr_code = self._create_qr_code(user_id, is_don)

            def callback(license_image):
                self._send_license_image(user_id, license_image)

            self.image_processor.process_license_async(user_id, background, user_data, qr_code, callback)

        except ValueError as e:
            self.send_vk_message(user_id, str(e))
            logging.error(f"Ошибка обработки: {e}")
            with self.cache_lock:
                self.user_processing[user_id] = False
        except Exception as e:
            self.send_vk_message(user_id, f"Ошибка: {e}")
            logging.error(f"Ошибка обработки: {e}")
            with self.cache_lock:
                self.user_processing[user_id] = False

    def handle_command(self, user_id, user_text):
        normalized_text = self.normalize_user_input(user_text)  # Нормализуем введенный текст
        state = self.user_state.get(user_id, {"menu": "main"})
    
        if normalized_text == "назад":
            if state["menu"] == "character":
                self.user_state[user_id] = {"menu": "main"}
                self.send_vk_message(user_id, "Что вы хотите сделать?", keyboard=self.create_keyboard("main_menu"))
            elif state["menu"] == "albums":
                self.user_state[user_id] = {"menu": "character"}
                self.send_vk_message(user_id, "Выберите персонажа для лицензии:", keyboard=self.create_keyboard("character_menu"))
            elif state["menu"] == "license_options":
                character_name = state.get("character")
                if character_name:
                    self.user_state[user_id] = {"menu": "albums", "character": character_name}
                    character_data = COMMANDS["characters"].get(character_name.lower())  # Персонажа ищем по нижнему регистру
                    if character_data:
                        keyboard_name = character_data.get("albums_keyboard")
                        keyboard = self.create_keyboard(keyboard_name)
                        self.send_vk_message(user_id, f"Выберите альбом для персонажа {character_data.get('albums_names')[0]}", keyboard=keyboard)
                else:
                    self.user_state[user_id] = {"menu": "main"}
                    self.send_vk_message(user_id, "Что вы хотите сделать?", keyboard=self.create_keyboard("main_menu"))
            else:
                self.user_state[user_id] = {"menu": "main"}
                self.send_vk_message(user_id, "Что вы хотите сделать?", keyboard=self.create_keyboard("main_menu"))

        elif normalized_text == "меню":
            self.user_state[user_id] = {"menu": "main"}
            self.send_vk_message(user_id, "Что вы хотите сделать?", keyboard=self.create_keyboard("main_menu"))
    
        else:
            logging.info(f"Проверка команды: {normalized_text} в COMMANDS['commands']")
            command = COMMANDS["commands"].get(normalized_text, COMMANDS["commands"]["default"])  # Используем нормализованный текст для ответа
            
            if "response" in command:
                keyboard = self.create_keyboard(command.get("keyboard"))
                self.send_vk_message(user_id, command["response"], keyboard=keyboard)
                if normalized_text == "лицензия":
                    self.user_state[user_id] = {"menu": "character"}
            elif state["menu"] == "character":
                character_data = COMMANDS["characters"].get(normalized_text)  # Проверка с нормализованным значением
                if character_data:
                    albums_keyboard = character_data.get("albums_keyboard")
                    if albums_keyboard:
                        self.user_state[user_id] = {"menu": "albums", "character": user_text}
                        keyboard = self.create_keyboard(albums_keyboard)
                        self.send_vk_message(user_id, f"Выберите альбом для персонажа {character_data.get('albums_names')[0]}", keyboard=keyboard)
                    else:
                        # Обработка случайной лицензии
                        if normalized_text == "создать случайную лицензию":
                            characters = list(COMMANDS["characters"].keys())
                            if "создать случайную лицензию" in characters:
                                characters.remove("создать случайную лицензию")
                            random_character = random.choice(characters)
                            albums_keyboard = COMMANDS["characters"].get(random_character).get("albums_keyboard")
                            if albums_keyboard:
                                albums = COMMANDS["albums"].get(albums_keyboard)
                                if albums:
                                    random_album = random.choice(albums)
                                    self.process_license_async(user_id, random_character, random_album["album_id"])
                            self.user_state[user_id] = {"menu": "license_options", "character": random_character}
                else:
                    self.send_vk_message(user_id, "Неизвестная команда", keyboard=self.create_keyboard("back_and_menu"))
            elif state["menu"] == "albums":
                character_name = state.get("character")
                albums_keyboard = COMMANDS["characters"].get(character_name).get("albums_keyboard")
                if albums_keyboard:
                    for album_name, album_id in self.config["albums_urls"].items():
                        if album_name.lower() == normalized_text:  # Сравнение с нормализованным текстом
                            self.process_license_async(user_id, character_name, album_id)
                            self.user_state[user_id] = {"menu": "license_options", "character": character_name, "album": album_name}
                            self.send_vk_message(user_id, "Лицензия создана", keyboard=self.create_keyboard("license_creation_menu"))
                            return
                    self.send_vk_message(user_id, "Неизвестная команда", keyboard=self.create_keyboard("back_and_menu"))
            elif state["menu"] == "license_options":
                self.send_vk_message(user_id, "Лицензия создана", keyboard=self.create_keyboard("license_creation_menu"))
            else:
                self.send_vk_message(user_id, "Неизвестная команда")

    def run(self):
        logging.info("Бот запущен. Ожидание сообщений...")
        self.load_all_albums()
        while True:
            try:
                for event in self.longpoll.listen():
                    # Игнорируем сообщения из бесед
                    if event.type == VkEventType.MESSAGE_NEW and event.to_me and event.from_user:
                        logging.info(f"Новое событие: {event}")
                        logging.info(f"Новое сообщение из event.text: {event.text}")
                        user_id = event.user_id
                        user_text = event.text
                        logging.info(f"Новое сообщение от {user_id}: {user_text} (до handle_command)")
                        self.handle_command(user_id, user_text)
                        logging.info(f"Новое сообщение от {user_id}: {user_text} (после handle_command)")

            except requests.exceptions.RequestException as e:
                logging.error(f"Проблема с сетью: {e}. Переподключение через 5 секунд...")
                time.sleep(5)
            except vk_api.exceptions.ApiError as e:
                logging.error(f"Проблема с API VK: {e}. Переподключение через 5 секунд...")
                time.sleep(5)
            except Exception as e:
                logging.error(f"Неизвестная ошибка в основном цикле: {e}. Переподключение через 5 секунд...")
                time.sleep(5)

if __name__ == "__main__":
    CONFIG = load_config(CONFIG_FILE)
    if not CONFIG:
        exit(1)
    COMMANDS = load_commands(COMMANDS_FILE)
    if not COMMANDS:
        exit(1)

    image_processor = ImageProcessor(CONFIG)
    num_threads = 8
    bot = VkBot(CONFIG, image_processor, num_threads=num_threads)
    bot.run()