← back to Qwen38 Viewer
qwen mail bridge: allowlisted email -> uncensored qwen -> inline reply (armed, launchd 2min)
b4efa8a5d2380e11cac906b4cc17175100a2cf5d · 2026-08-19 10:49:44 -0700 · steve
Files touched
Diff
commit b4efa8a5d2380e11cac906b4cc17175100a2cf5d
Author: steve <steve@designerwallcoverings.com>
Date: Wed Aug 19 10:49:44 2026 -0700
qwen mail bridge: allowlisted email -> uncensored qwen -> inline reply (armed, launchd 2min)
---
mail-bridge.py | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
diff --git a/mail-bridge.py b/mail-bridge.py
new file mode 100644
index 0000000..45a7b67
--- /dev/null
+++ b/mail-bridge.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+"""
+qwen mail bridge — lets ALLOWLISTED senders email qwen@agentabrams.com and get
+an inline reply from the local uncensored qwen3.8-27b-heretic (Ollama).
+
+ARMED per Steve's explicit authorization (2026-08-19).
+
+Hard safety controls (this fronts an UNCENSORED model + sends outbound mail):
+ * ALLOWLIST-ONLY: replies exclusively to the 4 addresses below. Any other
+ sender -> marked read, logged, NEVER answered (not an open relay).
+ * LOOP GUARD: never replies to itself, mailer-daemon, no-reply, or any message
+ carrying Auto-Submitted / Precedence:bulk / List-* headers.
+ * RATE CAP: at most MAX_PER_RUN replies per poll, MAX_PER_SENDER_DAY per sender.
+ * Body + token caps.
+Runs once per invocation (launchd StartInterval). Idempotent via IMAP \\Seen.
+"""
+import os, sys, json, ssl, time, email, imaplib, smtplib, re, pathlib, datetime
+from email.message import EmailMessage
+from email.utils import parseaddr, formataddr, make_msgid
+from urllib import request as urlreq
+
+MAILBOX = "qwen@agentabrams.com"
+IMAP_HOST = "imap.purelymail.com"
+SMTP_HOST = "smtp.purelymail.com"
+SMTP_PORT = 587
+OLLAMA = os.environ.get("OLLAMA_URL", "http://127.0.0.1:11434")
+MODEL = os.environ.get("MODEL", "qwen3.8-27b-heretic")
+
+ALLOWLIST = {a.lower() for a in [
+ "info@designerwallcoverings.com",
+ "steve@designerwallcoverings.com",
+ "browntwn@gmail.com",
+ "theagentabrams@gmail.com",
+]}
+MAX_PER_RUN = 6
+MAX_PER_SENDER_DAY = 40
+MAX_BODY_CHARS = 6000
+NUM_PREDICT = 1200
+
+STATE = pathlib.Path.home() / ".qwen-mail-bridge-state.json"
+LOG = pathlib.Path.home() / ".qwen-mail-bridge.log"
+
+def log(m):
+ line = f"{datetime.datetime.now().isoformat(timespec='seconds')} {m}"
+ print(line)
+ try: LOG.open("a").write(line + "\n")
+ except OSError: pass
+
+def get_pw():
+ # read the mailbox password from the master secrets .env (never logged)
+ p = os.environ.get("QWEN_MAIL_PASS")
+ if p: return p
+ envf = pathlib.Path.home() / "Projects/secrets-manager/.env"
+ for ln in envf.read_text().splitlines():
+ if ln.startswith("QWEN_MAIL_PASS="):
+ return ln.split("=", 1)[1].strip().strip('"').strip("'")
+ raise SystemExit("QWEN_MAIL_PASS not found")
+
+def load_state():
+ try: return json.loads(STATE.read_text())
+ except Exception: return {}
+def save_state(s):
+ try: STATE.write_text(json.dumps(s))
+ except OSError: pass
+
+def is_bulk(msg):
+ if (msg.get("Auto-Submitted") or "").lower() not in ("", "no"): return True
+ if (msg.get("Precedence") or "").lower() in ("bulk", "list", "junk"): return True
+ if msg.get("List-Id") or msg.get("List-Unsubscribe"): return True
+ return False
+
+def plain_body(msg):
+ if msg.is_multipart():
+ for part in msg.walk():
+ if part.get_content_type() == "text/plain" and "attachment" not in str(part.get("Content-Disposition")):
+ try: return part.get_content()
+ except Exception: return part.get_payload(decode=True).decode("utf-8", "ignore")
+ return ""
+ try: return msg.get_content()
+ except Exception: return msg.get_payload(decode=True).decode("utf-8", "ignore")
+
+def strip_quotes(body):
+ out = []
+ for ln in body.splitlines():
+ if ln.strip().startswith(">"): continue
+ if re.match(r"^On .* wrote:$", ln.strip()): break
+ if ln.strip() in ("--", "-- "): break
+ out.append(ln)
+ return "\n".join(out).strip()
+
+def ask_qwen(prompt):
+ data = json.dumps({"model": MODEL, "messages": [{"role": "user", "content": prompt}],
+ "stream": False, "keep_alive": "30m",
+ "options": {"temperature": 0.8, "num_predict": NUM_PREDICT}}).encode()
+ req = urlreq.Request(f"{OLLAMA}/api/chat", data=data, headers={"Content-Type": "application/json"})
+ with urlreq.urlopen(req, timeout=300) as r:
+ j = json.loads(r.read().decode())
+ return (j.get("message", {}) or {}).get("content", "").strip() or "(no response)"
+
+def main():
+ pw = get_pw()
+ state = load_state()
+ today = datetime.date.today().isoformat()
+ counts = state.get(today, {})
+ replied = 0
+
+ ctx = ssl.create_default_context()
+ M = imaplib.IMAP4_SSL(IMAP_HOST, 993, ssl_context=ctx)
+ M.login(MAILBOX, pw)
+ M.select("INBOX")
+ typ, data = M.search(None, "UNSEEN")
+ ids = data[0].split() if data and data[0] else []
+ log(f"poll: {len(ids)} unseen")
+
+ for num in ids:
+ if replied >= MAX_PER_RUN:
+ log("rate: MAX_PER_RUN hit, stopping"); break
+ typ, md = M.fetch(num, "(RFC822)")
+ msg = email.message_from_bytes(md[0][1])
+ frm = parseaddr(msg.get("From", ""))[1].lower()
+ subj = msg.get("Subject", "(no subject)")
+
+ # --- safety gates ---
+ if frm == MAILBOX or "mailer-daemon" in frm or frm.startswith("no-reply") or frm.startswith("noreply"):
+ log(f"skip loop-guard from={frm}"); M.store(num, "+FLAGS", "\\Seen"); continue
+ if is_bulk(msg):
+ log(f"skip bulk/auto from={frm}"); M.store(num, "+FLAGS", "\\Seen"); continue
+ if frm not in ALLOWLIST:
+ log(f"IGNORE non-allowlisted from={frm} subj={subj!r}"); M.store(num, "+FLAGS", "\\Seen"); continue
+ if counts.get(frm, 0) >= MAX_PER_SENDER_DAY:
+ log(f"skip per-sender cap from={frm}"); M.store(num, "+FLAGS", "\\Seen"); continue
+
+ body = strip_quotes(plain_body(msg))[:MAX_BODY_CHARS].strip()
+ if not body:
+ log(f"skip empty body from={frm}"); M.store(num, "+FLAGS", "\\Seen"); continue
+
+ log(f"ANSWER from={frm} subj={subj!r} chars={len(body)}")
+ try:
+ answer = ask_qwen(body)
+ except Exception as e:
+ log(f"qwen error: {e}"); continue
+
+ reply = EmailMessage()
+ reply["From"] = formataddr(("Qwen3.8 Heretic", MAILBOX))
+ reply["To"] = frm
+ reply["Subject"] = subj if subj.lower().startswith("re:") else f"Re: {subj}"
+ mid = msg.get("Message-ID")
+ if mid:
+ reply["In-Reply-To"] = mid
+ reply["References"] = ((msg.get("References", "") + " " + mid)).strip()
+ reply["Message-ID"] = make_msgid(domain="agentabrams.com")
+ reply["Auto-Submitted"] = "auto-replied" # be a good citizen; prevents remote loops
+ reply.set_content(answer + "\n\n— qwen3.8-27b-heretic (local, uncensored) via agentabrams.com")
+
+ s = smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=60)
+ s.starttls(context=ctx); s.login(MAILBOX, pw); s.send_message(reply); s.quit()
+
+ M.store(num, "+FLAGS", "\\Seen")
+ counts[frm] = counts.get(frm, 0) + 1
+ replied += 1
+ log(f"replied to {frm} ({len(answer)} chars)")
+
+ M.logout()
+ state = {today: counts} # keep only today's counts
+ save_state(state)
+ log(f"done: {replied} replied")
+
+if __name__ == "__main__":
+ main()
← 18c37a5 add case-insensitive shared access code Dust2026
·
back to Qwen38 Viewer
·
viewer: motion-graphics thinking feedback (pulsing orb, elap 78d2199 →