Compare commits

...

18 Commits

Author SHA1 Message Date
sergey 2d9f8942b0 Merge pull request 'fix zabbix api alert' (#7) from dev-2.0.5 into main
Reviewed-on: #7
2025-04-24 14:34:42 +00:00
sergey 605d086670 fix zabbix api alert 2025-04-24 17:31:07 +03:00
sergey 6a03ac0e77 Merge pull request 'replace host -> name' (#6) from dev-2.0.4 into main
Reviewed-on: #6
2025-04-22 15:35:39 +00:00
sergey 9ac5c80f73 replace host -> name 2025-04-22 18:35:11 +03:00
sergey 876b3901a5 fix " " 2025-04-22 17:35:16 +03:00
sergey 09a890a04a Merge pull request 'dev-2.0.2' (#5) from dev-2.0.2 into main
Reviewed-on: #5
2025-04-16 13:01:17 +00:00
sergey 8b6d38563c upd readme 2025-04-16 16:00:37 +03:00
sergey a5b6cdf9f7 add edit message + fix docker-compose 2025-04-15 14:59:24 +03:00
sergey 93e85f790e add edit message 2025-04-15 14:58:09 +03:00
sergey ab2b4a40de dev-2.0.2 2025-04-15 12:27:57 +03:00
sergey 6c1b0c68e5 Merge pull request 'dev-2.0.1' (#4) from dev-2.0.1 into main
Reviewed-on: #4
2025-04-14 13:38:14 +00:00
sergey 672c02f64b fix check zabbix api count 2025-04-14 16:28:24 +03:00
sergey e9ee7bb8b0 v 2.0.1
add check zabbix api
2025-04-14 15:49:47 +03:00
sergey ac78933106 check zabbix api 2025-04-14 14:12:48 +03:00
sergey 12c124863e add fail check and edit message 2025-04-13 15:30:37 +03:00
sergey 3e34f4567c Merge pull request 'dev-2.0.0' (#3) from dev-2.0.0 into main
Reviewed-on: #3
2025-03-16 18:52:27 +00:00
sergey cc8fe1a17e Merge pull request 'dicker-compose fix' (#2) from dev into main
Reviewed-on: #2
2025-03-13 11:08:38 +00:00
s.mostryukov 9475c8ef8f dicker-compose fix 2025-03-13 14:04:34 +03:00
11 changed files with 153 additions and 16 deletions

View File

@ -27,4 +27,9 @@
10. Адрес для подключения к redis
11. Порт для подключения к redis
12. Пароль для подключения к redis (если нужен)
12. Пароль для подключения к redis (если нужен)
UPD
Добавлена проверка доступности апи заббикса и алерт в телегу, если не доступен.
Добавлено изменнеие алерта на произвольный текст, если сообщение нельзя удалить из-за срока давности.

View File

@ -5,10 +5,16 @@ CFG__ZABBIX__URL=https://example.com
CFG__ZABBIX__TOKEN=string
CFG__ZABBIX__MIN_SEVERITY=0
CFG__ZABBIX__UPD_INTERVAL=60
CFG__ZABBIX__ALERT_FAIL_COUNT=3
CFG__ZABBIX__ALERT_TREAD_ID=0
CFG__ZABBIX__ALERT_TAG_USER=string
CFG__ZABBIX__ALERT_TEXT_UP=Zabbix service UP
CFG__ZABBIX__ALERT_TEXT_DOWN=Zabbix service DOWN
CFG__TGBOT__TOKEN=string
CFG__TGBOT__CHAT_ID=00000000
CFG__TGBOT__TREAD_ID=0
CFG__TGBOT__DELETE_MSG=string
CFG__REDIS__HOST=localhost
CFG__REDIS__PORT=6379

View File

@ -42,6 +42,7 @@ class TelegramBotConfig(BaseModel):
token: str
chat_id: int
tread_id: int
delete_msg: str
class ZabbixConfig(BaseModel):
@ -49,6 +50,13 @@ class ZabbixConfig(BaseModel):
token: str
min_severity: int
upd_interval: int
alert_fail_count: int
alert_tread_id: int
alert_tag_user: str = ""
alert_text_up: str
alert_text_down: str
change_state_count: int = 0
api_state: str = "UP"
class Settings(BaseSettings):

View File

@ -10,22 +10,30 @@ services:
command: [redis-server, --protected-mode yes, --port 6379, --requirepass, P@ssw0rd!]
tg-bot:
image: git.sm8255082.ru/osnova/zbx-tg-bot:2.0.0
image: git.sm8255082.ru/osnova/zbx-tg-bot:2.0.1
restart: always
volumes:
- ./logfile.log:/app/logfile.log
depends_on:
- redis
environment:
- OAA_CFG__LOG__LEVEL=30
- OAA_CFG__LOG__LEVEL_TO_FILE=30
- CFG__LOG__LEVEL=30
- CFG__LOG__LEVEL_TO_FILE=30
- CFG__ZABBIX__URL=https://zabbix.example.com
- CFG__ZABBIX__TOKEN=string
- CFG__ZABBIX__MIN_SEVERITY=2
- CFG__ZABBIX__UPD_INTERVAL=30
- CFG__ZABBIX__ALERT_FAIL_COUNT=3
- CFG__ZABBIX__ALERT_TREAD_ID=0
- CFG__ZABBIX__ALERT_TAG_USER=
- CFG__ZABBIX__ALERT_TEXT_UP=Zabbix service UP
- CFG__ZABBIX__ALERT_TEXT_DOWN=Zabbix service DOWN
- CFG__TGBOT__TOKEN=string
- CFG__TGBOT__CHAT_ID=000000
- CFG__TGBOT__TREAD_ID=0
- CFG__TGBOT__DELETE_MSG=Deleted
- CFG__REDIS__HOST=redis
- CFG__REDIS__PORT=6379

31
main.py
View File

@ -1,5 +1,5 @@
import logging as log
from zabbix import get_active_problems
from zabbix import get_active_problems, check_state
from config import conf, icon_dict
from redis_db import (
get_all_keys,
@ -8,13 +8,30 @@ from redis_db import (
del_value,
)
import asyncio
from telegram import del_message, send_message, start_bot
from telegram import (
del_message,
send_message,
start_bot,
send_zabbix_api_alert,
edit_message,
)
async def dashboard():
active_alerts = get_active_problems()
if active_alerts is None:
if check_state(False) == "DOWN":
send_state = await send_zabbix_api_alert(conf.zabbix.alert_text_down)
if send_state["status"] == 200:
conf.zabbix.api_state = "DOWN"
conf.zabbix.change_state_count = 0
return
else:
if check_state(True) == "UP":
send_state = await send_zabbix_api_alert(conf.zabbix.alert_text_up)
if send_state["status"] == 200:
conf.zabbix.api_state = "UP"
conf.zabbix.change_state_count = 0
telegram_alerts = await get_all_keys()
new_alerts_id = []
@ -34,7 +51,7 @@ async def dashboard():
for new_alert in new_alerts_id:
message = (
icon_dict[active_alerts[new_alert]["severity"]]
+ f"{active_alerts[new_alert]['host']}\n"
+ f" {active_alerts[new_alert]['host']}\n"
+ f"{active_alerts[new_alert]['name']}"
)
msg_id = await send_message(message=message, event_id=new_alert)
@ -48,8 +65,12 @@ async def dashboard():
if resp["status"] == 200:
await del_value(closed_alert)
if resp["status"] == 400:
log.warning(f"remove olg message {msg_id} from reddis")
await del_value(closed_alert)
resp = await edit_message(int(msg_id), conf.tgbot.delete_msg)
if resp["status"] == 200:
await del_value(closed_alert)
if resp["status"] == 400:
log.warning(f"remove olg message {msg_id} from reddis")
await del_value(closed_alert)
async def dashboard_loop():

View File

@ -1,6 +1,6 @@
[project]
name = "zbx-tg-bot"
version = "2.0.0"
version = "2.0.1"
description = "telegram bot for telegram-zabbix dashboard"
requires-python = ">=3.13"
dependencies = [

View File

@ -1,11 +1,15 @@
from .message import (
send_message,
edit_message,
del_message,
send_zabbix_api_alert,
)
from .bot import start_bot
__all__ = [
"send_message",
"edit_message",
"del_message",
"start_bot",
"send_zabbix_api_alert",
]

View File

@ -2,7 +2,8 @@ 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
from zabbix import event_close
from zabbix import event_acknowledge
import logging as log
tg_bot = Bot(token=conf.tgbot.token)

View File

@ -45,6 +45,35 @@ async def send_message(message: str, event_id: int) -> dict:
return {"status": e}
async def edit_message(message_id: int, message: str) -> dict:
url = f"https://api.telegram.org/bot{conf.tgbot.token}/editMessageText"
async with aiohttp.ClientSession() as session:
try:
async with session.post(
url,
json={
"chat_id": conf.tgbot.chat_id,
"message_id": message_id,
"text": message,
},
) as response:
if response.status == 200:
log.info(f"Message ID {message_id} edited")
return {
"status": response.status,
}
else:
log.warning(
f"Message ID {message_id} NOT edit. Response status: {response.status}"
)
return {
"status": response.status,
}
except Exception as e:
log.warning(f"Exception: {e}")
return {"status": e}
async def del_message(
message_id: int,
) -> dict:
@ -67,9 +96,36 @@ async def del_message(
log.warning(
f"Message ID {message_id} NOT deleted. Response status: {response.status}"
)
return {
"status": response.status,
}
except Exception as e:
log.warning(f"Exception: {e}")
return {"status": e}
async def send_zabbix_api_alert(message):
url = f"https://api.telegram.org/bot{conf.tgbot.token}/sendMessage"
params = {
"chat_id": conf.tgbot.chat_id,
"message_thread_id": conf.zabbix.alert_tread_id,
"text": conf.zabbix.alert_tag_user + "\n" + message,
}
async with aiohttp.ClientSession() as session:
try:
async with session.post(
url,
json=params,
) as response:
log.info(f"Response status: {response.status}")
resp = await response.json()
if response.status == 200:
log.info(f"Message with ID: {resp['result']['message_id']} send")
return {"status": response.status}
else:
log.warning(f"Message not send. Response status: {response.status}")
return {"status": response.status}
except Exception as e:
log.warning(f"Exception: {e}")
return {"status": e}

View File

@ -1,8 +1,8 @@
from .zabbix_api import get_active_problems, event_acknowledge, event_close
from .zabbix_api import get_active_problems, event_close, event_acknowledge, check_state
__all__ = [
"get_active_problems",
"event_acknowledge",
"event_close",
"check_state",
]

View File

@ -3,14 +3,15 @@ import logging as log
from pyexpat.errors import messages
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)
try:
api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token)
problems = api.problem.get(
output=[
"eventid",
@ -27,7 +28,7 @@ def get_active_problems() -> dict:
event_ids.append(problem["eventid"])
events = api.event.get(
selectHosts=["host"],
selectHosts=["name"],
eventids=event_ids,
output=[
"eventid",
@ -37,7 +38,7 @@ def get_active_problems() -> dict:
)
events_dict = {"event_ids": []}
for event in events:
event["host"] = event.pop("hosts", None)[0]["host"]
event["host"] = event.pop("hosts", None)[0]["name"]
events_dict[event["eventid"]] = event
events_dict["event_ids"].append(event["eventid"])
return events_dict
@ -80,3 +81,30 @@ def event_close(event_id: int, closed_by: str):
except:
log.warning(f"Closed event {event_id} from zabbix error")
return False
def check_state(success: bool) -> str | None:
if success:
if conf.zabbix.api_state == "UP":
log.info("Zabbix API is UP")
conf.zabbix.change_state_count = 0
else:
if conf.zabbix.change_state_count == conf.zabbix.alert_fail_count:
log.warning(
f"Zabbix API state changed to UP. Count {conf.zabbix.change_state_count}"
)
return "UP"
else:
conf.zabbix.change_state_count += 1
else:
if conf.zabbix.api_state == "DOWN":
log.info("Zabbix API is DOWN")
conf.zabbix.change_state_count = 0
else:
if conf.zabbix.change_state_count == conf.zabbix.alert_fail_count:
log.warning(
f"Zabbix API state changed to DOWN. Count {conf.zabbix.change_state_count}"
)
return "DOWN"
else:
conf.zabbix.change_state_count += 1