Skip to content

Commit d60958d

Browse files
committed
feat: add discord embed notification
1 parent 33cc93d commit d60958d

File tree

5 files changed

+136
-31
lines changed

5 files changed

+136
-31
lines changed

configuration/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ def get_notification_config() -> Notification:
100100
if discord_webhook is not None:
101101
discord.extend(discord_webhook.split(","))
102102

103+
discord_embed_webhook = os.environ.get("NOTIFICATION_DISCORD_EMBED_WEBHOOK")
104+
discord_embed = []
105+
if discord_embed_webhook is not None:
106+
discord_embed.extend(discord_embed_webhook.split(","))
107+
103108
slack_webhook = os.environ.get("NOTIFICATION_SLACK_WEBHOOK")
104109
slack = []
105110
if slack_webhook is not None:
@@ -120,6 +125,7 @@ def get_notification_config() -> Notification:
120125

121126
return Notification(
122127
discord=NotificationDiscord(discord),
128+
discord_embed=NotificationDiscord(discord_embed),
123129
slack=NotificationSlack(slack),
124130
telegram=NotificationTelegram(telegram),
125131
generic=NotificationGeneric(generic),

configuration/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ class NotificationGeneric:
244244
@frozen
245245
class Notification:
246246
discord: NotificationDiscord
247+
discord_embed: NotificationDiscord
247248
slack: NotificationSlack
248249
telegram: NotificationTelegram
249250
generic: NotificationGeneric

observer/message.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,20 @@ class Message:
2222
level: MessageLevel
2323
message: str
2424

25-
@classmethod
26-
def builder(cls) -> "MessageBuilder":
27-
return MessageBuilder()
28-
29-
30-
@define
31-
class MessageBuilder:
32-
level: MessageLevel | None = None
33-
message: str | None = None
34-
3525
network: int | None = None
3626
round: VotingEpoch | None = None
3727
protocol: ProtocolId | None = None
3828

39-
def copy(self) -> Self:
40-
return copy.copy(self)
41-
42-
def _build(self) -> Message:
43-
assert self.level is not None
44-
assert self.message is not None
29+
@classmethod
30+
def builder(cls) -> "MessageBuilder":
31+
return MessageBuilder()
4532

33+
def build_str(self, with_log=False) -> str:
4634
s = io.StringIO()
4735

36+
if with_log:
37+
s.write(f"[{self.level.name}] ")
38+
4839
if self.network is not None:
4940
network = ChainId.id_to_name(self.network)
5041
s.write(f"network:{network} ")
@@ -59,7 +50,32 @@ def _build(self) -> Message:
5950
s.write(self.message)
6051

6152
s.seek(0)
62-
return Message(level=self.level, message=s.read())
53+
return s.read()
54+
55+
56+
@define
57+
class MessageBuilder:
58+
level: MessageLevel | None = None
59+
message: str | None = None
60+
61+
network: int | None = None
62+
round: VotingEpoch | None = None
63+
protocol: ProtocolId | None = None
64+
65+
def copy(self) -> Self:
66+
return copy.copy(self)
67+
68+
def _build(self) -> Message:
69+
assert self.level is not None
70+
assert self.message is not None
71+
72+
return Message(
73+
level=self.level,
74+
message=self.message,
75+
network=self.network,
76+
round=self.round,
77+
protocol=self.protocol,
78+
)
6379

6480
def build(self, level: MessageLevel, message: str) -> Message:
6581
return self.copy().add(level=level, message=message)._build()

observer/notification.py

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
from collections import defaultdict
2+
from datetime import datetime, timezone
13
from typing import Any
24

35
import requests
46

7+
from configuration.config import ChainId, Protocol
58
from configuration.types import (
69
NotificationDiscord,
710
NotificationGeneric,
811
NotificationSlack,
912
NotificationTelegram,
1013
)
1114

12-
from .message import Message
15+
from .message import Message, MessageLevel
1316

1417

1518
def notify(
@@ -26,37 +29,111 @@ def notify(
2629
pass
2730

2831

29-
def notify_discord(config: NotificationDiscord, message: str) -> None:
32+
def notify_discord(config: NotificationDiscord, message: Message) -> None:
3033
for u in config.webhook_url:
3134
notify(
3235
u,
3336
"POST",
3437
headers={"Content-Type": "application/json"},
35-
json={"content": message},
38+
json={"content": message.build_str(with_log=True)},
3639
)
3740

3841

39-
def notify_slack(config: NotificationSlack, message: str) -> None:
42+
LEVEL_COLORS = defaultdict(
43+
lambda: 0x95A5A6,
44+
{
45+
MessageLevel.DEBUG: 0x3498DB, # Blue
46+
MessageLevel.INFO: 0x2ECC71, # Green
47+
MessageLevel.WARNING: 0xF1C40F, # Yellow
48+
MessageLevel.ERROR: 0xE74C3C, # Red
49+
MessageLevel.CRITICAL: 0x992D22, # Dark Red
50+
},
51+
)
52+
53+
54+
def get_icon_url(network: int) -> str:
55+
network_name = ChainId.id_to_name(network)
56+
return (
57+
"https://raw.githubusercontent.com/flare-foundation/fsp-observer/main"
58+
f"/assets/{network_name}.png"
59+
)
60+
61+
62+
def notify_discord_embed(config: NotificationDiscord, message: Message) -> None:
63+
color = LEVEL_COLORS[message.level]
64+
65+
fields = []
66+
if message.protocol is not None:
67+
fields.append(
68+
{
69+
"name": "Protocol",
70+
"value": Protocol.id_to_name(message.protocol).upper(),
71+
"inline": True,
72+
}
73+
)
74+
75+
if message.round is not None:
76+
fields.append(
77+
{
78+
"name": "Round",
79+
"value": str(message.round.id),
80+
"inline": True,
81+
}
82+
)
83+
84+
embed = {
85+
"author": {
86+
"name": f"fsp-observer @ {ChainId.id_to_name(message.network)}",
87+
"url": "https://github.com/flare-foundation/fsp-observer",
88+
},
89+
"title": f"{message.level.name.title()}",
90+
"description": f"{message.message}",
91+
"color": color,
92+
"fields": fields,
93+
"timestamp": datetime.now(timezone.utc).isoformat(),
94+
"footer": {
95+
"text": "Flare Network",
96+
"icon_url": get_icon_url(ChainId.FLARE),
97+
},
98+
}
99+
100+
if message.network is not None:
101+
embed["thumbnail"] = {
102+
"url": get_icon_url(message.network),
103+
"height": 32,
104+
"width": 32,
105+
}
106+
107+
for u in config.webhook_url:
108+
notify(
109+
u,
110+
"POST",
111+
headers={"Content-Type": "application/json"},
112+
json={"embeds": [embed]},
113+
)
114+
115+
116+
def notify_slack(config: NotificationSlack, message: Message) -> None:
40117
for u in config.webhook_url:
41118
notify(
42119
u,
43120
"POST",
44121
headers={"Content-Type": "application/json"},
45-
json={"text": message},
122+
json={"text": message.build_str(with_log=True)},
46123
)
47124

48125

49-
def notify_telegram(config: NotificationTelegram, message: str) -> None:
126+
def notify_telegram(config: NotificationTelegram, message: Message) -> None:
50127
for t in config.bot:
51128
notify(
52129
f"https://api.telegram.org/bot{t.bot_token}/sendMessage",
53130
"POST",
54131
headers={"Content-Type": "application/json"},
55-
json={"chat_id": t.chat_id, "text": message},
132+
json={"chat_id": t.chat_id, "text": message.build_str(with_log=True)},
56133
)
57134

58135

59-
def notify_generic(config: NotificationGeneric, issue: "Message") -> None:
136+
def notify_generic(config: NotificationGeneric, issue: Message) -> None:
60137
for u in config.webhook_url:
61138
notify(
62139
u,

observer/observer.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@
5555
from observer.validation.validation import extract_round_for_entity, validate_round
5656

5757
from .message import Message, MessageLevel
58-
from .notification import notify_discord, notify_generic, notify_slack, notify_telegram
58+
from .notification import (
59+
notify_discord,
60+
notify_discord_embed,
61+
notify_generic,
62+
notify_slack,
63+
notify_telegram,
64+
)
5965
from .voting_round import (
6066
VotingRoundManager,
6167
WTxData,
@@ -294,11 +300,10 @@ def log_message(config: Configuration, message: Message):
294300

295301
n = config.notification
296302

297-
lvl_msg = f"{message.level.name} {message.message}"
298-
299-
notify_discord(n.discord, lvl_msg)
300-
notify_slack(n.slack, lvl_msg)
301-
notify_telegram(n.telegram, lvl_msg)
303+
notify_discord(n.discord, message)
304+
notify_discord_embed(n.discord_embed, message)
305+
notify_slack(n.slack, message)
306+
notify_telegram(n.telegram, message)
302307
notify_generic(n.generic, message)
303308

304309

0 commit comments

Comments
 (0)