def send(subject, body=None, to=None, threaded=None, **kwargs):
"""
Send an email with the specified subject and body, using defaults from env if available.
Parameters:
- subject: the email subject (string)
- body: the plain-text body (string). Must be provided.
- to: recipient address (string). Defaults to ADMIN_EMAIL from the environment.
- threaded: if True, send the email asynchronously; if False, block and send; if None, auto-detect.
- **kwargs: reserved for future use.
Returns:
str ("Email sent successfully to ...") or error message, unless threaded is True (returns immediately).
"""
def _send_email():
_to = to or os.environ.get("ADMIN_EMAIL")
if not body:
gw.debug("No email body provided.")
return "No email body provided."
gw.debug(f"Preparing to send email to {_to}: {subject}")
# Load SMTP configuration from environment
sender_email = os.environ.get("MAIL_SENDER")
sender_password = os.environ.get("MAIL_PASSWORD")
smtp_server = os.environ.get("SMTP_SERVER")
smtp_port = os.environ.get("SMTP_PORT")
gw.debug(f"MAIL_SENDER: {sender_email}")
gw.debug(f"SMTP_SERVER: {smtp_server}")
gw.debug(f"SMTP_PORT: {smtp_port}")
gw.debug("Environment variables loaded.")
# If any required piece is missing, bail out
if not all([sender_email, sender_password, smtp_server, smtp_port]):
gw.debug("Missing one or more required email configuration details.")
return "Missing email configuration details."
# Construct the MIMEText message
msg = MIMEText(body)
msg['Subject'] = gw.resolve(subject)
msg['From'] = sender_email
msg['To'] = _to
gw.debug("Email MIME message constructed.")
gw.debug(f"Email headers: From={msg['From']}, To={msg['To']}, Subject={msg['Subject']}")
try:
gw.debug(f"Connecting to SMTP server: {smtp_server}:{smtp_port}")
server = smtplib.SMTP(smtp_server, int(smtp_port))
gw.debug("SMTP connection established. Starting TLS...")
server.starttls()
gw.debug("TLS started. Logging in...")
server.login(sender_email, sender_password)
gw.debug("Login successful. Sending message...")
server.send_message(msg)
server.quit()
gw.debug("Email sent and SMTP session closed.")
return "Email sent successfully to " + _to
except Exception as e:
gw.debug(f"Exception occurred while sending email: {e}")
return f"Error sending email: {e}"
# Auto-detect async mode if not specified
if threaded is None:
try:
asyncio.get_running_loop()
threaded = True
except RuntimeError:
threaded = False
if threaded:
try:
loop = asyncio.get_running_loop()
async def async_task():
result = await asyncio.to_thread(_send_email)
if result and "Error" in result:
gw.error(result)
asyncio.create_task(async_task())
except RuntimeError:
def thread_task():
result = _send_email()
if result and "Error" in result:
gw.error(result)
threading.Thread(target=thread_task, daemon=True).start()
return "Email send scheduled (async mode)"
else:
return _send_email()