Получение пустого сообщения при нажатии на кнопку в 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}")