diff --git a/compendium_v2/email/__init__.py b/compendium_v2/email/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..573a6f0368b5fb4aa2b978417fd5e8d67e02a183 --- /dev/null +++ b/compendium_v2/email/__init__.py @@ -0,0 +1,60 @@ +import smtplib +import threading +import logging +from typing import Sequence, Union +from sqlalchemy import select +from flask import current_app +from compendium_v2 import db +from compendium_v2.db.auth_model import User, ROLES + +logger = logging.getLogger(__name__) + + +def _send_mail(smtp_server, port, sender_email, recipients, message): + + try: + with smtplib.SMTP(smtp_server, port) as server: + server.sendmail(from_addr=sender_email, to_addrs=recipients, msg=message) + logger.debug('Successfully sent email') + except Exception: + logger.exception('Unable to send email:') + + +def send_mail( + contents: str, + subject: str = 'New user signed up for Compendium', + recipients: Union[str, Sequence[str]] = '' +): + if not current_app.config['MAIL_ENABLE']: + logger.warning('No mail configuration, cannot send email.') + return + + if not isinstance(contents, str): + raise ValueError('Contents must be a string.') + + if not isinstance(subject, str): + raise ValueError('Subject must be a string.') + + if not isinstance(recipients, (str, Sequence)): + raise ValueError('Recipients must be a string or a sequence of strings.') + + excluded_admins = set(email.lower() for email in current_app.config['MAIL_EXCLUDED_ADMINS']) + + admins = db.session.scalars(select(User).where(User.roles == ROLES.admin)).unique().all() + + admin_emails = [admin.email for admin in admins if admin.email.lower() not in excluded_admins] + + if not recipients: + recipients = admin_emails + + subject = subject.replace('\n', ' ') + message = f"""Subject: {subject}\n\n{contents}""" + + smtp_server = current_app.config['MAIL_SERVER'] + port = current_app.config['MAIL_PORT'] + sender_email = current_app.config['MAIL_SENDER_EMAIL'] + + # spin off a thread since this can take some time.. + logger.debug('Sending email') + thread = threading.Thread(target=_send_mail, args=(smtp_server, port, sender_email, recipients, message)) + thread.start()