From df5e98dda4c9a93f888779ce5f7198c652fd44e3 Mon Sep 17 00:00:00 2001 From: UdoChudo Date: Thu, 11 Jul 2024 16:17:36 +0500 Subject: [PATCH] Change menu buttons, fix bug with unsub, add States for users --- telezab.py | 216 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 169 insertions(+), 47 deletions(-) diff --git a/telezab.py b/telezab.py index c6c49fe..3ead4f5 100644 --- a/telezab.py +++ b/telezab.py @@ -5,7 +5,7 @@ import os import hashlib import logging from logging.config import dictConfig -from threading import Thread, Lock +from threading import Thread, Lock, Timer import sqlite3 import time @@ -50,6 +50,14 @@ bot = telebot.TeleBot(TOKEN) # Lock for database operations db_lock = Lock() +# Define states +NOTIFICATION_MODE = 1 +SETTINGS_MODE = 2 + +# Dictionary to keep track of user states and timers +user_states = {} +user_timers = {} + # Initialize SQLite database def init_db(): with db_lock: @@ -180,15 +188,50 @@ def log_user_event(chat_id, username, action): conn.commit() conn.close() +# Handle state transitions +def set_user_state(chat_id, state): + user_states[chat_id] = state + if state == SETTINGS_MODE: + start_settings_timer(chat_id) + elif state == NOTIFICATION_MODE: + cancel_settings_timer(chat_id) + +def start_settings_timer(chat_id): + if chat_id in user_timers: + user_timers[chat_id].cancel() + timer = Timer(30, transition_to_notification_mode, [chat_id]) + user_timers[chat_id] = timer + timer.start() + +def cancel_settings_timer(chat_id): + if chat_id in user_timers: + user_timers[chat_id].cancel() + del user_timers[chat_id] + +def transition_to_notification_mode(chat_id): + set_user_state(chat_id, NOTIFICATION_MODE) + + # Main menu for users -def show_main_menu(chat_id, is_whitelisted_user): +def show_main_menu(chat_id): markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) - if is_whitelisted_user: - markup.add('Подписаться', 'Отписаться', 'Мои подписки', 'Активные регионы', 'Помощь', 'Добавить регион', 'Удалить регион', 'Добавить в белый список', 'Удалить из белого списка') + if is_whitelisted(chat_id): + markup.add('Настройки', 'Помощь') else: markup.add('Регистрация') bot.send_message(chat_id, "Выберите действие:", reply_markup=markup) +# Settings menu for users +def show_settings_menu(chat_id): + if user_states != 'NOTIFICATION_MODE': + markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) + if str(chat_id) in ADMIN_CHAT_IDS: + markup.add('Подписаться', 'Отписаться', 'Мои подписки', 'Активные регионы', 'Добавить регион', 'Удалить регион', 'Назад') + else: + markup.add('Подписаться', 'Отписаться', 'Мои подписки', 'Активные регионы', 'Назад') + bot.send_message(chat_id, "Вы находитесь в режиме настроек. Выберите действие:", reply_markup=markup) + else: + pass # Handle /start command @bot.message_handler(commands=['start']) def handle_start(message): @@ -199,12 +242,10 @@ def handle_start(message): else: username = "N/A" - if is_whitelisted(chat_id): - show_main_menu(chat_id, is_whitelisted_user=True) - else: - show_main_menu(chat_id, is_whitelisted_user=False) + set_user_state(chat_id, NOTIFICATION_MODE) + show_main_menu(chat_id) - app.logger.info(f"User {chat_id} (@{username}) started with command /start.") + app.logger.info(f"User {chat_id} ({username}) started with command /start.") # Handle menu button presses @bot.message_handler(func=lambda message: True) @@ -212,9 +253,26 @@ def handle_menu_selection(message): chat_id = message.chat.id text = message.text.strip().lower() - if text == 'регистрация': - handle_register(message) - elif text == 'подписаться': + if user_states.get(chat_id, NOTIFICATION_MODE) == SETTINGS_MODE: + handle_settings_menu_selection(message) + else: + if text == 'регистрация': + handle_register(message) + elif text == 'настройки': + set_user_state(chat_id, SETTINGS_MODE) + show_settings_menu(chat_id) + elif text == 'помощь': + handle_help(message) + else: + bot.send_message(chat_id, "Команда не распознана или у вас нет прав для выполнения этой команды.") + show_main_menu(chat_id) + +# Handle settings menu button presses +def handle_settings_menu_selection(message): + chat_id = message.chat.id + text = message.text.strip().lower() + + if text == 'подписаться': handle_subscribe(message) elif text == 'отписаться': handle_unsubscribe(message) @@ -222,22 +280,19 @@ def handle_menu_selection(message): handle_my_subscriptions(message) elif text == 'активные регионы': handle_active_regions(message) - elif text == 'помощь': - handle_help(message) elif text == 'добавить регион' and str(chat_id) in ADMIN_CHAT_IDS: prompt_admin_for_region(chat_id, 'add') elif text == 'удалить регион' and str(chat_id) in ADMIN_CHAT_IDS: prompt_admin_for_region(chat_id, 'remove') - elif text == 'добавить в белый список' and str(chat_id) in ADMIN_CHAT_IDS: - prompt_admin_for_whitelist(chat_id, 'add') - elif text == 'удалить из белого списка' and str(chat_id) in ADMIN_CHAT_IDS: - prompt_admin_for_whitelist(chat_id, 'remove') + elif text == 'назад': + set_user_state(chat_id, NOTIFICATION_MODE) + show_main_menu(chat_id) else: - bot.send_message(chat_id, "Команда не распознана или у вас нет прав для выполнения этой команды.") - show_main_menu(chat_id, is_whitelisted(chat_id)) + bot.send_message(chat_id, "Команда не распознана.") + show_settings_menu(chat_id) # Handle /subscribe command to subscribe to a region -@bot.message_handler(commands=['subscribe', 'sub']) +@bot.message_handler(commands=['subscribe']) def handle_subscribe(message): chat_id = message.chat.id if not is_whitelisted(chat_id): @@ -258,7 +313,7 @@ def handle_subscribe(message): def process_subscription(message, chat_id, username): if message.text.lower() == 'отмена': bot.send_message(chat_id, "Действие отменено.") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) return region_ids = message.text.split(',') @@ -267,6 +322,10 @@ def process_subscription(message, chat_id, username): cursor = conn.cursor() for region_id in region_ids: region_id = region_id.strip() + if not region_id.isdigit(): + bot.send_message(chat_id, "Недопустимый формат. Введите только номера регионов.") + show_settings_menu(chat_id) + return query = 'INSERT OR IGNORE INTO subscriptions (chat_id, region_id, username, active) VALUES (?, ?, ?, TRUE)' app.logger.debug(f"Executing query: {query} with chat_id={chat_id}, region_id={region_id}, username={username}") cursor.execute(query, (chat_id, region_id, username)) @@ -278,8 +337,13 @@ def process_subscription(message, chat_id, username): conn.close() bot.send_message(chat_id, f"Подписка на регионы: {', '.join(region_ids)} оформлена.") app.logger.info(f"User {chat_id} (@{username}) subscribed to regions: {', '.join(region_ids)}.") + username = message.from_user.username + if username: + username = f"@{username}" + else: + username = "N/A" log_user_event(chat_id, username, f"Subscribed to regions: {', '.join(region_ids)}") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) # Handle /unsubscribe command to unsubscribe from a region @bot.message_handler(commands=['unsubscribe']) @@ -301,7 +365,7 @@ def handle_unsubscribe(message): def process_unsubscription(message, chat_id): if message.text.lower() == 'отмена': bot.send_message(chat_id, "Действие отменено.") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) return region_ids = message.text.split(',') @@ -310,32 +374,46 @@ def process_unsubscription(message, chat_id): cursor = conn.cursor() for region_id in region_ids: region_id = region_id.strip() + if not region_id.isdigit(): + bot.send_message(chat_id, "Недопустимый формат. Введите только номера регионов.") + show_settings_menu(chat_id) + return + + # Проверяем, существует ли указанный region_id в базе данных + query_check = 'SELECT COUNT(*) FROM regions WHERE region_id = ?' + cursor.execute(query_check, (region_id,)) + result = cursor.fetchone() + + if not result or result[0] == 0: + bot.send_message(chat_id, f"Регион с номером {region_id} не существует.") + show_settings_menu(chat_id) + return + query = 'UPDATE subscriptions SET active = FALSE WHERE chat_id = ? AND region_id = ?' app.logger.debug(f"Executing query: {query} with chat_id={chat_id}, region_id={region_id}") cursor.execute(query, (chat_id, region_id)) conn.commit() conn.close() bot.send_message(chat_id, f"Отписка от регионов: {', '.join(region_ids)} оформлена.") + app.logger.info(f"User {chat_id} unsubscribed from regions: {', '.join(region_ids)}.") username = message.from_user.username if username: username = f"@{username}" else: username = "N/A" - app.logger.info(f"User {chat_id} (@{username}) unsubscribed from regions: {', '.join(region_ids)}.") log_user_event(chat_id, username, f"Unsubscribed from regions: {', '.join(region_ids)}") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) # Handle /help command to provide instructions @bot.message_handler(commands=['help']) def handle_help(message): help_text = ( - "/start - Начать работу с ботом\n" - "/subscribe (/sub) - Подписаться на рассылку событий по региону. Укажите номер региона, например, /subscribe 01 - Адыгея\n" - "/unsubscribe - Отписаться от рассылки событий по региону. Укажите номер региона, например, /unsubscribe 01 - Адыгея\n" - "/register - Запросить регистрацию в боте" + "/start - Показать меню бота\n" + "Настройки - Перейти в режим настроек и управлять подписками\n" + "Помощь - Показать это сообщение" ) bot.send_message(message.chat.id, help_text) - show_main_menu(message.chat.id, is_whitelisted(message.chat.id)) + show_main_menu(message.chat.id) # Handle /register command for new user registration @bot.message_handler(commands=['register']) @@ -349,14 +427,15 @@ def handle_register(message): markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) markup.add('Подтвердить регистрацию', 'Отмена') - bot.send_message(chat_id, f"Ваш chat ID: {chat_id}, ваше имя пользователя: @{username}. Запрос на одобрение отправлен администратору.", reply_markup=markup) + bot.send_message(chat_id, f"Ваш chat ID: {chat_id}, ваше имя пользователя: {username}. Запрос на одобрение отправлен администратору.", reply_markup=markup) + log_user_event(chat_id, username, "Requested registration") bot.register_next_step_handler(message, process_register, chat_id, username) def process_register(message, chat_id, username): if message.text.lower() == 'отмена': bot.send_message(chat_id, "Регистрация отменена.") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_main_menu(chat_id) return if message.text.lower() == 'подтвердить регистрацию': @@ -370,10 +449,10 @@ def process_register(message, chat_id, username): reply_markup=markup ) bot.send_message(chat_id, "Запрос отправлен администратору для одобрения.") - app.logger.info(f"User {chat_id} (@{username}) requested registration.") + app.logger.info(f"User {chat_id} ({username}) requested registration.") else: bot.send_message(chat_id, "Некорректный выбор. Регистрация отменена.") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_main_menu(chat_id) # Handle admin region management commands def prompt_admin_for_region(chat_id, action): @@ -391,16 +470,59 @@ def process_add_region(message): with db_lock: conn = sqlite3.connect('telezab.db') cursor = conn.cursor() - query = 'INSERT OR IGNORE INTO regions (region_id, region_name) VALUES (?, ?)' - app.logger.debug(f"Executing query: {query} with region_id={region_id}, region_name={region_name}") - cursor.execute(query, (region_id, region_name)) + query = 'SELECT region_name, active FROM regions WHERE region_id = ?' + app.logger.debug(f"Executing query: {query} with region_id={region_id}") + cursor.execute(query, (region_id,)) + result = cursor.fetchone() + if result: + existing_region_name, active = result + if existing_region_name == region_name: + query = 'UPDATE regions SET active = TRUE WHERE region_id = ?' + app.logger.debug(f"Executing query: {query} with region_id={region_id}") + cursor.execute(query, (region_id,)) + bot.send_message(chat_id, f"Регион {region_id} - {region_name} активирован.") + app.logger.info(f"Admin {chat_id} reactivated region {region_id} - {region_name}.") + else: + markup = telebot.types.InlineKeyboardMarkup() + markup.add(telebot.types.InlineKeyboardButton(text="Заменить", callback_data=f"replace_{region_id}_{region_name}")) + markup.add(telebot.types.InlineKeyboardButton(text="Активировать старый", callback_data=f"reactivate_{region_id}")) + bot.send_message(chat_id, f"Регион {region_id} уже существует с названием {existing_region_name}. Хотите заменить его или активировать старый регион?", reply_markup=markup) + else: + query = 'INSERT OR IGNORE INTO regions (region_id, region_name) VALUES (?, ?)' + app.logger.debug(f"Executing query: {query} with region_id={region_id}, region_name={region_name}") + cursor.execute(query, (region_id, region_name)) + bot.send_message(chat_id, f"Регион {region_id} - {region_name} добавлен.") + app.logger.info(f"Admin {chat_id} added region {region_id} - {region_name}.") conn.commit() conn.close() - bot.send_message(chat_id, f"Регион {region_id} - {region_name} добавлен.") - app.logger.info(f"Admin {chat_id} added region {region_id} - {region_name}.") except (IndexError, ValueError): bot.send_message(chat_id, "Неверный формат. Используйте: ") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) + +@bot.callback_query_handler(func=lambda call: call.data.startswith("replace_") or call.data.startswith("reactivate_")) +def handle_region_action(call): + action, region_id, region_name = call.data.split("_", 2) + chat_id = call.message.chat.id + + with db_lock: + conn = sqlite3.connect('telezab.db') + cursor = conn.cursor() + if action == "replace": + query = 'UPDATE regions SET region_name = ?, active = TRUE WHERE region_id = ?' + app.logger.debug(f"Executing query: {query} with region_name={region_name}, region_id={region_id}") + cursor.execute(query, (region_name, region_id)) + bot.send_message(chat_id, f"Регион {region_id} обновлен до {region_name} и активирован.") + app.logger.info(f"Admin {chat_id} replaced and reactivated region {region_id} with {region_name}.") + elif action == "reactivate": + query = 'UPDATE regions SET active = TRUE WHERE region_id = ?' + app.logger.debug(f"Executing query: {query} with region_id={region_id}") + cursor.execute(query, (region_id,)) + bot.send_message(chat_id, f"Регион {region_id} активирован.") + app.logger.info(f"Admin {chat_id} reactivated region {region_id}.") + conn.commit() + conn.close() + + show_settings_menu(chat_id) def process_remove_region(message): chat_id = message.chat.id @@ -421,7 +543,7 @@ def process_remove_region(message): app.logger.info(f"Admin {chat_id} set region {region_id} to inactive and updated subscriptions.") except IndexError: bot.send_message(chat_id, "Неверный формат. Используйте: ") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) # Handle admin whitelist management commands def prompt_admin_for_whitelist(chat_id, action): @@ -442,7 +564,7 @@ def process_add_whitelist(message): log_user_event(new_chat_id, "N/A", f"Added to whitelist by {chat_id} (@{message.from_user.username})") except (IndexError, ValueError): bot.send_message(chat_id, "Неверный формат. Используйте: ") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) def process_remove_whitelist(message): chat_id = message.chat.id @@ -454,7 +576,7 @@ def process_remove_whitelist(message): log_user_event(remove_chat_id, "N/A", f"Removed from whitelist by {chat_id} (@{message.from_user.username})") except (IndexError, ValueError): bot.send_message(chat_id, "Неверный формат. Используйте: ") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) # Handle displaying active subscriptions for a user def handle_my_subscriptions(message): @@ -470,7 +592,7 @@ def handle_my_subscriptions(message): else: regions_list = format_regions_list(user_regions) bot.send_message(chat_id, f"Ваши активные подписки:\n{regions_list}") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) # Handle displaying all active regions def handle_active_regions(message): @@ -486,7 +608,7 @@ def handle_active_regions(message): else: regions_list = format_regions_list(regions) bot.send_message(chat_id, f"Активные регионы:\n{regions_list}") - show_main_menu(chat_id, is_whitelisted(chat_id)) + show_settings_menu(chat_id) @app.route('/webhook', methods=['POST']) def webhook(): @@ -553,7 +675,7 @@ if __name__ == '__main__': init_db() # Start Flask app in a separate thread - Thread(target=app.run, kwargs={'port': 5000, 'debug': False}, daemon=True).start() + Thread(target=app.run, kwargs={'port': 5000, 'use_reloader': False, 'debug': True}, daemon=True).start() # Start bot polling run_polling()