Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send custom email message to all subscribers #121

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# misc
*.csv
tmp_prod_db.json
email_to_send.txt

# python
*.pyc
Expand Down
52 changes: 52 additions & 0 deletions message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
from devtools import debug
import toolz

## A custom script that imports some classes from node_monitor
## and sends out a custom email message


## Import the deired email as text (this name is in the .gitignore)
with open('email_to_send.txt', 'r') as f:
email = f.read()


## Import nodemonitor
from node_monitor.node_monitor import NodeMonitor
from node_monitor.bot_email import EmailBot
from node_monitor.node_provider_db import NodeProviderDB
import node_monitor.load_config as c

# Initialize nodemonitor
email_bot = EmailBot(c.EMAIL_USERNAME, c.EMAIL_PASSWORD)
node_provider_db = NodeProviderDB(
c.DB_HOST, c.DB_NAME, c.DB_PORT,
c.DB_USERNAME, c.DB_PASSWORD)
nm = NodeMonitor(node_provider_db, email_bot, None, None)


# Initialize broadcaster (this is for email, slack, telegram)
# We don't need to use it (we can just send emails with email_bot)
subscribers = nm.node_provider_db.get_subscribers_as_dict()
email_lookup = nm.node_provider_db.get_emails_as_dict()
subscriber_principals = list(subscribers.keys())
broadcaster = nm._make_broadcaster()
unique_emails = list(toolz.unique(toolz.concat(list(email_lookup.values()))))
# debug(unique_emails)


## Set up the email parameters
recipients = ['george@aviatelabs.co']
subject = "Custom Email Subject"
message = email


def main():
print("Sending emails...")
# Uncomment this following line to actually send the emails
# email_bot.send_emails(recipients, subject, message)
print("Done!")


if __name__ == "__main__":
main()
77 changes: 29 additions & 48 deletions node_monitor/node_monitor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import time
from collections import deque
from typing import Deque, List, Dict, Optional
from typing import Deque, List, Dict, Optional, Callable
from toolz import groupby # type: ignore
import schedule
import logging
Expand Down Expand Up @@ -98,83 +98,64 @@ def _analyze(self) -> None:
self.actionables = {k: v for k, v
in self.compromised_nodes_by_provider.items()
if k in subscriber_ids}


def broadcast_alerts(self) -> None:
"""Broadcast relevant alerts to the appropriate channels. Retrieves
subscribers, node_labels, and email_recipients from the database."""

def _make_broadcaster(self) -> Callable[[str, str, str], None]:
"""A closure that returns a broadcast function with a local cache.
Allows the returned function to be run in a loop without
querying the database.
"""
subscribers = self.node_provider_db.get_subscribers_as_dict()
node_labels = self.node_provider_db.get_node_labels_as_dict()
email_recipients = self.node_provider_db.get_emails_as_dict()
slack_channels = self.node_provider_db.get_slack_channels_as_dict()
telegram_chats = self.node_provider_db.get_telegram_chats_as_dict()
for node_provider_id, nodes in self.actionables.items():

def broadcaster(node_provider_id: str,
subject: str, message: str) -> None:
"""Broadcasts a generic message to a subscriber through their
selected communication channel(s)."""
preferences = subscribers[node_provider_id]
subject, message = messages.nodes_compromised_message(nodes, node_labels)
# - - - - - - - - - - - - - - - - -
if preferences['notify_email'] == True:
recipients = email_recipients[node_provider_id]
logging.info(f"Sending alert email message to {recipients}...")
self.email_bot.send_emails(recipients, subject, message)
if preferences['notify_slack'] == True:
if preferences['notify_slack'] == True:
if self.slack_bot is not None:
channels = slack_channels[node_provider_id]
logging.info(f"Sending alert slack messages to {channels}...")
err1 = self.slack_bot.send_messages(channels, message)
if err1 is not None:
logging.error(f"SlackBot.send_message() failed with error: {err1}")
if preferences['notify_telegram'] == True:
if self.telegram_bot is not None:
chats = telegram_chats[node_provider_id]
logging.info(f"Sending alert telegram messages to {chats}...")
err2 = self.telegram_bot.send_messages(chats, message)
if err2 is not None:
logging.error(f"TelegramBot.send_message() failed with error: {err2}")
# - - - - - - - - - - - - - - - - -
return None

return broadcaster


def broadcast_alerts(self) -> None:
"""Broadcast relevant alerts to the appropriate channels."""
broadcaster = self._make_broadcaster()
node_labels = self.node_provider_db.get_node_labels_as_dict()
for node_provider_id, nodes in self.actionables.items():
logging.info(f"Broadcasting alert message to {node_provider_id}...")
subject, message = messages.nodes_compromised_message(nodes, node_labels)
broadcaster(node_provider_id, subject, message)


def broadcast_status_report(self) -> None:
"""Broadcasts a Node Status Report to all Node Providers.
Retrieves subscribers, node_labels, and email_recipients from the
database. Filters out Node Providers that are not subscribed to
status reports.
"""
"""Broadcasts a Node Status Report to all Node Providers."""
broadcaster = self._make_broadcaster()
subscribers = self.node_provider_db.get_subscribers_as_dict()
node_labels = self.node_provider_db.get_node_labels_as_dict()
email_recipients = self.node_provider_db.get_emails_as_dict()
slack_channels = self.node_provider_db.get_slack_channels_as_dict()
telegram_chats = self.node_provider_db.get_telegram_chats_as_dict()
latest_snapshot_nodes = self.snapshots[-1].nodes
all_nodes_by_provider: Dict[Principal, List[ic_api.Node]] = \
groupby(lambda node: node.node_provider_id, latest_snapshot_nodes)
reportable_nodes = {k: v for k, v
in all_nodes_by_provider.items()
if k in subscribers.keys()}
# - - - - - - - - - - - - - - - - -
for node_provider_id, nodes in reportable_nodes.items():
logging.info(f"Broadcasting status report {node_provider_id}...")
preferences = subscribers[node_provider_id]
subject, message = messages.nodes_status_message(nodes, node_labels)
# - - - - - - - - - - - - - - - - -
if preferences['notify_email'] == True:
recipients = email_recipients[node_provider_id]
logging.info(f"Sending status report email to {recipients}...")
self.email_bot.send_emails(recipients, subject, message)
if preferences['notify_slack'] == True:
if self.slack_bot is not None:
channels = slack_channels[node_provider_id]
logging.info(f"Sending status report slack message to {channels}...")
err1 = self.slack_bot.send_messages(channels, message)
if err1 is not None:
logging.error(f"SlackBot.send_message() failed with error: {err1}")
if preferences['notify_telegram'] == True:
if self.telegram_bot is not None:
chats = telegram_chats[node_provider_id]
logging.info(f"Sending status report telegram messages to {chats}...")
err2 = self.telegram_bot.send_messages(chats, message)
if err2 is not None:
logging.error(f"TelegramBot.send_message() failed with error: {err2}")
# - - - - - - - - - - - - - - - - -
broadcaster(node_provider_id, subject, message)


def step(self) -> None:
Expand Down
Loading