"""Send email to admins to about errors or other notworthy things.""" import pprint import smtplib import sys import traceback from email.mime.text import MIMEText from email.utils import formatdate, make_msgid import flask import requests from flask import current_app, g, has_request_context, request from . import wikidata_api def send_mail( subject: str, body: str, config: flask.config.Config | None = None ) -> None: """Send an email to admins, catch and ignore exceptions.""" try: send_mail_main(subject, body, config=config) except smtplib.SMTPDataError: pass # ignore email errors def send_mail_main( subject: str, body: str, config: flask.config.Config | None = None ) -> None: """Send an email to admins.""" if config is None: config = current_app.config mail_to = config["ADMIN_EMAIL"] mail_from = config["MAIL_FROM"] msg = MIMEText(body, "plain", "UTF-8") msg["Subject"] = subject msg["To"] = mail_to msg["From"] = mail_from msg["Date"] = formatdate() msg["Message-ID"] = make_msgid() extra_mail_headers: list[tuple[str, str]] = config.get("MAIL_HEADERS", []) for key, value in extra_mail_headers: assert key not in msg msg[key] = value s = smtplib.SMTP(config["SMTP_HOST"]) s.sendmail(mail_from, [mail_to], msg.as_string()) s.quit() def get_username() -> str: """Get the username for the current user.""" user: str if hasattr(g, "user"): if g.user.is_authenticated: user = g.user.username else: user = "not authenticated" else: user = "no user" return user def error_mail( subject: str, data: str, r: requests.Response, via_web: bool = True ) -> None: """Error mail.""" body = f""" remote URL: {r.url} status code: {r.status_code} request data: {data} status code: {r.status_code} content-type: {r.headers["content-type"]} reply: {r.text} """ if has_request_context(): body = f"site URL: {request.url}\nuser: {get_username()}\n" + body send_mail(subject, body) def open_changeset_error(session_id: int, changeset: str, r: requests.Response) -> None: """Send error mail when failing to open a changeset.""" username = g.user.username body = f""" user: {username} page: {r.url} message user: https://www.openstreetmap.org/message/new/{username} sent: {changeset} reply: {r.text} """ send_mail("error creating changeset", body) def send_traceback(info, prefix="osm-wikidata"): exception_name = sys.exc_info()[0].__name__ subject = f"{prefix} error: {exception_name}" body = f"user: {get_username()}\n" + info + "\n" + traceback.format_exc() send_mail(subject, body) def datavalue_missing(field: str, entity: wikidata_api.EntityType) -> None: """Send an email for a missing datavalue.""" qid = entity["title"] body = f"https://www.wikidata.org/wiki/{qid}\n\n{pprint.pformat(entity)}" subject = f"{qid}: datavalue missing in {field}" send_mail(subject, body)