#!/usr/bin/env python3 """Customer draft-email builder for the quote-to-proposal workflow. Creates a Gmail DRAFT (never sends) to the customer with the proposal PDF attached and the interactive proposal attached (phase 1) or linked via a live per-customer URL (phase 2, once the Cloudflare connector is live). Customer-facing rules enforced here: - zero em-dashes / en-dashes anywhere in generated copy - title "Account Relationship Manager" (carried by the signature) - customer never sees costs, margins, or lease factors - Sean's own addresses never on To/Cc (draft goes only to the customer) """ import os, re, subprocess, html as htmlmod BASE = os.path.dirname(os.path.abspath(__file__)) ACCOUNT = "d48c9e2643f643aeb3c4dacf347245b8" # sean@seanmattsson.com SIGNATURE_PATH = "/home/hatch/workspace/goals/sales-weapon-ae-copilot/hidden_files/signature.html" DASHES = re.compile(r"[\u2013\u2014]") def _clean(s): """Strip em/en dashes from generated copy (safety net).""" return DASHES.sub(",", str(s or "")) def _first_name(contact_name): return (contact_name or "").strip().split()[0] if contact_name else "there" def _money(v): return "$%s" % format(float(v), ",.2f") def load_signature(): try: return open(SIGNATURE_PATH).read() except Exception: return ("
Best regards,
Sean Mattsson
Account Relationship Manager
"
"Stratix Systems
Direct (484) 214-3895
Hi %s,
" % htmlmod.escape(first)) paras.append("Thanks for telling us about %s. %s
" % ( htmlmod.escape(company), _reason_line(raw))) timing = _timing_line(raw) if timing: paras.append("%s
" % timing) paras.append("The recommended configuration comes to %s/month on a %d-month plan, " "including the equipment, service, and supplies. Pricing is locked through %s.
" % ( total, term, htmlmod.escape(valid))) if live_url: paras.append("Your interactive proposal is ready here:
"
'%s
Open it and you can compare systems, adjust terms and options, " "and watch the monthly total update live. When you land on a configuration " "you like, hit \"Send Me This Configuration\" and it comes straight to me.
" % ( htmlmod.escape(live_url), htmlmod.escape(live_url))) else: paras.append("I have also attached an interactive version of the proposal. " "Save it and open it in any browser: you can compare systems, adjust terms " "and options, and watch the monthly total update live. When you land on a " "configuration you like, hit \"Send Me This Configuration\" and it comes " "straight to me.
") paras.append("The formal proposal is attached as a PDF as well.
") interest = _interest_line(raw) if interest: paras.append("%s
" % interest) paras.append("I will give you a call in the next day or two to walk through it. " "If you would rather pick a time, the booking link is below.
") paras.append("Best regards,
") paras.append(load_signature()) body = "\n".join(paras) assert not DASHES.search(body), "em/en dash leaked into customer draft" subject = "Your Stratix proposal is ready, %s" % first if first != "there" else \ "Your Stratix proposal is ready" return subject, body def build_gap_draft(priced): """'Quick question' draft for when pricing has gaps (needs_review).""" cust = priced.get("customer", {}) raw = priced.get("intake", {}).get("raw", {}) if isinstance(priced.get("intake"), dict) else {} first = _first_name(cust.get("contact_name")) gaps = priced.get("gaps", []) # translate internal gaps into plain customer questions questions = [] for g in gaps: gl = g.lower() if "volume" in gl: questions.append("about how many black and white and color pages you print in a typical month") elif "model" in gl or "accessory" in gl or "cost" in gl: questions.append("which specific model and options you had in mind") else: questions.append("a couple of details on your current setup") ask = "; ".join(dict.fromkeys(questions)) or "a couple of details on your current setup" body = ( "Hi %s,
" "Thanks for your request. I am putting your proposal together now and I need %s " "to price it accurately.
" "Could you reply with your best estimates? Even rough numbers are fine.
" "I will have your proposal over within a day of hearing back.
" "Best regards,
" "%s" % (htmlmod.escape(first), htmlmod.escape(ask), load_signature())) assert not DASHES.search(body), "em/en dash leaked into gap draft" return "Quick question on your Stratix request", body def create_draft(to_email, subject, html_body, attachments): """Create a Gmail draft via hatch_gws_cli. Returns the draft id or None.""" if not to_email or "@" not in to_email: return None cmd = ["hatch_gws_cli", "gmail", "--account", ACCOUNT, "+send", "--to", to_email, "--subject", subject, "--body", html_body, "--html", "--draft"] for a in attachments or []: if a and os.path.exists(a): cmd += ["-a", a] r = subprocess.run(cmd, capture_output=True, text=True, timeout=180) try: import json as _json data = _json.loads(r.stdout) return data.get("id") or data.get("draft", {}).get("id") except Exception: m = re.search(r'"id"\s*:\s*"([^"]+)"', r.stdout) return m.group(1) if m else None