import base64, json, os, random, re, smtplib, ssl, string, sys from email import encoders from email.header import Header from email.mime.base import MIMEBase from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText UTF_8 = "utf-8" def _cfg(): return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}")) def _randomword(n): return "".join(random.choice(string.ascii_lowercase) for _ in range(n)) def _header(s): if not s: return None return Header(" ".join(str(s).splitlines())) def connect(cfg): host = str(cfg.get("host") or "") port = int(cfg.get("port") or 25) fqdn = (str(cfg.get("fqdn") or "").strip()) or None tls = str(cfg.get("tls") or "None") ctx = None if cfg.get("insecure"): ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE if tls == "SSL/TLS": server = smtplib.SMTP_SSL(host, port, local_hostname=fqdn, context=ctx, timeout=60) else: server = smtplib.SMTP(host, port, local_hostname=fqdn, timeout=60) server.ehlo() if tls == "STARTTLS" or str(tls).lower() == "true": server.starttls(context=ctx) server.ehlo() user = str(cfg.get("username") or "") if user: server.login(user, str(cfg.get("password") or "")) return server def handle_html(html_body): """Extract inline data-URI images from the HTML and return (clean_body, image attachments).""" attachments = [] clean = "" last = 0 for i, m in enumerate(re.finditer(r'") msg.attach(part) if has_att: part = MIMEBase("application", "octet-stream") part.set_payload(base64.b64decode(att_b64)) encoders.encode_base64(part) part.add_header("Content-Disposition", "attachment", filename=str(att_name)) msg.attach(part) sender = str(inputs.get("sender") or cfg.get("from_address") or "") msg["Subject"] = _header(subject) msg["From"] = _header(sender) if inputs.get("reply_to"): msg["Reply-To"] = _header(inputs["reply_to"]) if to: msg["To"] = _header(",".join(to)) if cc: msg["CC"] = _header(",".join(cc)) for name, value in split_fields(inputs.get("additional_headers")): msg[name] = _header(value) return sender, to + cc + bcc, msg.as_string() def main(): cfg = _cfg() inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}")) sender, recipients, raw = build_message(cfg, inputs) if not sender: raise Exception("no sender address (set from_address in the config or pass sender)") server = connect(cfg) try: server.sendmail(sender, recipients, raw) finally: try: server.quit() except Exception: pass print(json.dumps({"ok": True, "sent_to": recipients})) try: main() except smtplib.SMTPException as e: print(json.dumps({"error": "SMTP error: " + str(e)})) sys.exit(1) except Exception as e: print(json.dumps({"error": str(e)})) sys.exit(1)