← All Problems

7. Strip Email Signature / Boilerplate from a Message

Confirmed Medium String Parsing
Grounding: Confirmed: For the email channel, Fin's dedicated component filters out email-signature content irrelevant to the customer's actual query. (Source: intercom.com/blog/fin-over-email-how-we-built/)

Problem

When a customer replies over email, the raw body Fin receives usually includes a trailing signature block ("Best regards, John") or other client-added boilerplate stacked after the actual message. That trailing content is irrelevant to the customer's question and can confuse a model if it's sent along as-is, so it needs to be trimmed off before the message reaches the answering pipeline.

Write a function that takes the raw email body and returns it with any trailing signature/boilerplate removed, leaving only the substantive message.

Source: src/7_strip_email_signature.py

def strip_signature(email_body: str) -> str

>>> strip_signature("My order hasn't arrived yet.\nBest regards,\nJohn")
"My order hasn't arrived yet."

>>> strip_signature("I love your product and use it daily.")
'I love your product and use it daily.'

Step-by-Step Approach

  1. Define a small set of known signature markers: exact-match lines like an email "signature delimiter" (--), and case-insensitive line prefixes like "best regards", "kind regards", "sent from my", "thanks,", "cheers,".
  2. Split the email body into lines and scan top-to-bottom, since a signature block is always trailing content — the first marker hit is the boundary.
  3. For each line, check the exact-match set first (a signature delimiter line is exactly "--" or "-- ", not merely a line containing those characters).
  4. Then lower-case the line and check whether it starts with any of the prefix markers.
  5. The moment either check hits, truncate the body to everything before that line, strip surrounding whitespace, and return it.
  6. If no marker is ever found, the whole body is legitimate message content — return it stripped, unchanged.

The key insight is treating this as a boundary-finding problem, not a filtering problem: once you find the first signature marker, everything after it is discarded wholesale, so a single linear scan with early return is both correct and efficient.

Reference solution

SIGNATURE_EXACT_MARKERS = {"--", "-- "}
SIGNATURE_PREFIX_MARKERS = (
    "best regards",
    "kind regards",
    "sent from my",
    "thanks,",
    "cheers,",
)


def strip_signature(email_body: str) -> str:
    # Scan lines top-to-bottom for the first signature marker, truncate there. O(n) lines.
    lines = email_body.split("\n")
    # enumerate gives index i for slicing
    for i, line in enumerate(lines):
        # O(1) set membership, exact match only
        if line in SIGNATURE_EXACT_MARKERS:
            return "\n".join(lines[:i]).strip()
        # normalize case before prefix check
        lower = line.lower()
        # startswith, not substring
        if any(lower.startswith(marker) for marker in SIGNATURE_PREFIX_MARKERS):
            return "\n".join(lines[:i]).strip()
    return email_body.strip()

Key Functions & Tricks

  • {"--", "-- "} — set literal for O(1) exact-match membership testing
  • (...) tuple — ordered constants, iterated via any(...) not membership-tested
  • str.split("\n") — breaks body into lines for indexed scan and reassembly
  • enumerate(lines) — yields (index, value) pairs in one pass
  • str.lower() — case-normalizes before the prefix check only
  • any(lower.startswith(marker) for marker in ...) — short-circuits on first match
  • lines[:i] — slice of every line before the signature, excluding it
  • str.strip() — trims whitespace on every return path for consistent output

How to Recognize This Pattern

Reach for a marker-based boundary scan whenever you need to strip a known-format trailer (signature, footer, disclaimer, quoted-reply chain) from otherwise free-form text — the shape is always "scan for the first occurrence of a known marker, then everything after it is discarded." A common variation is scanning from the bottom up instead when the marker set is ambiguous mid-body but reliable near the end (e.g. quoted "On <date>, X wrote:" reply chains). A common pitfall is using a substring check where an exact-match or prefix check is required — for example, a body line that happens to contain "thanks," mid-sentence rather than starting with it would be wrongly treated as a signature marker if you check for containment instead of a line prefix.