#!/usr/bin/env python3
"""
campaign_helpers.py — Shared Listmonk campaign building blocks.
Branded, email-client-safe HTML helpers (header, flag bar, stat tiles, CTA
button, footer, paragraphs, lists) plus the Listmonk POST helper. Extracted
from create_campaigns.py so multiple campaign scripts (the original 7-email
drips and one-off promos like the CRTC USF campaign) can share ONE source of
truth for the look-and-feel and the API plumbing.
Importing this module has NO side effects (it does not POST anything).
"""
import sys
import requests
# ── Listmonk connection ──────────────────────────────────────────────────────
LISTMONK_URL = "https://lists.performancewest.net"
AUTH = ("api", "6X1rKPea61N4rZ1S65Hx5zvqzbCj30F6nvEe9oVGH_Y")
# ── Brand constants ──────────────────────────────────────────────────────────
URL = "https://performancewest.net/order/canada-crtc"
PHONE = "1-888-411-0383"
EMAIL = "info@performancewest.net"
CONTACT = (
f'Email {EMAIL} '
f'or call {PHONE}'
)
# ── HTML helpers ─────────────────────────────────────────────────────────────
def hdr(eyebrow, headline, sub=None):
s = f'
{sub}
' if sub else ''
return (
''
''
''
' | '
'Telecom Services | '
'
| '
''
'| '
f' {eyebrow} '
f'{headline}{s}'
' |
|
'
)
def flagbar(left, right):
return (
''
''
' '
f'{left} | '
'→ | '
' '
f'{right} | '
'
|
'
)
def stats(*items):
w = 100 // len(items)
cells = "".join(
f' | '
for v, l in items
)
return f''
def cta(text, url):
return (
''
)
def carriers_block():
data = [
("Twilio","NASDAQ: TWLO"),("Bandwidth","NASDAQ: BAND"),("Telnyx","Washington DC"),("RingCentral","NYSE: RNG"),
("Vonage","Holmdel NJ"),("8x8","NASDAQ: EGHT"),("Zoom Phone","NASDAQ: ZM"),("Dialpad","San Ramon CA"),
("Google Voice","Alphabet / GOOGL"),("Ooma","NYSE: OOMA"),("Onvoy / Sinch","OMX: SINCH"),("Sangoma","NASDAQ: SANG"),
]
rows = ""
for i in range(0, len(data), 4):
cells = "".join(
f' | '
for n, t in data[i:i+4]
)
rows += f"{cells}
"
return (
''
'| '
' '
'Join other US voice carriers registered to do business in Canada '
' '
f''
' |
'
)
def ftr(note=""):
note_html = f'{note}
' if note else ''
return (
''
)
def bq(t):
return (
''
)
def P(t):
return f'{t}
'
def PS(t):
return f'{t}
'
def H2(t):
return f'{t}
'
def UL(*items):
return (
''
+ "".join(f'- {i}
' for i in items)
+ '
'
)
def assemble(hdr_html, fb_html, body_html, ftr_html):
inner = (
hdr_html + fb_html
+ f''
+ ftr_html
)
return (
''
''
)
def create_campaign(name, subject, lists, body_html, altbody=None, status="draft"):
"""POST a campaign to Listmonk.
altbody: optional plaintext alternative. Strongly recommended for
deliverability — an HTML-only campaign is a spam signal. Pass the output of
scripts._email_plaintext.html_to_text(body_html) (or a hand-written
plaintext). When omitted, Listmonk generates its own plaintext at send time.
"""
s = requests.Session()
s.auth = AUTH
payload = {
"name": name, "subject": subject, "lists": lists,
"type": "regular", "content_type": "html",
"body": body_html, "status": status,
}
if altbody is not None:
payload["altbody"] = altbody
r = s.post(f"{LISTMONK_URL}/api/campaigns", json=payload, timeout=30)
if not r.ok:
print(f" ERROR {r.status_code}: {r.text[:200]}", file=sys.stderr)
return None
cid = r.json().get('data', {}).get('id', '?')
print(f" [{cid}] {name}")
return cid