How many per cent of the code satisfies the required condition?

```
Задача: Создать Telegram бота на Python для продажи игровых ключей RVANKA и виртуальной валюты для различных игровых серверов.

Функционал:

1. Приветствие и выбор категории (Start Command Handler):

При получении команды /start бот отправляет приветственное сообщение пользователю, например: "Привет! 👋 Добро пожаловать в наш магазин. Выберите, что вас интересует:".

Под приветственным сообщением бот отображает две инлайн-кнопки:

Кнопка с текстом "Вирты" и callback_data "virts".

Кнопка с текстом "Ключи RVANKA" и callback_data "keys".

2. Категория "Ключи RVANKA" (Callback Query Handler):

Обработка выбора категории:

При нажатии на кнопку "Ключи RVANKA" (callback_data "keys") бот отображает сообщение: "Выберите время действия ключа:".

Под сообщением бот отображает инлайн-кнопки с вариантами времени ключа: 20, 60, 200, 600, 2000, 6000 часов.

Каждая кнопка имеет callback_data вида "key_time_<время>", например, "key_time_20" для ключа на 20 часов.

Обработка выбора времени ключа:

При нажатии на кнопку с временем ключа бот проверяет наличие ключей в соответствующем файле (keys_<время>.txt).

Если ключи есть:

Бот генерирует уникальный номер заказа (например, используя timestamp + случайное число).

Бот отправляет сообщение пользователю: "Вы выбрали ключ на <время> часов. Стоимость: 50 рублей. Для оплаты переведите 50 рублей на реквизиты: 4177 7777 8888 0900. ОБЯЗАТЕЛЬНО укажите в комментарии к платежу номер вашего заказа: #{номер_заказа}. Без комментария оплата не будет засчитана!".

Если ключей нет:

Бот отправляет сообщение пользователю: "Извините, ключи на <время> часов временно закончились.".

Подтверждение оплаты (отдельная функция, вызываемая вручную или по webhook):

Функция принимает номер заказа в качестве аргумента.

Функция проверяет, была ли произведена оплата с указанным номером заказа в комментарии (вне области бота, реализация зависит от способа приема платежей).

Если оплата подтверждена:

Функция отправляет сообщение пользователю @samuray_tag: "#{номер_заказа} Пользователь {Имя пользователя} произвел оплату. Выдать товар?".

Под сообщением отображается инлайн-кнопка "Выдать товар" с callback_data "confirm_<номер_заказа>".

Выдача ключа (Callback Query Handler):

При нажатии на кнопку "Выдать товар" (callback_data "confirm_<номер_заказа>") бот:

Определяет время ключа по номеру заказа (информация может храниться в базе данных или временном хранилище).

Открывает соответствующий файл с ключами (keys_<время>.txt).

Считывает первую строку файла (ключ).

Отправляет ключ пользователю.

Удаляет первую строку из файла.

Записывает информацию о выдаче ключа в лог-файл (время, имя пользователя, тип ключа).

Отправляет сообщение пользователю @samuray_tag: "Ключ выдан пользователю {Имя пользователя}.".

3. Категория "Вирты" (Callback Query Handler):

Обработка выбора категории:

При нажатии на кнопку "Вирты" (callback_data "virts") бот отображает сообщение: "Выберите сервер:".

Под сообщением бот отображает инлайн-кнопки с доступными серверами (список серверов указан в исходном промте).

Каждая кнопка имеет callback_data вида "server_<название_сервера>", например, "server_RED".

Обработка выбора сервера:

При нажатии на кнопку с сервером бот отправляет сообщение: "Введите желаемое количество виртов:".

Бот переводит пользователя в режим ожидания ввода количества виртов (используя bot.register_next_step_handler).

Обработка ввода количества виртов (Message Handler):

Бот получает сообщение от пользователя с количеством виртов.

Бот проверяет, является ли сообщение числом.

Если сообщение - число:

Бот рассчитывает стоимость виртов (количество * 30 / 1000000).

Бот генерирует уникальный номер заказа.

Бот отправляет сообщение пользователю: "Вы заказали <количество> виртов для сервера <название_сервера>. Стоимость: <стоимость> рублей. Для оплаты переведите <стоимость> рублей на реквизиты: 4177 7777 8888 0900. ОБЯЗАТЕЛЬНО укажите в комментарии к платежу номер вашего заказа: #{номер_заказа}. Без комментария оплата не будет засчитана!".

Если сообщение - не число:

Бот отправляет сообщение: "Некорректный ввод. Пожалуйста, введите число.".

Бот снова переводит пользователя в режим ожидания ввода количества виртов.

Подтверждение оплаты и выдача виртов (аналогично ключам):

Функция подтверждения оплаты отправляет сообщение @samuray_tag с информацией о заказе и кнопкой "Выдать товар".

При нажатии на кнопку "Выдать товар" бот:

Записывает информацию о выдаче виртов в лог-файл (время, имя пользователя, количество виртов, сервер).

Отправляет сообщение пользователю: "Вирты выданы. Приятной игры!".

Отправляет сообщение @samuray_tag: "Вирты выданы пользователю {Имя пользователя}.".

Технические требования:

Язык программирования: Python.

Библиотека: python-telegram-bot.

Хранение ключей: отдельные текстовые файлы для каждого типа ключа (keys_20.txt, keys_60.txt и т.д.).

Логирование: отдельный файл для записи информации о выданных ключах и виртах.

Структура кода:

Использовать функции для обработки различных этапов взаимодействия с пользователем (обработка команд, callback'ов, сообщений).

Разделить код на логические блоки (например, отдельные функции для работы с ключами и виртами).

Обработка ошибок: предусмотреть обработку возможных ошибок (например, некорректный ввод пользователя, отсутствие ключей в файле).

Дополнительные пожелания:

Код должен быть хорошо структурирован, документирован и легко читаем.

Бот должен быть стабильным, отказоустойчивым и безопасным.

Предусмотреть возможность добавления новых типов ключей и серверов без изменения кода бота.

Пример кода (начало):

import telebot
from telebot import types

bot = telebot.TeleBot('ВАШ_API_TOKEN')

# ... (функции для обработки команд, callback'ов, сообщений)

bot.polling()```

```Code
import telebot
from telebot import types
import random
import time
import os
import sqlite3

# --- Config ---
TOKEN = 'YOUR_BOT_TOKEN'
ADMIN = 'samuray_tag'
PAYMENT = "4177 7777 8888 0900"

# --- Database ---
db = sqlite3.connect('bot_data.db')
cur = db.cursor()

cur.execute('''
    CREATE TABLE IF NOT EXISTS keys (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        duration INTEGER,
        key TEXT UNIQUE
    )
''')

cur.execute('''
    CREATE TABLE IF NOT EXISTS orders (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER,
        username TEXT,
        type TEXT, 
        item_id INTEGER, 
        quantity INTEGER,
        cost REAL,
        status TEXT DEFAULT 'pending' 
    )
''')

cur.execute('''
    CREATE TABLE IF NOT EXISTS servers (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT UNIQUE
    )
''')

# Add default servers
servers = ['RED', 'GREEN', 'BLUE']
for srv in servers:
    try:
        cur.execute("INSERT INTO servers (name) VALUES (?)", (srv,))
    except sqlite3.IntegrityError:
        pass

db.commit()

# --- Bot ---
bot = telebot.TeleBot(TOKEN)

# --- Helpers ---
def gen_order_id():
    return str(int(time.time())) + str(random.randint(1000, 9999))

def log(action, user, details):
    with open('log.txt', 'a') as f:
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        f.write(f"{timestamp} - {action} - User: {user} - Details: {details}\n")

# --- Commands ---
@bot.message_handler(commands=['start'])
def start(msg):
    markup = types.InlineKeyboardMarkup()
    markup.add(types.InlineKeyboardButton("Virts", callback_data="virts"))
    markup.add(types.InlineKeyboardButton("Keys RVANKA", callback_data="keys"))
    bot.send_message(msg.chat.id, "Welcome! Choose:", reply_markup=markup)

# --- Callbacks ---
@bot.callback_query_handler(func=lambda call: True)
def callback(call):
    if call.data == "virts":
        choose_server(call.message)
    elif call.data == "keys":
        choose_key_duration(call.message)
    elif call.data.startswith("server_"):
        srv_id = int(call.data.split("_")[1])
        ask_virtual_quantity(call.message, srv_id)
    elif call.data.startswith("key_"):
        duration = int(call.data.split("_")[1])
        process_key_order(call.message, duration)
    elif call.data.startswith("confirm_"):
        order_id = int(call.data.split("_")[1])
        confirm_order(call.message, order_id)

# --- Server Choice ---
def choose_server(msg):
    cur.execute("SELECT id, name FROM servers")
    servers = cur.fetchall()
    markup = types.InlineKeyboardMarkup()
    for srv_id, srv_name in servers:
        markup.add(types.InlineKeyboardButton(srv_name, callback_data=f"server_{srv_id}"))
    bot.send_message(msg.chat.id, "Choose a server:", reply_markup=markup)

# --- Virtual Quantity ---
def ask_virtual_quantity(msg, srv_id):
    cur.execute("SELECT name FROM servers WHERE id = ?", (srv_id,))
    srv_name = cur.fetchone()[0]
    bot.send_message(msg.chat.id, f"You chose {srv_name}. Enter the desired millions of virtuals:")
    bot.register_next_step_handler(msg, lambda m: process_virtual_quantity(m, srv_id))

def process_virtual_quantity(msg, srv_id):
    try:
        qty = int(msg.text)
        cost = qty * 30 
        order_id = gen_order_id()

        cur.execute("INSERT INTO orders (user_id, username, type, item_id, quantity, cost) VALUES (?, ?, ?, ?, ?, ?)",
                       (msg.from_user.id, msg.from_user.username, 'virtual', srv_id, qty, cost))
        db.commit()

        bot.send_message(msg.chat.id, f"You ordered {qty}M virtuals for {srv_name}. "
                                          f"Cost: {cost} rubles.\n"
                                          f"Pay {cost} rubles to {PAYMENT}. "
                                          f"Order number: #{order_id}.\n"
                                          "Payment without the order number will not be confirmed.")

        # Admin notification
        admin_markup = types.InlineKeyboardMarkup()
        admin_markup.add(types.InlineKeyboardButton(f"#{order_id} User {msg.from_user.username} paid. Issue item?",
                                                   callback_data=f"confirm_{order_id}"))
        bot.send_message(chat_id=f"@{ADMIN}", text="New order:", reply_markup=admin_markup)

    except ValueError:
        bot.send_message(msg.chat.id, "Invalid quantity. Enter a number.")
        ask_virtual_quantity(msg, srv_id)

# --- Key Duration ---
def choose_key_duration(msg):
    markup = types.InlineKeyboardMarkup()
    cur.execute("SELECT DISTINCT duration FROM keys")
    durations = cur.fetchall()
    for (duration,) in durations:
        markup.add(types.InlineKeyboardButton(f"{duration} hours", callback_data=f"key_{duration}"))
    bot.send_message(msg.chat.id, "Choose key duration:", reply_markup=markup)

# --- Key Order ---
def process_key_order(msg, duration):
    order_id = gen_order_id()

    cur.execute("INSERT INTO orders (user_id, username, type, item_id, cost) VALUES (?, ?, ?, ?, ?)",
                       (msg.from_user.id, msg.from_user.username, 'key', duration, 50))
    db.commit()

    bot.send_message(msg.chat.id, f"You chose a key for {duration} hours. Cost: 50 rubles.\n"
                                      f"Pay 50 rubles to {PAYMENT}. "
                                      f"Order number: #{order_id}.\n"
                                      "Payment without the order number will not be confirmed.")

    # Admin notification
    admin_markup = types.InlineKeyboardMarkup()
    admin_markup.add(types.InlineKeyboardButton(f"#{order_id} User {msg.from_user.username} paid. Issue item?",
                                                callback_data=f"confirm_{order_id}"))
    bot.send_message(chat_id=f"@{ADMIN}", text="New order:", reply_markup=admin_markup)

# --- Order Confirmation ---
def confirm_order(msg, order_id):
    cur.execute("SELECT id, user_id, username, type, item_id, quantity FROM orders WHERE id = ? AND status = 'pending'", (order_id,))
    order = cur.fetchone()

    if order:
        if order[3] == 'key':
            issue_key(msg, order)
        elif order[3] == 'virtual':
            issue_virtual(msg, order)

        cur.execute("UPDATE orders SET status = 'completed' WHERE id = ?", (order_id,))
        db.commit()
    else:
        bot.send_message(msg.chat.id, "Order not found or already processed.")

# --- Key Issuance ---
def issue_key(msg, order):
    duration = order[4]
    cur.execute("SELECT key FROM keys WHERE duration = ? LIMIT 1", (duration,))
    key_row = cur.fetchone()

    if key_row:
        key = key_row[0]
        bot.send_message(chat_id=order[1], text=f"Your key: {key}")
        log("Key Issued", order[2], f"Duration: {duration} hours, Key: {key}")
        bot.send_message(msg.chat.id, f"Key issued to @{order[2]}.")
        cur.execute("DELETE FROM keys WHERE key = ?", (key,))
        db.commit()
    else:
        bot.send_message(msg.chat.id, "No keys available for this duration.")
        bot.send_message(chat_id=order[1], text="Sorry, keys for this duration are out of stock.")

# --- Virtual Issuance ---
def issue_virtual(msg, order):
    srv_id = order[4]
    qty = order[5]
    cur.execute("SELECT name FROM servers WHERE id = ?", (srv_id,))
    srv_name = cur.fetchone()[0]

    bot.send_message(chat_id=order[1], text=f"Your {qty}M virtuals for {srv_name} have been issued!")
    log("Virtual Issued", order[2], f"Server: {srv_name}, Quantity: {qty}M")
    bot.send_message(msg.chat.id, f"Virtuals issued to @{order[2]}.")

# --- Admin Commands ---
@bot.message_handler(commands=['addkey'])
def add_key(msg):
    if msg.from_user.username == ADMIN:
        args = msg.text.split()[1:]
        if len(args) == 2:
            try:
                duration = int(args[0])
                key = args[1]
                cur.execute("INSERT INTO keys (duration, key) VALUES (?, ?)", (duration, key))
                db.commit()
                bot.reply_to(msg, f"Key for {duration} hours added.")
            except ValueError:
                bot.reply_to(msg, "Invalid duration. Usage: /addkey <duration> <key>")
            except sqlite3.IntegrityError:
                bot.reply_to(msg, "Key already exists.")
        else:
            bot.reply_to(msg, "Invalid arguments. Usage: /addkey <duration> <key>")

@bot.message_handler(commands=['addserver'])
def add_server(msg):
    if msg.from_user.username == ADMIN:
        srv_name = msg.text.split()[1:]
        if srv_name:
            try:
                cur.execute("INSERT INTO servers (name) VALUES (?)", (" ".join(srv_name),))
                db.commit()
                bot.reply_to(msg, f"Server '{' '.join(srv_name)}' added.")
            except sqlite3.IntegrityError:
                bot.reply_to(msg, "Server already exists.")
        else:
            bot.reply_to(msg, "Invalid arguments. Usage: /addserver <server_name>")

# --- Start ---
if __name__ == '__main__':
    print("Bot started...")
    bot.polling()```