Change menu buttons, fix bug with unsub, add States for users

This commit is contained in:
Udo Chudo 2024-07-11 16:17:36 +05:00
parent 23824dc5fe
commit df5e98dda4

View File

@ -5,7 +5,7 @@ import os
import hashlib import hashlib
import logging import logging
from logging.config import dictConfig from logging.config import dictConfig
from threading import Thread, Lock from threading import Thread, Lock, Timer
import sqlite3 import sqlite3
import time import time
@ -50,6 +50,14 @@ bot = telebot.TeleBot(TOKEN)
# Lock for database operations # Lock for database operations
db_lock = Lock() 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 # Initialize SQLite database
def init_db(): def init_db():
with db_lock: with db_lock:
@ -180,15 +188,50 @@ def log_user_event(chat_id, username, action):
conn.commit() conn.commit()
conn.close() 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 # 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) markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True)
if is_whitelisted_user: if is_whitelisted(chat_id):
markup.add('Подписаться', 'Отписаться', 'Мои подписки', 'Активные регионы', 'Помощь', 'Добавить регион', 'Удалить регион', 'Добавить в белый список', 'Удалить из белого списка') markup.add('Настройки', 'Помощь')
else: else:
markup.add('Регистрация') markup.add('Регистрация')
bot.send_message(chat_id, "Выберите действие:", reply_markup=markup) 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 # Handle /start command
@bot.message_handler(commands=['start']) @bot.message_handler(commands=['start'])
def handle_start(message): def handle_start(message):
@ -199,12 +242,10 @@ def handle_start(message):
else: else:
username = "N/A" username = "N/A"
if is_whitelisted(chat_id): set_user_state(chat_id, NOTIFICATION_MODE)
show_main_menu(chat_id, is_whitelisted_user=True) show_main_menu(chat_id)
else:
show_main_menu(chat_id, is_whitelisted_user=False)
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 # Handle menu button presses
@bot.message_handler(func=lambda message: True) @bot.message_handler(func=lambda message: True)
@ -212,9 +253,26 @@ def handle_menu_selection(message):
chat_id = message.chat.id chat_id = message.chat.id
text = message.text.strip().lower() text = message.text.strip().lower()
if text == 'регистрация': if user_states.get(chat_id, NOTIFICATION_MODE) == SETTINGS_MODE:
handle_register(message) handle_settings_menu_selection(message)
elif text == 'подписаться': 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) handle_subscribe(message)
elif text == 'отписаться': elif text == 'отписаться':
handle_unsubscribe(message) handle_unsubscribe(message)
@ -222,22 +280,19 @@ def handle_menu_selection(message):
handle_my_subscriptions(message) handle_my_subscriptions(message)
elif text == 'активные регионы': elif text == 'активные регионы':
handle_active_regions(message) handle_active_regions(message)
elif text == 'помощь':
handle_help(message)
elif text == 'добавить регион' and str(chat_id) in ADMIN_CHAT_IDS: elif text == 'добавить регион' and str(chat_id) in ADMIN_CHAT_IDS:
prompt_admin_for_region(chat_id, 'add') prompt_admin_for_region(chat_id, 'add')
elif text == 'удалить регион' and str(chat_id) in ADMIN_CHAT_IDS: elif text == 'удалить регион' and str(chat_id) in ADMIN_CHAT_IDS:
prompt_admin_for_region(chat_id, 'remove') prompt_admin_for_region(chat_id, 'remove')
elif text == 'добавить в белый список' and str(chat_id) in ADMIN_CHAT_IDS: elif text == 'назад':
prompt_admin_for_whitelist(chat_id, 'add') set_user_state(chat_id, NOTIFICATION_MODE)
elif text == 'удалить из белого списка' and str(chat_id) in ADMIN_CHAT_IDS: show_main_menu(chat_id)
prompt_admin_for_whitelist(chat_id, 'remove')
else: else:
bot.send_message(chat_id, "Команда не распознана или у вас нет прав для выполнения этой команды.") bot.send_message(chat_id, "Команда не распознана.")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_settings_menu(chat_id)
# Handle /subscribe command to subscribe to a region # Handle /subscribe command to subscribe to a region
@bot.message_handler(commands=['subscribe', 'sub']) @bot.message_handler(commands=['subscribe'])
def handle_subscribe(message): def handle_subscribe(message):
chat_id = message.chat.id chat_id = message.chat.id
if not is_whitelisted(chat_id): if not is_whitelisted(chat_id):
@ -258,7 +313,7 @@ def handle_subscribe(message):
def process_subscription(message, chat_id, username): def process_subscription(message, chat_id, username):
if message.text.lower() == 'отмена': if message.text.lower() == 'отмена':
bot.send_message(chat_id, "Действие отменено.") bot.send_message(chat_id, "Действие отменено.")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_settings_menu(chat_id)
return return
region_ids = message.text.split(',') region_ids = message.text.split(',')
@ -267,6 +322,10 @@ def process_subscription(message, chat_id, username):
cursor = conn.cursor() cursor = conn.cursor()
for region_id in region_ids: for region_id in region_ids:
region_id = region_id.strip() 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)' 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}") 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)) cursor.execute(query, (chat_id, region_id, username))
@ -278,8 +337,13 @@ def process_subscription(message, chat_id, username):
conn.close() conn.close()
bot.send_message(chat_id, f"Подписка на регионы: {', '.join(region_ids)} оформлена.") bot.send_message(chat_id, f"Подписка на регионы: {', '.join(region_ids)} оформлена.")
app.logger.info(f"User {chat_id} (@{username}) subscribed to regions: {', '.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)}") 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 # Handle /unsubscribe command to unsubscribe from a region
@bot.message_handler(commands=['unsubscribe']) @bot.message_handler(commands=['unsubscribe'])
@ -301,7 +365,7 @@ def handle_unsubscribe(message):
def process_unsubscription(message, chat_id): def process_unsubscription(message, chat_id):
if message.text.lower() == 'отмена': if message.text.lower() == 'отмена':
bot.send_message(chat_id, "Действие отменено.") bot.send_message(chat_id, "Действие отменено.")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_settings_menu(chat_id)
return return
region_ids = message.text.split(',') region_ids = message.text.split(',')
@ -310,32 +374,46 @@ def process_unsubscription(message, chat_id):
cursor = conn.cursor() cursor = conn.cursor()
for region_id in region_ids: for region_id in region_ids:
region_id = region_id.strip() 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 = ?' 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}") app.logger.debug(f"Executing query: {query} with chat_id={chat_id}, region_id={region_id}")
cursor.execute(query, (chat_id, region_id)) cursor.execute(query, (chat_id, region_id))
conn.commit() conn.commit()
conn.close() conn.close()
bot.send_message(chat_id, f"Отписка от регионов: {', '.join(region_ids)} оформлена.") 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 username = message.from_user.username
if username: if username:
username = f"@{username}" username = f"@{username}"
else: else:
username = "N/A" 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)}") 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 # Handle /help command to provide instructions
@bot.message_handler(commands=['help']) @bot.message_handler(commands=['help'])
def handle_help(message): def handle_help(message):
help_text = ( help_text = (
"/start - Начать работу с ботом\n" "/start - Показать меню бота\n"
"/subscribe (/sub) - Подписаться на рассылку событий по региону. Укажите номер региона, например, /subscribe 01 - Адыгея\n" "Настройки - Перейти в режим настроек и управлять подписками\n"
"/unsubscribe - Отписаться от рассылки событий по региону. Укажите номер региона, например, /unsubscribe 01 - Адыгея\n" "Помощь - Показать это сообщение"
"/register - Запросить регистрацию в боте"
) )
bot.send_message(message.chat.id, help_text) 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 # Handle /register command for new user registration
@bot.message_handler(commands=['register']) @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 = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True)
markup.add('Подтвердить регистрацию', 'Отмена') 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") log_user_event(chat_id, username, "Requested registration")
bot.register_next_step_handler(message, process_register, chat_id, username) bot.register_next_step_handler(message, process_register, chat_id, username)
def process_register(message, chat_id, username): def process_register(message, chat_id, username):
if message.text.lower() == 'отмена': if message.text.lower() == 'отмена':
bot.send_message(chat_id, "Регистрация отменена.") bot.send_message(chat_id, "Регистрация отменена.")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_main_menu(chat_id)
return return
if message.text.lower() == 'подтвердить регистрацию': if message.text.lower() == 'подтвердить регистрацию':
@ -370,10 +449,10 @@ def process_register(message, chat_id, username):
reply_markup=markup reply_markup=markup
) )
bot.send_message(chat_id, "Запрос отправлен администратору для одобрения.") 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: else:
bot.send_message(chat_id, "Некорректный выбор. Регистрация отменена.") bot.send_message(chat_id, "Некорректный выбор. Регистрация отменена.")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_main_menu(chat_id)
# Handle admin region management commands # Handle admin region management commands
def prompt_admin_for_region(chat_id, action): def prompt_admin_for_region(chat_id, action):
@ -391,16 +470,59 @@ def process_add_region(message):
with db_lock: with db_lock:
conn = sqlite3.connect('telezab.db') conn = sqlite3.connect('telezab.db')
cursor = conn.cursor() cursor = conn.cursor()
query = 'INSERT OR IGNORE INTO regions (region_id, region_name) VALUES (?, ?)' query = 'SELECT region_name, active FROM regions WHERE region_id = ?'
app.logger.debug(f"Executing query: {query} with region_id={region_id}, region_name={region_name}") app.logger.debug(f"Executing query: {query} with region_id={region_id}")
cursor.execute(query, (region_id, region_name)) 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.commit()
conn.close() 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): except (IndexError, ValueError):
bot.send_message(chat_id, "Неверный формат. Используйте: <region_id> <region_name>") bot.send_message(chat_id, "Неверный формат. Используйте: <region_id> <region_name>")
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): def process_remove_region(message):
chat_id = message.chat.id 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.") app.logger.info(f"Admin {chat_id} set region {region_id} to inactive and updated subscriptions.")
except IndexError: except IndexError:
bot.send_message(chat_id, "Неверный формат. Используйте: <region_id>") bot.send_message(chat_id, "Неверный формат. Используйте: <region_id>")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_settings_menu(chat_id)
# Handle admin whitelist management commands # Handle admin whitelist management commands
def prompt_admin_for_whitelist(chat_id, action): 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})") log_user_event(new_chat_id, "N/A", f"Added to whitelist by {chat_id} (@{message.from_user.username})")
except (IndexError, ValueError): except (IndexError, ValueError):
bot.send_message(chat_id, "Неверный формат. Используйте: <chat_id>") bot.send_message(chat_id, "Неверный формат. Используйте: <chat_id>")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_settings_menu(chat_id)
def process_remove_whitelist(message): def process_remove_whitelist(message):
chat_id = message.chat.id 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})") log_user_event(remove_chat_id, "N/A", f"Removed from whitelist by {chat_id} (@{message.from_user.username})")
except (IndexError, ValueError): except (IndexError, ValueError):
bot.send_message(chat_id, "Неверный формат. Используйте: <chat_id>") bot.send_message(chat_id, "Неверный формат. Используйте: <chat_id>")
show_main_menu(chat_id, is_whitelisted(chat_id)) show_settings_menu(chat_id)
# Handle displaying active subscriptions for a user # Handle displaying active subscriptions for a user
def handle_my_subscriptions(message): def handle_my_subscriptions(message):
@ -470,7 +592,7 @@ def handle_my_subscriptions(message):
else: else:
regions_list = format_regions_list(user_regions) regions_list = format_regions_list(user_regions)
bot.send_message(chat_id, f"Ваши активные подписки:\n{regions_list}") 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 # Handle displaying all active regions
def handle_active_regions(message): def handle_active_regions(message):
@ -486,7 +608,7 @@ def handle_active_regions(message):
else: else:
regions_list = format_regions_list(regions) regions_list = format_regions_list(regions)
bot.send_message(chat_id, f"Активные регионы:\n{regions_list}") 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']) @app.route('/webhook', methods=['POST'])
def webhook(): def webhook():
@ -553,7 +675,7 @@ if __name__ == '__main__':
init_db() init_db()
# Start Flask app in a separate thread # 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 # Start bot polling
run_polling() run_polling()