Initial resurrection kit — 81 scripts, 62 references, configs, systemd units, crons, Docker compose files, Caddy config, master README

This commit is contained in:
root
2026-07-15 17:45:36 -04:00
commit ae056eaf83
197 changed files with 26127 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""Check shonuff@germainebrown.com inbox, find new replies, extract content.
Outputs JSON for the cron agent to summarize.
Silent if nothing new.
"""
import imaplib, email, json, os, hashlib
from email.utils import parsedate_to_datetime
from datetime import datetime, timezone
HOST = "mail.germainebrown.com"
PORT = 993
USER = "shonuff@germainebrown.com"
PW = "Catches.bullets1985"
SEEN_FILE = "/root/.hermes/scripts/.shonuff-processed.json"
TRUSTED = ["g@germainebrown.com"]
def load_seen():
if os.path.exists(SEEN_FILE):
return set(json.load(open(SEEN_FILE)))
return set()
def save_seen(seen):
json.dump(list(seen), open(SEEN_FILE, "w"))
def safe_str(val):
if val is None: return ""
if isinstance(val, str): return val
try: return str(val)
except: return ""
def get_text_body(msg):
"""Extract plain text from email, favoring text/plain."""
body = ""
if msg.is_multipart():
for part in msg.walk():
ct = part.get_content_type()
if ct == "text/plain":
payload = part.get_payload(decode=True)
if payload:
body += payload.decode("utf-8", errors="replace")
else:
payload = msg.get_payload(decode=True)
if payload:
body = payload.decode("utf-8", errors="replace")
# Strip excessive whitespace
import re
body = re.sub(r'\s+', ' ', body).strip()
return body[:2000] # limit to 2000 chars
def main():
seen = load_seen()
new_messages = []
try:
conn = imaplib.IMAP4_SSL(HOST, PORT)
conn.login(USER, PW)
conn.select("INBOX")
status, data = conn.search(None, "UNSEEN")
if status != "OK" or not data[0]:
conn.logout()
save_seen(seen)
return # silent
for num in data[0].split():
status, msg_data = conn.fetch(num, "(RFC822)")
if status != "OK":
continue
raw = email.message_from_bytes(msg_data[0][1])
sender = safe_str(raw.get("From", "")).strip()
subj = safe_str(raw.get("Subject", "(no subject)"))
msg_id = safe_str(raw.get("Message-ID", str(num)))
date = safe_str(raw.get("Date", ""))
# Skip Germaine
if any(t.lower() in sender.lower() for t in TRUSTED):
continue
# Skip self (shonuff's own Sent copies or BCC'd messages)
if "shonuff@germainebrown.com" in sender.lower():
continue
# Skip already processed
h = hashlib.md5(msg_id.encode()).hexdigest()
if h in seen:
continue
seen.add(h)
body = get_text_body(raw)
new_messages.append({
"from": sender,
"subject": subj,
"date": date,
"body_preview": body[:500],
"uid": num.decode(),
})
# Mark as read so we don't re-process
conn.store(num, "+FLAGS", "\\Seen")
conn.logout()
save_seen(seen)
except Exception as e:
print(json.dumps({"error": str(e)}))
return
if not new_messages:
return # silent
# Output JSON for the cron agent
print(json.dumps({"messages": new_messages}, indent=2))
if __name__ == "__main__":
main()