Skip to content

Commit 27d8060

Browse files
committed
feat: add notifications for slack, telegram and generic
1 parent 17455ec commit 27d8060

File tree

5 files changed

+146
-17
lines changed

5 files changed

+146
-17
lines changed

.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
IDENTITY_ADDRESS=address
22
RPC_URL=url
3-
DISCORD_WEBHOOK=webhook
3+
NOTIFICATION_DISCORD_WEBHOOK=https://discord.com/api/webhooks/secret/secret
4+
NOTIFICATION_TELEGRAM_BOT_TOKEN=secret
5+
NOTIFICATION_TELEGRAM_CHAT_ID=secret
6+
NOTIFICATION_SLACK_WEBHOOK=https://hooks.slack.com/services/secret/secret/secret
7+
NOTIFICATION_GENERIC_WEBHOOK=http://host:port/path

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@
66

77
deploy:
88

9+
currently we support push notification over:
10+
- discord webhook
11+
- slack webhook
12+
- telegram bot sendMessage method
13+
- generic url post
14+
15+
All of these can be configured via env variables. All of these have `NOTIFICATION` and
16+
aren't a required value for the observer to start. All example values below
17+
918
```bash
1019
docker run \
1120
-e RPC_URL="http://host/ext/bc/C/rpc" \
1221
-e IDENTITY_ADDRESS="0x0000000000000000000000000000000000000000" \
22+
-e NOTIFICATION_DISCORD_WEBHOOK="https://discord.com/api/webhooks/secret/secret" \
23+
-e NOTIFICATION_TELEGRAM_BOT_TOKEN="secret" \
24+
-e NOTIFICATION_TELEGRAM_CHAT_ID="secret" \
25+
-e NOTIFICATION_SLACK_WEBHOOK="https://hooks.slack.com/services/secret/secret/secret" \
26+
-e NOTIFICATION_GENERIC_WEBHOOK="http://host:port/path" \
1327
ghcr.io/flare-foundation/fsp-observer:main
1428
```
1529

@@ -46,7 +60,7 @@ todos:
4660
- notification plugins
4761
- [x] stdout logging
4862
- [x] discord
49-
- [ ] slack
50-
- [ ] telegram
63+
- [x] slack
64+
- [x] telegram
5165
- [ ] pager duty
52-
- [ ] generic post
66+
- [x] generic post

configuration/config.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@
44
from py_flare_common.fsp.epoch.timing import coston, coston2, flare, songbird
55
from web3 import Web3
66

7-
from .types import Configuration, Contracts, Epoch
7+
from .types import (
8+
Configuration,
9+
Contracts,
10+
Epoch,
11+
Notification,
12+
NotificationDiscord,
13+
NotificationGeneric,
14+
NotificationSlack,
15+
NotificationTelegram,
16+
)
817

918

1019
class ChainId:
@@ -57,6 +66,39 @@ def get_epoch(chain_id: int) -> Epoch:
5766
)
5867

5968

69+
def get_notification_config() -> Notification:
70+
discord = None
71+
discord_webhook = os.environ.get("NOTIFICATION_DISCORD_WEBHOOK")
72+
if discord_webhook is not None:
73+
discord = NotificationDiscord(discord_webhook)
74+
75+
slack = None
76+
slack_webhook = os.environ.get("NOTIFICATION_SLACK_WEBHOOK")
77+
if slack_webhook is not None:
78+
slack = NotificationSlack(slack_webhook)
79+
80+
telegram = None
81+
telegram_bot_token = os.environ.get("NOTIFICATION_TELEGRAM_BOT_TOKEN")
82+
telegram_chat_id = os.environ.get("NOTIFICATION_TELEGRAM_CHAT_ID")
83+
if telegram_bot_token is not None and telegram_chat_id is not None:
84+
telegram = NotificationTelegram(
85+
bot_token=telegram_bot_token,
86+
chat_id=telegram_chat_id,
87+
)
88+
89+
generic = None
90+
generic_webhook = os.environ.get("NOTIFICATION_GENERIC_WEBHOOK")
91+
if generic_webhook is not None:
92+
generic = NotificationGeneric(generic_webhook)
93+
94+
return Notification(
95+
discord=discord,
96+
slack=slack,
97+
telegram=telegram,
98+
generic=generic,
99+
)
100+
101+
60102
def get_config() -> Configuration:
61103
rpc_url = os.environ.get("RPC_URL")
62104

@@ -81,7 +123,7 @@ def get_config() -> Configuration:
81123
chain_id=chain_id,
82124
contracts=Contracts.get_contracts(w),
83125
epoch=get_epoch(chain_id),
84-
discord_webhook=os.environ.get("DISCORD_WEBHOOK"),
126+
notification=get_notification_config(),
85127
)
86128

87129
return config

configuration/types.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,40 @@ class Epoch:
179179
reward_epoch_factory: RewardEpochFactory
180180

181181

182+
@frozen
183+
class NotificationDiscord:
184+
webhook_url: str
185+
186+
187+
@frozen
188+
class NotificationSlack:
189+
webhook_url: str
190+
191+
192+
@frozen
193+
class NotificationTelegram:
194+
bot_token: str
195+
chat_id: str
196+
197+
198+
@frozen
199+
class NotificationGeneric:
200+
webhook_url: str
201+
202+
203+
@frozen
204+
class Notification:
205+
discord: NotificationDiscord | None
206+
slack: NotificationSlack | None
207+
telegram: NotificationTelegram | None
208+
generic: NotificationGeneric | None
209+
210+
182211
@frozen
183212
class Configuration:
184213
identity_address: ChecksumAddress
185214
chain_id: int
186215
contracts: Contracts
187216
rpc_url: str
188217
epoch: Epoch
189-
discord_webhook: str | None
218+
notification: Notification

observer/observer.py

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
from web3.middleware import ExtraDataToPOAMiddleware
2626

2727
from configuration.config import ChainId
28-
from configuration.types import Configuration
28+
from configuration.types import (
29+
Configuration,
30+
NotificationDiscord,
31+
NotificationGeneric,
32+
NotificationSlack,
33+
NotificationTelegram,
34+
)
2935
from observer.reward_epoch_manager import (
3036
Entity,
3137
SigningPolicy,
@@ -62,17 +68,38 @@ def from_vrs(cls, s: SSignature) -> Self:
6268
)
6369

6470

65-
def notify_discord(config: Configuration, message: str) -> None:
66-
if config.discord_webhook is None:
67-
return
68-
71+
def notify_discord(config: NotificationDiscord, message: str) -> None:
6972
requests.post(
70-
config.discord_webhook,
73+
config.webhook_url,
7174
headers={"Content-Type": "application/json"},
7275
json={"content": message},
7376
)
7477

7578

79+
def notify_slack(config: NotificationSlack, message: str) -> None:
80+
requests.post(
81+
config.webhook_url,
82+
headers={"Content-Type": "application/json"},
83+
json={"text": message},
84+
)
85+
86+
87+
def notify_telegram(config: NotificationTelegram, message: str) -> None:
88+
requests.post(
89+
f"https://api.telegram.org/bot{config.bot_token}/sendMessage",
90+
headers={"Content-Type": "application/json"},
91+
json={"chat_id": config.chat_id, "text": message},
92+
)
93+
94+
95+
def notify_generic(config: NotificationGeneric, issue: "Issue") -> None:
96+
requests.post(
97+
config.webhook_url,
98+
headers={"Content-Type": "application/json"},
99+
json={"level": issue.level.value, "message": issue.message},
100+
)
101+
102+
76103
async def find_voter_registration_blocks(
77104
w: AsyncWeb3,
78105
current_block_id: int,
@@ -259,9 +286,22 @@ def add_message(self, m: str) -> Self:
259286
return self
260287

261288

262-
def log_issue(config, issue: Issue):
289+
def log_issue(config: Configuration, issue: Issue):
263290
LOGGER.log(issue.level.value, issue.message)
264-
notify_discord(config, issue.level.name + " " + issue.message)
291+
292+
n = config.notification
293+
294+
if n.discord is not None:
295+
notify_discord(n.discord, issue.level.name + " " + issue.message)
296+
297+
if n.slack is not None:
298+
notify_slack(n.slack, issue.level.name + " " + issue.message)
299+
300+
if n.telegram is not None:
301+
notify_telegram(n.telegram, issue.level.name + " " + issue.message)
302+
303+
if n.generic is not None:
304+
notify_generic(n.generic, issue)
265305

266306

267307
def extract[T](
@@ -503,10 +543,10 @@ async def observer_loop(config: Configuration) -> None:
503543
# Issue(
504544
# IssueLevel.INFO,
505545
# MessageBuilder()
506-
# .add_network(config.chain)
546+
# .add_network(config.chain_id)
507547
# .add_protocol(100)
508548
# .add_round(VotingEpoch(12, None))
509-
# .build_with_message("testing message"),
549+
# .build_with_message("testing message" + str(config.notification)),
510550
# ),
511551
# )
512552
# return

0 commit comments

Comments
 (0)