2022-07-08 22:37:09 +02:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
import logging
|
|
|
|
|
2022-07-11 23:18:57 +02:00
|
|
|
import prometheus_client
|
|
|
|
from aiohttp import ClientError, web, web_request
|
2022-07-09 12:43:18 +02:00
|
|
|
from aiohttp_prometheus_exporter.handler import metrics
|
|
|
|
from aiohttp_prometheus_exporter.middleware import prometheus_middleware_factory
|
2022-07-06 00:54:13 +02:00
|
|
|
from diskcache import Cache
|
2022-07-11 23:18:57 +02:00
|
|
|
from nio import AsyncClient, LocalProtocolError
|
2022-07-04 01:03:24 +02:00
|
|
|
|
2022-07-10 02:54:35 +02:00
|
|
|
from matrix_alertbot.alert import Alert
|
2022-07-04 01:03:24 +02:00
|
|
|
from matrix_alertbot.chat_functions import send_text_to_room
|
|
|
|
from matrix_alertbot.config import Config
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
routes = web.RouteTableDef()
|
|
|
|
|
|
|
|
|
|
|
|
@routes.post("/alert")
|
|
|
|
async def create_alert(request: web_request.Request) -> web.Response:
|
|
|
|
data = await request.json()
|
|
|
|
logger.info(f"Received alert: {data}")
|
2022-07-06 00:54:13 +02:00
|
|
|
client: AsyncClient = request.app["client"]
|
|
|
|
config: Config = request.app["config"]
|
|
|
|
cache: Cache = request.app["cache"]
|
2022-07-04 01:03:24 +02:00
|
|
|
|
2022-07-11 23:18:57 +02:00
|
|
|
if "alerts" not in data:
|
|
|
|
return web.Response(status=400, body="Data must contain 'alerts' key.")
|
|
|
|
|
|
|
|
if not isinstance(data["alerts"], list):
|
|
|
|
return web.Response(status=400, body="Alerts must be a list.")
|
|
|
|
|
|
|
|
if len(data["alerts"]) == 0:
|
|
|
|
return web.Response(status=400, body="Alerts cannot be empty.")
|
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
plaintext = ""
|
|
|
|
html = ""
|
|
|
|
for i, alert in enumerate(data["alerts"]):
|
2022-07-11 23:18:57 +02:00
|
|
|
try:
|
|
|
|
alert = Alert.from_dict(alert)
|
|
|
|
except KeyError:
|
|
|
|
return web.Response(status=400, body=f"Invalid alert: {alert}.")
|
2022-07-04 01:03:24 +02:00
|
|
|
|
|
|
|
if i != 0:
|
|
|
|
plaintext += "\n"
|
|
|
|
html += "<br/>\n"
|
|
|
|
plaintext += alert.plaintext()
|
|
|
|
html += alert.html()
|
|
|
|
|
|
|
|
try:
|
2022-07-05 23:35:19 +02:00
|
|
|
event = await send_text_to_room(
|
2022-07-08 21:11:25 +02:00
|
|
|
client, config.room_id, plaintext, html, notice=False
|
2022-07-05 23:35:19 +02:00
|
|
|
)
|
2022-07-11 23:18:57 +02:00
|
|
|
except (LocalProtocolError, ClientError) as e:
|
2022-07-04 01:03:24 +02:00
|
|
|
logger.error(e)
|
2022-07-11 23:18:57 +02:00
|
|
|
return web.Response(
|
|
|
|
status=500, body="An error occured when sending alerts to Matrix room."
|
|
|
|
)
|
2022-07-04 01:03:24 +02:00
|
|
|
|
2022-07-06 00:54:13 +02:00
|
|
|
fingerprints = tuple(alert["fingerprint"] for alert in data["alerts"])
|
2022-07-06 01:19:34 +02:00
|
|
|
cache.set(
|
|
|
|
event.event_id, fingerprints, expire=config.cache_expire_time, tag="event"
|
|
|
|
)
|
2022-07-04 01:03:24 +02:00
|
|
|
return web.Response(status=200)
|
|
|
|
|
|
|
|
|
2022-07-08 22:46:04 +02:00
|
|
|
class Webhook:
|
2022-07-08 21:11:25 +02:00
|
|
|
def __init__(self, client: AsyncClient, cache: Cache, config: Config) -> None:
|
2022-07-04 01:03:24 +02:00
|
|
|
self.app = web.Application(logger=logger)
|
|
|
|
self.app["client"] = client
|
2022-07-06 00:54:13 +02:00
|
|
|
self.app["config"] = config
|
2022-07-04 01:03:24 +02:00
|
|
|
self.app["cache"] = cache
|
|
|
|
self.app.add_routes(routes)
|
2022-07-09 12:43:18 +02:00
|
|
|
|
2022-07-11 23:18:57 +02:00
|
|
|
prometheus_registry = prometheus_client.CollectorRegistry(auto_describe=True)
|
|
|
|
self.app.middlewares.append(
|
|
|
|
prometheus_middleware_factory(registry=prometheus_registry)
|
|
|
|
)
|
2022-07-09 12:43:18 +02:00
|
|
|
self.app.router.add_get("/metrics", metrics())
|
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
self.runner = web.AppRunner(self.app)
|
|
|
|
|
|
|
|
self.config = config
|
|
|
|
self.address = config.address
|
|
|
|
self.port = config.port
|
|
|
|
self.socket = config.socket
|
|
|
|
|
|
|
|
async def start(self) -> None:
|
|
|
|
await self.runner.setup()
|
|
|
|
|
|
|
|
site: web.BaseSite
|
|
|
|
if self.address and self.port:
|
|
|
|
site = web.TCPSite(self.runner, self.address, self.port)
|
|
|
|
logger.info(f"Listenning on {self.address}:{self.port}")
|
|
|
|
elif self.socket:
|
|
|
|
site = web.UnixSite(self.runner, self.socket)
|
|
|
|
logger.info(f"Listenning on unix://{self.socket}")
|
|
|
|
|
|
|
|
await site.start()
|
|
|
|
|
|
|
|
async def close(self) -> None:
|
|
|
|
await self.runner.cleanup()
|