campaigns: trucking reefer/PTO fuel-tax newsletter (Prime v IRS hook)
One-off industry-news newsletter: FreightWaves story on Prime suing the IRS over reefer-diesel excise tax, broadened to cover PTO/off-highway fuel use (dump hydraulics, mixers, boom/bucket trucks, etc.) since the Form 4136 off-highway credit isn't reefer-specific. CTA: keep fuel records + free DOT check. Includes not-a-law-firm/not-a-tax-advisor fine print. @TrackLink on both static links (safe: not per-subscriber). Creates as DRAFT.
This commit is contained in:
parent
f163ccea92
commit
24f401e71a
1 changed files with 263 additions and 0 deletions
263
scripts/workers/create_trucking_fueltax_newsletter.py
Normal file
263
scripts/workers/create_trucking_fueltax_newsletter.py
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
create_trucking_fueltax_newsletter.py — One-off trucking NEWSLETTER hooked on the
|
||||
FreightWaves story about Prime Inc. suing the IRS over reefer-diesel fuel tax.
|
||||
|
||||
Angle : industry news + a "keep your fuel records" call to action, because the
|
||||
federal fuel-excise credit Prime is fighting for (diesel burned by a
|
||||
refrigeration unit isn't used to propel a highway vehicle) is one small
|
||||
carriers can claim too -- and records are what position a carrier to
|
||||
recover money if a class action follows.
|
||||
Links : (1) the FreightWaves article, (2) our free DOT compliance check tool.
|
||||
BOTH are STATIC URLs, so @TrackLink is safe here (the @TrackLink 404 bug
|
||||
was specific to PER-SUBSCRIBER hrefs like ?dot={{...}} where Listmonk
|
||||
captured the URL before the token rendered). Static links register one
|
||||
redirect for everyone -- exactly what we want for click counts.
|
||||
Fine print: Performance West is a filing service, NOT a law firm or tax advisor.
|
||||
|
||||
Creates the campaign in Listmonk as a DRAFT. Sending is a separate, manual,
|
||||
STOP-and-confirm step (deliverability-sensitive: review audience + timing first).
|
||||
|
||||
python3 scripts/workers/create_trucking_fueltax_newsletter.py --list <id>
|
||||
python3 scripts/workers/create_trucking_fueltax_newsletter.py --list <id> --preview-only
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
if _REPO not in sys.path:
|
||||
sys.path.insert(0, _REPO)
|
||||
|
||||
from scripts._email_plaintext import html_to_text # noqa: E402
|
||||
from scripts.workers.campaign_helpers import create_campaign # noqa: E402
|
||||
|
||||
# ── Links ────────────────────────────────────────────────────────────────────
|
||||
ARTICLE_URL = (
|
||||
"https://www.freightwaves.com/news/"
|
||||
"prime-is-suing-the-irs-for-11-million-over-fuel-tax-it-paid-on-reefer-diesel-"
|
||||
"the-same-credit-it-is-fighting-for-is-one-small-carriers-can-claim-too"
|
||||
)
|
||||
DOT_CHECK_URL = "https://performancewest.net/tools/dot-compliance-check"
|
||||
SITE = "https://performancewest.net"
|
||||
|
||||
EMAIL = "info@performancewest.net"
|
||||
PHONE_TEL = "+18884110383"
|
||||
PHONE_DISPLAY = "(888) 411-0383"
|
||||
|
||||
# Listmonk click tracking: a link is only registered (and clicks recorded) when
|
||||
# its href ends with the literal "@TrackLink" marker. We also append UTM params so
|
||||
# Umami attributes the visit too. SAFE here because both target URLs are static.
|
||||
UTM_BASE = "utm_source=listmonk&utm_medium=email&utm_campaign=trucking-fueltax-newsletter"
|
||||
|
||||
|
||||
def _track(url: str, content: str) -> str:
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}{UTM_BASE}&utm_content={content}@TrackLink"
|
||||
|
||||
|
||||
# ── Trucking-branded HTML helpers (navy #1a2744 / orange #f97316), matching the
|
||||
# proven trucking campaign chrome (campaign 179). ───────────────────────────
|
||||
FONT = "Inter,system-ui,Arial,sans-serif"
|
||||
|
||||
|
||||
def header(eyebrow, headline):
|
||||
return (
|
||||
'<tr><td style="background:#1a2744;padding:24px 28px;">'
|
||||
f'<img src="{SITE}/images/logo.png" alt="Performance West" style="height:44px;margin-bottom:12px;display:block" />'
|
||||
f'<p style="margin:0 0 6px;font-size:11px;color:#f97316;letter-spacing:2px;text-transform:uppercase;font-weight:700;font-family:{FONT};">{eyebrow}</p>'
|
||||
f'<h1 style="color:#fff;margin:0;font-size:22px;font-weight:700;line-height:1.3;font-family:{FONT};">{headline}</h1>'
|
||||
'</td></tr>'
|
||||
)
|
||||
|
||||
|
||||
def P(t):
|
||||
return f'<p style="font-size:14px;line-height:1.7;margin:0 0 16px;color:#1f2937;font-family:{FONT};">{t}</p>'
|
||||
|
||||
|
||||
def H2(t):
|
||||
return f'<h2 style="font-size:16px;font-weight:700;color:#1e3a5f;margin:26px 0 12px;font-family:{FONT};">{t}</h2>'
|
||||
|
||||
|
||||
def UL(*items):
|
||||
lis = "".join(
|
||||
f'<li style="margin-bottom:9px;line-height:1.6;">{i}</li>' for i in items
|
||||
)
|
||||
return (
|
||||
f'<ul style="margin:0 0 18px;padding-left:20px;font-size:14px;color:#1f2937;font-family:{FONT};">{lis}</ul>'
|
||||
)
|
||||
|
||||
|
||||
def quote(t):
|
||||
return (
|
||||
'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 20px;"><tr>'
|
||||
'<td style="border-left:4px solid #f97316;background:#fff7ed;border-radius:0 8px 8px 0;padding:14px 18px;'
|
||||
f'font-size:14px;line-height:1.7;color:#7c2d12;font-family:{FONT};">{t}</td></tr></table>'
|
||||
)
|
||||
|
||||
|
||||
def cta(text, url, color="#f97316"):
|
||||
return (
|
||||
'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:4px 0 24px;"><tr>'
|
||||
'<td style="text-align:center;padding:6px 0;">'
|
||||
f'<a href="{url}" style="display:inline-block;padding:15px 42px;background:{color};color:#fff;'
|
||||
f'font-weight:700;border-radius:8px;text-decoration:none;font-size:15px;font-family:{FONT};'
|
||||
f'box-shadow:0 4px 12px rgba(249,115,22,0.35);">{text}</a>'
|
||||
'</td></tr></table>'
|
||||
)
|
||||
|
||||
|
||||
def disclaimer():
|
||||
return (
|
||||
'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 24px;"><tr>'
|
||||
'<td style="background:#f8fafc;border:1px solid #e5e7eb;border-radius:8px;padding:14px 18px;">'
|
||||
f'<p style="margin:0;font-size:11px;line-height:1.6;color:#6b7280;font-family:{FONT};">'
|
||||
'<strong style="color:#475569;">Please note:</strong> Performance West is a DOT compliance and '
|
||||
'filing service — we are <strong>not a law firm and not a tax advisor</strong>, and nothing in '
|
||||
'this email is legal or tax advice. This is industry news. Whether your reefer or PTO fuel qualifies for '
|
||||
'a federal fuel-tax credit, and whether to join any lawsuit or class action, depends on your specific '
|
||||
'situation — please talk to a qualified tax professional or attorney before filing a claim or '
|
||||
'taking action.</p></td></tr></table>'
|
||||
)
|
||||
|
||||
|
||||
def dot_check_block():
|
||||
return (
|
||||
'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:20px 0 8px;"><tr>'
|
||||
'<td style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:10px;padding:20px 22px;">'
|
||||
f'<p style="margin:0 0 6px;font-size:15px;font-weight:700;color:#1e3a5f;font-family:{FONT};">'
|
||||
'While you’re thinking about records — is your DOT compliance current?</p>'
|
||||
f'<p style="margin:0 0 14px;font-size:13px;line-height:1.6;color:#334155;font-family:{FONT};">'
|
||||
'Run our <strong>free DOT compliance check</strong>: enter your USDOT number and we’ll show you '
|
||||
'what’s due — MCS-150 biennial update, UCR, BOC-3 and more. No charge, no obligation.</p>'
|
||||
+ cta("Run my free DOT check →", _track(DOT_CHECK_URL, "dotcheck"), color="#1e40af")
|
||||
+ '</td></tr></table>'
|
||||
)
|
||||
|
||||
|
||||
def footer():
|
||||
return (
|
||||
'<tr><td style="padding:18px 28px;background:#f8fafc;border-top:1px solid #e5e7eb;text-align:center;">'
|
||||
f'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 12px;"><tr>'
|
||||
'<td style="background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:12px;text-align:center;">'
|
||||
f'<p style="margin:0;font-size:13px;color:#1e3a5f;font-weight:600;font-family:{FONT};">'
|
||||
f'Questions? Reply to this email, write <a href="mailto:{EMAIL}" style="color:#1e40af;text-decoration:none;">{EMAIL}</a> '
|
||||
f'or call <a href="tel:{PHONE_TEL}" style="color:#1e40af;text-decoration:none;">{PHONE_DISPLAY}</a>.</p>'
|
||||
'</td></tr></table>'
|
||||
f'<p style="margin:0;font-size:11px;color:#9ca3af;font-family:{FONT};">Performance West Inc. · '
|
||||
'525 Randall Ave Ste 100-1195, Cheyenne, WY 82001 · '
|
||||
f'<a href="{SITE}" style="color:#6b7280;">performancewest.net</a></p>'
|
||||
'<p style="margin:6px 0 0;font-size:11px;color:#9ca3af;font-family:Inter,sans-serif;">'
|
||||
'<a href="{{ UnsubscribeURL }}" style="color:#6b7280;">Unsubscribe</a></p>'
|
||||
'</td></tr>'
|
||||
)
|
||||
|
||||
|
||||
def build_body():
|
||||
inner = (
|
||||
P("There’s a fuel-tax story moving through trucking right now that could mean money back in your "
|
||||
"pocket — not just for reefer haulers, but for anyone whose diesel runs equipment that "
|
||||
"doesn’t move the truck. We wanted to put it in front of you.")
|
||||
+ quote(
|
||||
"<strong>FreightWaves reports</strong> that Prime Inc. is suing the IRS for about "
|
||||
"<strong>$11 million</strong> over federal excise tax it paid on diesel burned by its "
|
||||
"<strong>refrigerated (reefer) trailer units</strong>. Prime’s argument: diesel used to run "
|
||||
"a reefer unit isn’t used to propel a truck down the highway, so it shouldn’t carry the "
|
||||
"federal on-highway fuel tax — and the same credit it’s fighting for is one that small "
|
||||
"carriers can claim too."
|
||||
)
|
||||
+ H2("This isn’t just a reefer issue")
|
||||
+ P("The federal excise tax on diesel is about <strong>24.3 cents a gallon</strong>, and it’s "
|
||||
"meant to pay for diesel that <em>propels a vehicle down the highway</em>. The thing is, a lot of "
|
||||
"the diesel you buy never turns a wheel:")
|
||||
+ UL(
|
||||
"<strong>Reefer units</strong> — the fuel a refrigerated trailer burns keeping a load cold",
|
||||
"<strong>Power take-off (PTO) and auxiliary equipment</strong> — dump-truck hydraulics, "
|
||||
"cement mixers, bucket / boom / digger derricks, pumpers, blowers, refuse packers, sweepers, "
|
||||
"wreckers, and other equipment your engine runs while the truck sits still",
|
||||
"<strong>Other off-highway / non-propulsion use</strong> — idling auxiliary power and "
|
||||
"equipment that runs off the same tank",
|
||||
)
|
||||
+ P("Diesel used for those purposes generally <strong>isn’t propelling a highway vehicle</strong>, "
|
||||
"so it may not owe the federal on-highway fuel tax — and the tax you already paid on it may be "
|
||||
"refundable on <strong>IRS Form 4136</strong> (or via Form 8849). That is the same off-highway credit "
|
||||
"Prime is fighting for on its reefer fuel.")
|
||||
+ P("Over a year of runs that adds up — and if a class action follows Prime’s lawsuit, the "
|
||||
"carriers who kept clean records will be the ones positioned to claim their share.")
|
||||
+ H2("What to do now: gather and keep your fuel records")
|
||||
+ P("You don’t need to do anything drastic today. The single most important thing is simple: "
|
||||
"<strong>don’t throw your fuel records away</strong>, and start keeping them organized.")
|
||||
+ UL(
|
||||
"Save your <strong>fuel receipts and IFTA fuel summaries</strong> — especially anything that "
|
||||
"separates reefer or PTO fuel from the fuel that moves the truck",
|
||||
"If you can, <strong>track that fuel separately</strong> — a dedicated fuel card, pump, or "
|
||||
"tank reading for the reefer / PTO equipment makes this much easier to prove later",
|
||||
"Hold onto <strong>reefer run logs, PTO / engine hours, or equipment-meter readings</strong> if "
|
||||
"your unit records them",
|
||||
"Keep everything <strong>by quarter and by year</strong> — the cleaner the paper trail, the "
|
||||
"stronger any future claim",
|
||||
)
|
||||
+ P("If a class action or a clearer IRS position emerges, the carriers with documentation in hand will "
|
||||
"be the ones ready to act.")
|
||||
+ cta("Read the full story on FreightWaves →", _track(ARTICLE_URL, "article"))
|
||||
+ disclaimer()
|
||||
+ dot_check_block()
|
||||
+ P("Drive safe out there,")
|
||||
+ P("<strong>Justin Hannah</strong><br>Performance West Inc.<br>"
|
||||
f'<span style="font-size:12px;color:#64748b;font-family:{FONT};">DOT Compliance Services</span>')
|
||||
)
|
||||
return (
|
||||
'<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">'
|
||||
'<meta name="viewport" content="width=device-width,initial-scale=1">'
|
||||
'<style>@media only screen and (max-width:600px){.pw-wrap{width:100%!important;border-radius:0!important;}'
|
||||
'.pw-pad{padding:24px 16px!important;}}body,table,td,p,a{-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}'
|
||||
'table{border-collapse:collapse!important;}img{border:0;outline:none;text-decoration:none;}</style></head>'
|
||||
'<body style="margin:0;padding:0;background:#eef0f3;"><center>'
|
||||
'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#eef0f3;"><tr>'
|
||||
'<td style="padding:24px 10px;">'
|
||||
'<table role="presentation" class="pw-wrap" width="620" cellpadding="0" cellspacing="0" '
|
||||
'style="margin:0 auto;border-radius:10px;overflow:hidden;background:#fff;">'
|
||||
+ header("Industry News · Fuel Tax",
|
||||
"Prime is suing the IRS for $11M over reefer fuel tax — "
|
||||
"and the same credit may apply to your reefer or PTO fuel")
|
||||
+ '<tr><td class="pw-pad" style="padding:28px;">' + inner + '</td></tr>'
|
||||
+ footer()
|
||||
+ '</table></td></tr></table></center></body></html>'
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--list", type=int, help="Listmonk list id to target (the newsletter audience)")
|
||||
ap.add_argument("--name", default="Trucking Newsletter \u2014 Reefer/PTO Fuel-Tax Story + Keep Your Records")
|
||||
ap.add_argument("--subject",
|
||||
default="Are you owed a refund on the fuel tax you paid for your reefer or PTO?")
|
||||
ap.add_argument("--preview-only", action="store_true",
|
||||
help="write the rendered HTML to /tmp and do NOT touch Listmonk")
|
||||
args = ap.parse_args()
|
||||
|
||||
body = build_body()
|
||||
altbody = html_to_text(body)
|
||||
|
||||
out = "/tmp/trucking_fueltax_newsletter.html"
|
||||
with open(out, "w") as f:
|
||||
f.write(body)
|
||||
print(f"Rendered preview -> {out}")
|
||||
print(f"Body: {len(body):,} chars HTML / {len(altbody):,} chars plaintext")
|
||||
|
||||
if args.preview_only:
|
||||
return 0
|
||||
if not args.list:
|
||||
print("\nNo --list given; created preview only. Re-run with --list <id> to create the draft.",
|
||||
file=sys.stderr)
|
||||
return 0
|
||||
|
||||
print(f"\n=== Creating trucking fuel-tax newsletter (DRAFT, list {args.list}) ===")
|
||||
cid = create_campaign(args.name, args.subject, [args.list], body, altbody=altbody, status="draft")
|
||||
if cid:
|
||||
print(f"\nDraft created (id {cid}). Review/preview in Listmonk, then send manually.")
|
||||
return 0 if cid else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue