Initial commit
This commit is contained in:
commit
0771c87e41
6 changed files with 154 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/config.yaml
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
|||
FROM python:3-alpine
|
||||
LABEL description="A Matrix bot for Alertmanager"
|
||||
# Instal gcc to build wheels
|
||||
RUN apk add --no-cache gcc musl-dev
|
||||
# Force the stdout and stderr streams to be unbuffered
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
# Install requirements from PyPI
|
||||
COPY requirements.txt /var/www/alertbot/
|
||||
RUN pip install --no-cache-dir -r /var/www/alertbot/requirements.txt
|
||||
# Copy the all the necessary files
|
||||
COPY . /var/www/alertbot/
|
||||
# Move the rigth directory
|
||||
WORKDIR /var/www/alertbot
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT python3 /var/www/alertbot/bot.py
|
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Alertbot
|
118
bot.py
Executable file
118
bot.py
Executable file
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import asyncio
|
||||
import dataclasses
|
||||
import logging
|
||||
|
||||
import aiohttp.web
|
||||
import jinja2
|
||||
import markdown
|
||||
import nio
|
||||
import yaml
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Config:
|
||||
matrix_homeserver: str
|
||||
matrix_user: str
|
||||
matrix_password: str
|
||||
matrix_room: str
|
||||
webhook_token: str
|
||||
|
||||
|
||||
TEMPLATE = (
|
||||
"{% if status == 'resolved' %}"
|
||||
"<b><font color='green'>RÉSOLU</font></b> "
|
||||
"{% elif labels.severity == 'critical' %}"
|
||||
"@room <b><font color='red'>CRITIQUE</font></b> "
|
||||
"{% elif labels.severity == 'warning' %}"
|
||||
"<b><font color='orange'>ATTENTION</font></b> "
|
||||
"{% endif %}"
|
||||
"<b>{{ labels.alertname }}</b> "
|
||||
"{{ labels.instance }}<br/>"
|
||||
"<blockquote>{{ annotations.summary }}</blockquote>"
|
||||
)
|
||||
|
||||
|
||||
async def format_messages(config, src, dest):
|
||||
env = jinja2.Environment()
|
||||
template = env.from_string(TEMPLATE)
|
||||
|
||||
while True:
|
||||
message = await src.get()
|
||||
|
||||
for alert in message["alerts"]:
|
||||
rendered = template.render(**alert)
|
||||
await dest.put((rendered))
|
||||
|
||||
|
||||
async def send_notices(config, messages):
|
||||
client = nio.AsyncClient(config.matrix_homeserver, config.matrix_user)
|
||||
await client.login(config.matrix_password)
|
||||
|
||||
while True:
|
||||
formatted = await messages.get()
|
||||
|
||||
await client.room_send(
|
||||
config.matrix_room,
|
||||
message_type="m.room.message",
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"body": formatted, # à corriger
|
||||
"formatted_body": formatted,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def run_app(config, out, host, port):
|
||||
async def webhook(request):
|
||||
tokens = request.query.getall("token", [])
|
||||
if config.webhook_token not in tokens:
|
||||
logging.warning("Invalid tokens: %s", tokens)
|
||||
raise aiohttp.web.HTTPForbidden("Invalid tokens")
|
||||
message = await request.json()
|
||||
logging.debug("Incoming message: %s", message)
|
||||
await out.put(message)
|
||||
return aiohttp.web.Response()
|
||||
|
||||
app = aiohttp.web.Application()
|
||||
app.add_routes([aiohttp.web.post("/webhook", webhook)])
|
||||
|
||||
runner = aiohttp.web.AppRunner(app)
|
||||
await runner.setup()
|
||||
await aiohttp.web.TCPSite(runner, host, port).start()
|
||||
|
||||
|
||||
async def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c", "--config", default="config.yaml")
|
||||
parser.add_argument("--host", default="*")
|
||||
parser.add_argument("--port", type=int, default=8000)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.config) as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
config = Config(**config)
|
||||
|
||||
messages = asyncio.Queue()
|
||||
formatted = asyncio.Queue()
|
||||
|
||||
logging.info("Started Alertbot on %s:%d", args.host, args.port)
|
||||
|
||||
tasks = (
|
||||
asyncio.create_task(run_app(config, messages, args.host, args.port)),
|
||||
asyncio.create_task(format_messages(config, messages, formatted)),
|
||||
asyncio.create_task(send_notices(config, formatted)),
|
||||
)
|
||||
|
||||
# on propage les exceptions
|
||||
await asyncio.gather(*tasks, return_exceptions=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.get_event_loop().run_until_complete(main())
|
7
config.example.yaml
Normal file
7
config.example.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
matrix_homeserver: https://example.com
|
||||
matrix_user: "@alertbot:example.com"
|
||||
matrix_password: "Ch@ngeMe!?"
|
||||
matrix_room: "!room:example.com"
|
||||
webhook_token: "MyVerySecretToken"
|
||||
...
|
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
alertbot:
|
||||
image: alertbot
|
||||
restart: always
|
||||
volumes:
|
||||
- ./config.yaml:/var/www/alertbot/config.yaml:ro
|
||||
ports:
|
||||
- 8080:8080
|
||||
...
|
Loading…
Reference in a new issue