Add DOT check CTA to trucking deficiency emails
This commit is contained in:
parent
327c4c9790
commit
8400e27d12
2 changed files with 75 additions and 10 deletions
|
|
@ -55,7 +55,7 @@ CAMPAIGN_MCS150_ID = 186 # "MCS-150 Overdue — $1,000/Day Fine Risk"
|
|||
CAMPAIGN_INACTIVE_ID = 188 # "Inactive USDOT — Reactivate Before You Get Pulled Over"
|
||||
|
||||
# Public site for landing-page links injected into Listmonk subscriber attribs.
|
||||
SITE_DOMAIN = os.getenv("SITE_DOMAIN", "https://performancewest.com")
|
||||
SITE_DOMAIN = os.getenv("SITE_DOMAIN", "https://performancewest.net")
|
||||
|
||||
# ── Deficiency-flag segments (Phase 5) ──────────────────────────────────────
|
||||
# Each segment targets carriers by a `deficiency_flags` value (the TEXT[] column
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from __future__ import annotations
|
|||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import urllib.parse
|
||||
|
||||
# Allow both supported invocation styles:
|
||||
# python -m scripts.create_deficiency_source_campaigns
|
||||
|
|
@ -89,6 +90,25 @@ def _cta(label):
|
|||
)
|
||||
|
||||
|
||||
def _dot_check_cta():
|
||||
"""Secondary CTA to the free DOT compliance checker.
|
||||
|
||||
The primary CTA remains the highly specific order page for the detected
|
||||
deficiency. This second orange button gives carriers a broader way to verify
|
||||
their DOT status and see any other missing filings before they order.
|
||||
"""
|
||||
return (
|
||||
'<div style="text-align:center;margin:8px 0 24px">'
|
||||
'<p style="font-size:13px;color:#64748b;line-height:1.5;margin:0 0 10px">'
|
||||
'Want to verify everything else on your DOT profile first?</p>'
|
||||
f'<a href="{b.SITE_DOMAIN}/tools/dot-compliance-check?dot={{{{ .Subscriber.Attribs.dot_number }}}}'
|
||||
'&utm_source=listmonk&utm_medium=email&utm_campaign=deficiency_dot_check@TrackLink" '
|
||||
'style="display:inline-block;padding:13px 30px;background:#f97316;color:#fff;'
|
||||
'font-weight:700;border-radius:8px;text-decoration:none;font-size:15px">'
|
||||
'Run Free DOT Compliance Check →</a></div>'
|
||||
)
|
||||
|
||||
|
||||
def _body(headline, intro, bullets, price_headline, price_sub, cta_label):
|
||||
bullet_html = "".join(f"<li>{x}</li>" for x in bullets)
|
||||
alert = _alert(
|
||||
|
|
@ -104,7 +124,8 @@ def _body(headline, intro, bullets, price_headline, price_sub, cta_label):
|
|||
'computer. No Login.gov, no government portals, no hours on hold. We handle the paperwork so you can get '
|
||||
'back to trucking and making money.</p></div>'
|
||||
)
|
||||
return _HEADER + alert + _price_box(price_headline, price_sub) + reassure + _cta(cta_label) + _FOOTER
|
||||
return (_HEADER + alert + _price_box(price_headline, price_sub) + reassure
|
||||
+ _cta(cta_label) + _dot_check_cta() + _FOOTER)
|
||||
|
||||
|
||||
# ── Per-segment copy ────────────────────────────────────────────────────────
|
||||
|
|
@ -204,7 +225,7 @@ SEGMENTS = {
|
|||
|
||||
def find_existing(name: str) -> int | None:
|
||||
try:
|
||||
res = b.lm_api("/campaigns?query=" + name.replace(" ", "+") + "&per_page=100")
|
||||
res = b.lm_api("/campaigns?" + urllib.parse.urlencode({"query": name, "per_page": 100}))
|
||||
for c in res.get("data", {}).get("results", []):
|
||||
if c.get("name") == name:
|
||||
return c["id"]
|
||||
|
|
@ -213,13 +234,51 @@ def find_existing(name: str) -> int | None:
|
|||
return None
|
||||
|
||||
|
||||
def create_draft(seg_key: str, cfg: dict, dry: bool) -> int | None:
|
||||
existing = find_existing(cfg["name"])
|
||||
if existing:
|
||||
print(f" [{seg_key}] already exists -> id {existing} (skipping create)")
|
||||
return existing
|
||||
def configured_campaign_id(cfg: dict) -> int | None:
|
||||
raw = os.getenv(cfg["env"], "").strip()
|
||||
if not raw:
|
||||
return None
|
||||
try:
|
||||
return int(raw)
|
||||
except ValueError:
|
||||
print(f" [{cfg['env']}] invalid configured campaign id {raw!r}; falling back to name lookup")
|
||||
return None
|
||||
|
||||
|
||||
def update_existing_campaign(campaign_id: int, cfg: dict, body: str, dry: bool) -> None:
|
||||
"""Update an existing source draft body/metadata so future clones inherit it."""
|
||||
existing = b.get_base_campaign(campaign_id)
|
||||
payload = {
|
||||
"name": existing.get("name") or cfg["name"],
|
||||
"subject": cfg["subject"],
|
||||
"lists": [l["id"] for l in existing.get("lists", []) if isinstance(l, dict)] or [SOURCE_LIST_ID],
|
||||
"from_email": existing.get("from_email") or FROM_EMAIL,
|
||||
"type": existing.get("type") or "regular",
|
||||
"content_type": existing.get("content_type") or "html",
|
||||
"body": body,
|
||||
"altbody": existing.get("altbody"),
|
||||
"template_id": existing.get("template_id") or TEMPLATE_ID,
|
||||
"tags": existing.get("tags") or ["trucking", "deficiency", "source"],
|
||||
"messenger": existing.get("messenger") or "email",
|
||||
"headers": existing.get("headers") or [{"name": "Reply-To", "value": REPLY_TO}],
|
||||
}
|
||||
if dry:
|
||||
print(f" [{cfg['env']}] DRY-RUN would update source campaign {campaign_id} (body {len(body)} chars)")
|
||||
return
|
||||
b.lm_api(f"/campaigns/{campaign_id}", payload, "PUT")
|
||||
print(f" [{cfg['env']}] updated source campaign {campaign_id}")
|
||||
|
||||
|
||||
def create_draft(seg_key: str, cfg: dict, dry: bool, update_existing: bool = False) -> int | None:
|
||||
existing = configured_campaign_id(cfg) or find_existing(cfg["name"])
|
||||
body = _body(cfg["headline"], cfg["intro"], cfg["bullets"],
|
||||
cfg["price_headline"], cfg["price_sub"], cfg["cta"])
|
||||
if existing:
|
||||
if update_existing:
|
||||
update_existing_campaign(existing, cfg, body, dry)
|
||||
else:
|
||||
print(f" [{seg_key}] already exists -> id {existing} (skipping create)")
|
||||
return existing
|
||||
if dry:
|
||||
print(f" [{seg_key}] DRY-RUN would create '{cfg['name']}' (body {len(body)} chars)")
|
||||
return None
|
||||
|
|
@ -245,14 +304,20 @@ def create_draft(seg_key: str, cfg: dict, dry: bool) -> int | None:
|
|||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
ap.add_argument(
|
||||
"--update-existing",
|
||||
action="store_true",
|
||||
help="Update matching existing source draft campaigns instead of only reporting their IDs.",
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
print(f"Listmonk: {b.LISTMONK_URL}")
|
||||
print(f"Creating {len(SEGMENTS)} deficiency source campaigns "
|
||||
f"({'DRY-RUN' if args.dry_run else 'LIVE'}):\n")
|
||||
f"({'DRY-RUN' if args.dry_run else 'LIVE'}"
|
||||
f"{', update existing' if args.update_existing else ''}):\n")
|
||||
env_lines = []
|
||||
for key, cfg in SEGMENTS.items():
|
||||
cid = create_draft(key, cfg, args.dry_run)
|
||||
cid = create_draft(key, cfg, args.dry_run, args.update_existing)
|
||||
if cid:
|
||||
env_lines.append(f"{cfg['env']}={cid}")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue