This commit is contained in:
s.mostryukov 2025-03-12 17:44:35 +03:00
parent fc1e34e727
commit 273b972b49
12 changed files with 207 additions and 39 deletions

View File

@ -1,7 +1,8 @@
from .config import conf, STATIC_DIR
from .config import conf, STATIC_DIR, icon_dict
__all__ = [
"conf",
STATIC_DIR,
icon_dict,
]

View File

@ -12,6 +12,15 @@ import logging
BASE_DIR = Path(__file__).parent.parent
TEMPLATES_DIR = BASE_DIR / "web" / "templates"
STATIC_DIR = BASE_DIR / "web" / "static"
icon_dict = {
"0": "",
"1": "🟦",
"2": "🟨",
"3": "🟧",
"4": "🟥",
"5": "🟫",
"10": "",
}
class LogConfig(BaseModel):

13
docker/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM python:3.12-slim
WORKDIR /app
ADD config /app/config
ADD redis_db /app/redis_db
ADD telegram /app/telegram
ADD zabbix /app/zabbix
ADD main.py /app/main.py
ADD pyproject.toml /app/pyproject.toml
ADD uv.lock /app/uv.lock
RUN pip install --upgrade pip
RUN pip install uv
RUN uv sync --no-dev
CMD ["uv", "run", "main.py"]

View File

@ -0,0 +1,34 @@
version: '3.3'
services:
redis:
image: redis:latest
restart: always
volumes:
- ./local_redis_file/data:/data
command: [redis-server, --protected-mode yes, --port 6379, --requirepass, P@ssw0rd!]
tg-bot:
image: git.sm8255082.ru/osnova/zbx-tg-bot:1.0.0
restart: always
depends_on:
- redis
ports:
- "8000:8000"
environment:
- OAA_CFG__LOG__LEVEL=30
- OAA_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__TGBOT__TOKEN=string
- CFG__TGBOT__CHAT_ID=000000
- CFG__TGBOT__TREAD_ID=0
- CFG__REDIS__HOST=redis
- CFG__REDIS__PORT=6379
- CFG__REDIS__PWD=P@ssw0rd!

View File

@ -0,0 +1,12 @@
version: '3.3'
services:
redis:
image: redis:latest
restart: always
ports:
- "6379:6379"
volumes:
- ./local_redis_file/data:/data
#command: ["redis-server", --port 6379]
command: [redis-server, --protected-mode yes, --port 6379, --requirepass, P@ssw0rd!]

52
main.py
View File

@ -1,21 +1,61 @@
import logging as log
from zabbix import get_active_problems
from config import conf
from config import conf, icon_dict
from time import sleep
from redis_db import (
get_all_keys,
get_value,
set_value,
del_value,
)
import asyncio
from telegram import del_message, send_message
def main_loop():
async def main_loop():
active_alerts = get_active_problems()
for i in active_alerts:
print(i)
print(len(active_alerts))
telegram_alerts = await get_all_keys()
new_alerts_id = []
closed_alerts_id = []
for active_alert in active_alerts["event_ids"]:
if active_alert not in telegram_alerts:
new_alerts_id.append(active_alert)
for telegram_alert in telegram_alerts:
if telegram_alert not in active_alerts["event_ids"]:
closed_alerts_id.append(telegram_alert)
log.info("new " + str(new_alerts_id))
log.info("closed " + str(closed_alerts_id))
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]['name']}"
)
msg_id = await send_message(message)
if msg_id["status"] == 200:
await set_value(key=new_alert, value=msg_id["msg_id"])
for closed_alert in closed_alerts_id:
msg_id = await get_value(closed_alert)
if msg_id:
resp = await del_message(int(msg_id))
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)
if __name__ == "__main__":
log.info("Starting app")
try:
while True:
main_loop()
asyncio.run(main_loop())
sleep(conf.zabbix.upd_interval)
except KeyboardInterrupt:
log.info("Manual app stopped")

View File

@ -3,6 +3,9 @@ from .crud import (
set_value,
ping,
pop_value,
get_all,
get_all_keys,
del_value,
)
__all__ = [
@ -10,4 +13,7 @@ __all__ = [
"set_value",
"ping",
"pop_value",
"get_all",
"get_all_keys",
"del_value",
]

View File

@ -31,3 +31,29 @@ async def pop_value(key):
value = await redis_connect.client.getdel(key)
log.info("Get and delete %s = %s", key, value)
return value
async def del_value(key):
async with RedisManager() as redis_connect:
if redis_connect:
value = await redis_connect.client.delete(key)
log.info("Delete %s = %s", key, value)
async def get_all():
async with RedisManager() as redis_connect:
if redis_connect:
all_data = {}
async for key in redis_connect.client.scan_iter():
value = await redis_connect.client.get(key)
all_data[key] = value if value else None
return all_data
async def get_all_keys():
async with RedisManager() as redis_connect:
if redis_connect:
all_keys = []
async for key in redis_connect.client.scan_iter():
all_keys.append(key)
return all_keys

View File

@ -10,6 +10,7 @@ class RedisManager:
self.connect_params = {
"host": conf.redis.host,
"port": conf.redis.port,
"decode_responses": True,
}
if conf.redis.pwd:
self.connect_params["password"] = conf.redis.pwd

View File

@ -0,0 +1,9 @@
from .message import (
send_message,
del_message,
)
__all__ = [
"send_message",
"del_message",
]

View File

@ -6,8 +6,7 @@ from config import conf
async def send_message(
message: str,
) -> dict | None:
) -> dict:
url = f"https://api.telegram.org/bot{conf.tgbot.token}/sendMessage"
params = {
"chat_id": conf.tgbot.chat_id,
@ -16,6 +15,7 @@ async def send_message(
}
async with aiohttp.ClientSession() as session:
try:
async with session.post(
url,
json=params,
@ -28,14 +28,20 @@ async def send_message(
"status": response.status,
"msg_id": resp["result"]["message_id"],
}
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}
async def del_message(
message_id: int,
) -> dict | None:
) -> dict:
url = f"https://api.telegram.org/bot{conf.tgbot.token}/deleteMessage"
async with aiohttp.ClientSession() as session:
try:
async with session.post(
url,
json={
@ -49,4 +55,12 @@ async def del_message(
"status": response.status,
}
else:
log.warning(f"Response status: {response.status}")
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}

View File

@ -5,7 +5,7 @@ from zabbix_utils import ZabbixAPI
from config import conf
def get_active_problems():
def get_active_problems() -> dict:
api = ZabbixAPI(url=conf.zabbix.url, token=conf.zabbix.token)
try:
problems = api.problem.get(
@ -32,8 +32,11 @@ def get_active_problems():
"severity",
],
)
events_dict = {"event_ids": []}
for event in events:
event["host"] = event.pop("hosts", None)[0]["host"]
return events
events_dict[event["eventid"]] = event
events_dict["event_ids"].append(event["eventid"])
return events_dict
except:
log.warning("Get event from zabbix error")