Skill: idrs/dhl-tracking (version 1.0.0)

--- name: dhl-tracking description: Track DHL parcels in Germany via the dhl.de API. Use when a user asks to track a DHL package, check delivery status, monitor a shipment, or wants notifications about a DHL Sendungsnummer. Works with standard DHL parcel tracking numbers (00340...). No API key required. --- # DHL Tracking Track DHL parcels via the `dhl.de` internal API endpoint using simple HTTP requests. ## Important: Why this skill exists DHL's tracking website (`dhl.de/sendungsverfolgung`) is a JavaScript SPA protected by Akamai Bot Manager. The SPA renders tracking data client-side after passing bot checks — headless browsers (even with stealth patches) fail due to TLS fingerprinting. **The solution:** A simple `curl` to `www.dhl.de/int-verfolgen/data/search` with minimal headers returns full tracking JSON. No auth, no cookies, no JS needed. ## Quick Usage ### Bash (one-liner) ```bash bash scripts/dhl_track.sh TRACKING_NUMBER ``` ### Python (formatted output) ```bash python3 scripts/dhl_track.py TRACKING_NUMBER ``` ### Direct curl ```bash curl -s 'https://www.dhl.de/int-verfolgen/data/search?piececode=TRACKING_NUMBER&language=de' \ -H 'Accept: application/json' -H 'User-Agent: Mozilla/5.0' ``` ## Response Structure ``` sendungen[0].sendungsdetails.sendungsverlauf: .status → Current status text (German) .fortschritt → Progress (0-5) .maximalFortschritt → Always 5 .events[] → Array of {datum, status, ruecksendung} sendungen[0].sendungsdetails.zustellung: .zustellzeitfensterVon/Bis → Delivery window sendungen[0].sendungsdetails.istZugestellt → boolean ``` ### Progress values - **0-1**: Label created / picked up - **2**: In transit - **3**: In recipient's region - **4**: Out for delivery (loaded onto delivery vehicle) - **5**: Delivered ## Monitoring Pattern To monitor a package and notify when close to delivery: 1. Run `python3 scripts/dhl_track.py TRACKING_NUMBER` periodically (cron, every 30 min) 2. Parse `fortschritt` from stderr JSON output 3. Notify user when `fortschritt >= 4` (out for delivery) ## What does NOT work (and why this skill exists) - **`web_fetch` on dhl.de** → only gets static HTML shell, no tracking data (JS SPA) - **Headless Chrome on dhl.de** → Akamai blocks internal API via TLS fingerprinting (returns 404) - **Stealth patches** (fake UA, hide webdriver, fake plugins) → still detected by TLS/JA3 fingerprint - **`nolp.dhl.de` legacy endpoint** → now also protected by Akamai (returns 500) - **DHL public API (`api-eu.dhl.com`)** → requires API key registration - **Third-party trackers** (17track, parcelsapp) → often delayed or incomplete for DE parcels ## Notes - Works for German DHL Paket numbers (typically `0034...`) - Language: `language=de` (German) or `language=en` (English) - Be reasonable with polling (max once per minute) - If the API starts returning 404/HTML, the Akamai protection may have expanded — try varying the User-Agent or adding a `Referer: https://www.dhl.de/` header

path: scripts/dhl_track.py
content: #!/usr/bin/env python3
"""DHL Sendungsverfolgung - parse tracking JSON into human-readable summary.

Usage: 
  ./dhl_track.sh <TRACKING_NUMBER> | python3 dhl_track.py
  python3 dhl_track.py <TRACKING_NUMBER>
"""
import sys, json, subprocess
from datetime import datetime

def fetch_tracking(tracking_number: str, lang: str = "de") -> dict:
    """Fetch tracking data from DHL nolp API."""
    import urllib.request
    url = f"https://www.dhl.de/int-verfolgen/data/search?piececode={tracking_number}&language={lang}"
    req = urllib.request.Request(url, headers={
        "Accept": "application/json",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
    })
    with urllib.request.urlopen(req, timeout=10) as resp:
        return json.loads(resp.read())

def parse_tracking(data: dict) -> dict:
    """Parse DHL tracking JSON into a clean summary."""
    if not data.get("sendungen"):
        return {"error": "Keine Sendung gefunden", "raw": data}
    
    s = data["sendungen"][0]
    details = s.get("sendungsdetails", {})
    verlauf = details.get("sendungsverlauf", {})
    zustellung = details.get("zustellung", {})
    
    events = []
    for e in verlauf.get("events", []):
        events.append({
            "time": e.get("datum", ""),
            "status": e.get("status", ""),
            "return": e.get("ruecksendung", False)
        })
    
    return {
        "tracking_number": s.get("id"),
        "status": verlauf.get("status", "Unbekannt"),
        "progress": f"{verlauf.get('fortschritt', '?')}/{verlauf.get('maximalFortschritt', '?')}",
        "last_update": verlauf.get("datumAktuellerStatus", ""),
        "delivered": details.get("istZugestellt", False),
        "country": details.get("zielland", ""),
        "delivery_window": {
            "from": zustellung.get("zustellzeitfensterVon", ""),
            "to": zustellung.get("zustellzeitfensterBis", "")
        },
        "events": events
    }

def format_summary(parsed: dict) -> str:
    """Format parsed tracking into readable text."""
    if "error" in parsed:
        return f"❌ {parsed['error']}"
    
    progress_num = int(parsed["progress"].split("/")[0]) if "/" in parsed["progress"] else 0
    progress_max = int(parsed["progress"].split("/")[1]) if "/" in parsed["progress"] else 5
    bar = "●" * progress_num + "○" * (progress_max - progress_num)
    
    lines = [
        f"📦 DHL Tracking: {parsed['tracking_number']}",
        f"📊 Fortschritt: [{bar}] {parsed['progress']}",
        f"📍 Status: {parsed['status']}",
        f"🏳️ Zielland: {parsed['country']}",
        f"✅ Zugestellt: {'Ja' if parsed['delivered'] else 'Nein'}",
        "",
        "📋 Sendungsverlauf:"
    ]
    
    for e in parsed["events"]:
        try:
            dt = datetime.fromisoformat(e["time"])
            time_str = dt.strftime("%d.%m. %H:%M")
        except:
            time_str = e["time"]
        lines.append(f"  {time_str} — {e['status']}")
    
    return "\n".join(lines)

if __name__ == "__main__":
    if len(sys.argv) > 1:
        data = fetch_tracking(sys.argv[1])
    else:
        data = json.load(sys.stdin)
    
    parsed = parse_tracking(data)
    print(format_summary(parsed))
    
    # Also output JSON to stderr for programmatic use
    print(json.dumps(parsed, indent=2, ensure_ascii=False), file=sys.stderr)


path: scripts/dhl_track.sh
content: #!/bin/bash
# DHL Sendungsverfolgung via dhl.de API
# Usage: dhl_track.sh <TRACKING_NUMBER> [language]
# Returns JSON with tracking status, events, and progress

TRACKING_NUMBER="${1:?Usage: dhl_track.sh <TRACKING_NUMBER> [language]}"
LANG="${2:-de}"

curl -s "https://www.dhl.de/int-verfolgen/data/search?piececode=${TRACKING_NUMBER}&language=${LANG}" \
  -H 'Accept: application/json' \
  -H 'User-Agent: Mozilla/5.0'


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)