Add buttons for better using in TelegramApp

This commit is contained in:
Udo Chudo 2024-07-10 14:08:37 +05:00
parent f5ae48a7e9
commit 60a493e7f0
3 changed files with 143 additions and 168 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
/telezab.db /telezab.db
/.env /.env
/.idea /.idea
/TODO.txt

9
Dockerfile Normal file
View File

@ -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"]

View File

@ -8,7 +8,7 @@ from logging.config import dictConfig
from threading import Thread, Lock from threading import Thread, Lock
import sqlite3 import sqlite3
import time import time
import re
# Load environment variables # Load environment variables
load_dotenv() load_dotenv()
@ -133,7 +133,7 @@ def get_regions():
with db_lock: with db_lock:
conn = sqlite3.connect('telezab.db') conn = sqlite3.connect('telezab.db')
cursor = conn.cursor() 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() regions = cursor.fetchall()
conn.close() conn.close()
return regions return regions
@ -148,6 +148,7 @@ def get_user_subscribed_regions(chat_id):
FROM subscriptions FROM subscriptions
JOIN regions ON subscriptions.region_id = regions.region_id JOIN regions ON subscriptions.region_id = regions.region_id
WHERE subscriptions.chat_id = ? AND subscriptions.skip = FALSE WHERE subscriptions.chat_id = ? AND subscriptions.skip = FALSE
ORDER BY regions.region_id
''', (chat_id,)) ''', (chat_id,))
regions = cursor.fetchall() regions = cursor.fetchall()
conn.close() conn.close()
@ -157,51 +158,63 @@ def get_user_subscribed_regions(chat_id):
def format_regions_list(regions): def format_regions_list(regions):
return '\n'.join([f"{region_id} - {region_name}" for region_id, region_name in 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 # Handle /start command
@bot.message_handler(commands=['start']) @bot.message_handler(commands=['start'])
def handle_start(message): def handle_start(message):
chat_id = message.chat.id 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 username = message.from_user.username
if username: if username:
username = f"@{username}" username = f"@{username}"
else: else:
username = "N/A" username = "N/A"
bot.send_message(chat_id, "Выполните комманду /register для начала работы") if is_whitelisted(chat_id):
app.logger.info(f"User {chat_id} ({username}) started receiving alerts.") 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 app.logger.info(f"User {chat_id} ({username}) started with command /start.")
@bot.message_handler(commands=['stop'])
def handle_stop(message): # Handle menu button presses
@bot.message_handler(func=lambda message: True)
def handle_menu_selection(message):
chat_id = message.chat.id chat_id = message.chat.id
if not is_whitelisted(chat_id): text = message.text.strip().lower()
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.")
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 # Handle /subscribe command to subscribe to a region
@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):
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}") app.logger.info(f"Unauthorized access attempt by {chat_id}")
return return
@ -212,137 +225,79 @@ def handle_subscribe(message):
username = "N/A" username = "N/A"
regions_list = format_regions_list(get_regions()) 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) bot.register_next_step_handler(message, process_subscription, chat_id, username)
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))
return return
region_ids = message.text.split(',') 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: with db_lock:
conn = sqlite3.connect('telezab.db') conn = sqlite3.connect('telezab.db')
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()
# Проверка существования региона в таблице 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 (?, ?, ?)' 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}") 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))
conn.commit() conn.commit()
conn.close() conn.close()
bot.send_message(chat_id, f"Subscribed to regions: {', '.join(region_ids)}.")
if invalid_regions: app.logger.info(f"User {chat_id} ({username}) subscribed to regions: {', '.join(region_ids)}.")
bot.send_message(chat_id, f"Следующие регионы не существуют: {', '.join(invalid_regions)}. Пожалуйста, проверьте и введите снова.") show_main_menu(chat_id, is_whitelisted(chat_id))
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)
# Handle /unsubscribe command to unsubscribe from a region # Handle /unsubscribe command to unsubscribe from a region
@bot.message_handler(commands=['unsubscribe'])
def handle_unsubscribe(message): def handle_unsubscribe(message):
chat_id = message.chat.id chat_id = message.chat.id
if not is_whitelisted(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}") app.logger.info(f"Unauthorized access attempt by {chat_id}")
return return
user_regions = get_user_subscribed_regions(chat_id) user_regions = get_user_subscribed_regions(chat_id)
if not user_regions: if not user_regions:
bot.send_message(chat_id, "Вы ещё не подписались ни на один регион для получения событий") bot.send_message(chat_id, "You are not subscribed to any regions.")
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}\n\nНапишите 'отмена' для прекращения процедуры подписки.") bot.send_message(chat_id, f"Отправьте номер или номера регионов от которых хотите отписаться (через запятую):\n{regions_list}\n\nНапишите 'отмена' для отмены.")
bot.register_next_step_handler(message, process_unsubscription, chat_id) bot.register_next_step_handler(message, process_unsubscription, chat_id)
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))
return return
region_ids = message.text.split(',') 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: with db_lock:
conn = sqlite3.connect('telezab.db') conn = sqlite3.connect('telezab.db')
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()
# Проверка существования подписки query = 'DELETE FROM subscriptions WHERE chat_id = ? AND region_id = ?'
cursor.execute('SELECT COUNT(*) FROM subscriptions WHERE chat_id = ? AND region_id = ?', (chat_id, region_id)) app.logger.debug(f"Executing query: {query} with chat_id={chat_id}, region_id={region_id}")
if cursor.fetchone()[0] == 0: cursor.execute(query, (chat_id, region_id))
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))
conn.commit() conn.commit()
conn.close() conn.close()
bot.send_message(chat_id, f"Unsubscribed from regions: {', '.join(region_ids)}.")
if invalid_unsubscriptions: app.logger.info(f"User {chat_id} unsubscribed from regions: {', '.join(region_ids)}.")
bot.send_message(chat_id, f"Вы не подписаны на следующие регионы: {', '.join(invalid_unsubscriptions)}.") show_main_menu(chat_id, is_whitelisted(chat_id))
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)
# Handle /help command to provide instructions # Handle /help command to provide instructions
@bot.message_handler(commands=['help'])
def handle_help(message): def handle_help(message):
help_text = ( help_text = (
"/subscribe - Подписаться на рассылку событий по региону.\n" "/start - Начать работу с ботом\n"
"/unsubscribe - Отписаться от рассылки событий по региону.\n" "/subscribe - Подписаться на рассылку событий по региону. Необходимо указать номер региона пример /subscribe 01 - Адыгея\n"
"/unsubscribe - Отписаться от рассылки событий по региону. Необходимо указать номер региона пример /unsubscribe 01 - Адыгея\n"
"/register - Запросить регистрацию в боте" "/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))
# Handle /register command for new user registration # Handle /register command for new user registration
@bot.message_handler(commands=['register'])
def handle_register(message): def handle_register(message):
chat_id = message.chat.id chat_id = message.chat.id
username = message.from_user.username username = message.from_user.username
@ -351,62 +306,46 @@ def handle_register(message):
else: else:
username = "N/A" 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: def process_register(message, chat_id, username):
bot.send_message( if message.text.lower() == 'отмена':
admin_chat_id, bot.send_message(chat_id, "Регистрация отменена.")
f"User {username} ({chat_id}) is requesting to register.\n" show_main_menu(chat_id, is_whitelisted(chat_id))
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}")
return return
try: if message.text.lower() == 'подтвердить регистрацию':
new_chat_id = int(message.text.split()[1]) for admin_chat_id in ADMIN_CHAT_IDS:
add_to_whitelist(new_chat_id) markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True)
bot.send_message(chat_id, f"Chat ID {new_chat_id} added to the whitelist.") markup.add(f'/add_whitelist {chat_id}', 'Отмена')
app.logger.info(f"Admin {chat_id} added {new_chat_id} to the whitelist.") bot.send_message(
except (IndexError, ValueError): admin_chat_id,
bot.send_message(chat_id, "Invalid command format. Use /add_whitelist <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) # Handle admin region management commands
@bot.message_handler(commands=['remove_whitelist']) def prompt_admin_for_region(chat_id, action):
def handle_remove_whitelist(message): if action == 'add':
bot.send_message(chat_id, "Введите ID и название региона в формате: <region_id> <region_name>")
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 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: try:
remove_chat_id = int(message.text.split()[1]) region_id, region_name = message.text.split()[0], ' '.join(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 <chat_id>")
# 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:])
with db_lock: with db_lock:
conn = sqlite3.connect('telezab.db') conn = sqlite3.connect('telezab.db')
cursor = conn.cursor() cursor = conn.cursor()
@ -418,19 +357,13 @@ def handle_add_region(message):
bot.send_message(chat_id, f"Region {region_id} - {region_name} added.") 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}.") app.logger.info(f"Admin {chat_id} added region {region_id} - {region_name}.")
except (IndexError, ValueError): except (IndexError, ValueError):
bot.send_message(chat_id, "Invalid command format. Use /add_region <region_id> <region_name>") bot.send_message(chat_id, "Invalid format. Use: <region_id> <region_name>")
show_main_menu(chat_id, is_whitelisted(chat_id))
# Handle /remove_region command to remove a region (Admin only) def process_remove_region(message):
@bot.message_handler(commands=['remove_region'])
def handle_remove_region(message):
chat_id = message.chat.id 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: try:
region_id = message.text.split()[1] region_id = message.text.split()[0]
with db_lock: with db_lock:
conn = sqlite3.connect('telezab.db') conn = sqlite3.connect('telezab.db')
cursor = conn.cursor() 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.") 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.") app.logger.info(f"Admin {chat_id} removed region {region_id} and updated subscriptions.")
except IndexError: except IndexError:
bot.send_message(chat_id, "Invalid command format. Use /remove_region <region_id>") bot.send_message(chat_id, "Invalid format. Use: <region_id>")
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: <chat_id>")
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: <chat_id>")
show_main_menu(chat_id, is_whitelisted(chat_id))
@app.route('/webhook', methods=['POST']) @app.route('/webhook', methods=['POST'])
def webhook(): def webhook():