"""Update Listmonk campaign CTAs with graduated calls-to-action and UTM tracking. Instead of every email pushing to the order form, we use a graduated approach: - Early emails: educational links to service page - Middle emails: invite to live chat (Tawk.to) - Late emails: discount code + order form Also adds UTM parameters for tracking. """ import json import re import subprocess import sys LISTMONK = "http://localhost:9100" API_USER = "api" API_PASS = "6X1rKPea61N4rZ1S65Hx5zvqzbCj30F6nvEe9oVGH_Y" SERVICE_PAGE = "https://performancewest.net/services/telecom/canada-crtc" ORDER_PAGE = "https://performancewest.net/order/canada-crtc" CONTACT_PAGE = "https://performancewest.net/contact" PHONE = "1-888-411-0383" # Graduated CTA strategy per campaign # Format: (campaign_id, new_cta_text, new_cta_url, chat_line) # chat_line: optional line added above footer inviting to live chat CAMPAIGN_CTAS = { # === List 3: General Carriers (scheduled: 15-18) === # CRTC 4/7 — M&A / exit angle 15: { "cta_text": "See how the Canadian structure works \u2192", "cta_url": SERVICE_PAGE, "utm": "crtc-4-mna", "chat_line": True, }, # CRTC 5/7 — Objection handling 16: { "cta_text": "Have questions? Let\u2019s chat \u2192", "cta_url": SERVICE_PAGE, "utm": "crtc-5-objections", "chat_line": True, }, # CRTC 6/7 — Process walkthrough 17: { "cta_text": "See the full setup process \u2192", "cta_url": SERVICE_PAGE, "utm": "crtc-6-process", "chat_line": True, }, # CRTC 7/7 — Final close 18: { "cta_text": "Start your Canadian carrier setup \u2192", "cta_url": ORDER_PAGE, "utm": "crtc-7-close", "chat_line": True, "discount_line": True, }, # === List 6: ISP / Broadband (scheduled: 9-12) === # ISP 4/7 — Canadian revenue opportunity 9: { "cta_text": "See how Canadian registration works \u2192", "cta_url": SERVICE_PAGE, "utm": "isp-4-revenue", "chat_line": True, }, # ISP 5/7 — M&A / exit angle 10: { "cta_text": "See the Canadian carrier structure \u2192", "cta_url": SERVICE_PAGE, "utm": "isp-5-mna", "chat_line": True, }, # ISP 6/7 — Process walkthrough 11: { "cta_text": "See what\u2019s involved \u2192", "cta_url": SERVICE_PAGE, "utm": "isp-6-process", "chat_line": True, }, # ISP 7/7 — FCC enforcement / final close 12: { "cta_text": "Start your Canadian carrier setup \u2192", "cta_url": ORDER_PAGE, "utm": "isp-7-close", "chat_line": True, "discount_line": True, }, # === List 4: Outside Counsel (scheduled: 22-23) === # COUNSEL 4/5 — Setup and compliance overview 22: { "cta_text": "Review the full setup process \u2192", "cta_url": SERVICE_PAGE, "utm": "counsel-4-overview", "chat_line": True, }, # COUNSEL 5/5 — Referral arrangement 23: { "cta_text": "Discuss a referral arrangement \u2192", "cta_url": CONTACT_PAGE, "utm": "counsel-5-referral", "chat_line": True, }, } # Chat invitation block (inserted before footer) CHAT_BLOCK = f"""
Questions? We're online.
Chat with us live on our website (look for the chat icon in the bottom-right corner), or call {PHONE}.
""" DISCOUNT_BLOCK = """
Split it into 4 payments: Pay ~$975/month with Klarna Pay in 4. Start your Canadian carrier setup today — pay over time.
""" def curl_get(path): cmd = ["curl", "-s", "-u", f"{API_USER}:{API_PASS}", f"{LISTMONK}{path}"] r = subprocess.run(cmd, capture_output=True, text=True, timeout=10) return json.loads(r.stdout) def curl_put(path, data): cmd = ["curl", "-s", "-X", "PUT", "-u", f"{API_USER}:{API_PASS}", "-H", "Content-Type: application/json", "-d", json.dumps(data), f"{LISTMONK}{path}"] r = subprocess.run(cmd, capture_output=True, text=True, timeout=10) return json.loads(r.stdout) if r.stdout else {"error": r.stderr} def update_campaign(campaign_id, config): """Update a campaign's CTA button URL/text and add chat block.""" # Get current campaign result = curl_get(f"/api/campaigns/{campaign_id}") campaign = result.get("data", {}) if not campaign: print(f" Campaign {campaign_id} not found") return False name = campaign["name"] body = campaign.get("body", "") status = campaign.get("status", "?") if status not in ("draft", "scheduled"): print(f" SKIP {campaign_id} ({name}) — status={status}, already sent") return False # Build UTM URL base_url = config["cta_url"] utm = config["utm"] separator = "&" if "?" in base_url else "?" new_url = f"{base_url}{separator}utm_source=listmonk&utm_medium=email&utm_campaign={utm}" # Replace CTA button URL old_url = "https://performancewest.net/order/canada-crtc" body = body.replace(old_url, new_url) # Replace CTA button text new_text = config["cta_text"] # Match common CTA patterns for pattern in [ r"(>)\s*Start your Canadian carrier setup[^<]*()", r"(>)\s*See what's included[^<]*()", r"(>)\s*See the full setup[^<]*()", r"(>)\s*See the full structure[^<]*()", r"(>)\s*See the Canadian carrier structure[^<]*()", r"(>)\s*Get started[^<]*()", ]: body = re.sub(pattern, f"\\1{new_text}\\2", body, flags=re.IGNORECASE) # Add chat block before footer (find the footer section) if config.get("chat_line"): # Insert before the footer divider or the unsubscribe section footer_markers = [ "", '