Получение пустого сообщения при нажатии на кнопку в Telegram

Хочу прописать панель управления, которая выводит 2 кнопки по нажатии которых выполняются отдельные команды

Панель сделал, кнопки сделал, но нажимая на них в терминал выводится следующее

[2025-02-26 01:05:39] Получено сообщение: /panel [2025-02-26 01:05:39] handle_message вызвана с сообщением: /panel (после нажатия на кнопку) [2025-02-26 01:05:40] Получено обновление без текста сообщения

(заранее извиняюсь за оформление, постарался оставить только то, что как то может взаимодействовать с задачей)

import telebot
import signal
import sys
import datetime
import time
import os
import sqlite3
import re
import pickle  # Импортируем pickle
import threading
from telebot import types

# Список ID разработчиков (имеют полный доступ)
DEV_USERS_IDS = [1241613863]

# Имя файла базы данных
DATABASE_FILE = "bot_database.db"

# Имя таблицы для мутов
MUTES_TABLE = "mutes"

bot = telebot.TeleBot(BOT_TOKEN)
ADMIN_USER_IDS = []
running = True

def load_admins_from_database():
    """Загружает ID администраторов из базы данных в список ADMIN_USER_IDS."""
    global ADMIN_USER_IDS
    try:
        conn = sqlite3.connect(DATABASE_FILE)
        cursor = conn.cursor()
        cursor.execute("SELECT user_id FROM admins")
        ADMIN_USER_IDS = [row[0] for row in cursor.fetchall()]
        conn.close()
        log_message(f"Загружены администраторы из базы данных: {ADMIN_USER_IDS}")
    except Exception as e:
        log_message(f"Ошибка при загрузке администраторов из базы данных: {e}")

def save_last_update_info(update_id, last_update_time):
    """Сохраняет last_update_id и last_update_time в файл."""
    try:
        with open("last_update_info.pickle", "wb") as f:
            pickle.dump((update_id, last_update_time), f)
        log_message(f"last_update_id {update_id} и last_update_time {last_update_time} сохранены в файл.")
    except Exception as e:
        log_message(f"Ошибка при сохранении last_update_info: {e}")


def load_last_update_info():
    """Загружает last_update_id и last_update_time из файла."""
    try:
        with open("last_update_info.pickle", "rb") as f:
            update_id, last_update_time = pickle.load(f)
        log_message(f"last_update_id {update_id} и last_update_time {last_update_time} загружены из файла.")
        return update_id, last_update_time
    except FileNotFoundError:
        log_message("Файл last_update_info не найден, начинаем с 0 и текущим временем.")
        return 0, datetime.datetime.now(datetime.timezone.utc)
    except Exception as e:
        log_message(f"Ошибка при загрузке last_update_info: {e}")
        return 0, datetime.datetime.now(datetime.timezone.utc)

def is_dev_user(user_id):
    return user_id in DEV_USERS_IDS


def is_admin(user_id):
    return user_id in ADMIN_USER_IDS

@bot.message_handler(commands=['panel'])
def panel_command(message):
    """Выводит панель разработчика с инлайн-кнопками."""
    user_id = message.from_user.id
    if is_dev_user(user_id) or is_admin(user_id):
        markup = types.InlineKeyboardMarkup(row_width=2)
        item1 = types.InlineKeyboardButton("Ping", callback_data='ping')
        item2 = types.InlineKeyboardButton("Restart", callback_data='restart')
        markup.add(item1, item2)
        bot.send_message(message.chat.id, "Панель разработчика. Упрощённое управление", reply_markup=markup)
    else:
        bot.reply_to(message, "У вас нет прав для выполнения этой команды.")

@bot.callback_query_handler(func=lambda call: True)
def callback_inline(call):
    """Обрабатывает нажатия на инлайн-кнопки."""
    try:
        print(f"Получен callback_query: {call.data}")
        if call.message:
            if call.data == 'ping':
                print("Отправляем команду /ping")
                bot.send_message(call.message.chat.id, "/ping")
            elif call.data == 'restart':
                print("Отправляем команду /restart")
                bot.send_message(call.message.chat.id, "/restart")

            # Убираем кнопки после нажатия (опционально)
            bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id, reply_markup=None)

    except Exception as e:
        print(f"Ошибка при обработке callback: {e}")

    except Exception as e:
        print(f"Ошибка при обработке callback: {e}")

def get_user_access_level(user_id):
    """Определяет уровень доступа пользователя (Пользователь, Администратор, Разработчик)."""
    if is_dev_user(user_id):
        return "Разработчик"
    elif is_admin(user_id):
        return "Администратор"
    else:
        return "Пользователь"

# Обработчик для перехвата удаленных сообщений
@bot.message_handler(content_types=['text', 'photo', 'video', 'document', 'audio', 'sticker', 'location', 'contact'], func=lambda message: True)
def log_deleted_message(message):
    print("log_deleted_message вызвана!")  # Добавляем print
    if is_log_chat(message.chat.id):
        try:
            #bot.delete_message(message.chat.id, message.message_id) #Теперь не удаляем
            log_message(f"Сообщение от {message.from_user.id}: {message.text} в чате: {message.chat.id}") #Убираем проверку на текст
            bot.send_message(DEV_CHAT_ID, f"Сообщение от {message.from_user.id}: {message.text} в чате: {message.chat.id}")
        except Exception as e:
            log_message(f"Ошибка при логировании удаленного сообщения: {e}")

# Обработчик для перехвата измененных сообщений
@bot.edited_message_handler(func=lambda message: True)
def log_edited_message(message):
    print("log_edited_message вызвана!")  # Добавляем print
    if is_log_chat(message.chat.id):
        try:
            bot.send_message(DEV_CHAT_ID, f"Изменено сообщение: {message.text} в чате: {message.chat.id}")
            log_message(f"Изменено сообщение: {message.text} в чате: {message.chat.id}")
        except Exception as e:
            log_message(f"Ошибка при логировании измененного сообщения: {e}")

def check_mutes():
    """Проверяет, не истекло ли время мута, и снимает мут."""
    while running:
        try:
            conn = sqlite3.connect(DATABASE_FILE)
            cursor = conn.cursor()
            cursor.execute(f"SELECT user_id, chat_id FROM {MUTES_TABLE} WHERE end_time < ?", (time.time(),))
            mutes_to_remove = cursor.fetchall()
            conn.close()

            for user_id, chat_id in mutes_to_remove:
                unmute_user(user_id, chat_id) #Удаляем проверку

        except Exception as e:
            log_message(f"Ошибка при проверке мутов: {e}")

        time.sleep(60)  # Проверяем каждую минуту

def start_mute_checker():
    """Запускает поток для проверки и снятия мутов."""
    mute_checker_thread = threading.Thread(target=check_mutes)
    mute_checker_thread.daemon = True
    mute_checker_thread.start()

@bot.message_handler(commands=['mute'])
def mute_command(message):
    """Замутить пользователя."""
    user_id = message.from_user.id
    if is_dev_user(user_id) or is_admin(user_id):
        try:
            if message.reply_to_message:
                user_id_to_mute = message.reply_to_message.from_user.id
                if is_muted(user_id_to_mute, message.chat.id):  # Проверяем, замучен ли уже
                    bot.reply_to(message, f"Пользователь {message.reply_to_message.from_user.username} уже замучен в этом чате.")
                    return
                try:
                    mute_duration = int(message.text.split(" ", 1)[1])  # Достаём длительность мута из команды
                    mute_user(user_id_to_mute, message.chat.id, mute_duration)
                    bot.reply_to(message, f"Пользователь {message.reply_to_message.from_user.username} замучен на {mute_duration} секунд.")
                except ValueError:
                    bot.reply_to(message, "Неверный формат времени. Укажите время в секундах.")
                except IndexError:
                    bot.reply_to(message, "Укажите время мута в секундах.")

            else:
                try:
                    user_id_to_mute = int(message.text.split()[1])
                    if is_muted(user_id_to_mute, message.chat.id):  # Проверяем, замучен ли уже
                        bot.reply_to(message, f"Пользователь с ID {user_id_to_mute} уже замучен в этом чате.")
                        return
                    mute_duration = int(message.text.split()[2])
                    mute_user(user_id_to_mute, message.chat.id, mute_duration)
                    bot.reply_to(message, f"Пользователь с ID {user_id_to_mute} замучен на {mute_duration} секунд.")
                except ValueError:
                    bot.reply_to(message, "Неверный формат ID или времени. Укажите ID пользователя и время в секундах.")
                except IndexError:
                    bot.reply_to(message, "Укажите ID пользователя и время мута в секундах.")

        except Exception as e:
            bot.reply_to(message, f"Ошибка при выполнении команды: {e}")
    else:
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(commands=['unmute'])
def unmute_command(message):
    """Снимает мут с пользователя."""
    user_id = message.from_user.id
    if is_dev_user(user_id) or is_admin(user_id):
        try:
            if message.reply_to_message:
                user_id_to_unmute = message.reply_to_message.from_user.id
                if not is_muted(user_id_to_unmute, message.chat.id):  # Проверяем, замучен ли вообще
                    bot.reply_to(message, f"Пользователь {message.reply_to_message.from_user.username} не замучен в этом чате.")
                    return
                unmute_user(user_id_to_unmute, message.chat.id, send_notification=False)  # Убираем отправку сообщения
                bot.reply_to(message, f"Пользователь {message.reply_to_message.from_user.username} размучен.")

            else:
                try:
                    user_id_to_unmute = int(message.text.split()[1])
                    if not is_muted(user_id_to_unmute, message.chat.id):  # Проверяем, замучен ли вообще
                        bot.reply_to(message, f"Пользователь с ID {user_id_to_unmute} не замучен в этом чате.")
                        return
                    unmute_user(user_id_to_unmute, message.chat.id, send_notification=False)  # Убираем отправку сообщения
                    bot.reply_to(message, f"Пользователь с ID {user_id_to_unmute} размучен.")
                except ValueError:
                    bot.reply_to(message, "Неверный формат ID пользователя.")
                except IndexError:
                    bot.reply_to(message, "Укажите ID пользователя.")

        except Exception as e:
            bot.reply_to(message, f"Ошибка при выполнении команды: {e}")
    else:
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(func=lambda message: True, content_types=['text', 'photo', 'video', 'document', 'audio', 'sticker'])
def check_muted(message):
    """Проверяет, не замучен ли пользователь."""
    is_user_muted = is_muted(message.from_user.id, message.chat.id)
    if message.chat.type in ['group', 'supergroup']:
        if is_user_muted:
            try:
                bot.delete_message(message.chat.id, message.message_id)
                log_message(f"Удалено сообщение от замученного пользователя {message.from_user.id} в чате {message.chat.id}.")
            except Exception as e:
                log_message(f"Ошибка при удалении сообщения: {e}")
    elif message.chat.type == 'private' and is_user_muted:
        try:
            # Тут можно либо ничего не делать, либо ответить пользователю, что он замучен
            bot.send_message(message.chat.id, "Вы не можете отправлять сообщения, так как замучены в группе.")
            log_message(f"Попытка отправки сообщения от замученного пользователя {message.from_user.id} в личные сообщения боту.")
        except Exception as e:
            log_message(f"Ошибка при отправке сообщения о муте в ЛС: {e}")

@bot.message_handler(commands=['stop'])
def stop_command(message):
    global running
    user_id = message.from_user.id
    if is_dev_user(user_id): #Удалили is_admin(user_id)
        log_message(f"Пользователь {user_id} ({message.from_user.username}) запросил остановку бота.")
        bot.reply_to(message, "Бот остановлен.")
        running = False
        save_last_update_info(last_update_id, datetime.datetime.now(datetime.timezone.utc))
        bot.stop_polling()
        sys.exit(0)
    else:
        log_message(
            f"Попытка остановки бота пользователем {user_id} ({message.from_user.username}), доступ запрещен.")
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(func=lambda message: message.text.startswith('+admin'))
def admin_command(message):
    user_id = message.from_user.id
    if is_dev_user(user_id):  #Удалили is_admin(user_id)
        try:
            user_id_to_add = int(message.text.split()[1])  # Получаем ID пользователя из сообщения
            result = add_bot_admin(user_id_to_add, user_id)
            if result is True:
                bot.reply_to(message,
                             f"Пользователь с ID {user_id_to_add} добавлен в список администраторов бота и в базу данных.")
            elif result is False:
                bot.reply_to(message, f"Пользователь с ID {user_id_to_add} уже является администратором бота.")
            else:
                bot.reply_to(message, "Не удалось добавить пользователя в список администраторов бота.")
        except IndexError:
            bot.reply_to(message, "Не указан ID пользователя. Используйте: +admin <user_id>")
        except ValueError:
            bot.reply_to(message, "ID пользователя должен быть целым числом.")
    else:
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(func=lambda message: message.text.startswith('-admin'))
def remove_admin_command(message):
    user_id = message.from_user.id
    if is_dev_user(user_id): #Удалили is_admin(user_id)
        try:
            user_id_to_remove = int(message.text.split()[1])  # Получаем ID пользователя из сообщения
            result = remove_bot_admin(user_id_to_remove, user_id)
            if result is True:
                bot.reply_to(message,
                             f"Пользователь с ID {user_id_to_remove} удален из списка администраторов бота и из базы данных.")
            elif result is False:
                bot.reply_to(message, f"Пользователь с ID {user_id_to_remove} не является администратором бота.")
            else:
                bot.reply_to(message, "Не удалось удалить пользователя из списка администраторов бота.")
        except IndexError:
            bot.reply_to(message, "Не указан ID пользователя. Используйте: -admin <user_id>")
        except ValueError:
            bot.reply_to(message, "ID пользователя должен быть целым числом.")
    else:
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(commands=['restart'])
def restart_command(message):
    user_id = message.from_user.id
    if is_dev_user(user_id):
        log_message(f"Пользователь {user_id} ({message.from_user.username}) перезапускает бота.")
        bot.reply_to(message, "Перезапускаюсь...")
        save_last_update_info(last_update_id, datetime.datetime.now(datetime.timezone.utc))
        bot.stop_polling()
        os.execl(sys.executable, sys.executable, *sys.argv)

    else:
        log_message(
            f"Попытка перезапуска бота пользователем {user_id} ({message.from_user.username}), доступ запрещен.")
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

def get_all_users():
    """Получает список всех пользователей из базы данных."""
    try:
        conn = sqlite3.connect(DATABASE_FILE)
        cursor = conn.cursor()
        cursor.execute("SELECT user_id FROM users")
        user_ids = [row[0] for row in cursor.fetchall()]
        conn.close()

        users_data = []
        for user_id in user_ids:
            try:
                chat = bot.get_chat(user_id)
                first_name = chat.first_name or "Не указано"
                username = chat.username or "Нет username"  # Получаем username
                users_data.append((user_id, first_name, username))  # Добавляем username в кортеж
            except telebot.apihelper.ApiTelegramException as e:
                log_message(f"Не удалось получить информацию о пользователе {user_id}: {e}")
                users_data.append((user_id, "Не удалось получить имя", "Не удалось получить username"))

        return users_data
    except Exception as e:
        log_message(f"Ошибка при получении списка всех пользователей: {e}")
        return []

def panel_command(message):
    """Выводит панель разработчика с инлайн-кнопками."""
    user_id = message.from_user.id
    if is_dev_user(user_id) or is_admin(user_id):
        markup = types.InlineKeyboardMarkup(row_width=2)
        item1 = types.InlineKeyboardButton("Ping", callback_data='ping')
        item2 = types.InlineKeyboardButton("Restart", callback_data='restart')
        markup.add(item1, item2)
        bot.send_message(message.chat.id, "Панель разработчика. Упрощённое управление", reply_markup=markup)
    else:
        bot.reply_to(message, "У вас нет прав для выполнения этой команды.")

@bot.callback_query_handler(func=lambda call: True)
def callback_inline(call):
    """Обрабатывает нажатия на инлайн-кнопки."""
    try:
        print(f"Получен callback_query: {call.data}")  # Добавляем отладочный вывод
        if call.message:
            if call.data == 'ping':
                print("Отправляем команду /ping")  # Добавляем отладочный вывод
                bot.send_message(call.message.chat.id, "/ping")  # Отправляем команду
            elif call.data == 'restart':
                print("Отправляем команду /restart")  # Добавляем отладочный вывод
                bot.send_message(call.message.chat.id, "/restart")  # Отправляем команду

            # Убираем кнопки после нажатия (опционально)
            bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id, reply_markup=None)

    except Exception as e:
        print(f"Ошибка при обработке callback: {e}")

@bot.message_handler(commands=['users'])
def users_command(message):
    """Выводит список всех пользователей бота."""
    user_id = message.from_user.id
    if is_dev_user(user_id) or is_admin(user_id):
        try:
            users = get_all_users()

            if users:
                text = "Список всех пользователей бота:\n"
                for i, (user_id, first_name, username) in enumerate(users):  # Достаем username из кортежа
                    text += f"{i + 1}. {first_name} (ID={user_id}) (@{username})\n"
                bot.reply_to(message, text)
            else:
                bot.reply_to(message, "В базе данных нет пользователей.")

        except Exception as e:
            bot.reply_to(message, f"Ошибка при выполнении команды: {e}")
    else:
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(commands=['profile'])
def profile_command(message):
    user_id = message.from_user.id
    username = message.from_user.username or "Не указано"
    access_level = get_user_access_level(user_id) #Получаем уровень доступа тут

    profile_text = f"""
    ? Ваш профиль
    
    Telegram ID: {user_id}
    Имя пользователя: @{username}
    Уровень доступа: {access_level}
    """
    bot.reply_to(message, profile_text)


@bot.message_handler(commands=['ping'])
def ping_command(message):
    try:
        start_time = time.time()
        msg = bot.reply_to(message, "Понг!")

        end_time = time.time()
        ping_time = round((end_time - start_time) * 1000)
        bot.edit_message_text(f"Понг! Время ответа: {ping_time} мс", chat_id=message.chat.id,
                              message_id=msg.message_id)
    except Exception as e:
        bot.reply_to(message, f"Произошла ошибка при вычислении времени ответа: {e}")

@bot.message_handler(commands=['admins'])
def admins_command(message):
    """Выводит список администраторов с именами и username."""
    user_id = message.from_user.id
    if is_dev_user(user_id) or is_admin(user_id):
        try:
            load_admins_from_database()
            if ADMIN_USER_IDS:
                admin_list = ""
                for i, admin_id in enumerate(ADMIN_USER_IDS):
                    try:
                        chat = bot.get_chat(admin_id)
                        admin_info = f"{i+1}. {chat.first_name or ''} "
                        if chat.username:
                            admin_info += f"(@{chat.username})"
                        admin_list += f"{admin_info}\n"
                    except telebot.apihelper.ApiTelegramException as e:
                        admin_list += f"{i+1}. (Не удалось получить информацию об администраторе ID: {admin_id})\n"
                bot.reply_to(message, f"?Список администраторов бота:\n{admin_list}")
            else:
                bot.reply_to(message, "Список администраторов пуст.")
        except Exception as e:
            bot.reply_to(message, f"Произошла ошибка при получении списка администраторов: {e}")
    else:
        bot.reply_to(message, "У вас нет прав на выполнение этой команды.")

@bot.message_handler(commands=['commands'])
def commands_command(message):
    """Выводит список команд, сгруппированный по уровням доступа, в зависимости от прав пользователя."""
    user_id = message.from_user.id
    access_level = get_user_access_level(user_id)

    commands_by_access = {
        "Пользователь": [
            ("/profile", "Показать профиль пользователя"),
            ("/ping", "Проверить время ответа бота"),
            ("/commands", "Показать список команд"),
            ("/about", "Узнать информацию о пользователе"),
            ("/muted", "Показать список замученных пользователей")
        ],
        "Администратор": [
            ("/kick", "Удалить пользователя из чата"),
            ("/admins", "Показать список администраторов"),
            ("/admins_id", "Вывести список администраторов с ID"),
            ("/mute", "Замутить пользователя"),
            ("/unmute", "Размутить пользователя")
        ],
        "Разработчик": [
            ("/stop", "Остановить бота"),
            ("/restart", "Перезапустить бота"),
            ("/chat_on", "Включить работу бота в этом чате"),
            ("/chat_off", "Выключить работу бота в этом чате"),
            ("/users", "Показать список всех пользователей бота")
        ]
    }

    access_levels_order = ["Пользователь", "Администратор", "Разработчик"]
    text = ""

    for current_access_level in access_levels_order:
        if access_levels_order.index(current_access_level) <= access_levels_order.index(access_level):
            if current_access_level in commands_by_access:
                text += f"<b>Доступ \"{current_access_level}\":</b>\n"
                for command, description in commands_by_access[current_access_level]:
                    text += f"{command} - {description}\n"
                text += "\n"

    try:
        bot.reply_to(message, text, parse_mode="HTML")
    except Exception as e:
        log_message(f"Ошибка при отправке списка команд: {e}")

# Middleware для добавления пользователя в базу данных
def user_middleware(function):
    def wrapper(message):
        try:
            user_id = message.from_user.id
            add_user_to_database(user_id)  # Добавляем пользователя в базу данных
        except Exception as e:
            log_message(f"Ошибка в middleware: {e}")
        return function(message)
    return wrapper

@user_middleware
def handle_message(message):
    """Обрабатывает входящие сообщения."""
    log_message(f"handle_message вызвана с сообщением: {message.text}")

    # Проверяем, является ли сообщение обычным сообщением
    if message.content_type != 'text':
        log_message("Игнорируем обновление, не являющееся текстовым сообщением.")
        return

    try:
        chat_id = message.chat.id

        # Проверяем, выключен ли бот в чате
        if is_chat_offline(chat_id):
            if message.text == "/chat_on":
                chat_on_command(message)
            else:
                log_message(f"Бот не отвечает в выключенном чате {chat_id}")
            return  # Прекращаем дальнейшую обработку

        bot_username = bot.get_me().username  # Получаем имя бота (ВНЕ условия if)

        message_time = datetime.datetime.fromtimestamp(message.date, tz=datetime.timezone.utc)
        if message_time > last_update_time:  # Сравниваем времена
            time.sleep(0.1)  # Добавляем небольшую задержку
            check_muted(message)  # Всегда вызываем check_muted

            command = message.text.split('@')[0] # Получаем команду без @username

            if command == '/kick':
                kick_command(message)
            elif command == "/ping":
                ping_command(message)
            elif command == "/panel":
                panel_command(message)

        else:
            log_message(f"Игнорируем старое сообщение: {message.text}")

    except Exception as e:
        print(f"Ошибка при обработке сообщения: {e}")
finally:
    print("Бот завершил работу.")
    save_last_update_info(last_update_id, datetime.datetime.now(datetime.timezone.utc))
    bot.stop_polling()
    sys.exit(0)

try:
    bot.polling(none_stop=True, allowed_updates=['message', 'callback_query', 'commands'])
except Exception as e:
    print(f"Ошибка при запуске polling: {e}")

Ответы (0 шт):