package main import ( "encoding/json" "errors" "fmt" "html/template" "io" "log" "net/http" "os" "sort" "strconv" "strings" ) var stopIDs map[int] int; type Favourite struct { Name string `json:"name"` Code string `json:"code"` ID string `json:"id"` } type CarFlags struct { Disabled bool `json:"disabled"` AirConditioner bool `json:"klimatik"` BicycleRack bool `json:"bicycle_rack"` DoubleDecker bool `json:"doubledecker"` ByTimeTable bool `json:"by_timetable"` } type Car struct { DepartureTime string `json:"departure_time"` Flags CarFlags `json:"flags"` Destination int `json:"destination"` CourseID int `json:"course_id"` DestinationName string `json:"destination_name"` CalculatedTime string `json:"calc_time"` DirectionFlag int `json:"direction_flag"` TTCourseStopID int `json:"ttcoursestop_id"` } type Line struct { Transport string `json:"transport"` ID int `json:"id"` Name string `json:"name"` Cars []Car `json:"cars"` Color string } type VirtualPanelData struct { ID int `json:"id"` Lines []Line `json:"lines"` } type SofiaTraffic struct { Data VirtualPanelData `json:"virtual_panel_data"` } type OutputData struct { ID string Lines []Line } type CacheData struct { TargetID int `json:"target_id"` DataID int `json:"data_id"` } func lessLine(left Line, right Line) bool { val := strings.Compare(left.Transport, right.Transport) if val != 0 { return val < 0 } else { l, lerr := strconv.Atoi(left.Name) r, rerr := strconv.Atoi(right.Name) switch { case lerr == nil && rerr == nil: return l < r case lerr == nil && rerr != nil: return true case lerr != nil && rerr == nil: return false case lerr != nil && rerr != nil: val = strings.Compare(left.Name, right.Name) return val < 0 } } return true } func handle(resp http.ResponseWriter, req *http.Request) { id := strings.TrimPrefix(req.URL.Path, "/") find_id := req.URL.Query().Get("find") if len(find_id) != 0 { find_int, err := strconv.Atoi(find_id) if err != nil { log.Println(err) show500(resp) return } target_id := stopIDs[find_int] http.Redirect(resp, req, fmt.Sprintf("/%d", target_id), http.StatusFound) } else if len(id) == 0 { showFavourites(resp) } else { showTable(resp, id) } } func show500(resp http.ResponseWriter) { resp.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(resp, "

500 Internal Server Error

") } func setLineColor(line *Line) { switch { case line.Transport == "A": line.Color = "FF0000" case line.Transport == "ТБ": line.Color = "0000FF" case line.Transport == "ТМ": line.Color = "FF8000" } } func getSofiaTrafficData(id string) (SofiaTraffic, error) { var data SofiaTraffic url := fmt.Sprintf("https://www.sofiatraffic.bg/interactivecard/virtual_panel?stop_id=%s", id) respAPI, err := http.Get(url) if err != nil { log.Println(err) return data, errors.New("get failed") } if respAPI.StatusCode != 200 { log.Printf("Status code for ID %v is %v", id, respAPI.StatusCode) return data, errors.New("bad status code") } if respAPI.Body != nil { defer respAPI.Body.Close() } body, err := io.ReadAll(respAPI.Body) if err != nil { log.Println(err) return data, errors.New("failed to read body") } err = json.Unmarshal(body, &data) if err != nil { log.Println(err) return data, errors.New("failed to unmarshal json") } return data, nil } func showTable(resp http.ResponseWriter, id string) { data, err := getSofiaTrafficData(id) if err != nil { show500(resp) return } // Sort Lines sort.Slice(data.Data.Lines, func (i int, j int) bool { return lessLine(data.Data.Lines[i], data.Data.Lines[j]) }) // Set Line Colors for idx := range(data.Data.Lines) { setLineColor(&(data.Data.Lines[idx])) } var dataOut OutputData dataOut.ID = fmt.Sprintf("%04d", data.Data.ID) dataOut.Lines = data.Data.Lines templ := template.Must(template.ParseFiles("stop.html")) templ.Execute(resp, dataOut) } func readFavourites() []Favourite { var favourites []Favourite jsonFile, err := os.Open("favourites.json") if err != nil { log.Println(err) return favourites } defer jsonFile.Close() bytes, err := io.ReadAll(jsonFile) if err != nil { log.Println(err) return favourites } err = json.Unmarshal(bytes, &favourites) if err != nil { log.Println(err) } return favourites } func showFavourites(resp http.ResponseWriter) { // Read And Parse JSON favourites := readFavourites() data := make(map[string] []Favourite) data["Favourites"] = favourites // Read And Execute Template templ := template.Must(template.ParseFiles("index.html")) templ.Execute(resp, data) } func loadIDs() { stopIDs = make(map[int]int) var cache []CacheData jsonFile, err := os.Open("cache.json") if err != nil { log.Println(err) return } defer jsonFile.Close() bytes, err := io.ReadAll(jsonFile) if err != nil { log.Println(err) return } err = json.Unmarshal(bytes, &cache) if err != nil { log.Println(err) return } for _, val := range(cache) { stopIDs[val.TargetID] = val.DataID } } func main() { loadIDs() http.HandleFunc("/", handle) log.Println("Server Running") http.ListenAndServe(":8000", nil) }