summaryrefslogtreecommitdiff
path: root/livetimes.sh
blob: 29ffff389809e8fcc490c4c3418bcbd3a6e0d73c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/bin/bash

read stop

# function to provide data to a for loop
_jq() {
  echo ${json} | base64 --decode | jq -r ${1}
}

_liveresult() {
  stops=$(curl --fail --silent -H "App-Key: A4E247A48AAF12BF2E97D9F346E56" https://admin.libertybus.je/api/v1/stops/"${stop}")
  stopname=$(echo $stops | jq '.[].name')
  livetimes=$(curl --fail --silent -H "App-Key: A4E247A48AAF12BF2E97D9F346E56" https://admin.libertybus.je/api/v1/soj/stop_updates/${stop})
  exitcode=$?
  # sort livetimes by service_number and eta
  livetimes=$(echo ${livetimes} | jq 'sort_by((.service_number | sub("[^0-9]$"; "") | tonumber), (.service_number | sub("[^A-Z]$"; "") | tostring), .eta)')
  livetimeslen=$(echo ${livetimes} | jq '. | length')
  if [ $exitcode != 0 ]; then
    _curlerror
  elif [ $livetimeslen = 0 ]; then
    echo "No results found."
    exit 1
  elif [[ ${stopname,,} == *"bus"* ]] || [[ ${stopname,,} == *"station"* ]] || [[ ${stopname,,} == *"stand"* ]]; then
      _rejectbusstation
  else
    for json in $(echo "${livetimes}" | jq -r '.[] | @base64'); do
      if [[ $(_jq '.service_number') != $outroute ]]; then
        result=$(echo $result | sed 's/,$//')
        result="$result | Dest: $(_jq '.destination')[$(_jq '.service_number')] - ETAs: $(_jq '.eta'), "
      else
        result="$result $(_jq '.eta'), "
      fi
      outroute=$(_jq '.service_number')
    done
    result=$(echo $result | sed 's/,$//' | sed 's/|//' )
    echo -e $result
    exit 0
  fi
}

_stopsreturn() {
  stops=$(curl --fail --silent -H "App-Key: A4E247A48AAF12BF2E97D9F346E56" https://admin.libertybus.je/api/v1/stops/"${stop}")
  exitcode=$?
  stopslen=$(echo ${stops} | jq '. | length')
  # if length of $stops array is 1 then only one bus stop matches and we can return live times for that stop
  if [ $exitcode != 0 ]; then
    _curlerror
  elif [ $stopslen = 1 ]; then
    stop=$(echo ${stops} | jq '.[].stop_id')
    _liveresult
  elif [ $stopslen = 0 ]; then
    echo "No results found."
    exit 1
  # multiple stops returned in $stops so we will return stop names and codes so user can retry
  else
    for json in $(echo "${stops}" | jq -r '.[] | @base64'); do
      result="$result Stop: $(_jq '.name') - Code: $(_jq '.stop_id') |"
      #result="$result Code: $(_jq '.stop_id') |"
    done
    result=$(echo $result | sed 's/ |$//')
    echo -e $result
    exit 0
  fi
}

_rejectbusstation() {
  echo "If you want to see when the next bus for a given route leaves the bus station then try ""!bus <route>"" where <route> is a valid bus route or try ""!bus -h"" for help."
  exit 1
}

# function for now in case I need to reuse somewhere else...
_help() {
  echo ""$error"Usage: 'Usage: '!bus <input> (<time>)' where <input> is: a valid route number, this returns the next bus leaving the station, if <time> is also specified then the next bus after that time is returned; a full or partial bus stop name, if only one result is returned then live times for that stop are returned, if multiple results are returned then a list of matching stops are returned; a valid bus stop code, this returns live times for that stop; -h --help, returns this help info."
  exit 0
}

_curlerror() {
  echo "cURL error $exitcode - exiting."
  exit 1
}

_nextbus() {
  if [ -z ${time} ]; then
    time=$(date +"%H:%M")
  fi
  day=$(date +"%a" | tr [:upper:] [:lower:])
  # set routeday to one of three timetable options
  if [[ $(date +%u) -lt 6 ]]; then
    routeday=mon_fri
  else
    routeday=$(date +"%a" | tr [:upper:] [:lower:])
  fi
  # select all stops from todays trips, where days_of_operation is true for tday, for a given service_number, where stop_name contains "Bus Station Stand"
  nextbus=$(echo $timetables | jq -r --arg routeday "$routeday" --arg day "$day" --arg stop "$stop" '.timetables[] | select(.service_number == $stop) | .timetable.outbound[$routeday].trips[] | select(.days_of_operation[$day] == true) | .stops[] | select(.stop_name | contains("Bus Station Stand"))')
  for json in $(echo "${nextbus}"  | jq -r '. | @base64'); do
    departuretime=$(_jq '.departure_time' | sed s/://g)
    time=$(echo $time | sed s/://g)
    # if we allow midnight then day rollover could get messed up (e.g. 00:00 is specified on a Friday then it'll find next bus after 00:00 on Friday and not Saturday)
    if [[ $time == "0000" ]]; then
      time=2359
    fi
    if [[ ${departuretime#0} -ge ${time#0} ]]; then
      echo "Next route "$stop $futuretime"leaving from $(_jq '.stop_name') at $(_jq '.departure_time')"
      exit 0
    fi
  done
  # if program didn't exit inside for loop then we know the next bus is on a future day so we need to check future days, unfortunately plenty of routes have days where no bus runs so we need to loop until we find the next day with a running bus
  # for loop in range 7 used to prevent possible infinite loop with other methods - if there's not a bus for the next week then something is probably broken
  for i in {1..7}; do
    day=$(date --date="$i day" +"%a" | tr [:upper:] [:lower:])
    case $day in
      mon|tue|wed|thu|fri)
        routeday=mon_fri;;
      sat)
        routeday=sat;;
      sun)
        routeday=sun;;
      *) //
    esac
    nextbus=$(echo $timetables | jq -r --arg routeday "$routeday" --arg day "$day" --arg stop "$stop" '.timetables[] | select(.service_number == $stop) | .timetable.outbound[$routeday].trips[] | select(.days_of_operation[$day] == true) | .stops[] | select(.stop_name | contains("Bus Station Stand"))')
      if [[ ! -z "${nextbus// }" ]]; then
        echo $(echo "Next route "$stop $futuretime"leaving from $(echo $nextbus | jq -s '.[0].stop_name') at $(echo $nextbus | jq -s '.[0].departure_time') on $(date -d $day +%A)" | sed 's/"//g')
        exit 0
      fi
    done
    # if program didn't exit inside for loop then something is probably broken
    echo "Something broken - hopefully no one ever sees this..."
    exit 1
}

timetables=$(curl --fail --silent -H "App-Key: A4E247A48AAF12BF2E97D9F346E56" https://admin.libertybus.je/cache/timetables/timetable_full.json)
services=$(echo $timetables | jq '.timetables[].service_number')
if [ "$stop" == "-h" ] || [ "$stop" == "--help" ] || [[ -z "${stop// }" ]]; then
  _help
# checks if input pattern matches a 4 digit bus stop code
elif [[ ${stop,,} == "source" ]]; then
  echo "https://www.blatech.co.uk/ars/liberty-bus-live-times"
  exit 0
elif [[ "$stop" =~ ^-?[0-9]+$ ]] && [ $(echo ${#stop} == 4) ]; then
  _liveresult
# checks if input is a valid bus route
elif [[ ${services[@]} =~ ${stop^^} ]]; then
  _nextbus
# if input has one space and something resembling a time then the user is probably trying to search for the next bus leaving the station for a given route after the given time
elif [[ $(echo $stop | tr -cd ' \t' | wc -c) == 1 ]] && [[ "$stop" =~ [0-9][0-9]:[0-9][0-9] ]]; then
  time=$(echo $stop | cut -d ' ' -f2)
  stop=$(echo $stop | cut -d ' ' -f1)
  if [[ ! "$time" =~ ^(0[0-9]|1[0-9]|2[0-3]|[0-9]):[0-5][0-9]$ ]]; then
    # prepend help output with $error
    error="Invalid time, specify time with HH:MM '!bus <route> <time>' - "
    _help
  elif [[ ! ${services[@]} =~ ${stop^^} ]]; then
    echo $stop
    echo $services
    # prepend help output with $error
    error="Invalid route provided - "
    _help
  else
    # futuretime adds clarity to _nextbus output
    futuretime="after $time, "
    _nextbus
  fi
elif [[ ${stop,,} == *"bus"* ]] || [[ ${stop,,} == *"bus station"* ]] || [[ ${stop,,} == *"stand"* ]]; then
  _rejectbusstation
# input not matching any of the ifs can be searched against API
else
 _stopsreturn
fi