# SecondBrain — (c) 2026 Atlas Corporation. All Rights Reserved. Proprietary; see LICENSE. """Connector: Email over IMAP. Use an app password (not your main password). Message bodies pass through the engine scrub, so any secrets inside emails are redacted.""" import imaplib, email from email.header import decode_header, make_header from .base import Connector, Field, register, feed @register class ImapEmailConnector(Connector): id = "email" name = "Email (IMAP)" blurb = "Pull recent messages from any IMAP mailbox (Gmail, Outlook, self-hosted…)." fields = [ Field("host", "IMAP host", default="imap.gmail.com"), Field("port", "IMAP port", default="993"), Field("user", "Username / email address"), Field("password", "App password", secret=True), Field("folder", "Folder", default="INBOX"), Field("limit", "Max recent messages", default="200"), ] def _open(self, cfg): M = imaplib.IMAP4_SSL(cfg["host"], int(cfg.get("port") or 993)) M.login(cfg["user"], cfg["password"]) return M def test(self, cfg): M = self._open(cfg); M.select(cfg.get("folder", "INBOX"), readonly=True); M.logout() return "login ok" def sync(self, cfg): M = self._open(cfg) M.select(cfg.get("folder", "INBOX"), readonly=True) _, data = M.search(None, "ALL") ids = data[0].split()[-int(cfg.get("limit") or 200):] items = [] for i in ids: _, d = M.fetch(i, "(RFC822)") if not d or not isinstance(d[0], tuple): continue msg = email.message_from_bytes(d[0][1]) def H(h): try: return str(make_header(decode_header(msg.get(h, "") or ""))) except Exception: return msg.get(h, "") or "" body = "" if msg.is_multipart(): for p in msg.walk(): if p.get_content_type() == "text/plain": try: body = (p.get_payload(decode=True) or b"").decode("utf-8", "ignore"); break except Exception: pass else: try: body = (msg.get_payload(decode=True) or b"").decode("utf-8", "ignore") except Exception: body = str(msg.get_payload()) items.append({"text": f"[email from {H('From')}] {H('Subject')}\n{body[:1500]}", "project": "email", "ref": H('From')[:80], "ts": H('Date'), "role": "email"}) M.logout() return feed(items, "events")