From 60a493e7f09009dd3c714b34bbe5156b5d63eb5c Mon Sep 17 00:00:00 2001 From: UdoChudo Date: Wed, 10 Jul 2024 14:08:37 +0500 Subject: [PATCH] Add buttons for better using in TelegramApp --- .gitignore | 3 +- Dockerfile | 9 ++ telezab.py | 299 +++++++++++++++++++++++------------------------------ 3 files changed, 143 insertions(+), 168 deletions(-) create mode 100644 Dockerfile diff --git a/.gitignore b/.gitignore index 1f3bc3f..b286bd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /telezab.db /.env -/.idea \ No newline at end of file +/.idea +/TODO.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b1e7bae --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM python:3.11.9-slim +LABEL authors="UdoChudo" +WORKDIR /app +COPY . /app +RUN pip install gunicorn +RUN pip install --no-cache-dir -r requests.txt +EXPOSE 5000 +ENV FLASK_APP=telezab.py +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "telezab:app"] \ No newline at end of file diff --git a/telezab.py b/telezab.py index c1377ba..668c49e 100644 --- a/telezab.py +++ b/telezab.py @@ -8,7 +8,7 @@ from logging.config import dictConfig from threading import Thread, Lock import sqlite3 import time -import re + # Load environment variables load_dotenv() @@ -133,7 +133,7 @@ def get_regions(): with db_lock: conn = sqlite3.connect('telezab.db') cursor = conn.cursor() - cursor.execute('SELECT region_id, region_name FROM regions') + cursor.execute('SELECT region_id, region_name FROM regions ORDER BY region_id') regions = cursor.fetchall() conn.close() return regions @@ -148,6 +148,7 @@ def get_user_subscribed_regions(chat_id): FROM subscriptions JOIN regions ON subscriptions.region_id = regions.region_id WHERE subscriptions.chat_id = ? AND subscriptions.skip = FALSE + ORDER BY regions.region_id ''', (chat_id,)) regions = cursor.fetchall() conn.close() @@ -157,51 +158,63 @@ def get_user_subscribed_regions(chat_id): def format_regions_list(regions): return '\n'.join([f"{region_id} - {region_name}" for region_id, region_name in regions]) +# Main menu for users +def show_main_menu(chat_id, is_whitelisted_user): + markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) + if is_whitelisted_user: + markup.add('Подписаться', 'Отписаться', 'Помощь', 'Add Region', 'Remove Region', 'Add Whitelist', 'Remove Whitelist') + else: + markup.add('Register') + bot.send_message(chat_id, "Выберите действие:", reply_markup=markup) + # Handle /start command @bot.message_handler(commands=['start']) def handle_start(message): chat_id = message.chat.id - if not is_whitelisted(chat_id): - bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.") - app.logger.info(f"Unauthorized access attempt by {chat_id}") - return - username = message.from_user.username if username: username = f"@{username}" else: username = "N/A" - bot.send_message(chat_id, "Выполните комманду /register для начала работы") - app.logger.info(f"User {chat_id} ({username}) started receiving alerts.") + if is_whitelisted(chat_id): + show_main_menu(chat_id, is_whitelisted_user=True) + else: + show_main_menu(chat_id, is_whitelisted_user=False) -# Handle /stop command to stop receiving messages -@bot.message_handler(commands=['stop']) -def handle_stop(message): + app.logger.info(f"User {chat_id} ({username}) started with command /start.") + +# Handle menu button presses +@bot.message_handler(func=lambda message: True) +def handle_menu_selection(message): chat_id = message.chat.id - if not is_whitelisted(chat_id): - bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.") - app.logger.info(f"Unauthorized access attempt by {chat_id}") - return - - with db_lock: - conn = sqlite3.connect('telezab.db') - cursor = conn.cursor() - query = 'DELETE FROM subscriptions WHERE chat_id = ?' - app.logger.debug(f"Executing query: {query} with chat_id={chat_id}") - cursor.execute(query, (chat_id,)) - conn.commit() - conn.close() - bot.send_message(chat_id, "Вы перестали получать события Zabbix'а :c") - app.logger.info(f"User {chat_id} stopped receiving alerts.") + text = message.text.strip().lower() + if text == 'register': + handle_register(message) + elif text == 'подписаться': + handle_subscribe(message) + elif text == 'отписаться': + handle_unsubscribe(message) + elif text == 'помощь': + handle_help(message) + elif text == 'add region' and str(chat_id) in ADMIN_CHAT_IDS: + prompt_admin_for_region(chat_id, 'add') + elif text == 'remove region' and str(chat_id) in ADMIN_CHAT_IDS: + prompt_admin_for_region(chat_id, 'remove') + elif text == 'add whitelist' and str(chat_id) in ADMIN_CHAT_IDS: + prompt_admin_for_whitelist(chat_id, 'add') + elif text == 'remove whitelist' and str(chat_id) in ADMIN_CHAT_IDS: + prompt_admin_for_whitelist(chat_id, 'remove') + else: + bot.send_message(chat_id, "Команда не распознана или у вас нет прав для выполнения этой команды.") + show_main_menu(chat_id, is_whitelisted(chat_id)) # Handle /subscribe command to subscribe to a region -@bot.message_handler(commands=['subscribe']) def handle_subscribe(message): chat_id = message.chat.id if not is_whitelisted(chat_id): - bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.") + bot.send_message(chat_id, "You are not authorized to use this bot.") app.logger.info(f"Unauthorized access attempt by {chat_id}") return @@ -212,137 +225,79 @@ def handle_subscribe(message): username = "N/A" regions_list = format_regions_list(get_regions()) - bot.send_message(chat_id, f"Отправьте номер или номера регионов, на которые хотите подписаться (через запятую):\n{regions_list}\n\nНапишите 'отмена' для отмены.") + bot.send_message(chat_id, f"Отправьте номер или номера регионов на которые хотите подписаться (через запятую):\n{regions_list}\n\nНапишите 'отмена' для отмены.") bot.register_next_step_handler(message, process_subscription, chat_id, username) 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)) return region_ids = message.text.split(',') - # Проверка формата ввода - if not all(re.match(r'^\d+$', region_id.strip()) for region_id in region_ids): - bot.send_message(chat_id, "Неверный формат команды. Пожалуйста, введите только цифры, разделенные запятыми.") - bot.register_next_step_handler(message, process_subscription, chat_id, username) - return - - invalid_regions = [] - already_subscribed = [] with db_lock: conn = sqlite3.connect('telezab.db') cursor = conn.cursor() for region_id in region_ids: region_id = region_id.strip() - # Проверка существования региона в таблице regions - cursor.execute('SELECT COUNT(*) FROM regions WHERE region_id = ?', (region_id,)) - if cursor.fetchone()[0] == 0: - invalid_regions.append(region_id) - continue - - # Проверка существующей подписки - cursor.execute('SELECT COUNT(*) FROM subscriptions WHERE chat_id = ? AND region_id = ?', (chat_id, region_id)) - if cursor.fetchone()[0] > 0: - already_subscribed.append(region_id) - continue - query = 'INSERT OR IGNORE INTO subscriptions (chat_id, region_id, username) VALUES (?, ?, ?)' 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)) - conn.commit() conn.close() - - if invalid_regions: - bot.send_message(chat_id, f"Следующие регионы не существуют: {', '.join(invalid_regions)}. Пожалуйста, проверьте и введите снова.") - bot.register_next_step_handler(message, process_subscription, chat_id, username) - return - - if already_subscribed: - bot.send_message(chat_id, f"Вы уже подписаны на следующие регионы: {', '.join(already_subscribed)}.") - if len(already_subscribed) == len(region_ids): - bot.register_next_step_handler(message, process_subscription, chat_id, username) - return - - subscribed_regions = [region_id for region_id in region_ids if region_id not in invalid_regions and region_id not in already_subscribed] - if subscribed_regions: - bot.send_message(chat_id, f"Подписка на регионы: {', '.join(subscribed_regions)} оформлена.") - app.logger.info(f"User {chat_id} ({username}) subscribed to regions: {', '.join(subscribed_regions)}.") - else: - bot.send_message(chat_id, "Не удалось оформить подписку на указанные регионы. Пожалуйста, попробуйте снова.") - bot.register_next_step_handler(message, process_subscription, chat_id, username) - + bot.send_message(chat_id, f"Subscribed to regions: {', '.join(region_ids)}.") + app.logger.info(f"User {chat_id} ({username}) subscribed to regions: {', '.join(region_ids)}.") + show_main_menu(chat_id, is_whitelisted(chat_id)) # Handle /unsubscribe command to unsubscribe from a region -@bot.message_handler(commands=['unsubscribe']) def handle_unsubscribe(message): chat_id = message.chat.id if not is_whitelisted(chat_id): - bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.") + bot.send_message(chat_id, "You are not authorized to use this bot.") app.logger.info(f"Unauthorized access attempt by {chat_id}") return user_regions = get_user_subscribed_regions(chat_id) if not user_regions: - bot.send_message(chat_id, "Вы ещё не подписались ни на один регион для получения событий") + bot.send_message(chat_id, "You are not subscribed to any regions.") else: regions_list = format_regions_list(user_regions) - bot.send_message(chat_id, f"Отправьте номер или номера регионов, от которых хотите отписаться (через запятую):\n{regions_list}\n\nНапишите 'отмена' для прекращения процедуры подписки.") + bot.send_message(chat_id, f"Отправьте номер или номера регионов от которых хотите отписаться (через запятую):\n{regions_list}\n\nНапишите 'отмена' для отмены.") bot.register_next_step_handler(message, process_unsubscription, chat_id) def process_unsubscription(message, chat_id): if message.text.lower() == 'отмена': bot.send_message(chat_id, "Действие отменено.") + show_main_menu(chat_id, is_whitelisted(chat_id)) return region_ids = message.text.split(',') - # Проверка формата ввода - if not all(re.match(r'^\d+$', region_id.strip()) for region_id in region_ids): - bot.send_message(chat_id, "Неверный формат команды. Пожалуйста, введите только цифры, разделенные запятыми.") - bot.register_next_step_handler(message, process_unsubscription, chat_id) - return - - invalid_unsubscriptions = [] - valid_unsubscriptions = [] with db_lock: conn = sqlite3.connect('telezab.db') cursor = conn.cursor() for region_id in region_ids: region_id = region_id.strip() - # Проверка существования подписки - cursor.execute('SELECT COUNT(*) FROM subscriptions WHERE chat_id = ? AND region_id = ?', (chat_id, region_id)) - if cursor.fetchone()[0] == 0: - invalid_unsubscriptions.append(region_id) - else: - valid_unsubscriptions.append(region_id) - query = 'DELETE FROM subscriptions 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)) + query = 'DELETE FROM subscriptions 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() - - if invalid_unsubscriptions: - bot.send_message(chat_id, f"Вы не подписаны на следующие регионы: {', '.join(invalid_unsubscriptions)}.") - if valid_unsubscriptions: - bot.send_message(chat_id, f"Отписка от регионов: {', '.join(valid_unsubscriptions)} выполнена.") - app.logger.info(f"User {chat_id} unsubscribed from regions: {', '.join(valid_unsubscriptions)}.") - if not invalid_unsubscriptions and not valid_unsubscriptions: - bot.send_message(chat_id, "Не удалось выполнить отписку. Пожалуйста, попробуйте снова.") - bot.register_next_step_handler(message, process_unsubscription, chat_id) - + bot.send_message(chat_id, f"Unsubscribed from regions: {', '.join(region_ids)}.") + app.logger.info(f"User {chat_id} unsubscribed from regions: {', '.join(region_ids)}.") + show_main_menu(chat_id, is_whitelisted(chat_id)) # Handle /help command to provide instructions -@bot.message_handler(commands=['help']) def handle_help(message): help_text = ( - "/subscribe - Подписаться на рассылку событий по региону.\n" - "/unsubscribe - Отписаться от рассылки событий по региону.\n" + "/start - Начать работу с ботом\n" + "/subscribe - Подписаться на рассылку событий по региону. Необходимо указать номер региона пример /subscribe 01 - Адыгея\n" + "/unsubscribe - Отписаться от рассылки событий по региону. Необходимо указать номер региона пример /unsubscribe 01 - Адыгея\n" "/register - Запросить регистрацию в боте" ) bot.send_message(message.chat.id, help_text) + show_main_menu(message.chat.id, is_whitelisted(message.chat.id)) # Handle /register command for new user registration -@bot.message_handler(commands=['register']) def handle_register(message): chat_id = message.chat.id username = message.from_user.username @@ -351,62 +306,46 @@ def handle_register(message): else: username = "N/A" - bot.send_message(chat_id, f"Your chat ID is {chat_id} and your username is {username}. Requesting admin approval...") + markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) + markup.add('Подтвердить регистрацию', 'Отмена') + bot.send_message(chat_id, f"Your chat ID is {chat_id} and your username is {username}. Requesting admin approval...", reply_markup=markup) + bot.register_next_step_handler(message, process_register, chat_id, username) - for admin_chat_id in ADMIN_CHAT_IDS: - bot.send_message( - admin_chat_id, - f"User {username} ({chat_id}) is requesting to register.\n" - f"Do you approve this action?\n" - f"/add_whitelist {chat_id}" - ) - app.logger.info(f"User {chat_id} ({username}) requested registration.") - -# Handle /add_whitelist command to add a user to the whitelist (Admin only) -@bot.message_handler(commands=['add_whitelist']) -def handle_add_whitelist(message): - chat_id = message.chat.id - if str(chat_id) not in ADMIN_CHAT_IDS: - bot.send_message(chat_id, "Вы не авторизованы для использования этой команды.") - app.logger.info(f"Unauthorized admin command attempt by {chat_id}") +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)) return - try: - new_chat_id = int(message.text.split()[1]) - add_to_whitelist(new_chat_id) - bot.send_message(chat_id, f"Chat ID {new_chat_id} added to the whitelist.") - app.logger.info(f"Admin {chat_id} added {new_chat_id} to the whitelist.") - except (IndexError, ValueError): - bot.send_message(chat_id, "Invalid command format. Use /add_whitelist ") + if message.text.lower() == 'подтвердить регистрацию': + for admin_chat_id in ADMIN_CHAT_IDS: + markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True) + markup.add(f'/add_whitelist {chat_id}', 'Отмена') + bot.send_message( + admin_chat_id, + f"User {username} ({chat_id}) is requesting to register.\n" + f"Do you approve this action?", + reply_markup=markup + ) + bot.send_message(chat_id, "Запрос отправлен администратору для одобрения.") + 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)) -# Handle /remove_whitelist command to remove a user from the whitelist (Admin only) -@bot.message_handler(commands=['remove_whitelist']) -def handle_remove_whitelist(message): +# Handle admin region management commands +def prompt_admin_for_region(chat_id, action): + if action == 'add': + bot.send_message(chat_id, "Введите ID и название региона в формате: ") + bot.register_next_step_handler_by_chat_id(chat_id, process_add_region) + elif action == 'remove': + bot.send_message(chat_id, "Введите ID региона, который хотите удалить") + bot.register_next_step_handler_by_chat_id(chat_id, process_remove_region) + +def process_add_region(message): chat_id = message.chat.id - if str(chat_id) not in ADMIN_CHAT_IDS: - bot.send_message(chat_id, "Вы не авторизованы для использования этой команды.") - app.logger.info(f"Unauthorized admin command attempt by {chat_id}") - return - try: - remove_chat_id = int(message.text.split()[1]) - remove_from_whitelist(remove_chat_id) - bot.send_message(chat_id, f"Chat ID {remove_chat_id} removed from the whitelist.") - app.logger.info(f"Admin {chat_id} removed {remove_chat_id} from the whitelist.") - except (IndexError, ValueError): - bot.send_message(chat_id, "Invalid command format. Use /remove_whitelist ") - -# Handle /add_region command to add a new region (Admin only) -@bot.message_handler(commands=['add_region']) -def handle_add_region(message): - chat_id = message.chat.id - if str(chat_id) not in ADMIN_CHAT_IDS: - bot.send_message(chat_id, "Вы не авторизованы для использования этой команды.") - app.logger.info(f"Unauthorized admin command attempt by {chat_id}") - return - - try: - region_id, region_name = message.text.split()[1], ' '.join(message.text.split()[2:]) + region_id, region_name = message.text.split()[0], ' '.join(message.text.split()[1:]) with db_lock: conn = sqlite3.connect('telezab.db') cursor = conn.cursor() @@ -418,19 +357,13 @@ def handle_add_region(message): bot.send_message(chat_id, f"Region {region_id} - {region_name} added.") app.logger.info(f"Admin {chat_id} added region {region_id} - {region_name}.") except (IndexError, ValueError): - bot.send_message(chat_id, "Invalid command format. Use /add_region ") + bot.send_message(chat_id, "Invalid format. Use: ") + show_main_menu(chat_id, is_whitelisted(chat_id)) -# Handle /remove_region command to remove a region (Admin only) -@bot.message_handler(commands=['remove_region']) -def handle_remove_region(message): +def process_remove_region(message): chat_id = message.chat.id - if str(chat_id) not in ADMIN_CHAT_IDS: - bot.send_message(chat_id, "Вы не авторизованы для использования этой команды.") - app.logger.info(f"Unauthorized admin command attempt by {chat_id}") - return - try: - region_id = message.text.split()[1] + region_id = message.text.split()[0] with db_lock: conn = sqlite3.connect('telezab.db') cursor = conn.cursor() @@ -445,7 +378,39 @@ def handle_remove_region(message): bot.send_message(chat_id, f"Region {region_id} removed and all subscriptions updated.") app.logger.info(f"Admin {chat_id} removed region {region_id} and updated subscriptions.") except IndexError: - bot.send_message(chat_id, "Invalid command format. Use /remove_region ") + bot.send_message(chat_id, "Invalid format. Use: ") + show_main_menu(chat_id, is_whitelisted(chat_id)) + +# Handle admin whitelist management commands +def prompt_admin_for_whitelist(chat_id, action): + if action == 'add': + bot.send_message(chat_id, "Введите ID пользователя, которого хотите добавить в whitelist") + bot.register_next_step_handler_by_chat_id(chat_id, process_add_whitelist) + elif action == 'remove': + bot.send_message(chat_id, "Введите ID пользователя, которого хотите удалить из whitelist") + bot.register_next_step_handler_by_chat_id(chat_id, process_remove_whitelist) + +def process_add_whitelist(message): + chat_id = message.chat.id + try: + new_chat_id = int(message.text.split()[0]) + add_to_whitelist(new_chat_id) + bot.send_message(chat_id, f"Chat ID {new_chat_id} added to the whitelist.") + app.logger.info(f"Admin {chat_id} added {new_chat_id} to the whitelist.") + except (IndexError, ValueError): + bot.send_message(chat_id, "Invalid format. Use: ") + show_main_menu(chat_id, is_whitelisted(chat_id)) + +def process_remove_whitelist(message): + chat_id = message.chat.id + try: + remove_chat_id = int(message.text.split()[0]) + remove_from_whitelist(remove_chat_id) + bot.send_message(chat_id, f"Chat ID {remove_chat_id} removed from the whitelist.") + app.logger.info(f"Admin {chat_id} removed {remove_chat_id} from the whitelist.") + except (IndexError, ValueError): + bot.send_message(chat_id, "Invalid format. Use: ") + show_main_menu(chat_id, is_whitelisted(chat_id)) @app.route('/webhook', methods=['POST']) def webhook():