package api import ( "encoding/json" "fmt" "io" "log" "math" "net/http" "net/url" "strconv" "strings" "time" "github.com/gin-gonic/gin" ) type IDFMMonitoredStopVisit struct { RecordedAtTime time.Time `json:"RecordedAtTime"` ItemIdentifier string `json:"ItemIdentifier"` MonitoringRef struct { Value string `json:"value"` } `json:"MonitoringRef"` MonitoredVehicleJourney struct { LineRef struct { Value string `json:"value"` } `json:"LineRef"` OperatorRef struct { Value string `json:"value"` } `json:"OperatorRef"` FramedVehicleJourneyRef struct { DataFrameRef struct { Value string `json:"value"` } `json:"DataFrameRef"` DatedVehicleJourneyRef string `json:"DatedVehicleJourneyRef"` } `json:"FramedVehicleJourneyRef"` DirectionName []struct { Value string `json:"value"` } `json:"DirectionName"` DestinationRef struct { Value string `json:"value"` } `json:"DestinationRef"` DestinationName []struct { Value string `json:"value"` } `json:"DestinationName"` JourneyNote []struct { Value string `json:"value"` } `json:"JourneyNote"` MonitoredCall struct { StopPointName []struct { Value string `json:"value"` } `json:"StopPointName"` VehicleAtStop bool `json:"VehicleAtStop"` DestinationDisplay []struct { Value string `json:"value"` } `json:"DestinationDisplay"` ExpectedArrivalTime time.Time `json:"ExpectedArrivalTime"` ExpectedDepartureTime time.Time `json:"ExpectedDepartureTime"` DepartureStatus string `json:"DepartureStatus"` } `json:"MonitoredCall"` } `json:"MonitoredVehicleJourney"` } type IDFMRealTime struct { Siri struct { ServiceDelivery struct { ResponseTimestamp time.Time `json:"ResponseTimestamp"` ProducerRef string `json:"ProducerRef"` ResponseMessageIdentifier string `json:"ResponseMessageIdentifier"` StopMonitoringDelivery []struct { ResponseTimestamp time.Time `json:"ResponseTimestamp"` Version string `json:"Version"` Status string `json:"Status"` MonitoredStopVisit []IDFMMonitoredStopVisit `json:"MonitoredStopVisit"` } `json:"StopMonitoringDelivery"` } `json:"ServiceDelivery"` } `json:"siri"` } type PGSchedule struct { Destination string `json:"destination"` Message string `json:"message"` } func convertLineCode(code string) string { return strings.TrimSuffix(code, ":") } func getRealTime(code string, stations []string) ([]IDFMMonitoredStopVisit, error) { rurl, err := url.JoinPath(IDFM_BASEURL, "stop-monitoring") if err != nil { return nil, err } requrl, err := url.Parse(rurl) if err != nil { return nil, err } var stops []IDFMMonitoredStopVisit for _, station := range stations { reqquery := url.Values{} reqquery.Add("MonitoringRef", station) reqquery.Add("LineRef", "STIF:Line::"+code+":") requrl.RawQuery = reqquery.Encode() req, err := http.NewRequest("GET", requrl.String(), nil) if err != nil { return stops, err } req.Header.Add("Accept", "application/json") req.Header.Add("apikey", IDFM_TOKEN) res, err := http.DefaultClient.Do(req) if err != nil { return stops, err } defer res.Body.Close() if res.StatusCode >= 400 { v, _ := io.ReadAll(res.Body) log.Println("Schedule not found: ", string(v)) return nil, fmt.Errorf("Schedule not found") } var schedules IDFMRealTime dec := json.NewDecoder(res.Body) if err = dec.Decode(&schedules); err != nil { return stops, err } for _, smd := range schedules.Siri.ServiceDelivery.StopMonitoringDelivery { stops = append(stops, smd.MonitoredStopVisit...) } } return stops, nil } func declareSchedulesRoutes(router *gin.RouterGroup) { router.GET("/schedules/:type/:code/:station/:way", func(c *gin.Context) { t := convertLineType(string(c.Param("type"))) code := searchLine(t, string(c.Param("code"))) station := string(c.Param("station")) way := string(c.Param("way")) var stations []string if !strings.HasPrefix(station, "STIF:Stop") { if _, err := strconv.ParseInt(station, 10, 64); err != nil { stations, err = searchStation(code, station) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } } else { stations = []string{"STIF:StopArea:IDFM:SP:" + station + ":"} } } else { stations = []string{station} } if way != "A+R" && len(stations) == 2 { if way == "A" { stations = []string{stations[0]} } else { stations = []string{stations[1]} } } schedules, err := getRealTime(code, stations) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } pgs := []PGSchedule{} for _, vehicule := range schedules { msg := vehicule.MonitoredVehicleJourney.MonitoredCall.ExpectedDepartureTime.String() if t == "metro" || t == "bus" || t == "noctiliens" || t == "tramway" { if vehicule.MonitoredVehicleJourney.MonitoredCall.VehicleAtStop { if t == "metro" { msg = "Train à quai" } else { msg = "A l'arret" } } else if time.Until(vehicule.MonitoredVehicleJourney.MonitoredCall.ExpectedDepartureTime) < 0 { if t == "metro" { msg = "Train retardé" } else { msg = "…" } } else { msg = fmt.Sprintf("%d mn", int(math.Floor(time.Until(vehicule.MonitoredVehicleJourney.MonitoredCall.ExpectedDepartureTime).Minutes()))) } } pgs = append(pgs, PGSchedule{ Destination: vehicule.MonitoredVehicleJourney.MonitoredCall.DestinationDisplay[0].Value, Message: msg, }) } c.JSON(http.StatusOK, APIResult(c, map[string][]PGSchedule{ "schedules": pgs, })) }) }