diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index be78013..cab7fc2 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -9,7 +9,7 @@ services: command: [redis-server, --protected-mode yes, --port 6379, --requirepass, P@ssw0rd!] tg-bot: - image: git.sm8255082.ru/osnova/zbx-tg-bot:1.0.0 + image: git.sm8255082.ru/osnova/zbx-tg-bot:1.1.0 restart: always depends_on: - redis diff --git a/main.py b/main.py index 9cf1785..125ebe7 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,6 @@ import logging as log from zabbix import get_active_problems from config import conf, icon_dict -from time import sleep from redis_db import ( get_all_keys, get_value, @@ -9,10 +8,10 @@ from redis_db import ( del_value, ) import asyncio -from telegram import del_message, send_message +from telegram import del_message, send_message, start_bot -async def main_loop(): +async def dashboard(): active_alerts = get_active_problems() if active_alerts is None: return @@ -38,7 +37,7 @@ async def main_loop(): + f"{active_alerts[new_alert]['host']}\n" + f"{active_alerts[new_alert]['name']}" ) - msg_id = await send_message(message) + msg_id = await send_message(message=message, event_id=new_alert) if msg_id["status"] == 200: await set_value(key=new_alert, value=msg_id["msg_id"]) @@ -53,12 +52,21 @@ async def main_loop(): await del_value(closed_alert) +async def dashboard_loop(): + log.info("Dashboard loop started") + while True: + await dashboard() + await asyncio.sleep(conf.zabbix.upd_interval) + + +async def main(): + await asyncio.gather(dashboard_loop(), start_bot()) + + if __name__ == "__main__": log.info("Starting app") try: - while True: - asyncio.run(main_loop()) - sleep(conf.zabbix.upd_interval) + asyncio.run(main()) except KeyboardInterrupt: log.info("Manual app stopped") log.info("App stopped") diff --git a/telegram/__init__.py b/telegram/__init__.py index e82d7ff..9a89e27 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -2,8 +2,10 @@ from .message import ( send_message, del_message, ) +from .bot import start_bot __all__ = [ "send_message", "del_message", + "start_bot", ] diff --git a/telegram/bot.py b/telegram/bot.py new file mode 100644 index 0000000..67695b0 --- /dev/null +++ b/telegram/bot.py @@ -0,0 +1,52 @@ +from config import conf +from aiogram import Bot, Dispatcher, types +from aiogram.fsm.storage.memory import MemoryStorage +from aiogram import F +from zabbix import event_acknowledge, event_close +import logging as log + +tg_bot = Bot(token=conf.tgbot.token) +storage = MemoryStorage() +dp = Dispatcher(storage=storage) + + +@dp.callback_query(F.data.startswith("h")) +async def handle_mute_1h(callback_query: types.CallbackQuery): + + if event_acknowledge(int(callback_query.data[1:]), 1): + new_text = ( + callback_query.message.text + + "\n" + + callback_query.from_user.username + + " Замьютил на час" + ) + await callback_query.message.edit_text(new_text) + + +@dp.callback_query(F.data.startswith("d")) +async def handle_mute_1d(callback_query: types.CallbackQuery): + if event_acknowledge(int(callback_query.data[1:]), 24): + new_text = ( + callback_query.message.text + + "\n" + + callback_query.from_user.username + + " Замьютил на сутки" + ) + await callback_query.message.edit_text(new_text) + + +@dp.callback_query(F.data.startswith("c")) +async def handle_close(callback_query: types.CallbackQuery): + if event_close(int(callback_query.data[1:])): + new_text = ( + callback_query.message.text + + "\n" + + callback_query.from_user.username + + " Закрыл" + ) + await callback_query.message.edit_text(new_text) + + +async def start_bot(): + log.info("Telegram bot loop started") + await dp.start_polling(tg_bot) diff --git a/telegram/message.py b/telegram/message.py index a2908d9..2a3922f 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -2,16 +2,25 @@ import logging as log import aiohttp from config import conf +import json -async def send_message( - message: str, -) -> dict: +async def send_message(message: str, event_id: int) -> dict: url = f"https://api.telegram.org/bot{conf.tgbot.token}/sendMessage" + inline_buttons = [ + [ + {"text": "🛠 на 1 час", "callback_data": f"h{event_id}"}, + {"text": "🛠 на 1 день", "callback_data": f"d{event_id}"}, + {"text": "✅ Закрыть", "callback_data": f"c{event_id}"}, + ], + ] + reply_markup = {"inline_keyboard": inline_buttons} + params = { "chat_id": conf.tgbot.chat_id, "message_thread_id": conf.tgbot.tread_id, "text": message, + "reply_markup": json.dumps(reply_markup), } async with aiohttp.ClientSession() as session: diff --git a/zabbix/__init__.py b/zabbix/__init__.py index 7c9de56..7a4de3f 100644 --- a/zabbix/__init__.py +++ b/zabbix/__init__.py @@ -1,6 +1,8 @@ -from .zabbix_api import get_active_problems +from .zabbix_api import get_active_problems, event_acknowledge, event_close __all__ = [ "get_active_problems", + "event_acknowledge", + "event_close", ] diff --git a/zabbix/zabbix_api.py b/zabbix/zabbix_api.py index 0036abd..180c47b 100644 --- a/zabbix/zabbix_api.py +++ b/zabbix/zabbix_api.py @@ -4,6 +4,8 @@ from zabbix_utils import ZabbixAPI from config import conf +from datetime import datetime, timedelta + def get_active_problems() -> dict: api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token) @@ -40,3 +42,39 @@ def get_active_problems() -> dict: return events_dict except: log.warning("Get event from zabbix error") + + +def event_acknowledge(event_id: int, mute_time: int): + api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token) + if mute_time == 0: + mute_to = 0 + else: + mute_to = int((datetime.now() + timedelta(hours=mute_time)).timestamp()) + try: + response = api.event.acknowledge( + eventids=event_id, + action=34, + suppress_until=mute_to, + ) + if response: + log.info(f"Event {event_id} acknowledged") + return True + except: + log.warning(f"Acknowledge event {event_id} from zabbix error") + return False + + +def event_close(event_id: int): + api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token) + try: + response = api.event.acknowledge( + eventids=event_id, + action=1, + ) + print(response) + if response: + log.info(f"Event {event_id} closed") + return True + except: + log.warning(f"Closed event {event_id} from zabbix error") + return False