Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
Иконка ресурса

project | Админ-помощник 1.0

Осуждён за гениальность или проклят за талант?
Окт
155
473
Пользователь
Revero добавил(а) новый ресурс:

project | Админ-помощник - VK-бот для управления администрацией игровых серверов с интеграцией MariaDB

ало, ало опять нубо рп? да да

Всем йоу

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


Система синхронизации администраторов - функция для автоматической проверки соответствия администраторов в базе данных сервера и в беседе...​

Узнать больше об этом ресурсе...
 
Окт
256
148
Пользователь
что делать у меня ошибка
"MessageMin" is immutable and does not support item assignment
Traceback (most recent call last):
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\dispatch\base.py", line 22, in route
await view.handle_event(event, ctx_api, self.state_dispenser)
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\dispatch\views\abc\message.py", line 48, in handle_event
message = await self.get_message(event, ctx_api)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\dispatch\views\bot\message.py", line 28, in get_message
return message_min(event, ctx_api)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\tools\dev\mini_types\bot\message.py", line 75, in message_min
setattr(message, "unprepared_ctx_api", ctx_api)
File "pydantic/main.py", line 382, in pydantic.main.BaseModel.__setattr__
TypeError: "MessageMin" is immutable and does not support item assignment
вот код:
BOT:
import vk_api
from vkbottle.bot import Bot, Message
from vkbottle import Keyboard, Callback, CtxStorage
from vkbottle.dispatch.rules import ABCRule
from vkbottle.modules import logger
import mariadb
import sys
import re
import asyncio
import time
import json
import os
from datetime import datetime

VK_TOKEN = 'vk1.a.WECZua7uDU4S9tRyznSbDbcMu40xp2af808L7s2YUzkkrgZ8_duoFqevXGViR5xk64K6NjCUcA9gaOVsg83MarFF8h4PdFO4zyyiSa_LVSOK7t-gIz7MEriAsId-0J3A3PE4UdVFPE1WyvW4kouePg-s_Eu3t0rAC2-UXo4sOiZsIt05hJhGYxL3h2iwViP49oNLCO_NKJDiXY4AXUCLWw'
GROUP_ID = 229693070

DB_CONFIG = {
    'host': '172.18.0.1',
    'user': 'gs123452',
    'password': 'ta727DeiSeH7',
    'database': 'gs123452',
    'port': '3306'
}

CONFIG_FILE = 'config.json'
CONFIG = {}
DEVELOPER_IDS = [743580182, 425762738]
bot = Bot(token=VK_TOKEN)

class StartsWithRule(ABCRule[Message]):
    def __init__(self, command: str):
        self.command = command.lower()

    async def check(self, event: Message) -> bool:
        return event.text.lower().startswith(self.command)

def get_help_text(user_id, detailed=False):
    if not detailed:
        text = "СПИСОК КОМАНД БОТА\n\n"
        text += "📘 Общие команды:\n"
        text += "▪ /acadmin [ник] — Проверить, является ли игрок админом.\n"
        text += "▪ /help — Показать это сообщение.\n"
        if has_permission(user_id):
            text += "\n👑 Команды для администраторов:\n"
            text += "▪ /setadmin [ник] [уровень] — Установить админ-уровень.\n"
            text += "▪ /awarn [ник] [+/-число] — Изменить кол-во админ-варнов.\n"
            text += "▪ /prefix [ник] [префикс] — Установить префикс.\n"
            text += "▪ /alog [ник/ID] ... — Поиск по логам администратора.\n"
            text += "▪ /sync — Принудительная синхронизация ников.\n"
            text += "▪ /set_sync_chat — Назначить чат для синхронизации.\n"
            text += "▪ /set_notify_chat — Назначить чат для уведомлений.\n"
            text += "▪ /show_config — Показать текущие настройки чатов.\n"
        if is_developer(user_id):
            text += "\n🛠 Команды для разработчиков:\n"
            text += "▪ /grant_access — Выдать доступ к боту.\n"
            text += "▪ /revoke_access — Забрать доступ к боту.\n"
            text += "▪ /access_list — Показать список с доступом.\n"
        return text
    else:
        text = "ПОДРОБНОЕ ОПИСАНИЕ КОМАНД\n\n"
        text += "📘 Общие команды:\n"
        text += "▪ /acadmin Nick_Name\n"
        text += "   ▹ Проверяет, есть ли у игрока админ-права (уровень 1 и выше) в базе данных.\n\n"
        if has_permission(user_id):
            text += "👑 Команды для администраторов:\n"
            text += "▪ /alog [админ] [цель/self]\n"
            text += "   ▹ Поиск по логам. /alog Nick - все логи. /alog Nick self - действия на себя. /alog Admin1 Admin2 - взаимодействие.\n\n"
            text += "▪ /setadmin Nick_Name 5\n"
            text += "   ▹ Устанавливает игроку Nick_Name админ-уровень 5.\n\n"
            text += "▪ /awarn Nick_Name +1 (или -1)\n"
            text += "   ▹ Добавляет (+1) или снимает (-1) один админ-варн игроку Nick_Name.\n\n"
            text += "▪ /prefix Nick_Name [New Prefix]\n"
            text += "   ▹ Устанавливает игроку Nick_Name новый префикс.\n\n"
            text += "▪ /sync\n"
            text += "   ▹ Запускает немедленную проверку админов из БД и ников из чата ВК.\n\n"
            text += "▪ /set_sync_chat и /set_notify_chat\n"
            text += "   ▹ Назначить текущий чат для синхронизации или уведомлений.\n\n"
        if is_developer(user_id):
            text += "🛠 Команды для разработчиков:\n"
            text += "▪ /grant_access и /revoke_access\n"
            text += "   ▹ Написать команду, ответив на сообщение пользователя, или упомянув его через @. Выдаёт/забирает права на админ-команды бота.\n\n"
            text += "▪ /access_list\n"
            text += "   ▹ Показывает, кто из пользователей ВК имеет доступ к боту.\n"
        return text

def is_developer(user_id): return user_id in DEVELOPER_IDS
def has_permission(user_id): return is_developer(user_id) or user_id in CONFIG.get('access_list', [])
def load_config():
    global CONFIG
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'r', encoding='utf-8') as f: CONFIG = json.load(f)
    else: CONFIG = {'target_chat_id': 0, 'notification_chat_id': 0}
    if 'access_list' not in CONFIG: CONFIG['access_list'] = []
    save_config()
def save_config():
    with open(CONFIG_FILE, 'w', encoding='utf-8') as f: json.dump(CONFIG, f, indent=4, ensure_ascii=False)
def get_db_connection():
    try: return mariadb.connect(**DB_CONFIG)
    except mariadb.Error: return None
def db_request(func, *args, **kwargs):
    return asyncio.get_event_loop().run_in_executor(None, func, *args, **kwargs)

def _set_admin_level(nickname, admin_level):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    try:
        cur = conn.cursor()
        cur.execute("UPDATE accounts SET admin = ? WHERE name = ?", (admin_level, nickname))
        msg = f"❌ Игрок '{nickname}' не найден." if cur.rowcount == 0 else f"✅ Админ-уровень игрока '{nickname}' изменен на {admin_level}."
        if cur.rowcount > 0: conn.commit()
    except mariadb.Error as err: msg = f"Ошибка БД: {err}"
    finally: conn.close()
    return msg
def _check_admin_status(nickname):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    try:
        cur = conn.cursor()
        cur.execute("SELECT admin FROM accounts WHERE name = ?", (nickname,))
        res = cur.fetchone()
        if not res: msg = f"❌ Игрок '{nickname}' не найден."
        else: msg = f"✅ Да, {nickname} является админом (ур: {res[0]})." if res[0] >= 1 else f"❌ Нет, {nickname} не админ (ур: {res[0]})."
    except mariadb.Error as err: msg = f"Ошибка БД: {err}"
    finally: conn.close()
    return msg
def _manage_admin_warn(nickname, change_amount):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    try:
        conn.autocommit = False
        cursor = conn.cursor()
        cursor.execute("SELECT admin_warn FROM accounts WHERE name = ? FOR UPDATE", (nickname,))
        result = cursor.fetchone()
        if result is None: msg = f"❌ Игрок '{nickname}' не найден."
        else:
            current_warns = result[0]
            new_warns = max(0, current_warns + change_amount)
            cursor.execute("UPDATE accounts SET admin_warn = ? WHERE name = ?", (new_warns, nickname))
            conn.commit()
            msg = f"✅ Варны администратора {nickname} изменены.\nБыло: {current_warns}, Стало: {new_warns}."
    except mariadb.Error as err: conn.rollback(); msg = f"Ошибка БД: {err}"
    finally: conn.autocommit = True; conn.close()
    return msg
def _set_prefix(nickname, new_prefix):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    if len(new_prefix.encode('cp1251', errors='ignore')) > 15: return f"❌ Ошибка: Префикс слишком длинный."
    try:
        cur = conn.cursor()
        cur.execute("UPDATE accounts SET prefix = ? WHERE name = ?", (new_prefix, nickname))
        msg = f"❌ Игрок '{nickname}' не найден." if cur.rowcount == 0 else f"✅ Префикс для '{nickname}' изменен на: {new_prefix}"
        if cur.rowcount > 0: conn.commit()
    except mariadb.Error as err: msg = f"Ошибка БД: {err}"
    finally: conn.close()
    return msg
def _get_account_info(identifier):
    query = "SELECT id, name FROM accounts WHERE id = ?" if identifier.isdigit() else "SELECT id, name FROM accounts WHERE name = ?"
    params = (int(identifier),) if identifier.isdigit() else (identifier,)
    conn = get_db_connection()
    if not conn: return None
    try: cur = conn.cursor(); cur.execute(query, params); return cur.fetchone()
    finally: conn.close()
def _get_admin_logs(actor_id, target_id=None):
    conn = get_db_connection()
    if not conn: return "Не удалось подключиться к БД."
    query, params = "SELECT description, time FROM action_log WHERE acc_id = ? ", [actor_id]
    if target_id is not None:
        query += "AND description LIKE ? "
        params.append(f"%[acc:{target_id}]%")
    query += "ORDER BY time DESC LIMIT 15"
    try:
        cur = conn.cursor(); cur.execute(query, tuple(params)); logs = cur.fetchall()
        if not logs: return "Логи для указанных критериев не найдены."
        return "\n".join([f"[{datetime.fromtimestamp(ts).strftime('%d.%m.%Y %H:%M:%S')}] {desc}" for desc, ts in logs])
    except mariadb.Error as err: return f"Ошибка БД: {err}"
    finally: conn.close()
def _get_all_db_admins():
    conn = get_db_connection()
    if not conn: return []
    try: cur = conn.cursor(); cur.execute("SELECT name FROM accounts WHERE admin >= 1"); return [r[0] for r in cur.fetchall()]
    finally: conn.close()
def parse_nicknames_from_message(text): return re.findall(r'—\s*(\w+)', text)
async def process_and_notify(parsed_nicks):
    nid = CONFIG.get('notification_chat_id', 0)
    if not nid: return
    db_admins = await db_request(_get_all_db_admins)
    parsed_nicks_lower = [n.lower() for n in parsed_nicks]
    missing = [a for a in db_admins if a.lower() not in parsed_nicks_lower]
    msg = "✅ Синхронизация завершена." if not missing else "⚠️ Внимание! Админы из БД, не найденные в ВК:\n" + "\n".join(f"{i}. {a}" for i, a in enumerate(missing, 1))
    await bot.api.messages.send(peer_id=nid, message=msg, random_id=0)
async def hourly_sync_task():
    logger.info("Hourly sync task started.")
    while True:
        await asyncio.sleep(3600)
        tid = CONFIG.get('target_chat_id', 0)
        if tid:
            try: await bot.api.messages.send(peer_id=tid, message="/nlist", random_id=0)
            except Exception as e: logger.error(f"Error in hourly sync: {e}")
def get_target_id_from_message(message: Message):
    if message.reply_message: return message.reply_message.from_id
    if message.fwd_messages: return message.fwd_messages[0].from_id
    match = re.search(r"\[id(\d+)\|.+\]", message.text)
    return int(match.group(1)) if match else None

# ИСПРАВЛЕННЫЙ raw event обработчик
@bot.on.raw_event("message_event", dataclass=dict)
async def handle_message_event(event: dict):
    try:
        if event["object"]["payload"].get("command") == "detailed_help":
            await bot.api.messages.edit(
                peer_id=event["object"]["peer_id"],
                conversation_message_id=event["object"]["conversation_message_id"],
                message=get_help_text(event["object"]["user_id"], detailed=True),
                keyboard=Keyboard().get_json()
            )
    except Exception as e:
        logger.error(f"Error in message_event handler: {e}")

@bot.on.message(text=["/help", "/помощь"])
async def help_handler(message: Message):
    keyboard = Keyboard(inline=True)
    keyboard.add(Callback("Для непонятных", payload={"command": "detailed_help"}))
    await message.answer(get_help_text(message.from_id), keyboard=keyboard)

@bot.on.message(text="/acadmin <nickname>")
async def acadmin_handler(message: Message, nickname: str):
    response = await db_request(_check_admin_status, nickname)
    await message.answer(response)

async def check_permissions(message: Message) -> bool:
    if not has_permission(message.from_id):
        await message.answer("❌ У вас недостаточно прав для выполнения этой команды.")
        return False
    return True

@bot.on.message(text="/setadmin <nickname> <level:int>")
async def setadmin_handler(message: Message, nickname: str, level: int):
    if not await check_permissions(message): return
    response = await db_request(_set_admin_level, nickname, level)
    await message.answer(response)

@bot.on.message(text="/awarn <nickname> <amount>")
async def awarn_handler(message: Message, nickname: str, amount: str):
    if not await check_permissions(message): return
    if (amount.startswith(('+', '-'))) and amount[1:].isdigit():
        response = await db_request(_manage_admin_warn, nickname, int(amount))
    else:
        response = "❌ Формат: /awarn ник +/-количество"
    await message.answer(response)

@bot.on.message(StartsWithRule("/prefix"))
async def prefix_handler(message: Message):
    if not await check_permissions(message): return
    parts = message.text.split()
    if len(parts) < 3:
        return await message.answer("❌ Формат: /prefix ник префикс")
    nickname = parts[1]
    prefix_text = " ".join(parts[2:])
    response = await db_request(_set_prefix, nickname, prefix_text)
    await message.answer(response)

@bot.on.message(StartsWithRule("/alog"))
async def alog_handler(message: Message):
    if not await check_permissions(message): return
    parts = message.text.split()
    if len(parts) < 2:
        return await message.answer("❌ Формат: /alog [ник/ID актора] [ник/ID цели | self]")
        
    actor_identifier = parts[1]
    actor_info = await db_request(_get_account_info, actor_identifier)
    
    actor_id, actor_name = None, None
    if actor_info:
        actor_id, actor_name = actor_info
    elif actor_identifier.isdigit():
        actor_id = int(actor_identifier)
        actor_name = f"Удаленный аккаунт"
    else:
        return await message.answer(f"❌ Администратор (актор) с ником '{actor_identifier}' не найден.")

    target_id = None
    header = f"📋 Все логи для {actor_name}[{actor_id}]:\n\n"
    
    if len(parts) > 2:
        target_identifier = parts[2]
        if target_identifier.lower() == 'self':
            target_id = actor_id
            header = f"📋 Логи действий {actor_name}[{actor_id}] на самого себя:\n\n"
        else:
            target_info = await db_request(_get_account_info, target_identifier)
            if not target_info:
                if target_identifier.isdigit():
                    target_id = int(target_identifier)
                    target_name = "Удаленный/несуществующий аккаунт"
                else:
                    return await message.answer(f"❌ Целевой игрок '{target_identifier}' не найден.")
            else:
                target_id, target_name = target_info
            header = f"📋 Логи {actor_name}[{actor_id}] для {target_name}[{target_id}]:\n\n"
    
    response_logs = await db_request(_get_admin_logs, actor_id, target_id)
    await message.answer(header + response_logs)

@bot.on.message(text="/sync")
async def sync_handler(message: Message):
    if not await check_permissions(message): return
    tid = CONFIG.get('target_chat_id', 0)
    if not tid: return await message.answer("❌ Sync-чат не настроен.")
    await message.answer("Отправляю /nlist...")
    await bot.api.messages.send(peer_id=tid, message="/nlist", random_id=0)

@bot.on.message(text="/set_sync_chat")
async def set_sync_chat_handler(message: Message):
    if not await check_permissions(message): return
    CONFIG['target_chat_id'] = message.peer_id
    save_config()
    await message.answer("✅ Этот чат назначен для отправки /nlist.")

@bot.on.message(text="/set_notify_chat")
async def set_notify_chat_handler(message: Message):
    if not await check_permissions(message): return
    CONFIG['notification_chat_id'] = message.peer_id
    save_config()
    await message.answer("✅ Сюда будут приходить уведомления.")

@bot.on.message(text="/show_config")
async def show_config_handler(message: Message):
    if not await check_permissions(message): return
    sync, notify = CONFIG.get('target_chat_id', 0), CONFIG.get('notification_chat_id', 0)
    await message.answer(f"Настройки:\nSync-чат: {sync or 'Не задан'}\nNotify-чат: {notify or 'Не задан'}")

async def check_developer(message: Message) -> bool:
    if not is_developer(message.from_id):
        await message.answer("❌ У вас недостаточно прав для выполнения этой команды.")
        return False
    return True

@bot.on.message(text="/grant_access")
async def grant_access_handler(message: Message):
    if not await check_developer(message): return
    target_id = get_target_id_from_message(message)
    if not target_id: return await message.answer("❌ Укажите пользователя (ответом или @упоминанием).")
    if target_id in CONFIG['access_list']: return await message.answer(f"✅ Пользователь [id{target_id}|уже] имеет доступ.")
    CONFIG['access_list'].append(target_id); save_config()
    await message.answer(f"✅ Доступ для [id{target_id}|пользователя] выдан.")

@bot.on.message(text="/revoke_access")
async def revoke_access_handler(message: Message):
    if not await check_developer(message): return
    target_id = get_target_id_from_message(message)
    if not target_id: return await message.answer("❌ Укажите пользователя.")
    if is_developer(target_id): return await message.answer("❌ Нельзя отозвать права у разработчика.")
    if target_id not in CONFIG['access_list']: return await message.answer(f"❌ У пользователя [id{target_id}|уже] нет доступа.")
    CONFIG['access_list'].remove(target_id); save_config()
    await message.answer(f"✅ Доступ для [id{target_id}|пользователя] отозван.")

@bot.on.message(text="/access_list")
async def access_list_handler(message: Message):
    if not await check_developer(message): return
    msg = "👥 Список пользователей с доступом к боту:\n\n"
    all_ids = DEVELOPER_IDS + CONFIG['access_list']
    if not all_ids: return await message.answer(msg + "Список пуст.")
    users_info = await bot.api.users.get(user_ids=all_ids)
    for user in users_info:
        status = "Разработчик" if user.id in DEVELOPER_IDS else "Администратор"
        msg += f"▪ [id{user.id}|{user.first_name} {user.last_name}] — {status}\n"
    await message.answer(msg)
    
@bot.on.message()
async def default_handler(message: Message):
    if message.peer_id == CONFIG.get('target_chat_id', 0) and "Список пользователей с никами:" in message.text:
        parsed_nicks = parse_nicknames_from_message(message.text)
        if parsed_nicks:
            await process_and_notify(parsed_nicks)

async def on_startup():
    load_config()
    logger.info("Bot started!")
    print("✅ БОТ ЗАПУЩЕН И РАБОТАЕТ!")
    print("📝 Команды: /help")
    print("👑 ID разработчиков:", DEVELOPER_IDS)
    asyncio.create_task(hourly_sync_task())

if __name__ == "__main__":
    bot.loop_wrapper.add_task(on_startup())
    print("🔄 Запуск бота...")
    bot.run_forever()
 
Дек
13
10
Пользователь
что делать у меня ошибка
"MessageMin" is immutable and does not support item assignment
Traceback (most recent call last):
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\dispatch\base.py", line 22, in route
await view.handle_event(event, ctx_api, self.state_dispenser)
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\dispatch\views\abc\message.py", line 48, in handle_event
message = await self.get_message(event, ctx_api)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\dispatch\views\bot\message.py", line 28, in get_message
return message_min(event, ctx_api)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Acer\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\vkbottle\tools\dev\mini_types\bot\message.py", line 75, in message_min
setattr(message, "unprepared_ctx_api", ctx_api)
File "pydantic/main.py", line 382, in pydantic.main.BaseModel.__setattr__
TypeError: "MessageMin" is immutable and does not support item assignment
вот код:
BOT:
import vk_api
from vkbottle.bot import Bot, Message
from vkbottle import Keyboard, Callback, CtxStorage
from vkbottle.dispatch.rules import ABCRule
from vkbottle.modules import logger
import mariadb
import sys
import re
import asyncio
import time
import json
import os
from datetime import datetime

VK_TOKEN = 'vk1.a.WECZua7uDU4S9tRyznSbDbcMu40xp2af808L7s2YUzkkrgZ8_duoFqevXGViR5xk64K6NjCUcA9gaOVsg83MarFF8h4PdFO4zyyiSa_LVSOK7t-gIz7MEriAsId-0J3A3PE4UdVFPE1WyvW4kouePg-s_Eu3t0rAC2-UXo4sOiZsIt05hJhGYxL3h2iwViP49oNLCO_NKJDiXY4AXUCLWw'
GROUP_ID = 229693070

DB_CONFIG = {
    'host': '172.18.0.1',
    'user': 'gs123452',
    'password': 'ta727DeiSeH7',
    'database': 'gs123452',
    'port': '3306'
}

CONFIG_FILE = 'config.json'
CONFIG = {}
DEVELOPER_IDS = [743580182, 425762738]
bot = Bot(token=VK_TOKEN)

class StartsWithRule(ABCRule[Message]):
    def __init__(self, command: str):
        self.command = command.lower()

    async def check(self, event: Message) -> bool:
        return event.text.lower().startswith(self.command)

def get_help_text(user_id, detailed=False):
    if not detailed:
        text = "СПИСОК КОМАНД БОТА\n\n"
        text += "📘 Общие команды:\n"
        text += "▪ /acadmin [ник] — Проверить, является ли игрок админом.\n"
        text += "▪ /help — Показать это сообщение.\n"
        if has_permission(user_id):
            text += "\n👑 Команды для администраторов:\n"
            text += "▪ /setadmin [ник] [уровень] — Установить админ-уровень.\n"
            text += "▪ /awarn [ник] [+/-число] — Изменить кол-во админ-варнов.\n"
            text += "▪ /prefix [ник] [префикс] — Установить префикс.\n"
            text += "▪ /alog [ник/ID] ... — Поиск по логам администратора.\n"
            text += "▪ /sync — Принудительная синхронизация ников.\n"
            text += "▪ /set_sync_chat — Назначить чат для синхронизации.\n"
            text += "▪ /set_notify_chat — Назначить чат для уведомлений.\n"
            text += "▪ /show_config — Показать текущие настройки чатов.\n"
        if is_developer(user_id):
            text += "\n🛠 Команды для разработчиков:\n"
            text += "▪ /grant_access — Выдать доступ к боту.\n"
            text += "▪ /revoke_access — Забрать доступ к боту.\n"
            text += "▪ /access_list — Показать список с доступом.\n"
        return text
    else:
        text = "ПОДРОБНОЕ ОПИСАНИЕ КОМАНД\n\n"
        text += "📘 Общие команды:\n"
        text += "▪ /acadmin Nick_Name\n"
        text += "   ▹ Проверяет, есть ли у игрока админ-права (уровень 1 и выше) в базе данных.\n\n"
        if has_permission(user_id):
            text += "👑 Команды для администраторов:\n"
            text += "▪ /alog [админ] [цель/self]\n"
            text += "   ▹ Поиск по логам. /alog Nick - все логи. /alog Nick self - действия на себя. /alog Admin1 Admin2 - взаимодействие.\n\n"
            text += "▪ /setadmin Nick_Name 5\n"
            text += "   ▹ Устанавливает игроку Nick_Name админ-уровень 5.\n\n"
            text += "▪ /awarn Nick_Name +1 (или -1)\n"
            text += "   ▹ Добавляет (+1) или снимает (-1) один админ-варн игроку Nick_Name.\n\n"
            text += "▪ /prefix Nick_Name [New Prefix]\n"
            text += "   ▹ Устанавливает игроку Nick_Name новый префикс.\n\n"
            text += "▪ /sync\n"
            text += "   ▹ Запускает немедленную проверку админов из БД и ников из чата ВК.\n\n"
            text += "▪ /set_sync_chat и /set_notify_chat\n"
            text += "   ▹ Назначить текущий чат для синхронизации или уведомлений.\n\n"
        if is_developer(user_id):
            text += "🛠 Команды для разработчиков:\n"
            text += "▪ /grant_access и /revoke_access\n"
            text += "   ▹ Написать команду, ответив на сообщение пользователя, или упомянув его через @. Выдаёт/забирает права на админ-команды бота.\n\n"
            text += "▪ /access_list\n"
            text += "   ▹ Показывает, кто из пользователей ВК имеет доступ к боту.\n"
        return text

def is_developer(user_id): return user_id in DEVELOPER_IDS
def has_permission(user_id): return is_developer(user_id) or user_id in CONFIG.get('access_list', [])
def load_config():
    global CONFIG
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'r', encoding='utf-8') as f: CONFIG = json.load(f)
    else: CONFIG = {'target_chat_id': 0, 'notification_chat_id': 0}
    if 'access_list' not in CONFIG: CONFIG['access_list'] = []
    save_config()
def save_config():
    with open(CONFIG_FILE, 'w', encoding='utf-8') as f: json.dump(CONFIG, f, indent=4, ensure_ascii=False)
def get_db_connection():
    try: return mariadb.connect(**DB_CONFIG)
    except mariadb.Error: return None
def db_request(func, *args, **kwargs):
    return asyncio.get_event_loop().run_in_executor(None, func, *args, **kwargs)

def _set_admin_level(nickname, admin_level):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    try:
        cur = conn.cursor()
        cur.execute("UPDATE accounts SET admin = ? WHERE name = ?", (admin_level, nickname))
        msg = f"❌ Игрок '{nickname}' не найден." if cur.rowcount == 0 else f"✅ Админ-уровень игрока '{nickname}' изменен на {admin_level}."
        if cur.rowcount > 0: conn.commit()
    except mariadb.Error as err: msg = f"Ошибка БД: {err}"
    finally: conn.close()
    return msg
def _check_admin_status(nickname):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    try:
        cur = conn.cursor()
        cur.execute("SELECT admin FROM accounts WHERE name = ?", (nickname,))
        res = cur.fetchone()
        if not res: msg = f"❌ Игрок '{nickname}' не найден."
        else: msg = f"✅ Да, {nickname} является админом (ур: {res[0]})." if res[0] >= 1 else f"❌ Нет, {nickname} не админ (ур: {res[0]})."
    except mariadb.Error as err: msg = f"Ошибка БД: {err}"
    finally: conn.close()
    return msg
def _manage_admin_warn(nickname, change_amount):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    try:
        conn.autocommit = False
        cursor = conn.cursor()
        cursor.execute("SELECT admin_warn FROM accounts WHERE name = ? FOR UPDATE", (nickname,))
        result = cursor.fetchone()
        if result is None: msg = f"❌ Игрок '{nickname}' не найден."
        else:
            current_warns = result[0]
            new_warns = max(0, current_warns + change_amount)
            cursor.execute("UPDATE accounts SET admin_warn = ? WHERE name = ?", (new_warns, nickname))
            conn.commit()
            msg = f"✅ Варны администратора {nickname} изменены.\nБыло: {current_warns}, Стало: {new_warns}."
    except mariadb.Error as err: conn.rollback(); msg = f"Ошибка БД: {err}"
    finally: conn.autocommit = True; conn.close()
    return msg
def _set_prefix(nickname, new_prefix):
    conn, msg = get_db_connection(), "Не удалось подключиться к БД."
    if not conn: return msg
    if len(new_prefix.encode('cp1251', errors='ignore')) > 15: return f"❌ Ошибка: Префикс слишком длинный."
    try:
        cur = conn.cursor()
        cur.execute("UPDATE accounts SET prefix = ? WHERE name = ?", (new_prefix, nickname))
        msg = f"❌ Игрок '{nickname}' не найден." if cur.rowcount == 0 else f"✅ Префикс для '{nickname}' изменен на: {new_prefix}"
        if cur.rowcount > 0: conn.commit()
    except mariadb.Error as err: msg = f"Ошибка БД: {err}"
    finally: conn.close()
    return msg
def _get_account_info(identifier):
    query = "SELECT id, name FROM accounts WHERE id = ?" if identifier.isdigit() else "SELECT id, name FROM accounts WHERE name = ?"
    params = (int(identifier),) if identifier.isdigit() else (identifier,)
    conn = get_db_connection()
    if not conn: return None
    try: cur = conn.cursor(); cur.execute(query, params); return cur.fetchone()
    finally: conn.close()
def _get_admin_logs(actor_id, target_id=None):
    conn = get_db_connection()
    if not conn: return "Не удалось подключиться к БД."
    query, params = "SELECT description, time FROM action_log WHERE acc_id = ? ", [actor_id]
    if target_id is not None:
        query += "AND description LIKE ? "
        params.append(f"%[acc:{target_id}]%")
    query += "ORDER BY time DESC LIMIT 15"
    try:
        cur = conn.cursor(); cur.execute(query, tuple(params)); logs = cur.fetchall()
        if not logs: return "Логи для указанных критериев не найдены."
        return "\n".join([f"[{datetime.fromtimestamp(ts).strftime('%d.%m.%Y %H:%M:%S')}] {desc}" for desc, ts in logs])
    except mariadb.Error as err: return f"Ошибка БД: {err}"
    finally: conn.close()
def _get_all_db_admins():
    conn = get_db_connection()
    if not conn: return []
    try: cur = conn.cursor(); cur.execute("SELECT name FROM accounts WHERE admin >= 1"); return [r[0] for r in cur.fetchall()]
    finally: conn.close()
def parse_nicknames_from_message(text): return re.findall(r'—\s*(\w+)', text)
async def process_and_notify(parsed_nicks):
    nid = CONFIG.get('notification_chat_id', 0)
    if not nid: return
    db_admins = await db_request(_get_all_db_admins)
    parsed_nicks_lower = [n.lower() for n in parsed_nicks]
    missing = [a for a in db_admins if a.lower() not in parsed_nicks_lower]
    msg = "✅ Синхронизация завершена." if not missing else "⚠️ Внимание! Админы из БД, не найденные в ВК:\n" + "\n".join(f"{i}. {a}" for i, a in enumerate(missing, 1))
    await bot.api.messages.send(peer_id=nid, message=msg, random_id=0)
async def hourly_sync_task():
    logger.info("Hourly sync task started.")
    while True:
        await asyncio.sleep(3600)
        tid = CONFIG.get('target_chat_id', 0)
        if tid:
            try: await bot.api.messages.send(peer_id=tid, message="/nlist", random_id=0)
            except Exception as e: logger.error(f"Error in hourly sync: {e}")
def get_target_id_from_message(message: Message):
    if message.reply_message: return message.reply_message.from_id
    if message.fwd_messages: return message.fwd_messages[0].from_id
    match = re.search(r"\[id(\d+)\|.+\]", message.text)
    return int(match.group(1)) if match else None

# ИСПРАВЛЕННЫЙ raw event обработчик
@bot.on.raw_event("message_event", dataclass=dict)
async def handle_message_event(event: dict):
    try:
        if event["object"]["payload"].get("command") == "detailed_help":
            await bot.api.messages.edit(
                peer_id=event["object"]["peer_id"],
                conversation_message_id=event["object"]["conversation_message_id"],
                message=get_help_text(event["object"]["user_id"], detailed=True),
                keyboard=Keyboard().get_json()
            )
    except Exception as e:
        logger.error(f"Error in message_event handler: {e}")

@bot.on.message(text=["/help", "/помощь"])
async def help_handler(message: Message):
    keyboard = Keyboard(inline=True)
    keyboard.add(Callback("Для непонятных", payload={"command": "detailed_help"}))
    await message.answer(get_help_text(message.from_id), keyboard=keyboard)

@bot.on.message(text="/acadmin <nickname>")
async def acadmin_handler(message: Message, nickname: str):
    response = await db_request(_check_admin_status, nickname)
    await message.answer(response)

async def check_permissions(message: Message) -> bool:
    if not has_permission(message.from_id):
        await message.answer("❌ У вас недостаточно прав для выполнения этой команды.")
        return False
    return True

@bot.on.message(text="/setadmin <nickname> <level:int>")
async def setadmin_handler(message: Message, nickname: str, level: int):
    if not await check_permissions(message): return
    response = await db_request(_set_admin_level, nickname, level)
    await message.answer(response)

@bot.on.message(text="/awarn <nickname> <amount>")
async def awarn_handler(message: Message, nickname: str, amount: str):
    if not await check_permissions(message): return
    if (amount.startswith(('+', '-'))) and amount[1:].isdigit():
        response = await db_request(_manage_admin_warn, nickname, int(amount))
    else:
        response = "❌ Формат: /awarn ник +/-количество"
    await message.answer(response)

@bot.on.message(StartsWithRule("/prefix"))
async def prefix_handler(message: Message):
    if not await check_permissions(message): return
    parts = message.text.split()
    if len(parts) < 3:
        return await message.answer("❌ Формат: /prefix ник префикс")
    nickname = parts[1]
    prefix_text = " ".join(parts[2:])
    response = await db_request(_set_prefix, nickname, prefix_text)
    await message.answer(response)

@bot.on.message(StartsWithRule("/alog"))
async def alog_handler(message: Message):
    if not await check_permissions(message): return
    parts = message.text.split()
    if len(parts) < 2:
        return await message.answer("❌ Формат: /alog [ник/ID актора] [ник/ID цели | self]")
       
    actor_identifier = parts[1]
    actor_info = await db_request(_get_account_info, actor_identifier)
   
    actor_id, actor_name = None, None
    if actor_info:
        actor_id, actor_name = actor_info
    elif actor_identifier.isdigit():
        actor_id = int(actor_identifier)
        actor_name = f"Удаленный аккаунт"
    else:
        return await message.answer(f"❌ Администратор (актор) с ником '{actor_identifier}' не найден.")

    target_id = None
    header = f"📋 Все логи для {actor_name}[{actor_id}]:\n\n"
   
    if len(parts) > 2:
        target_identifier = parts[2]
        if target_identifier.lower() == 'self':
            target_id = actor_id
            header = f"📋 Логи действий {actor_name}[{actor_id}] на самого себя:\n\n"
        else:
            target_info = await db_request(_get_account_info, target_identifier)
            if not target_info:
                if target_identifier.isdigit():
                    target_id = int(target_identifier)
                    target_name = "Удаленный/несуществующий аккаунт"
                else:
                    return await message.answer(f"❌ Целевой игрок '{target_identifier}' не найден.")
            else:
                target_id, target_name = target_info
            header = f"📋 Логи {actor_name}[{actor_id}] для {target_name}[{target_id}]:\n\n"
   
    response_logs = await db_request(_get_admin_logs, actor_id, target_id)
    await message.answer(header + response_logs)

@bot.on.message(text="/sync")
async def sync_handler(message: Message):
    if not await check_permissions(message): return
    tid = CONFIG.get('target_chat_id', 0)
    if not tid: return await message.answer("❌ Sync-чат не настроен.")
    await message.answer("Отправляю /nlist...")
    await bot.api.messages.send(peer_id=tid, message="/nlist", random_id=0)

@bot.on.message(text="/set_sync_chat")
async def set_sync_chat_handler(message: Message):
    if not await check_permissions(message): return
    CONFIG['target_chat_id'] = message.peer_id
    save_config()
    await message.answer("✅ Этот чат назначен для отправки /nlist.")

@bot.on.message(text="/set_notify_chat")
async def set_notify_chat_handler(message: Message):
    if not await check_permissions(message): return
    CONFIG['notification_chat_id'] = message.peer_id
    save_config()
    await message.answer("✅ Сюда будут приходить уведомления.")

@bot.on.message(text="/show_config")
async def show_config_handler(message: Message):
    if not await check_permissions(message): return
    sync, notify = CONFIG.get('target_chat_id', 0), CONFIG.get('notification_chat_id', 0)
    await message.answer(f"Настройки:\nSync-чат: {sync or 'Не задан'}\nNotify-чат: {notify or 'Не задан'}")

async def check_developer(message: Message) -> bool:
    if not is_developer(message.from_id):
        await message.answer("❌ У вас недостаточно прав для выполнения этой команды.")
        return False
    return True

@bot.on.message(text="/grant_access")
async def grant_access_handler(message: Message):
    if not await check_developer(message): return
    target_id = get_target_id_from_message(message)
    if not target_id: return await message.answer("❌ Укажите пользователя (ответом или @упоминанием).")
    if target_id in CONFIG['access_list']: return await message.answer(f"✅ Пользователь [id{target_id}|уже] имеет доступ.")
    CONFIG['access_list'].append(target_id); save_config()
    await message.answer(f"✅ Доступ для [id{target_id}|пользователя] выдан.")

@bot.on.message(text="/revoke_access")
async def revoke_access_handler(message: Message):
    if not await check_developer(message): return
    target_id = get_target_id_from_message(message)
    if not target_id: return await message.answer("❌ Укажите пользователя.")
    if is_developer(target_id): return await message.answer("❌ Нельзя отозвать права у разработчика.")
    if target_id not in CONFIG['access_list']: return await message.answer(f"❌ У пользователя [id{target_id}|уже] нет доступа.")
    CONFIG['access_list'].remove(target_id); save_config()
    await message.answer(f"✅ Доступ для [id{target_id}|пользователя] отозван.")

@bot.on.message(text="/access_list")
async def access_list_handler(message: Message):
    if not await check_developer(message): return
    msg = "👥 Список пользователей с доступом к боту:\n\n"
    all_ids = DEVELOPER_IDS + CONFIG['access_list']
    if not all_ids: return await message.answer(msg + "Список пуст.")
    users_info = await bot.api.users.get(user_ids=all_ids)
    for user in users_info:
        status = "Разработчик" if user.id in DEVELOPER_IDS else "Администратор"
        msg += f"▪ [id{user.id}|{user.first_name} {user.last_name}] — {status}\n"
    await message.answer(msg)
   
@bot.on.message()
async def default_handler(message: Message):
    if message.peer_id == CONFIG.get('target_chat_id', 0) and "Список пользователей с никами:" in message.text:
        parsed_nicks = parse_nicknames_from_message(message.text)
        if parsed_nicks:
            await process_and_notify(parsed_nicks)

async def on_startup():
    load_config()
    logger.info("Bot started!")
    print("✅ БОТ ЗАПУЩЕН И РАБОТАЕТ!")
    print("📝 Команды: /help")
    print("👑 ID разработчиков:", DEVELOPER_IDS)
    asyncio.create_task(hourly_sync_task())

if __name__ == "__main__":
    bot.loop_wrapper.add_task(on_startup())
    print("🔄 Запуск бота...")
    bot.run_forever()


pip uninstall vkbottle pydantic -y
pip install vkbottle==2.0.0 pydantic==1.10.13
 
Окт
256
148
Пользователь
pip uninstall vkbottle pydantic -y
pip install vkbottle==2.0.0 pydantic==1.10.13
C:\Users\Acer>pip install vkbottle==2.0.0 pydantic==1.10.13
Defaulting to user installation because normal site-packages is not writeable
ERROR: Could not find a version that satisfies the requirement vkbottle==2.0.0 (from versions: 1.0.0, 2.7.1, 2.7.2, 2.7.3, 2.7.4, 2.7.5, 2.7.6, 2.7.7, 2.7.8, 2.7.9, 2.7.10, 2.7.11, 2.7.12, 3.0, 3.0.1, 3.0.2, 3.0.3, 4.0.0, 4.0.1, 4.0.2, 4.0.3, 4.0.4, 4.1.0, 4.1.1, 4.1.2, 4.1.3, 4.1.4, 4.1.4.post1, 4.1.5, 4.1.6, 4.1.8, 4.1.9, 4.1.10, 4.1.11, 4.1.12, 4.1.13, 4.2.0, 4.2.1, 4.2.2, 4.3.0, 4.3.1, 4.3.2, 4.3.3, 4.3.4, 4.3.5, 4.3.6, 4.3.7, 4.3.8, 4.3.9, 4.3.10, 4.3.11, 4.3.12, 4.4.0.dev1, 4.4.1.dev1, 4.4.1, 4.4.2, 4.4.2.post1, 4.4.3, 4.4.3.post1, 4.4.4, 4.4.5, 4.4.6, 4.5.0, 4.5.1, 4.5.2, 4.6.0, 4.6.1, 4.6.2)

[notice] A new release of pip is available: 25.0.1 -> 26.0.1
[notice] To update, run: C:\Users\Acer\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\python.exe -m pip install --upgrade pip
ERROR: No matching distribution found for vkbottle==2.0.0
 
Сверху