Add function for grabbing and sending triggers from zabbix to user
Some many fixes Now i can accept registration requests from my phone :D
This commit is contained in:
parent
2c06ccde2e
commit
3838645d7e
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
/.idea
|
/.idea
|
||||||
/TODO.txt
|
/TODO.txt
|
||||||
/logs
|
/logs
|
||||||
/__pycache__
|
/__pycache__
|
||||||
|
/venv
|
||||||
@ -19,7 +19,4 @@ pyTelegramBotAPI==4.21.0
|
|||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
pyzabbix==1.3.1
|
pyzabbix==1.3.1
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
telebot==0.0.5
|
pytz~=2024.1
|
||||||
urllib3==2.2.2
|
|
||||||
Werkzeug==3.0.3
|
|
||||||
yarl==1.9.4
|
|
||||||
394
telezab.py
394
telezab.py
@ -15,6 +15,9 @@ import json
|
|||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from pyzabbix import ZabbixAPI
|
from pyzabbix import ZabbixAPI
|
||||||
import requests
|
import requests
|
||||||
|
from pytz import timezone
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import re
|
||||||
|
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
@ -31,11 +34,21 @@ LOG_PATH_EVENTS = os.getenv('LOG_PATH_EVENTS', 'logs/events.log')
|
|||||||
os.makedirs(os.path.dirname(LOG_PATH_ERRORS), exist_ok=True)
|
os.makedirs(os.path.dirname(LOG_PATH_ERRORS), exist_ok=True)
|
||||||
os.makedirs(os.path.dirname(LOG_PATH_EVENTS), exist_ok=True)
|
os.makedirs(os.path.dirname(LOG_PATH_EVENTS), exist_ok=True)
|
||||||
|
|
||||||
|
class UTF8StreamHandler(logging.StreamHandler):
|
||||||
|
def __init__(self, stream=None):
|
||||||
|
super().__init__(stream)
|
||||||
|
self.setStream(stream)
|
||||||
|
|
||||||
|
def setStream(self, stream):
|
||||||
|
super().setStream(stream)
|
||||||
|
if hasattr(stream, 'reconfigure'):
|
||||||
|
stream.reconfigure(encoding='utf-8')
|
||||||
|
|
||||||
# Define handlers based on environment variables
|
# Define handlers based on environment variables
|
||||||
handlers = {}
|
handlers = {}
|
||||||
if ENABLE_CONSOLE_LOGGING:
|
if ENABLE_CONSOLE_LOGGING:
|
||||||
handlers['console'] = {
|
handlers['console'] = {
|
||||||
'class': 'logging.StreamHandler',
|
'class': 'telezab.UTF8StreamHandler',
|
||||||
'stream': 'ext://sys.stdout', # Output to console
|
'stream': 'ext://sys.stdout', # Output to console
|
||||||
'formatter': 'console',
|
'formatter': 'console',
|
||||||
}
|
}
|
||||||
@ -44,11 +57,13 @@ if ENABLE_FILE_LOGGING:
|
|||||||
'class': 'logging.FileHandler',
|
'class': 'logging.FileHandler',
|
||||||
'filename': LOG_PATH_ERRORS,
|
'filename': LOG_PATH_ERRORS,
|
||||||
'formatter': 'error',
|
'formatter': 'error',
|
||||||
|
'encoding': 'utf-8', # Ensure UTF-8 encoding
|
||||||
}
|
}
|
||||||
handlers['file_events'] = {
|
handlers['file_events'] = {
|
||||||
'class': 'logging.FileHandler',
|
'class': 'logging.FileHandler',
|
||||||
'filename': LOG_PATH_EVENTS,
|
'filename': LOG_PATH_EVENTS,
|
||||||
'formatter': 'event',
|
'formatter': 'event',
|
||||||
|
'encoding': 'utf-8', # Ensure UTF-8 encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
@ -175,13 +190,13 @@ def is_whitelisted(chat_id):
|
|||||||
|
|
||||||
|
|
||||||
# Add user to whitelist
|
# Add user to whitelist
|
||||||
def add_to_whitelist(chat_id):
|
def add_to_whitelist(chat_id, username):
|
||||||
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 whitelist (chat_id) VALUES (?)'
|
query = 'INSERT OR IGNORE INTO whitelist (chat_id, username) VALUES (?, ?)'
|
||||||
app.logger.debug(f"Executing query: {query} with chat_id={chat_id}")
|
app.logger.debug(f"Executing query: {query} with chat_id={chat_id}, username={username}")
|
||||||
cursor.execute(query, (chat_id,))
|
cursor.execute(query, (chat_id, username))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@ -530,11 +545,38 @@ def handle_register(message):
|
|||||||
else:
|
else:
|
||||||
username = "N/A"
|
username = "N/A"
|
||||||
|
|
||||||
markup = telebot.types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True)
|
bot.send_message(chat_id,
|
||||||
markup.add('Подтвердить регистрацию', 'Отмена')
|
f"Ваш chat ID: {chat_id}, ваше имя пользователя: {username}. Запрос на одобрение отправлен администратору.")
|
||||||
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)
|
|
||||||
|
for admin_chat_id in ADMIN_CHAT_IDS:
|
||||||
|
markup = telebot.types.InlineKeyboardMarkup()
|
||||||
|
markup.add(
|
||||||
|
telebot.types.InlineKeyboardButton(text="Подтвердить", callback_data=f"approve_{chat_id}_{username}"),
|
||||||
|
telebot.types.InlineKeyboardButton(text="Отменить", callback_data=f"decline_{chat_id}_{username}")
|
||||||
|
)
|
||||||
|
bot.send_message(
|
||||||
|
admin_chat_id,
|
||||||
|
f"Пользователь {username} ({chat_id}) запрашивает регистрацию. Вы подтверждаете это действие?",
|
||||||
|
reply_markup=markup
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bot.callback_query_handler(func=lambda call: call.data.startswith("approve_") or call.data.startswith("decline_"))
|
||||||
|
def handle_admin_response(call):
|
||||||
|
action, chat_id, username = call.data.split("_")
|
||||||
|
if action == "approve":
|
||||||
|
add_to_whitelist(int(chat_id), username)
|
||||||
|
bot.send_message(chat_id, "Ваша регистрация одобрена администратором.")
|
||||||
|
bot.send_message(call.message.chat.id, f"Пользователь {username} ({chat_id}) добавлен в белый список.")
|
||||||
|
log_user_event(chat_id, username, "Approved registration")
|
||||||
|
elif action == "decline":
|
||||||
|
bot.send_message(chat_id, "Ваша регистрация отклонена администратором.")
|
||||||
|
bot.send_message(call.message.chat.id, f"Пользователь {username} ({chat_id}) отклонен.")
|
||||||
|
log_user_event(chat_id, username, "Declined registration")
|
||||||
|
bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=None)
|
||||||
|
bot.answer_callback_query(call.id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_register(message, chat_id, username):
|
def process_register(message, chat_id, username):
|
||||||
@ -572,6 +614,7 @@ def prompt_admin_for_region(chat_id, action):
|
|||||||
|
|
||||||
def process_add_region(message):
|
def process_add_region(message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
region_id, region_name = message.text.split()[0], ' '.join(message.text.split()[1:])
|
region_id, region_name = message.text.split()[0], ' '.join(message.text.split()[1:])
|
||||||
with db_lock:
|
with db_lock:
|
||||||
@ -593,6 +636,7 @@ def process_add_region(message):
|
|||||||
markup = telebot.types.InlineKeyboardMarkup()
|
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"replace_{region_id}_{region_name}"))
|
||||||
markup.add(telebot.types.InlineKeyboardButton(text="Активировать старый", callback_data=f"reactivate_{region_id}"))
|
markup.add(telebot.types.InlineKeyboardButton(text="Активировать старый", callback_data=f"reactivate_{region_id}"))
|
||||||
|
markup.add(telebot.types.InlineKeyboardButton(text="Отмена", callback_data=f"cancel_region_{chat_id}"))
|
||||||
bot.send_message(chat_id, f"Регион {region_id} уже существует с названием {existing_region_name}. Хотите заменить его или активировать старый регион?", reply_markup=markup)
|
bot.send_message(chat_id, f"Регион {region_id} уже существует с названием {existing_region_name}. Хотите заменить его или активировать старый регион?", reply_markup=markup)
|
||||||
else:
|
else:
|
||||||
query = 'INSERT OR IGNORE INTO regions (region_id, region_name) VALUES (?, ?)'
|
query = 'INSERT OR IGNORE INTO regions (region_id, region_name) VALUES (?, ?)'
|
||||||
@ -606,7 +650,7 @@ def process_add_region(message):
|
|||||||
bot.send_message(chat_id, "Неверный формат. Используйте: <region_id> <region_name>")
|
bot.send_message(chat_id, "Неверный формат. Используйте: <region_id> <region_name>")
|
||||||
|
|
||||||
|
|
||||||
@bot.callback_query_handler(func=lambda call: call.data.startswith("replace_") or call.data.startswith("reactivate_"))
|
@bot.callback_query_handler(func=lambda call: call.data.startswith("replace_") or call.data.startswith("reactivate_") or call.data.startswith("cancel_region_"))
|
||||||
def handle_region_action(call):
|
def handle_region_action(call):
|
||||||
parts = call.data.split("_", 2)
|
parts = call.data.split("_", 2)
|
||||||
action = parts[0]
|
action = parts[0]
|
||||||
@ -635,12 +679,14 @@ def handle_region_action(call):
|
|||||||
cursor.execute(query, (region_id,))
|
cursor.execute(query, (region_id,))
|
||||||
bot.send_message(chat_id, f"Регион {region_id} активирован.")
|
bot.send_message(chat_id, f"Регион {region_id} активирован.")
|
||||||
app.logger.info(f"Admin {chat_id} reactivated region {region_id}.")
|
app.logger.info(f"Admin {chat_id} reactivated region {region_id}.")
|
||||||
|
elif action == "cancel_region":
|
||||||
|
bot.send_message(chat_id, "Действие отменено.")
|
||||||
|
app.logger.info(f"Admin {chat_id} canceled region action.")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=None)
|
||||||
bot.answer_callback_query(call.id) # Завершение обработки callback
|
|
||||||
show_settings_menu(chat_id)
|
show_settings_menu(chat_id)
|
||||||
|
bot.answer_callback_query(call.id) # Завершение обработки callback
|
||||||
|
|
||||||
def process_remove_region(message):
|
def process_remove_region(message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
@ -672,40 +718,40 @@ def process_remove_region(message):
|
|||||||
show_settings_menu(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):
|
||||||
if action == 'add':
|
# if action == 'add':
|
||||||
bot.send_message(chat_id, "Введите ID пользователя, которого хотите добавить в белый список")
|
# bot.send_message(chat_id, "Введите ID пользователя, которого хотите добавить в белый список")
|
||||||
bot.register_next_step_handler_by_chat_id(chat_id, process_add_whitelist)
|
# bot.register_next_step_handler_by_chat_id(chat_id, process_add_whitelist)
|
||||||
elif action == 'remove':
|
# elif action == 'remove':
|
||||||
bot.send_message(chat_id, "Введите ID пользователя, которого хотите удалить из белого списка")
|
# bot.send_message(chat_id, "Введите ID пользователя, которого хотите удалить из белого списка")
|
||||||
bot.register_next_step_handler_by_chat_id(chat_id, process_remove_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} добавлен в белый список.")
|
||||||
|
# app.logger.info(f"Admin {chat_id} added {new_chat_id} to the whitelist.")
|
||||||
|
# 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, "Неверный формат. Используйте: <chat_id>")
|
||||||
|
# show_settings_menu(chat_id)
|
||||||
|
|
||||||
|
|
||||||
def process_add_whitelist(message):
|
# def process_remove_whitelist(message):
|
||||||
chat_id = message.chat.id
|
# chat_id = message.chat.id
|
||||||
try:
|
# try:
|
||||||
new_chat_id = int(message.text.split()[0])
|
# remove_chat_id = int(message.text.split()[0])
|
||||||
add_to_whitelist(new_chat_id)
|
# remove_from_whitelist(remove_chat_id)
|
||||||
bot.send_message(chat_id, f"Chat ID {new_chat_id} добавлен в белый список.")
|
# bot.send_message(chat_id, f"Chat ID {remove_chat_id} удален из белого списка.")
|
||||||
app.logger.info(f"Admin {chat_id} added {new_chat_id} to the whitelist.")
|
# app.logger.info(f"Admin {chat_id} removed {remove_chat_id} from the whitelist.")
|
||||||
log_user_event(new_chat_id, "N/A", f"Added to 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_settings_menu(chat_id)
|
# show_settings_menu(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} удален из белого списка.")
|
|
||||||
app.logger.info(f"Admin {chat_id} removed {remove_chat_id} from the whitelist.")
|
|
||||||
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, "Неверный формат. Используйте: <chat_id>")
|
|
||||||
show_settings_menu(chat_id)
|
|
||||||
|
|
||||||
|
|
||||||
# Handle displaying active subscriptions for a user
|
# Handle displaying active subscriptions for a user
|
||||||
@ -828,59 +874,128 @@ async def check_telegram_api():
|
|||||||
app.logger.error(f"Error checking Telegram API: {e}")
|
app.logger.error(f"Error checking Telegram API: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_region_number(host):
|
||||||
|
# Используем регулярное выражение для извлечения цифр после первого символа и до первой буквы
|
||||||
|
match = re.match(r'^.\d+', host)
|
||||||
|
if match:
|
||||||
|
return match.group(0)[1:] # Возвращаем строку без первого символа
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@app.route('/webhook', methods=['POST'])
|
@app.route('/webhook', methods=['POST'])
|
||||||
def webhook():
|
def webhook():
|
||||||
data = request.get_json()
|
try:
|
||||||
app.logger.info(f"Received data: {data}")
|
data = request.get_json()
|
||||||
|
app.logger.info(f"Received data: {data}")
|
||||||
|
|
||||||
event_hash = hash_data(data)
|
event_hash = hash_data(data)
|
||||||
|
|
||||||
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 COUNT(*) FROM events')
|
cursor.execute('SELECT COUNT(*) FROM events')
|
||||||
count = cursor.fetchone()[0]
|
count = cursor.fetchone()[0]
|
||||||
|
|
||||||
if count >= 200:
|
if count >= 200:
|
||||||
query = 'DELETE FROM events WHERE id = (SELECT MIN(id) FROM events)'
|
query = 'DELETE FROM events WHERE id = (SELECT MIN(id) FROM events)'
|
||||||
app.logger.debug(f"Executing query: {query}")
|
app.logger.debug(f"Executing query: {query}")
|
||||||
cursor.execute(query)
|
cursor.execute(query)
|
||||||
|
|
||||||
query = 'INSERT OR IGNORE INTO events (hash, data, delivered) VALUES (?, ?, ?)'
|
# Извлечение номера региона из поля host
|
||||||
app.logger.debug(f"Executing query: {query} with hash={event_hash}, data={data}, delivered={False}")
|
region_id = extract_region_number(data.get("host"))
|
||||||
cursor.execute(query, (event_hash, str(data), False))
|
if region_id is None:
|
||||||
|
app.logger.error(f"Failed to extract region number from host: {data.get('host')}")
|
||||||
|
return jsonify({"status": "error", "message": "Invalid host format"}), 400
|
||||||
|
|
||||||
# Fetch chat_ids to send the alert
|
# Fetch chat_ids to send the alert
|
||||||
region_id = data.get("region")
|
query = 'SELECT chat_id, username FROM subscriptions WHERE region_id = ? AND active = TRUE AND skip = FALSE'
|
||||||
query = 'SELECT chat_id, username FROM subscriptions WHERE region_id = ? AND active = TRUE AND skip = FALSE'
|
app.logger.debug(f"Executing query: {query} with region_id={region_id}")
|
||||||
app.logger.debug(f"Executing query: {query} with region_id={region_id}")
|
cursor.execute(query, (region_id,))
|
||||||
cursor.execute(query, (region_id,))
|
results = cursor.fetchall()
|
||||||
results = cursor.fetchall()
|
|
||||||
|
|
||||||
# Check if the region is active
|
# Check if the region is active
|
||||||
query = 'SELECT active FROM regions WHERE region_id = ?'
|
query = 'SELECT active FROM regions WHERE region_id = ?'
|
||||||
cursor.execute(query, (region_id,))
|
cursor.execute(query, (region_id,))
|
||||||
region_active = cursor.fetchone()[0]
|
region_row = cursor.fetchone()
|
||||||
|
|
||||||
if region_active:
|
if region_row and region_row[0]: # Check if region_row is not None and if active
|
||||||
message = format_message(data)
|
message = format_message(data)
|
||||||
for chat_id, username in results:
|
undelivered = False
|
||||||
app.logger.debug(f"Queueing message: {message} to chat_id={chat_id}, username={username}")
|
for chat_id, username in results:
|
||||||
send_to_queue({'chat_id': chat_id, 'message': message})
|
app.logger.debug(f"Queueing message: {message} to chat_id={chat_id}, username={username}")
|
||||||
app.logger.info(f"Queued alert for {chat_id} ({username}) for region {region_id}")
|
try:
|
||||||
|
send_to_queue({'chat_id': chat_id, 'message': message})
|
||||||
|
app.logger.info(f"Queued alert for {chat_id} ({username}) for region {region_id}")
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Failed to send message to {chat_id} ({username}): {e}")
|
||||||
|
undelivered = True
|
||||||
|
|
||||||
conn.commit()
|
if undelivered:
|
||||||
conn.close()
|
query = 'INSERT OR IGNORE INTO events (hash, data, delivered) VALUES (?, ?, ?)'
|
||||||
|
app.logger.debug(f"Executing query: {query} with hash={event_hash}, data={data}, delivered={False}")
|
||||||
|
cursor.execute(query, (event_hash, str(data), False))
|
||||||
|
|
||||||
return jsonify({"status": "success"}), 200
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return jsonify({"status": "success"}), 200
|
||||||
|
|
||||||
|
except sqlite3.OperationalError as e:
|
||||||
|
app.logger.error(f"Database operation error: {e}")
|
||||||
|
return jsonify({"status": "error", "message": "Database operation error"}), 500
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
app.logger.error(f"Value error: {e}")
|
||||||
|
return jsonify({"status": "error", "message": "Invalid data"}), 400
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Unexpected error: {e}")
|
||||||
|
return jsonify({"status": "error", "message": "Internal server error"}), 500
|
||||||
|
|
||||||
|
|
||||||
def format_message(data):
|
def format_message(data):
|
||||||
return (f"Zabbix Alert\n"
|
try:
|
||||||
f"Host: {data['host']}\n"
|
status_emoji = "⚠️" if data['status'].upper() == "PROBLEM" else "✅"
|
||||||
f"Item: {data['item']}\n"
|
priority_map = {
|
||||||
f"Trigger: {data['trigger']}\n"
|
'High': 'Высокая',
|
||||||
f"Value: {data['value']}")
|
'Disaster': 'Авария'
|
||||||
|
}
|
||||||
|
priority = priority_map.get(data['severity'], 'Неизвестно')
|
||||||
|
message = (
|
||||||
|
f"{status_emoji} Host: {data['host']}\n"
|
||||||
|
f"Сообщение: {data['msg']}\n"
|
||||||
|
f"Дата получения: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(data['date_reception'])))}\n"
|
||||||
|
f"Критичность: {priority}\n"
|
||||||
|
f"Теги: {data['tags']}\n"
|
||||||
|
f"Статус: {data['status']}"
|
||||||
|
)
|
||||||
|
if 'link' in data:
|
||||||
|
message += f'\nСсылка на график: {data['link']}'
|
||||||
|
return message
|
||||||
|
except KeyError as e:
|
||||||
|
app.logger.error(f"Missing key in data: {e}")
|
||||||
|
raise ValueError(f"Missing key in data: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/add_user', methods=['POST'])
|
||||||
|
def add_user():
|
||||||
|
data = request.get_json()
|
||||||
|
telegram_id = data.get('telegram_id')
|
||||||
|
chat_id = data.get('chat_id')
|
||||||
|
|
||||||
|
if telegram_id and chat_id:
|
||||||
|
add_to_whitelist(chat_id, telegram_id)
|
||||||
|
return jsonify({"status": "success"}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({"status": "failure", "reason": "Invalid data"}), 400
|
||||||
|
|
||||||
|
|
||||||
|
# def format_message(data):
|
||||||
|
# return (f"Zabbix Alert\n"
|
||||||
|
# f"Host: {data['host']}\n"
|
||||||
|
# f"Item: {data['item']}\n"
|
||||||
|
# f"Trigger: {data['trigger']}\n"
|
||||||
|
# f"Value: {data['value']}")
|
||||||
|
|
||||||
|
|
||||||
# Handle active triggers
|
# Handle active triggers
|
||||||
@ -933,6 +1048,8 @@ def handle_region_selection(call):
|
|||||||
|
|
||||||
if not filtered_groups:
|
if not filtered_groups:
|
||||||
bot.send_message(chat_id, "Нет групп хостов, соответствующих данному региону.")
|
bot.send_message(chat_id, "Нет групп хостов, соответствующих данному региону.")
|
||||||
|
bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=None)
|
||||||
|
bot.answer_callback_query(call.id)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Отправка списка групп хостов пользователю в виде кнопок
|
# Отправка списка групп хостов пользователю в виде кнопок
|
||||||
@ -945,6 +1062,8 @@ def handle_region_selection(call):
|
|||||||
logging.error(f"Error connecting to Zabbix API: {e}")
|
logging.error(f"Error connecting to Zabbix API: {e}")
|
||||||
bot.send_message(chat_id, "Не удалось подключиться к Zabbix API. Пожалуйста, попробуйте позже.")
|
bot.send_message(chat_id, "Не удалось подключиться к Zabbix API. Пожалуйста, попробуйте позже.")
|
||||||
|
|
||||||
|
bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=None)
|
||||||
|
bot.answer_callback_query(call.id)
|
||||||
|
|
||||||
@bot.callback_query_handler(func=lambda call: call.data.startswith("group_"))
|
@bot.callback_query_handler(func=lambda call: call.data.startswith("group_"))
|
||||||
def handle_group_selection(call):
|
def handle_group_selection(call):
|
||||||
@ -959,12 +1078,16 @@ def handle_group_selection(call):
|
|||||||
elif not triggers:
|
elif not triggers:
|
||||||
bot.send_message(chat_id, "Нет активных проблем по указанной группе за последние 24 часа.")
|
bot.send_message(chat_id, "Нет активных проблем по указанной группе за последние 24 часа.")
|
||||||
else:
|
else:
|
||||||
bot.send_message(chat_id, triggers, parse_mode="Markdown")
|
for trigger in triggers:
|
||||||
|
bot.send_message(chat_id, trigger, parse_mode="html")
|
||||||
|
time.sleep(1/5)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error processing group selection: {e}")
|
logging.error(f"Error processing group selection: {e}")
|
||||||
bot.send_message(chat_id, "Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже.")
|
bot.send_message(chat_id, "Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже.")
|
||||||
|
|
||||||
|
bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=None)
|
||||||
|
bot.answer_callback_query(call.id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@bot.callback_query_handler(func=lambda call: call.data.startswith("prev_") or call.data.startswith("next_"))
|
@bot.callback_query_handler(func=lambda call: call.data.startswith("prev_") or call.data.startswith("next_"))
|
||||||
@ -985,14 +1108,15 @@ def handle_pagination(call):
|
|||||||
bot.answer_callback_query(call.id) # Завершение обработки callback
|
bot.answer_callback_query(call.id) # Завершение обработки callback
|
||||||
|
|
||||||
|
|
||||||
# Функция для получения активных триггеров из Zabbix API
|
|
||||||
def get_zabbix_triggers(group_id):
|
def get_zabbix_triggers(group_id):
|
||||||
try:
|
try:
|
||||||
|
# Создайте подключение к вашему Zabbix серверу
|
||||||
zapi = ZabbixAPI(ZABBIX_URL)
|
zapi = ZabbixAPI(ZABBIX_URL)
|
||||||
zapi.login(api_token=ZABBIX_API_TOKEN)
|
zapi.login(api_token=ZABBIX_API_TOKEN)
|
||||||
|
|
||||||
# Получение триггеров уровня "Высокая" и "Авария" за последние 24 часа для указанной группы хостов
|
# Получение триггеров уровня "Высокая" и "Авария" за последние 24 часа для указанной группы хостов
|
||||||
time_from = int(time.time()) - 24 * 3600 # последние 24 часа
|
time_from = int(time.time()) - 24 * 3600 # последние 24 часа
|
||||||
|
time_till = int(time.time())
|
||||||
triggers = zapi.trigger.get(
|
triggers = zapi.trigger.get(
|
||||||
output=["triggerid", "description", "priority"],
|
output=["triggerid", "description", "priority"],
|
||||||
selectHosts=["hostid", "name"],
|
selectHosts=["hostid", "name"],
|
||||||
@ -1002,22 +1126,46 @@ def get_zabbix_triggers(group_id):
|
|||||||
active=1,
|
active=1,
|
||||||
withLastEventUnacknowledged=1,
|
withLastEventUnacknowledged=1,
|
||||||
time_from=time_from,
|
time_from=time_from,
|
||||||
|
time_till=time_till,
|
||||||
expandDescription=1,
|
expandDescription=1,
|
||||||
expandComment=1,
|
expandComment=1,
|
||||||
selectItems=["itemid", "lastvalue"]
|
selectItems=["itemid", "lastvalue"],
|
||||||
|
selectLastEvent=["clock"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Московское время
|
||||||
|
moskva_tz = timezone('Europe/Moscow')
|
||||||
|
|
||||||
priority_map = {
|
priority_map = {
|
||||||
'4': 'Высокая',
|
'4': 'Высокая',
|
||||||
'5': 'Авария'
|
'5': 'Авария'
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger_messages = []
|
trigger_messages = []
|
||||||
|
current_time = datetime.now(moskva_tz)
|
||||||
|
one_day_ago = current_time - timedelta(days=1)
|
||||||
|
|
||||||
for trigger in triggers:
|
for trigger in triggers:
|
||||||
|
# Получаем время последнего события
|
||||||
|
event_time_epoch = int(trigger['lastEvent']['clock'])
|
||||||
|
event_time = datetime.fromtimestamp(event_time_epoch, tz=moskva_tz)
|
||||||
|
|
||||||
|
# Проверяем, не старше ли событие чем на сутки
|
||||||
|
if event_time < one_day_ago:
|
||||||
|
continue
|
||||||
|
|
||||||
description = trigger['description']
|
description = trigger['description']
|
||||||
host = trigger['hosts'][0]['name']
|
host = trigger['hosts'][0]['name']
|
||||||
priority = priority_map.get(trigger['priority'], 'Неизвестно')
|
priority = priority_map.get(trigger['priority'], 'Неизвестно')
|
||||||
trigger_id = trigger['triggerid']
|
|
||||||
|
|
||||||
|
# Генерация ссылки на график
|
||||||
|
item_ids = [item['itemid'] for item in trigger['items']]
|
||||||
|
graph_links = []
|
||||||
|
for item_id in item_ids:
|
||||||
|
graph_link = f'<a href="{ZABBIX_URL}/history.php?action=showgraph&itemids[]={item_id}">Ссылка на график</a>'
|
||||||
|
graph_links.append(graph_link)
|
||||||
|
graph_links_str = "\n".join(graph_links)
|
||||||
|
|
||||||
# Заменяем {HOST.NAME} на имя хоста
|
# Заменяем {HOST.NAME} на имя хоста
|
||||||
description = description.replace("{HOST.NAME}", host)
|
description = description.replace("{HOST.NAME}", host)
|
||||||
@ -1028,30 +1176,76 @@ def get_zabbix_triggers(group_id):
|
|||||||
if lastvalue_placeholder in description:
|
if lastvalue_placeholder in description:
|
||||||
description = description.replace(lastvalue_placeholder, item['lastvalue'])
|
description = description.replace(lastvalue_placeholder, item['lastvalue'])
|
||||||
|
|
||||||
message = f"*Host*: {host}\n*Критичность*: {priority}\n*Описание*: {description}"
|
event_time_formatted = event_time.strftime('%Y-%m-%d %H:%M:%S Мск')
|
||||||
if trigger_id:
|
|
||||||
message += f"\n[Ссылка на триггер]({ZABBIX_URL}/tr_events.php?triggerid={trigger_id})"
|
message = (f"<b>Host</b>: {host}\n"
|
||||||
|
f"<b>Критичность</b>: {priority}\n"
|
||||||
|
f"<b>Описание</b>: {description}\n"
|
||||||
|
f"<b>Время создания</b>: {event_time_formatted}\n"
|
||||||
|
f"{graph_links_str}")
|
||||||
|
|
||||||
trigger_messages.append(message)
|
trigger_messages.append(message)
|
||||||
|
|
||||||
return "\n---\n".join(trigger_messages)
|
return trigger_messages
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error connecting to Zabbix API: {e}")
|
logging.error(f"Error connecting to Zabbix API: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# def split_message(message):
|
||||||
|
# max_length = 4096
|
||||||
|
# if len(message) <= max_length:
|
||||||
|
# return [message]
|
||||||
|
#
|
||||||
|
# parts = []
|
||||||
|
# while len(message) > max_length:
|
||||||
|
# split_index = message[:max_length].rfind('\n')
|
||||||
|
# if split_index == -1:
|
||||||
|
# split_index = max_length
|
||||||
|
# parts.append(message[:split_index])
|
||||||
|
# message = message[split_index:]
|
||||||
|
#
|
||||||
|
# parts.append(message)
|
||||||
|
# return parts
|
||||||
|
|
||||||
|
|
||||||
|
# def split_message_by_delimiter(messages, delimiter="---"):
|
||||||
|
# max_length = 4096
|
||||||
|
# parts = []
|
||||||
|
# current_part = ""
|
||||||
|
#
|
||||||
|
# for message in messages:
|
||||||
|
# if len(current_part) + len(message) + len(delimiter) <= max_length:
|
||||||
|
# if current_part:
|
||||||
|
# current_part += f"\n\n{delimiter}\n\n"
|
||||||
|
# current_part += message
|
||||||
|
# else:
|
||||||
|
# parts.append(current_part)
|
||||||
|
# current_part = message
|
||||||
|
#
|
||||||
|
# if current_part:
|
||||||
|
# parts.append(current_part)
|
||||||
|
#
|
||||||
|
# return parts
|
||||||
|
|
||||||
|
async def send_group_messages(chat_id, messages):
|
||||||
|
for message in messages:
|
||||||
|
await send_message(chat_id, message, is_notification=True)
|
||||||
|
await asyncio.sleep(1) # Delay between messages to comply with rate limit
|
||||||
|
|
||||||
# Test functions for admin
|
# Test functions for admin
|
||||||
def simulate_event(message):
|
def simulate_event(message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
test_event = {
|
test_event = {
|
||||||
"region": "12",
|
"host": "12",
|
||||||
"host": "Тестовое сообщение",
|
"msg": "Тестовое сообщение",
|
||||||
"item": "Марий Эл",
|
"date_reception": "12757175125",
|
||||||
"trigger": "Спасайте её",
|
"severity": "5",
|
||||||
"value": "Авария!"
|
"tags": "OTC,OFS,NFS",
|
||||||
|
"status": "Авария!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.logger.info(f"Simulating event: {test_event}")
|
app.logger.info(f"Simulating event: {test_event}")
|
||||||
# Use requests to simulate a POST request
|
# Use requests to simulate a POST request
|
||||||
response = requests.post('http://localhost:5000/webhook', json=test_event)
|
response = requests.post('http://localhost:5000/webhook', json=test_event)
|
||||||
@ -1069,7 +1263,7 @@ def simulate_triggers(message):
|
|||||||
trigger_messages.append(f"Регион {region_id}:\n{triggers}")
|
trigger_messages.append(f"Регион {region_id}:\n{triggers}")
|
||||||
|
|
||||||
if trigger_messages:
|
if trigger_messages:
|
||||||
bot.send_message(chat_id, "\n\n".join(trigger_messages), parse_mode="Markdown")
|
bot.send_message(chat_id, "\n\n".join(trigger_messages), parse_mode="html")
|
||||||
else:
|
else:
|
||||||
bot.send_message(chat_id, "Нет активных проблем по указанным регионам за последние 24 часа.")
|
bot.send_message(chat_id, "Нет активных проблем по указанным регионам за последние 24 часа.")
|
||||||
|
|
||||||
@ -1082,7 +1276,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, 'host': '0.0.0.0', 'debug': False, 'use_reloader': False}, daemon=True).start()
|
Thread(target=app.run, kwargs={'port': 5000, 'host': '0.0.0.0', 'debug': True, 'use_reloader': False}, daemon=True).start()
|
||||||
|
|
||||||
# Start bot polling in a separate thread
|
# Start bot polling in a separate thread
|
||||||
Thread(target=run_polling, daemon=True).start()
|
Thread(target=run_polling, daemon=True).start()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user