Skill: manmal/a-nach-b (version 1.0.0)

--- name: anachb description: Austrian public transport (VOR AnachB) for all of Austria. Query real-time departures, search stations/stops, plan routes between locations, and check service disruptions. Use when asking about Austrian trains, buses, trams, metro (U-Bahn), or directions involving public transport in Austria. --- # VOR AnachB - Austrian Public Transport API Query Austrian public transport for real-time departures, route planning, and service disruptions using the HAFAS API. ## Quick Reference | Script | Purpose | |--------|---------| | `search.sh` | Find stations/stops by name | | `departures.sh` | Real-time departures at a station | | `route.sh` | Plan a trip between two locations | | `disruptions.sh` | Current service disruptions | **API:** HAFAS (Hacon Fahrplan-Auskunfts-System) **Endpoint:** `https://vao.demo.hafas.de/gate` --- ## 1. Search Stations/Stops Find station IDs by name: ```bash ./search.sh "Stephansplatz" ./search.sh "Wien Hauptbahnhof" ./search.sh "Linz" ./search.sh "Salzburg Hbf" ``` Returns station names, IDs (extId), and coordinates. **Response fields:** - `name`: Station name - `extId`: Station ID for use in other queries - `type`: S (Station), A (Address), P (POI) - `coordinates`: WGS84 coordinates (lon/lat in 1e-6 format) --- ## 2. Real-Time Departures Get next departures from a station: ```bash ./departures.sh <station-id> [count] # Examples: ./departures.sh 490132000 # Wien Stephansplatz, 10 departures ./departures.sh 490132000 20 # Wien Stephansplatz, 20 departures ./departures.sh 490060200 # Wien Hauptbahnhof ./departures.sh 444130000 # Linz Hbf ./departures.sh 455000100 # Salzburg Hbf ``` **Response fields:** - `line`: Line name (U1, S1, RJ, etc.) - `direction`: Final destination - `departure`: Scheduled departure time - `delay`: Delay in minutes (if any) - `platform`: Platform/track number --- ## 3. Route Planning Plan a trip between two stations: ```bash ./route.sh <from-id> <to-id> [results] # Examples: ./route.sh 490132000 490060200 # Stephansplatz → Hauptbahnhof ./route.sh 490132000 444130000 5 # Wien → Linz, 5 results ./route.sh "Graz Hbf" "Wien Hbf" # Search by name (slower) ``` **Response fields:** - `departure`: Departure time - `arrival`: Arrival time - `duration`: Trip duration - `changes`: Number of transfers - `legs`: Array of trip segments with line info --- ## 4. Disruptions Check current service disruptions: ```bash ./disruptions.sh [category] # Examples: ./disruptions.sh # All disruptions ./disruptions.sh TRAIN # Train disruptions only ./disruptions.sh BUS # Bus disruptions only ``` --- ## Common Station IDs | Station | ID | |---------|-----| | Wien Stephansplatz | 490132000 | | Wien Hauptbahnhof | 490134900 | | Wien Westbahnhof | 490024300 | | Wien Praterstern | 490056100 | | Wien Karlsplatz | 490024600 | | Wien Schwedenplatz | 490119500 | | Linz Hbf | 444116400 | | Salzburg Hbf | 455000200 | | Graz Hbf | 460086000 | | Innsbruck Hbf | 481070100 | | Klagenfurt Hbf | 492019500 | | St. Pölten Hbf | 431543300 | | Wiener Neustadt Hbf | 430521000 | | Krems a.d. Donau | 431046400 | **Tip:** Always use `./search.sh` to find the correct station ID. --- ## Transport Types | Code | Type | |------|------| | ICE/RJ/RJX | High-speed trains | | IC/EC | InterCity/EuroCity | | REX/R | Regional Express/Regional | | S | S-Bahn (suburban rail) | | U | U-Bahn (Vienna metro) | | STR | Tram/Straßenbahn | | BUS | Bus | | AST | Demand-responsive transport | --- ## API Details (for advanced usage) The scripts use the HAFAS JSON API. For custom queries: ```bash curl -s -X POST "https://vao.demo.hafas.de/gate" \ -H "Content-Type: application/json" \ -d '{ "svcReqL": [{ "req": { ... }, "meth": "METHOD_NAME", "id": "1|1|" }], "client": {"id": "VAO", "v": "1", "type": "AND", "name": "nextgen"}, "ver": "1.73", "lang": "de", "auth": {"aid": "nextgen", "type": "AID"} }' ``` **Available methods:** - `LocMatch` - Location/station search - `StationBoard` - Departures/arrivals - `TripSearch` - Route planning - `HimSearch` - Disruptions/service messages - `JourneyDetails` - Details of a specific journey --- ## Tips 1. **Find station IDs first**: Always use `search.sh` to find the correct station ID before querying departures or routes. 2. **Station vs Stop**: Major stations have multiple platforms - the main station ID covers all platforms. 3. **Real-time data**: Departures include real-time delays when available. 4. **Austria-wide**: This API covers all Austrian public transport, not just Vienna. 5. **Cross-border**: Some routes extend to neighboring countries (Germany, Czech Republic, etc.).

path: departures.sh
content: #!/bin/bash
# Get real-time departures from an Austrian public transport station
# Usage: ./departures.sh <station-id> [count]
#
# Find station IDs with: ./search.sh <name>
# Example: ./departures.sh 490132000        # Wien Stephansplatz
# Example: ./departures.sh 490060200 20     # Wien Hauptbahnhof, 20 results

STATION_ID="${1:-}"
COUNT="${2:-10}"

if [ -z "$STATION_ID" ]; then
    echo "Usage: $0 <station-id> [count]"
    echo ""
    echo "Find station IDs with: ./search.sh <name>"
    echo ""
    echo "Common station IDs:"
    echo "  490132000 - Wien Stephansplatz"
    echo "  490134900 - Wien Hauptbahnhof"
    echo "  490024300 - Wien Westbahnhof"
    echo "  444116400 - Linz Hbf"
    echo "  455000200 - Salzburg Hbf"
    echo "  460086000 - Graz Hbf"
    exit 1
fi

curl -s -X POST "https://vao.demo.hafas.de/gate" \
  -H "Content-Type: application/json" \
  -d '{
    "svcReqL": [{
      "req": {
        "stbLoc": {"extId": "'"$STATION_ID"'", "type": "S"},
        "type": "DEP",
        "maxJny": '"$COUNT"'
      },
      "meth": "StationBoard",
      "id": "1|2|"
    }],
    "client": {"id": "VAO", "v": "1", "type": "AND", "name": "nextgen"},
    "ver": "1.73",
    "lang": "de",
    "auth": {"aid": "nextgen", "type": "AID"}
  }' | jq '
  .svcResL[0].res as $res |
  ($res.common.prodL // []) as $products |
  ($res.common.locL // []) as $locations |
  ($res.common.dirL // []) as $directions |
  
  # Get station name
  ($locations[0].name // "Unknown") as $stationName |
  
  # Helper to format time
  def formatTime: if . and (. | length) >= 4 then "\(.[0:2]):\(.[2:4])" else null end;
  
  # Helper to calculate delay
  def calcDelay(real; sched):
    if real and sched and (real | length) >= 4 and (sched | length) >= 4 then
      ((real[0:2] | tonumber) * 60 + (real[2:4] | tonumber)) -
      ((sched[0:2] | tonumber) * 60 + (sched[2:4] | tonumber))
    else null end;
  
  {
    station: $stationName,
    departures: [
      ($res.jnyL // [])[:'"$COUNT"'] | .[] | 
      (if .dirX != null and $directions[.dirX] != null then $directions[.dirX].txt else .dirTxt end) as $dir |
      calcDelay(.stbStop.dTimeR; .stbStop.dTimeS) as $delayMin |
      {
        line: ($products[.prodX // 0].name // "?"),
        direction: ($dir // null),
        departure: ((.stbStop.dTimeR // .stbStop.dTimeS) | formatTime),
        scheduled: (.stbStop.dTimeS | formatTime),
        delay: (if $delayMin != null and $delayMin > 0 then "+\($delayMin)min" elif $delayMin != null and $delayMin < 0 then "\($delayMin)min" else null end),
        platform: (.stbStop.dPlatfS // .stbStop.dPlatfR // null),
        cancelled: (if .stbStop.dCncl == true then true else null end)
      }
    ]
  }
'


path: disruptions.sh
content: #!/bin/bash
# Get current service disruptions in Austrian public transport
# Usage: ./disruptions.sh [max-results]
#
# Example: ./disruptions.sh       # Get up to 20 disruptions
# Example: ./disruptions.sh 50    # Get up to 50 disruptions

MAX_RESULTS="${1:-20}"

curl -s -X POST "https://vao.demo.hafas.de/gate" \
  -H "Content-Type: application/json" \
  -d '{
    "svcReqL": [{
      "req": {
        "maxNum": '"$MAX_RESULTS"',
        "himFltrL": [{"mode": "INC", "type": "HIMCAT", "value": "*"}]
      },
      "meth": "HimSearch",
      "id": "1|4|"
    }],
    "client": {"id": "VAO", "v": "1", "type": "AND", "name": "nextgen"},
    "ver": "1.73",
    "lang": "de",
    "auth": {"aid": "nextgen", "type": "AID"}
  }' | jq '
  .svcResL[0].res as $res |
  ($res.common.himL // []) as $messages |
  
  if ($messages | length) == 0 then
    {
      status: "ok",
      message: "No disruptions currently reported",
      count: 0,
      disruptions: []
    }
  else
    {
      count: ($messages | length),
      disruptions: [
        $messages[] | {
          id: .hid,
          category: .cat,
          priority: .prio,
          head: .head,
          text: .text,
          validFrom: .sDate,
          validTo: .eDate,
          affectedLines: ((.affProdRefL // []) | map(.name) | if . == [] then null else . end)
        }
      ]
    }
  end
'


path: route.sh
content: #!/bin/bash
# Plan a trip between two stations in Austrian public transport
# Usage: ./route.sh <from-id> <to-id> [results]
#
# Find station IDs with: ./search.sh <name>
# Example: ./route.sh 490132000 490060200     # Stephansplatz → Hauptbahnhof
# Example: ./route.sh 490132000 444130000 5   # Wien → Linz, 5 results

FROM_ID="${1:-}"
TO_ID="${2:-}"
COUNT="${3:-3}"

if [ -z "$FROM_ID" ] || [ -z "$TO_ID" ]; then
    echo "Usage: $0 <from-station-id> <to-station-id> [results]"
    echo ""
    echo "Find station IDs with: ./search.sh <name>"
    echo ""
    echo "Example: $0 490132000 490060200     # Stephansplatz → Hauptbahnhof"
    echo "Example: $0 490132000 444130000 5   # Wien → Linz, 5 results"
    exit 1
fi

curl -s -X POST "https://vao.demo.hafas.de/gate" \
  -H "Content-Type: application/json" \
  -d '{
    "svcReqL": [{
      "req": {
        "depLocL": [{"extId": "'"$FROM_ID"'", "type": "S"}],
        "arrLocL": [{"extId": "'"$TO_ID"'", "type": "S"}],
        "getPasslist": false,
        "maxChg": 5,
        "numF": '"$COUNT"'
      },
      "meth": "TripSearch",
      "id": "1|3|"
    }],
    "client": {"id": "VAO", "v": "1", "type": "AND", "name": "nextgen"},
    "ver": "1.73",
    "lang": "de",
    "auth": {"aid": "nextgen", "type": "AID"}
  }' | jq '
  .svcResL[0].res as $res |
  ($res.common.prodL // []) as $products |
  ($res.common.locL // []) as $locations |
  ($res.common.dirL // []) as $directions |
  
  # Get from/to names
  (($res.outConL // [])[0].dep.locX // 0) as $fromIdx |
  (($res.outConL // [])[0].arr.locX // 0) as $toIdx |
  ($locations[$fromIdx].name // "Unknown") as $fromName |
  ($locations[$toIdx].name // "Unknown") as $toName |
  
  # Helper function to format time
  def formatTime: if . and (. | length) >= 4 then "\(.[0:2]):\(.[2:4])" else . end;
  
  # Helper function to format duration string HHMMSS
  def formatDuration: 
    if . and (. | length) >= 6 then
      (.[0:2] | tonumber) as $h | (.[2:4] | tonumber) as $m |
      if $h > 0 then "\($h)h \($m)min" else "\($m)min" end
    else . end;
  
  {
    from: $fromName,
    to: $toName,
    trips: [
      ($res.outConL // [])[:'"$COUNT"'] | .[] | {
        departure: (.dep.dTimeS // "?") | formatTime,
        arrival: (.arr.aTimeS // "?") | formatTime,
        duration: (.dur // null) | formatDuration,
        changes: (.chg // 0),
        legs: [
          (.secL // []) | .[] | 
          select(.type == "JNY") |
          {
            line: ($products[.jny.prodX // 0].name // "?"),
            direction: (if .jny.dirX != null and $directions[.jny.dirX] != null then $directions[.jny.dirX].txt else null end),
            from: ($locations[.dep.locX // 0].name // "?"),
            departure: (.dep.dTimeS // "?") | formatTime,
            to: ($locations[.arr.locX // 0].name // "?"),
            arrival: (.arr.aTimeS // "?") | formatTime
          }
        ]
      }
    ]
  }
'


path: search.sh
content: #!/bin/bash
# Search for stations/stops in Austrian public transport
# Usage: ./search.sh <name>
#
# Returns: Station name, ID, type, and coordinates

QUERY="${1:-}"

if [ -z "$QUERY" ]; then
    echo "Usage: $0 <station-name>"
    echo "Example: $0 Stephansplatz"
    echo "Example: $0 \"Wien Hauptbahnhof\""
    exit 1
fi

curl -s -X POST "https://vao.demo.hafas.de/gate" \
  -H "Content-Type: application/json" \
  -d '{
    "svcReqL": [{
      "req": {
        "input": {
          "loc": {"name": "'"$QUERY"'"},
          "field": "S"
        }
      },
      "meth": "LocMatch",
      "id": "1|1|"
    }],
    "client": {"id": "VAO", "v": "1", "type": "AND", "name": "nextgen"},
    "ver": "1.73",
    "lang": "de",
    "auth": {"aid": "nextgen", "type": "AID"}
  }' | jq -r '
  .svcResL[0].res.match.locL // [] | 
  map(select(.type == "S" or .type == "A")) |
  .[:15] |
  map({
    name: .name,
    id: .extId,
    type: (if .type == "S" then "Station" elif .type == "A" then "Address" else .type end),
    coordinates: (if .crd then "\(.crd.y / 1000000), \(.crd.x / 1000000)" else null end)
  })
'


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