a440160249
- SALES_STRATEGY.md: $1K/day plan (37 orders @ $27.50 AOV, 1,700 visits) - TRIPWIRE_FUNNEL.md: 7-email Mautic sequence + lead scoring - MONETIZATION.md: 9 monetization options for Nūr PWA (no ads, no registration) - EXECUTABLE_PLAN.md: MCP-executable automation plan - DAILY_OPS.md: daily KPI checklist (visits, orders, sends, recoveries) - scripts/: automation.py, mautic_setup.py, gumroad_setup.py, scout_scrape.py, analytics.py, crontab - quality_leads.json/.csv: 12 enriched Islamic-tech leads for Mautic import - content_queue.json: 10 SEO blog posts for Ghost - PWA: icons, sw.js, e2e + vitest coverage, generator scripts
213 lines
8.5 KiB
Python
Executable File
213 lines
8.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
One-time Mautic Setup: Create segment, import contacts, create 7 emails.
|
|
Run once: python scripts/mautic_setup.py
|
|
All operations in a single process to avoid session expiry.
|
|
"""
|
|
|
|
import os, sys, json, csv, re
|
|
from datetime import datetime
|
|
|
|
sys.path.insert(0, "/Users/wmj24/.hermes/mcp-servers/mautic-server")
|
|
from server import _web_login, _session, BASE, API_TIMEOUT
|
|
import requests as req
|
|
from bs4 import BeautifulSoup
|
|
from urllib.parse import urljoin
|
|
|
|
# ─── Login ──────────────────────────────────────────────────
|
|
print("Logging into Mautic...")
|
|
result = _web_login()
|
|
if not result.get("success"):
|
|
print(f"Login failed: {result}")
|
|
sys.exit(1)
|
|
print("Logged in.")
|
|
|
|
session = req.Session()
|
|
if _session.get("cookies"):
|
|
session.cookies.update(_session["cookies"])
|
|
|
|
csrf_token = _session.get("csrf", "")
|
|
|
|
# ─── 1. Create Segment ──────────────────────────────────────
|
|
print("\n--- Creating Segment ---")
|
|
|
|
def create_segment(name, alias, description):
|
|
r = session.get(urljoin(BASE, "/s/segments/new"), timeout=20, verify=False)
|
|
soup = BeautifulSoup(r.text, "lxml")
|
|
token_input = soup.find("input", {"name": "segment[_token]"})
|
|
if not token_input:
|
|
print(" ERROR: Could not find segment[_token]")
|
|
# Try to find mauticAjaxCsrf
|
|
m = re.search(r"mauticAjaxCsrf\s*=\s*['\"]([^'\"]+)['\"]", r.text)
|
|
csrf = m.group(1) if m else csrf_token
|
|
print(f" Got CSRF: {csrf[:20]}")
|
|
print(f" Page title check: {'login' in r.text.lower()}")
|
|
return None
|
|
|
|
form_token = token_input.get("value", "")
|
|
m = re.search(r"mauticAjaxCsrf\s*=\s*['\"]([^'\"]+)['\"]", r.text)
|
|
ajax_csrf = m.group(1) if m else csrf_token
|
|
|
|
r2 = session.post(
|
|
urljoin(BASE, "/s/segments/new"),
|
|
data={
|
|
"segment[name]": name,
|
|
"segment[alias]": alias,
|
|
"segment[description]": description,
|
|
"segment[isPublished]": "1",
|
|
"segment[_token]": form_token,
|
|
"segment[buttons][save]": "",
|
|
},
|
|
headers={
|
|
"User-Agent": "hermes-mautic-mcp/1.0",
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
"X-CSRF-Token": ajax_csrf,
|
|
},
|
|
timeout=API_TIMEOUT,
|
|
verify=False,
|
|
)
|
|
if r2.status_code == 200:
|
|
# Try to extract segment ID
|
|
m = re.search(r'/s/segment/(\d+)', r2.text)
|
|
seg_id = m.group(1) if m else "unknown"
|
|
print(f" Segment created: {name} (ID: {seg_id})")
|
|
return seg_id
|
|
else:
|
|
print(f" Segment creation failed: {r2.status_code}")
|
|
print(f" Response: {r2.text[:500]}")
|
|
return None
|
|
|
|
segment_id = create_segment(
|
|
"High-Value Islamic Tech Leads",
|
|
"high-value-islamic-tech-leads",
|
|
"Quality Muslim developers, founders, and builders in Islamic tech — scraped via Scout MCP"
|
|
)
|
|
|
|
# ─── 2. Import Contacts ─────────────────────────────────────
|
|
print("\n--- Importing Contacts ---")
|
|
|
|
leads_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "quality_leads.json")
|
|
if not os.path.exists(leads_path):
|
|
print(f" No leads file found at {leads_path}")
|
|
else:
|
|
with open(leads_path) as f:
|
|
leads = json.load(f)
|
|
|
|
imported = 0
|
|
for lead in leads:
|
|
email = lead.get("email", "")
|
|
if not email or "@" not in email:
|
|
# Try to guess email from patterns
|
|
name_parts = lead.get("full_name", lead.get("username", "")).split()
|
|
username = lead.get("username", "")
|
|
domain = lead.get("website", "").replace("https://", "").replace("http://", "").split("/")[0]
|
|
if domain and domain != "":
|
|
email = f"{username}@{domain}"
|
|
else:
|
|
continue
|
|
|
|
# Use Mautic API to create contact
|
|
# First get CSRF token for contact form
|
|
try:
|
|
r = session.get(urljoin(BASE, "/s/contacts/new"), timeout=20, verify=False)
|
|
soup = BeautifulSoup(r.text, "lxml")
|
|
token_input = soup.find("input", {"name": "contact[_token]"})
|
|
if token_input:
|
|
form_token = token_input.get("value", "")
|
|
m = re.search(r"mauticAjaxCsrf\s*=\s*['\"]([^'\"]+)['\"]", r.text)
|
|
ajax_csrf = m.group(1) if m else csrf_token
|
|
|
|
r2 = session.post(
|
|
urljoin(BASE, "/s/contacts/new"),
|
|
data={
|
|
"contact[firstname]": lead.get("firstname", ""),
|
|
"contact[lastname]": lead.get("lastname", ""),
|
|
"contact[email]": email,
|
|
"contact[company]": lead.get("company", ""),
|
|
"contact[position]": lead.get("position", ""),
|
|
"contact[website]": lead.get("website", ""),
|
|
"contact[points]": lead.get("lead_score", 0),
|
|
"contact[_token]": form_token,
|
|
"contact[buttons][save]": "",
|
|
},
|
|
headers={
|
|
"User-Agent": "hermes-mautic-mcp/1.0",
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
"X-CSRF-Token": ajax_csrf,
|
|
},
|
|
timeout=API_TIMEOUT,
|
|
verify=False,
|
|
)
|
|
if r2.status_code == 200:
|
|
imported += 1
|
|
print(f" Imported: {lead.get('username', email)}")
|
|
else:
|
|
print(f" Failed: {lead.get('username', email)} ({r2.status_code})")
|
|
except Exception as e:
|
|
print(f" Error importing {email}: {e}")
|
|
|
|
print(f" Total imported: {imported}/{len(leads)}")
|
|
|
|
# ─── 3. Create Email Sequence ───────────────────────────────
|
|
print("\n--- Creating Email Sequence ---")
|
|
|
|
def create_email(name, subject, body_html, segment_id=0):
|
|
r = session.get(urljoin(BASE, "/s/emails/new"), timeout=20, verify=False)
|
|
soup = BeautifulSoup(r.text, "lxml")
|
|
token_input = soup.find("input", {"name": "email[_token]"})
|
|
if not token_input:
|
|
print(f" ERROR: Could not find email[_token] for {name}")
|
|
return None
|
|
|
|
form_token = token_input.get("value", "")
|
|
m = re.search(r"mauticAjaxCsrf\s*=\s*['\"]([^'\"]+)['\"]", r.text)
|
|
ajax_csrf = m.group(1) if m else csrf_token
|
|
|
|
r2 = session.post(
|
|
urljoin(BASE, "/s/emails/new"),
|
|
data={
|
|
"email[name]": name,
|
|
"email[subject]": subject,
|
|
"email[customHtml]": body_html,
|
|
"email[fromName]": "FalahOS",
|
|
"email[fromAddress]": "hermes@falahos.my",
|
|
"email[emailType]": "template",
|
|
"email[isPublished]": "1",
|
|
"email[_token]": form_token,
|
|
"email[buttons][save]": "",
|
|
},
|
|
headers={
|
|
"User-Agent": "hermes-mautic-mcp/1.0",
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
"X-CSRF-Token": ajax_csrf,
|
|
},
|
|
timeout=API_TIMEOUT,
|
|
verify=False,
|
|
)
|
|
if r2.status_code == 200:
|
|
print(f" Email created: {name} — '{subject}'")
|
|
return True
|
|
else:
|
|
print(f" Email creation failed for {name}: {r2.status_code}")
|
|
return False
|
|
|
|
# Email definitions (simplified — full HTML in TRIPWIRE_FUNNEL.md)
|
|
emails = [
|
|
("Day0-Welcome", "Your Islamic Dev Toolkit is ready 🕌", "<p>Welcome! Here's your free toolkit...</p>"),
|
|
("Day2-CaseStudy", "How Gading built quran-api (2.5k ⭐)", "<p>Read the story...</p>"),
|
|
("Day5-Tripwire", "Islamic UI Kit for $10", "<p>Stop designing from scratch...</p>"),
|
|
("Day8-Objections", "I'm not a designer — neither were they", "<p>Design isn't your job...</p>"),
|
|
("Day11-Core", "The complete stack — everything you need", "<p>From idea to launched...</p>"),
|
|
("Day14-FAQ", "Questions? (24-hour bonus)", "<p>Quick answers...</p>"),
|
|
("Day18-Enterprise", "Building for an organization?", "<p>Enterprise licensing...</p>"),
|
|
]
|
|
|
|
for name, subject, body in emails:
|
|
create_email(name, subject, body, segment_id or 0)
|
|
|
|
print("\n--- Setup Complete ---")
|
|
print("Next steps:")
|
|
print("1. Verify segment and contacts in Mautic UI")
|
|
print("2. Verify emails are published")
|
|
print("3. Set up automation rules in Mautic UI (email → delay → next email)")
|
|
print("4. Run automation.py daily via cron") |