webhook_server 1.0.0__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {webhook_server-1.0.0 → webhook_server-1.0.2}/PKG-INFO +8 -1
- {webhook_server-1.0.0 → webhook_server-1.0.2}/README.md +7 -0
- {webhook_server-1.0.0 → webhook_server-1.0.2}/pyproject.toml +1 -1
- webhook_server-1.0.2/webhook_server/datadog.py +46 -0
- {webhook_server-1.0.0 → webhook_server-1.0.2}/webhook_server/webhooks.py +4 -8
- webhook_server-1.0.0/webhook_server/datadog.py +0 -79
- {webhook_server-1.0.0 → webhook_server-1.0.2}/webhook_server/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: webhook_server
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Create a webhook server with Flask to forward Datadog alerts to Telegram
|
|
5
5
|
Keywords: datadog,webhook,telegram
|
|
6
6
|
Author: daisukixci
|
|
@@ -88,6 +88,13 @@ curl -X POST -H "Content-Type: application/json"\
|
|
|
88
88
|
|
|
89
89
|
### Documentation
|
|
90
90
|
|
|
91
|
+
#### publish
|
|
92
|
+
```
|
|
93
|
+
uv version VERSION
|
|
94
|
+
uv build
|
|
95
|
+
uv publish --token=$(op read "op://Private/PyPI/publish_token")
|
|
96
|
+
```
|
|
97
|
+
|
|
91
98
|
### Contribute
|
|
92
99
|
Open to any PR/comments/issues
|
|
93
100
|
|
|
@@ -61,6 +61,13 @@ curl -X POST -H "Content-Type: application/json"\
|
|
|
61
61
|
|
|
62
62
|
### Documentation
|
|
63
63
|
|
|
64
|
+
#### publish
|
|
65
|
+
```
|
|
66
|
+
uv version VERSION
|
|
67
|
+
uv build
|
|
68
|
+
uv publish --token=$(op read "op://Private/PyPI/publish_token")
|
|
69
|
+
```
|
|
70
|
+
|
|
64
71
|
### Contribute
|
|
65
72
|
Open to any PR/comments/issues
|
|
66
73
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "webhook_server"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.2"
|
|
4
4
|
description = "Create a webhook server with Flask to forward Datadog alerts to Telegram"
|
|
5
5
|
authors = [{ name = "daisukixci", email = "piquenot.gaetan+pip@gmail.com" }]
|
|
6
6
|
requires-python = ">=3.10,<4"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass, asdict
|
|
3
|
+
from datetime import datetime, UTC
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class DatadogOrg:
|
|
9
|
+
id: int
|
|
10
|
+
name: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class DatadogAlertContent:
|
|
15
|
+
title: str
|
|
16
|
+
event_type: str
|
|
17
|
+
body: str
|
|
18
|
+
date: int
|
|
19
|
+
org: DatadogOrg
|
|
20
|
+
id: int
|
|
21
|
+
last_updated: int
|
|
22
|
+
|
|
23
|
+
def to_dict(self) -> dict:
|
|
24
|
+
return asdict(self)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DatadogAlert:
|
|
28
|
+
def __init__(self, alert: DatadogAlertContent):
|
|
29
|
+
self._alert = alert
|
|
30
|
+
|
|
31
|
+
def get_message(self) -> str:
|
|
32
|
+
message = f"""<b>{self._alert.title}</b>
|
|
33
|
+
In Org <b>{self._alert.org['name']}</b>({self._alert.org['id']})
|
|
34
|
+
At {datetime.fromtimestamp(int(self._alert.date), tz=UTC).strftime("%Y/%m/%d %H:%M")}
|
|
35
|
+
{_clean_msg(self._alert.body)}"""
|
|
36
|
+
return message
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _markdown_to_html_links(markdown_text):
|
|
40
|
+
pattern = r"\[([^\]]+)\]\(([^)]+)\)"
|
|
41
|
+
html_text = re.sub(pattern, r'<a href="\2">\1</a>', markdown_text)
|
|
42
|
+
return html_text
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _clean_msg(msg: str) -> str:
|
|
46
|
+
return _markdown_to_html_links(msg.strip("%"))
|
|
@@ -3,12 +3,9 @@ from flask.typing import ResponseReturnValue
|
|
|
3
3
|
import requests
|
|
4
4
|
|
|
5
5
|
from flask import request, jsonify
|
|
6
|
-
import logger
|
|
7
6
|
from . import app, basic_auth
|
|
8
7
|
from .datadog import DatadogAlert, DatadogAlertContent
|
|
9
8
|
|
|
10
|
-
webhookLogger = logger.get_json_logger(__name__)
|
|
11
|
-
|
|
12
9
|
|
|
13
10
|
@app.route("/")
|
|
14
11
|
def ping() -> ResponseReturnValue:
|
|
@@ -21,13 +18,13 @@ def webhookHandler(chat_id) -> ResponseReturnValue:
|
|
|
21
18
|
try:
|
|
22
19
|
content = request.get_json()
|
|
23
20
|
except requests.exceptions.RequestException:
|
|
24
|
-
|
|
21
|
+
app.logger.exception("failed to parse request payload")
|
|
25
22
|
return jsonify({"message": "Bad request"}), HTTPStatus.BAD_REQUEST
|
|
26
23
|
|
|
27
24
|
try:
|
|
28
25
|
alert = DatadogAlert(DatadogAlertContent(**content))
|
|
29
26
|
except TypeError:
|
|
30
|
-
|
|
27
|
+
app.logger.exception("failed to build DatadogAlert")
|
|
31
28
|
return jsonify({"message": "Bad request"}), HTTPStatus.BAD_REQUEST
|
|
32
29
|
|
|
33
30
|
url = (
|
|
@@ -40,12 +37,11 @@ def webhookHandler(chat_id) -> ResponseReturnValue:
|
|
|
40
37
|
payload = {
|
|
41
38
|
"chat_id": chat_id,
|
|
42
39
|
"text": alert.get_message(),
|
|
43
|
-
"parse_mode": "
|
|
40
|
+
"parse_mode": "HTML",
|
|
44
41
|
}
|
|
45
|
-
print(payload)
|
|
46
42
|
req = requests.post(url=url, data=payload, timeout=5)
|
|
47
43
|
telegram_response = req.json()
|
|
48
44
|
return jsonify(telegram_response)
|
|
49
45
|
except requests.exceptions.RequestException:
|
|
50
|
-
|
|
46
|
+
app.logger.exception("failed to post to Telegram")
|
|
51
47
|
return jsonify({"message": "Bad request"}), HTTPStatus.BAD_REQUEST
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from dataclasses import dataclass, asdict
|
|
3
|
-
from datetime import datetime, UTC
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@dataclass
|
|
8
|
-
class DatadogOrg:
|
|
9
|
-
id: int
|
|
10
|
-
name: str
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@dataclass
|
|
14
|
-
class DatadogAlertContent:
|
|
15
|
-
title: str
|
|
16
|
-
event_type: str
|
|
17
|
-
body: str
|
|
18
|
-
date: int
|
|
19
|
-
org: DatadogOrg
|
|
20
|
-
id: int
|
|
21
|
-
last_updated: int
|
|
22
|
-
|
|
23
|
-
def to_dict(self) -> dict:
|
|
24
|
-
return asdict(self)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class DatadogAlert:
|
|
28
|
-
def __init__(self, alert: DatadogAlertContent):
|
|
29
|
-
self._alert = alert
|
|
30
|
-
|
|
31
|
-
def get_message(self) -> str:
|
|
32
|
-
message = f"""*{_datadog_to_telegram_md(self._alert.title)}*
|
|
33
|
-
In Org {_escape_md(self._alert.org['name'])}\({_escape_md(self._alert.org['id'])}\)
|
|
34
|
-
At {_escape_md(datetime.fromtimestamp(int(self._alert.date), tz=UTC).strftime("%Y/%m/%d %H:%M"))}
|
|
35
|
-
{_datadog_to_telegram_md(self._alert.body)}"""
|
|
36
|
-
return message
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _escape_md(text: str) -> str:
|
|
40
|
-
return re.sub(r"([_*\[\]()~`>#+\-=|{}.!])", r"\\\1", text)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _datadog_to_telegram_md(text: str) -> str:
|
|
44
|
-
text = text.strip().strip("%").strip()
|
|
45
|
-
|
|
46
|
-
links = []
|
|
47
|
-
|
|
48
|
-
def extract_link(match):
|
|
49
|
-
links.append((match.group(1), match.group(2)))
|
|
50
|
-
return f"LINKTOKEN{len(links)-1}END"
|
|
51
|
-
|
|
52
|
-
text = re.sub(r"\[\[(.*?)\]\((.*?)\)\]", extract_link, text)
|
|
53
|
-
|
|
54
|
-
# Process lines
|
|
55
|
-
lines = text.splitlines()
|
|
56
|
-
result = []
|
|
57
|
-
|
|
58
|
-
for line in lines:
|
|
59
|
-
line = line.strip()
|
|
60
|
-
|
|
61
|
-
if line.startswith("## "):
|
|
62
|
-
content = _escape_md(line[3:])
|
|
63
|
-
result.append(f"*{content}*")
|
|
64
|
-
continue
|
|
65
|
-
|
|
66
|
-
if line.startswith("- - -"):
|
|
67
|
-
continue
|
|
68
|
-
|
|
69
|
-
result.append(_escape_md(line))
|
|
70
|
-
|
|
71
|
-
text = "\n".join(result)
|
|
72
|
-
|
|
73
|
-
# Restore links
|
|
74
|
-
for i, (label, url) in enumerate(links):
|
|
75
|
-
safe_label = _escape_md(label)
|
|
76
|
-
print(safe_label + " " + url)
|
|
77
|
-
text = text.replace(f"LINKTOKEN{i}END", f"[{safe_label}]({url})")
|
|
78
|
-
|
|
79
|
-
return text
|
|
File without changes
|