Compare commits
7 Commits
ec66aa40de
...
06afbc187d
Author | SHA1 | Date |
---|---|---|
histausse | 06afbc187d | 3 years ago |
histausse | d9ef6690fc | 3 years ago |
histausse | d8aff91f6f | 3 years ago |
histausse | 80891eeda2 | 3 years ago |
histausse | eb8cdefab4 | 3 years ago |
histausse | 1172db1d95 | 3 years ago |
histausse | f544d8eef9 | 3 years ago |
@ -1,5 +1,3 @@
|
||||
# Kassandra
|
||||
|
||||
# Kassandra
|
||||
|
||||
Kassandra is a matrix bot receiving alert from [alermanager](https://www.prometheus.io/docs/alerting/latest/alertmanager/) end send them to a [matrix](matrix.org/) room.
|
||||
Kassandra is a matrix bot receiving alert from [alermanager](https://www.prometheus.io/docs/alerting/latest/alertmanager/) end send them to a [matrix](matrix.org/) room.
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
username: kassandra
|
||||
homeserver: https://matrix.org
|
||||
password: beware of greeks bearing gifts
|
||||
port: 8000
|
||||
host: 127.0.0.1
|
||||
alert_rooms:
|
||||
- "#troy:matrix.org"
|
||||
- "#ithaca:matrix.org"
|
||||
...
|
@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=42.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
@ -0,0 +1,19 @@
|
||||
[metadata]
|
||||
name = kassandra
|
||||
author = Jean-Marie 'Histausse' Mineau
|
||||
author_email = histausse@protonmail
|
||||
description = A bot sending alert from alertmanager to matrix
|
||||
long_description = file:README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://gitea.auro.re/histausse/kassandra
|
||||
|
||||
[options]
|
||||
packages = kassandra
|
||||
python_requires = >=3.9.2
|
||||
package_dir = =src
|
||||
install_requires =
|
||||
PyYAML>=5.4.1
|
||||
matrix-bot @ git+https://gitea.auro.re/histausse/matrix-bot.git
|
||||
|
||||
[options.package_data]
|
||||
matrix_bot = py.typed
|
@ -0,0 +1,5 @@
|
||||
from setuptools import setup
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup()
|
||||
|
@ -0,0 +1,43 @@
|
||||
import argparse
|
||||
import asyncio
|
||||
|
||||
from .config import load_config
|
||||
from .bot import send_messages
|
||||
from .format import format_alerts
|
||||
from .webhook import run_webhook
|
||||
|
||||
async def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c", "--config", default="config.yaml")
|
||||
args = parser.parse_args()
|
||||
|
||||
config = load_config(args.config)
|
||||
alert_queue = asyncio.Queue()
|
||||
message_queue = asyncio.Queue()
|
||||
|
||||
bot_corout = send_messages(
|
||||
message_queue,
|
||||
config.username,
|
||||
config.homeserver,
|
||||
config.password,
|
||||
config.alert_rooms
|
||||
)
|
||||
format_corout = format_alerts(
|
||||
alert_queue,
|
||||
message_queue
|
||||
)
|
||||
webhook_corout = run_webhook(
|
||||
alert_queue,
|
||||
config.host,
|
||||
config.port
|
||||
)
|
||||
|
||||
|
||||
await asyncio.gather(
|
||||
bot_corout,
|
||||
format_corout,
|
||||
webhook_corout
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
@ -0,0 +1,57 @@
|
||||
"""
|
||||
The bot that send messages.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import (
|
||||
Any,
|
||||
NoReturn
|
||||
)
|
||||
from matrix_bot.client import Client
|
||||
from matrix_bot.invite_policy import WhiteList
|
||||
from .format import Message
|
||||
|
||||
async def __send_messsages(
|
||||
message_queue: asyncio.Queue[Message],
|
||||
bot: Client,
|
||||
rooms: list[str]
|
||||
)->NoReturn:
|
||||
"""
|
||||
Actually send the messages from the queue.
|
||||
"""
|
||||
while True:
|
||||
message = await message_queue.get()
|
||||
for room in rooms:
|
||||
await bot.send_formated_message(
|
||||
room,
|
||||
message.formated_body,
|
||||
unformated_message=message.body
|
||||
)
|
||||
message_queue.task_done()
|
||||
|
||||
async def send_messages(
|
||||
message_queue: asyncio.Queue[dict[str, Any]], # For now, type will change in the futur
|
||||
username: str,
|
||||
homeserver: str,
|
||||
password: str,
|
||||
alert_rooms: list[str]
|
||||
):
|
||||
"""
|
||||
Initialize the bot and send messages added to the queue to the alert_rooms.
|
||||
"""
|
||||
|
||||
bot = await Client(
|
||||
username,
|
||||
homeserver,
|
||||
password
|
||||
)
|
||||
invite_policy = await WhiteList(bot, alert_rooms)
|
||||
bot.set_invite_policy(invite_policy)
|
||||
await asyncio.gather(
|
||||
bot.run(),
|
||||
__send_messsages(
|
||||
message_queue,
|
||||
bot,
|
||||
alert_rooms
|
||||
)
|
||||
)
|
@ -0,0 +1,24 @@
|
||||
"""
|
||||
Config related tools.
|
||||
"""
|
||||
|
||||
import dataclasses
|
||||
import yaml
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Config:
|
||||
username: str
|
||||
homeserver: str
|
||||
password: str
|
||||
port: int
|
||||
host: str
|
||||
alert_rooms: list[str]
|
||||
|
||||
def load_config(file:str)->Config:
|
||||
"""
|
||||
Load the config from the config file.
|
||||
"""
|
||||
with open(file, 'r') as f:
|
||||
data = yaml.load(f, Loader=yaml.loader.SafeLoader)
|
||||
return Config(**data)
|
||||
|
@ -0,0 +1,41 @@
|
||||
"""
|
||||
Format the alert message.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import dataclasses
|
||||
import json
|
||||
from typing import (
|
||||
Any,
|
||||
NoReturn,
|
||||
Optional
|
||||
)
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Message:
|
||||
body: str
|
||||
formated_body: Optional[str]
|
||||
|
||||
def format_alert(
|
||||
alert: dict[str, Any]
|
||||
)->Message:
|
||||
"""
|
||||
Format an alert in json format to a nice string.
|
||||
"""
|
||||
body = json.dumps(alert, indent=4)
|
||||
formated_body = f"<pre><code>{body}</code></pre>\n"
|
||||
return Message(body, formated_body)
|
||||
|
||||
async def format_alerts(
|
||||
alert_queue: asyncio.Queue[dict[str,Any]],
|
||||
message_queue: asyncio.Queue[Message]
|
||||
)->NoReturn:
|
||||
"""
|
||||
Read alerts from alert_queue, format them, and put them in message_queue.
|
||||
"""
|
||||
while True:
|
||||
alert = await alert_queue.get()
|
||||
message = format_alert(alert)
|
||||
await message_queue.put(message)
|
||||
alert_queue.task_done()
|
||||
|
@ -0,0 +1,35 @@
|
||||
"""
|
||||
The webhook receiving the alerts from alertmanager.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import aiohttp.web
|
||||
import aiohttp.web_request
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
NoReturn
|
||||
)
|
||||
|
||||
ENDPOINT = "/webhook"
|
||||
|
||||
async def run_webhook(
|
||||
alert_queue: asyncio.Queue[dict[str, Any]],
|
||||
host: str,
|
||||
port: int
|
||||
)->NoReturn:
|
||||
"""
|
||||
Run the webhook endpoint and put the alerts in the queue.
|
||||
"""
|
||||
|
||||
async def handler(request:aiohttp.web_request.Request):
|
||||
alert = await request.json()
|
||||
await alert_queue.put(alert)
|
||||
return aiohttp.web.Response()
|
||||
|
||||
app = aiohttp.web.Application()
|
||||
app.add_routes([aiohttp.web.post(ENDPOINT, handler)])
|
||||
runner = aiohttp.web.AppRunner(app)
|
||||
await runner.setup()
|
||||
site = aiohttp.web.TCPSite(runner, host, port)
|
||||
await site.start()
|
Loading…
Reference in New Issue