scripts/treno: add script for querying Trenitalia API
This commit is contained in:
parent
c7b68ee90d
commit
6572a5968a
201
scripts/treno
Executable file
201
scripts/treno
Executable file
@ -0,0 +1,201 @@
|
||||
#!/bin/sh
|
||||
|
||||
print_usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS] COMMAND [COMMAND_OPTIONS]
|
||||
|
||||
Get information about Trenitalia trains
|
||||
|
||||
Options:
|
||||
-h --help print this message
|
||||
|
||||
Commands:
|
||||
|
||||
search FROM TO [OPTIONS] search for trains between two stations
|
||||
-i --interactive allows selecting a train for more information
|
||||
-d --date DATE use DATE instead of now
|
||||
|
||||
status TRAIN_ID [--board STATION] get realtime information about a train
|
||||
-b --board STATION prints the platform number when boarding
|
||||
then train from a given station
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Prints realtime information about a train
|
||||
train_status() {
|
||||
train="$1"
|
||||
test "$2" = "-b" -o "$2" = "--board" && board_station="$3"
|
||||
|
||||
# Resolve the station name, if necessary
|
||||
if test "${board_station#id:}" = "$board_station"; then
|
||||
board_station=$(station_search "$board_station" | sed 's/^8300/S/g')
|
||||
else
|
||||
board_station=${board_station#id:}
|
||||
fi
|
||||
|
||||
url='http://www.viaggiatreno.it/infomobilita/resteasy/viaggiatreno'
|
||||
today="$(date -d "$(date +%F)" +%s)000"
|
||||
station=$(curl -s "$url/cercaNumeroTrenoTrenoAutocomplete/$1" |
|
||||
grep "$today" | cut -d\| -f2 | cut -d- -f2)
|
||||
curl -s "$url/andamentoTreno/$station/$train/$today" |
|
||||
jq -r --arg board_station "$board_station" '
|
||||
# Functions to color text
|
||||
def red($x): "\u001B[1;31m\($x)\u001B[0m";
|
||||
def green($x): "\u001B[1;32m\($x)\u001B[0m";
|
||||
def yellow($x): "\u001B[1;36m\($x)\u001B[0m";
|
||||
|
||||
(if .tipoTreno == "PG" then green("regolare")
|
||||
elif .tipoTreno == "ST" then red("cancellato")
|
||||
elif .tipoTreno == "DV" then yellow("deviato")
|
||||
elif .tipoTreno == "PP" or
|
||||
.tipoTreno == "SI" or
|
||||
.tipoTreno == "SF" then yellow(.subTitle)
|
||||
else "sconosciuto"
|
||||
end) as $status
|
||||
| (if .nonPartito
|
||||
then []
|
||||
else ["ultima posizione: " + .stazioneUltimoRilevamento
|
||||
+ "(\(.compOraUltimoRilevamento))"]
|
||||
end) as $position
|
||||
| (if .ritardo <= 0
|
||||
then green(.compRitardo[0])
|
||||
else red(.compRitardo[0])
|
||||
end) as $late
|
||||
| (if $board_station
|
||||
then ["binario arrivo: " +
|
||||
(.fermate[] | select(.id == $board_station)
|
||||
| .binarioProgrammatoArrivoDescrizione)]
|
||||
else []
|
||||
end) as $platform
|
||||
| ["nome: \(.compNumeroTreno)",
|
||||
"stato: \($status)",
|
||||
"ritardo: \($late)"
|
||||
] + $platform + $position
|
||||
| join("\n")'
|
||||
}
|
||||
|
||||
|
||||
# Searches for a station by name and returns its ID
|
||||
station_search() {
|
||||
url='https://www.lefrecce.it/Channels.Website.BFF.WEB/website'
|
||||
# URL-encode the search string
|
||||
encoded=$(printf '%s' "$1" | jq -sRr '@uri')
|
||||
curl -s "$url/locations/search?name=$encoded&limit=1" | jq '.[0].id'
|
||||
}
|
||||
|
||||
|
||||
# Searches for a route between two stations
|
||||
route_search() {
|
||||
url='https://www.lefrecce.it/Channels.Website.BFF.WEB/website'
|
||||
query=$(cat <<EOF
|
||||
{
|
||||
"departureLocationId": "$1",
|
||||
"arrivalLocationId": "$2",
|
||||
"departureTime": "$(date -d "$3" --iso-8601=s)",
|
||||
"adults": 1,
|
||||
"children": 0,
|
||||
"criteria": {
|
||||
"frecceOnly": false,
|
||||
"regionalOnly": false,
|
||||
"noChanges": false,
|
||||
"order": "DEPARTURE_DATE",
|
||||
"limit": 6,
|
||||
"offset": 0
|
||||
},
|
||||
"advancedSearchRequest": { "bestFare": false }
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
printf 'data: \033[36m%s\033[0m\n\n' "$(date -d "$3" +'%F %R')"
|
||||
curl -s --json "$query" "$url/ticket/solutions" |
|
||||
jq -r '
|
||||
"%FT%T.%G%z" as $timefmt |
|
||||
|
||||
# Functions to color text
|
||||
def red($x): "\u001B[1;31m\($x)\u001B[0m";
|
||||
def green($x): "\u001B[1;32m\($x)\u001B[0m";
|
||||
def blue($x): "\u001B[1;34m\($x)\u001B[0m";
|
||||
|
||||
def parse_iso8601:
|
||||
# Parse an ISO 8601 date with timezone information
|
||||
capture("(?<no_tz>[^.]*)(?<frac_sec>\\.\\d+)?(?:(?:(?<tz_sgn>[-+])(?<tz_hr>\\d{2}):?(?<tz_min>\\d{2})?)|Z)$") |
|
||||
(.no_tz + "Z" | fromdateiso8601)
|
||||
+ ("0"+.frac_sec | tonumber)
|
||||
- (.tz_sgn + "60" | tonumber)
|
||||
* ((.tz_hr // "0" | tonumber) * 60 + (.tz_min // "0" | tonumber))
|
||||
| todateiso8601 | strptime("%FT%TZ");
|
||||
|
||||
def time:
|
||||
# Extracts "HH:MM" from a timestamp
|
||||
parse_iso8601 | mktime | strftime("%R");
|
||||
|
||||
def delta($stop; $start):
|
||||
# Computes the time elapsed between two dates
|
||||
[$stop, $start]
|
||||
| map(strptime($timefmt) | mktime) # seconds
|
||||
| .[0] - .[1]
|
||||
| ((./60) % 60) as $m
|
||||
| (./3600 | floor) as $h
|
||||
| (if $h > 0 then "\($h)h " else "" end) + "\($m)min";
|
||||
|
||||
def print_trip:
|
||||
# Prints a trip "station(time) → station(time)"
|
||||
(.duration // delta(.arrivalTime; .departureTime)) as $delta |
|
||||
"\(.origin)(\(green(.departureTime | time)))" +
|
||||
" → \(.destination)(\(.arrivalTime | time)) " +
|
||||
blue($delta);
|
||||
|
||||
def print_train:
|
||||
# Prints the train name and its trip
|
||||
"\(.train.description // .train.trainCategory): " + print_trip;
|
||||
|
||||
def print_summary:
|
||||
(.trains | length) as $ntrain |
|
||||
(if $ntrain > 2 then (", " + red("\($ntrain-1) cambi"))
|
||||
elif $ntrain == 2 then (", " + red("1 cambio"))
|
||||
else "" end) as $switch |
|
||||
"- " + print_trip + $switch + "\n "
|
||||
+ (.nodes | map(print_train) | join("\n "));
|
||||
|
||||
.solutions | map(.solution | print_summary) | join("\n\n")'
|
||||
}
|
||||
|
||||
handle_search() {
|
||||
# Get station IDs
|
||||
start_id=$(station_search "$1")
|
||||
stop_id=$(station_search "$2")
|
||||
shift 2
|
||||
|
||||
while :; do
|
||||
case "$1" in
|
||||
-d|--date) date=$2; shift ;;
|
||||
-i|--interactive) interactive=1; shift ;;
|
||||
-?*) print_usage ;;
|
||||
*) break
|
||||
esac
|
||||
done
|
||||
|
||||
results=$(route_search "$start_id" "$stop_id" "${date:-now}")
|
||||
printf '%s\n\n' "$results"
|
||||
|
||||
if test -n "$interactive"; then
|
||||
# Ask user to select a train
|
||||
info=$(printf '%s' "$results" | fzf --ansi --no-sort --disabled | cut -d: -f1)
|
||||
# Print the train status
|
||||
train_id=$(printf '%s' "$info" | cut -d: -f1 | rev | cut -d' ' -f 1 | rev)
|
||||
# convert between different API conventions
|
||||
station_id=$(printf '%s' "$start_id" | sed 's/^8300/S/g')
|
||||
train_status "$train_id" --board "id:$station_id"
|
||||
fi
|
||||
}
|
||||
|
||||
test $# -eq 0 && print_usage
|
||||
|
||||
cmd="$1"; shift
|
||||
case "$cmd" in
|
||||
search) handle_search "$@" ;;
|
||||
status) train_status "$@" ;;
|
||||
*) print_usage
|
||||
esac
|
Loading…
Reference in New Issue
Block a user