Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
3.4 KiB
Python
74 lines
3.4 KiB
Python
"""Insert chat invitation + discount blocks into Listmonk campaigns."""
|
|
import json
|
|
import subprocess
|
|
|
|
API_USER = "api"
|
|
API_PASS = "6X1rKPea61N4rZ1S65Hx5zvqzbCj30F6nvEe9oVGH_Y"
|
|
LISTMONK = "http://localhost:9100"
|
|
|
|
DISCOUNT = '<tr><td style="padding:20px 40px 10px 40px;"><table cellpadding="0" cellspacing="0" border="0" width="100%" style="background:#eff6ff;border-radius:8px;border:1px solid #bfdbfe;"><tr><td style="padding:16px 20px;font-family:Arial,sans-serif;font-size:14px;color:#1e3a5f;line-height:1.5;"><strong>Split it into 4 payments:</strong> Pay ~$975/month with Klarna Pay in 4. Start your Canadian carrier setup today — pay over time.</td></tr></table></td></tr>'
|
|
|
|
CHAT = '<tr><td style="padding:10px 40px 20px 40px;"><table cellpadding="0" cellspacing="0" border="0" width="100%" style="background:#f0f4f8;border-radius:8px;border:1px solid #e2e8f0;"><tr><td style="padding:16px 20px;font-family:Arial,sans-serif;font-size:14px;color:#475569;line-height:1.5;"><strong style="color:#1e3a5f;">Questions? We\'re online.</strong><br>Chat with us live on <a href="https://performancewest.net/services/telecom/canada-crtc" style="color:#e63f2a;text-decoration:none;">our website</a> (look for the chat icon in the bottom-right), or call <strong>1-888-411-0383</strong>.</td></tr></table></td></tr>'
|
|
|
|
FOOTER_MARKER = 'style="display:block;margin:0 auto 10px;width:70px'
|
|
|
|
def api_get(path):
|
|
r = subprocess.run(["curl", "-s", "-u", f"{API_USER}:{API_PASS}", f"{LISTMONK}{path}"],
|
|
capture_output=True, text=True, timeout=10)
|
|
return json.loads(r.stdout)
|
|
|
|
def api_put(path, data):
|
|
r = subprocess.run(["curl", "-s", "-X", "PUT", "-u", f"{API_USER}:{API_PASS}",
|
|
"-H", "Content-Type: application/json", "-d", json.dumps(data),
|
|
f"{LISTMONK}{path}"], capture_output=True, text=True, timeout=10)
|
|
return json.loads(r.stdout) if r.stdout else {}
|
|
|
|
# Campaigns that need discount + chat (final close emails)
|
|
DISCOUNT_CAMPAIGNS = [12, 18]
|
|
# Campaigns that need only chat
|
|
CHAT_CAMPAIGNS = [9, 10, 11, 15, 16, 17, 22, 23]
|
|
|
|
for cid in DISCOUNT_CAMPAIGNS + CHAT_CAMPAIGNS:
|
|
d = api_get(f"/api/campaigns/{cid}")
|
|
body = d["data"]["body"]
|
|
name = d["data"]["name"]
|
|
add_discount = cid in DISCOUNT_CAMPAIGNS
|
|
|
|
if "We're online" in body or "We are online" in body:
|
|
print(f" SKIP {cid:3d} | {name[:55]} | already has chat block")
|
|
continue
|
|
|
|
if FOOTER_MARKER not in body:
|
|
print(f" SKIP {cid:3d} | {name[:55]} | no footer marker")
|
|
continue
|
|
|
|
idx = body.index(FOOTER_MARKER)
|
|
tr_start = body[:idx].rfind("<tr><td")
|
|
if tr_start < 0:
|
|
print(f" SKIP {cid:3d} | {name[:55]} | can't find insertion point")
|
|
continue
|
|
|
|
insert = ""
|
|
if add_discount:
|
|
insert += DISCOUNT
|
|
insert += CHAT
|
|
|
|
body = body[:tr_start] + insert + body[tr_start:]
|
|
|
|
# Listmonk requires lists + other fields in PUT
|
|
lists = [l["id"] for l in d["data"].get("lists", [])]
|
|
result = api_put(f"/api/campaigns/{cid}", {
|
|
"name": d["data"]["name"],
|
|
"subject": d["data"]["subject"],
|
|
"body": body,
|
|
"lists": lists,
|
|
"content_type": d["data"].get("content_type", "richtext"),
|
|
"type": d["data"].get("type", "regular"),
|
|
})
|
|
if "data" in result:
|
|
tag = "discount+chat" if add_discount else "chat"
|
|
print(f" OK {cid:3d} | {name[:55]} | +{tag}")
|
|
else:
|
|
print(f" FAIL {cid:3d} | {name[:55]}")
|
|
|
|
print("\nDone")
|