Skip to content
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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ To use Telegram you first need to create a custom bot, the easiest mechanism for
```
You need to find your `CHAT_ID` as indicated above and save that. You may have to send a few "/start" messages to your bot before you get a response that looks like this. Once you've got your CHAT ID as well as your API token, you're all good to go!

#### Email (SMTP)

You will need an account on an SMTP server.

* `hostname`: server domain/ip
* `port`: server port, usually will be `587`
* `username`: account username, usually (_but not always_) the same as your email address
* `password`: account password
* `email_from`: email address to send alerts **from**, usually the same as your username
* `email_to`: email address to send alerts **to**

If your account has MFA (multi-factor authentication) you will need to refer to your provider's instructions to generate a security token to use instead of your normal password.

#### More coming soon ...

I plan to add more alerting options as soon as possible! Please feel free to open a PR if you have a particular service
Expand Down Expand Up @@ -204,6 +217,12 @@ Here are the possible arguments:
* `-pt` `--pushbullet-token`: (str) your pushbullet token (only required if `"--alerter-type=PUSHBULLET"`)
* `-tt` `--telegram-token`: (str) your telegram API token (only required if `"--alerter-type=TELEGRAM"`)
* `-tci` `--telegram-chat-id`: (str) your telgram chat ID (only required if `"--alerter-type=TELEGRAM"`)
* `-eh` `--email-host`: (str) your email server's hostname (only required if `"--alerter-type=EMAIL"`)
* `-ep` `--email-port`: (int) your email server's port (only required if `"--alerter-type=EMAIL"`)
* `-eu` `--email-username`: (str) username of your SMTP account (only required if `"--alerter-type=EMAIL"`)
* `-ep` `--email-password`: (str) password of your SMTP account (only required if `"--alerter-type=EMAIL"`)
* `-ef` `--email-from`: (str) email address to send notifications from (only required if `"--alerter-type=EMAIL"`)
* `-et` `--email-to`: (str) email address to send notifications to (only required if `"--alerter-type=EMAIL"`)

And here are the possible flags:
* `-V` `--verbose`: (bool) use this flag if you want to run the server in verbose mode, meaning it will log updates to the command line as it runs (default=`false`)
Expand Down
75 changes: 75 additions & 0 deletions discogs_alert/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,66 @@
envvar="DA_TELEGRAM_CHAT_ID",
help="chat ID for telegram bot notification service.",
)
@click.option(
"-eh",
"--email-host",
cls=da_click.RequiredIf,
required_if=lambda x: x["alerter_type"] == da_alert.AlerterType.EMAIL,
required_if_str="alerter_type=AlerterType.EMAIL",
type=str,
envvar="DA_EMAIL_HOSTNAME",
help="hostname of SMTP server used for email notification service.",
)
@click.option(
"-ep",
"--email-port",
cls=da_click.RequiredIf,
required_if=lambda x: x["alerter_type"] == da_alert.AlerterType.EMAIL,
required_if_str="alerter_type=AlerterType.EMAIL",
type=int,
envvar="DA_EMAIL_PORT",
help="port of SMTP server used for email notification service.",
)
@click.option(
"-eu",
"--email-username",
cls=da_click.RequiredIf,
required_if=lambda x: x["alerter_type"] == da_alert.AlerterType.EMAIL,
required_if_str="alerter_type=AlerterType.EMAIL",
type=str,
envvar="DA_EMAIL_USERNAME",
help="username of SMTP account for email notification service.",
)
@click.option(
"-ep",
"--email-password",
cls=da_click.RequiredIf,
required_if=lambda x: x["alerter_type"] == da_alert.AlerterType.EMAIL,
required_if_str="alerter_type=AlerterType.EMAIL",
type=str,
envvar="DA_EMAIL_PASSWORD",
help="password of SMTP account for email notification service.",
)
@click.option(
"-ef",
"--email-from",
cls=da_click.RequiredIf,
required_if=lambda x: x["alerter_type"] == da_alert.AlerterType.EMAIL,
required_if_str="alerter_type=AlerterType.EMAIL",
type=str,
envvar="DA_EMAIL_FROM",
help="email address to mail from for email notification service.",
)
@click.option(
"-et",
"--email-to",
cls=da_click.RequiredIf,
required_if=lambda x: x["alerter_type"] == da_alert.AlerterType.EMAIL,
required_if_str="alerter_type=AlerterType.EMAIL",
type=str,
envvar="DA_EMAIL_TO",
help="email address to send to for email notification service.",
)
@click.option(
"-V", "--verbose", default=False, is_flag=True, help="use flag if you want to see logs as the program runs"
)
Expand Down Expand Up @@ -216,6 +276,12 @@ def main(
pushbullet_token,
telegram_token,
telegram_chat_id,
email_hostname,
email_port,
email_username,
email_password,
email_from,
email_to,
verbose,
test,
):
Expand All @@ -235,6 +301,15 @@ def main(
alerter_kwargs = {"pushbullet_token": pushbullet_token}
elif alerter_type == da_alert.AlerterType.TELEGRAM:
alerter_kwargs = {"telegram_token": telegram_token, "telegram_chat_id": telegram_chat_id}
elif alerter_type == da_alert.AlerterType.EMAIL:
alerter_kwargs = {
"email_hostname": email_hostname,
"email_port": email_port,
"email_username": email_username,
"email_password": email_password,
"email_from": email_from,
"email_to": email_to
}
else:
raise ValueError("We should never get here")

Expand Down
4 changes: 4 additions & 0 deletions discogs_alert/alert/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@
from discogs_alert.alert.base import Alerter
from discogs_alert.alert.pushbullet import PushbulletAlerter
from discogs_alert.alert.telegram import TelegramAlerter
from discogs_alert.alert.email import EmailAlerter


@enum.unique
class AlerterType(enum.IntEnum):
PUSHBULLET = enum.auto()
TELEGRAM = enum.auto()
EMAIL = enum.auto()


def get_alerter(alerter_type: AlerterType, alerter_kwargs: Dict[str, Any]) -> Alerter:
if alerter_type == AlerterType.PUSHBULLET:
alerter_cls = PushbulletAlerter
elif alerter_type == AlerterType.TELEGRAM:
alerter_cls = TelegramAlerter
elif alerter_type == AlerterType.EMAIL:
alerter_cls = EmailAlerter
else:
raise ValueError("`alerter_type` must be a valid AlerterType value")

Expand Down
55 changes: 55 additions & 0 deletions discogs_alert/alert/email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import logging

import ssl, smtplib

from email.message import EmailMessage

from discogs_alert.alert.base import AlertDict, Alerter

logger = logging.getLogger(__name__)

class EmailAlerter(Alerter):
def __init__(
self,
email_hostname: str,
email_port: int,
email_username: str,
email_password: str,
email_from: str,
email_to: str
):
self.hostname = email_hostname
self.port = email_port
self.username = email_username
self.password = email_password
self.email_from = email_from
self.email_to = email_to

def get_all_alerts(self) -> AlertDict:
return {}

def SendAlert(self, message_title: str, message_body: str):
context = ssl.create_default_context()

with smtplib.SMTP(self.hostname, self.port) as client:
client.starttls(context = context)

try:
client.login(self.username, self.password)
except smtplib.SMTPConnectError:
logger.critical(f"Unable to reach SMTP server at {self.hostname}.")
except smtplib.SMTPAuthenticationError:
logger.critical(f"Email user/password was rejected by {self.hostname}:{self.port}.")

message = EmailMessage()
message['subject'] = message_title
message['from'] = self.email_from
message['to'] = self.email_to
message.set_content(message_body)

try:
client.send_message(message)
except smtplib.SMTPSenderRefused:
logger.critical(f"Sender email {self.email_from} refused to send message.")
except smtplib.SMTPRecipientsRefused:
logger.critical(f"Recipient email {self.email_to} refused to receive message.")