aboutsummaryrefslogtreecommitdiffstats
path: root/atb/atb.go
blob: eb629a49681cda3de74296e046db20535c56549f (plain) (blame)
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
package atb

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// DefaultURL is the default AtB API URL.
const DefaultURL = "http://st.atb.no/New/InfoTransit/UserServices.asmx"

// Client represents a client which communicates with AtBs API.
type Client struct {
	Username string
	Password string
	URL      string
}

// BusStops represents a list of bus stops.
type BusStops struct {
	Stops []BusStop `json:"Fermate"`
}

// BusStop represents a bus stop.
type BusStop struct {
	StopID      int    `json:"cinFermata"`
	NodeID      string `json:"codAzNodo"`
	Description string `json:"descrizione"`
	Longitude   string `json:"lon"`
	Latitude    int    `json:"lat"`
	MobileCode  string `json:"codeMobile"`
	MobileName  string `json:"nomeMobile"`
}

// Forecasts represents a list of forecasts.
type Forecasts struct {
	Nodes     []NodeInfo `json:"InfoNodo"`
	Forecasts []Forecast `json:"Orari"`
	Total     int        `json:"total"`
}

// NodeInfo represents a bus stop, returned as a part of a forecast.
type NodeInfo struct {
	Name              string `json:"nome_Az"`
	NodeID            string `json:"codAzNodo"`
	NodeName          string `json:"nomeNodo"`
	NodeDescription   string `json:"descrNodo"`
	BitMaskProperties string `json:"bitMaskProprieta"`
	MobileCode        string `json:"codeMobile"`
	Longitude         string `json:"coordLon"`
	Latitude          string `json:"coordLat"`
}

// Forecast represents a single forecast.
type Forecast struct {
	LineID                  string `json:"codAzLinea"`
	LineDescription         string `json:"descrizioneLinea"`
	RegisteredDepartureTime string `json:"orario"`
	ScheduledDepartureTime  string `json:"orarioSched"`
	StationForecast         string `json:"statoPrevisione"`
	Destination             string `json:"capDest"`
}

type busStopsRequest struct {
	XMLName xml.Name `xml:"Envelope"`
	Result  []byte   `xml:"Body>GetBusStopsListResponse>GetBusStopsListResult"`
}

type forecastRequest struct {
	XMLName xml.Name `xml:"Envelope"`
	Result  []byte   `xml:"Body>getUserRealTimeForecastByStopResponse>getUserRealTimeForecastByStopResult"`
}

// NewFromConfig creates a new client where name is the path to the config file.
func NewFromConfig(name string) (*Client, error) {
	data, err := ioutil.ReadFile(name)
	if err != nil {
		return nil, err
	}
	var client Client
	if err := json.Unmarshal(data, &client); err != nil {
		return nil, err
	}
	if client.URL == "" {
		client.URL = DefaultURL
	}
	return &client, nil
}

func (c *Client) postXML(body string, dst interface{}) error {
	resp, err := http.Post(c.URL, "application/soap+xml", strings.NewReader(body))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	dec := xml.NewDecoder(resp.Body)
	if err := dec.Decode(&dst); err != nil {
		return err
	}
	return nil
}

// BusStops retrieves bus stops from AtBs API.
func (c *Client) BusStops() (BusStops, error) {
	req := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <GetBusStopsList xmlns="http://miz.it/infotransit">
      <auth>
        <user>%s</user>
        <password>%s</password>
      </auth>
    </GetBusStopsList>
  </soap12:Body>
</soap12:Envelope>`, c.Username, c.Password)

	var stopsRequest busStopsRequest
	if err := c.postXML(req, &stopsRequest); err != nil {
		return BusStops{}, err
	}

	var stops BusStops
	if err := json.Unmarshal(stopsRequest.Result, &stops); err != nil {
		return BusStops{}, err
	}
	return stops, nil
}

// Forecasts retrieves forecasts from AtBs API, using nodeID to identify the bus stop.
func (c *Client) Forecasts(nodeID int) (Forecasts, error) {
	req := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <getUserRealTimeForecastByStop xmlns="http://miz.it/infotransit">
      <auth>
        <user>%s</user>
        <password>%s</password>
      </auth>
      <busStopId>%d</busStopId>
    </getUserRealTimeForecastByStop>
  </soap12:Body>
</soap12:Envelope>`, c.Username, c.Password, nodeID)

	var forecastRequest forecastRequest
	if err := c.postXML(req, &forecastRequest); err != nil {
		return Forecasts{}, err
	}

	var forecasts Forecasts
	if err := json.Unmarshal(forecastRequest.Result, &forecasts); err != nil {
		return Forecasts{}, err
	}
	return forecasts, nil
}