package main import ( "encoding/json" "fmt" "golang.org/x/net/html" "io" "net/http" "regexp" ) type SteamStruct struct { url string baseUrl string apiUrl string idPrefix string deals DealsMap } func newSteamApi() SteamStruct { return SteamStruct{ url: "https://store.steampowered.com/search/results?force_infinite=1&maxprice=free&specials=1", baseUrl: "https://store.steampowered.com/app/", apiUrl: "https://store.steampowered.com/api/appdetails?appids=", idPrefix: "steam-", deals: make(map[string]Deal), } } type steamApiBodyGame struct { Success bool `json:"success"` Data struct { Type string `json:"type"` Name string `json:"name"` IsFree bool `json:"is_free"` PriceOverview struct { Currency string `json:"currency"` Initial int `json:"initial"` Final int `json:"final"` DiscountPercent int `json:"discount_percent"` InitialFormatted string `json:"initial_formatted"` FinalFormatted string `json:"final_formatted"` } `json:"price_overview"` Platforms struct { Windows bool `json:"windows"` Mac bool `json:"mac"` Linux bool `json:"linux"` } `json:"platforms"` } `json:"data"` } type steamApiBody map[string]steamApiBodyGame func (e SteamStruct) load() error { client := &http.Client{} reqStore, err := http.NewRequest("GET", e.url, nil) if err != nil { return err } resStore, err := client.Do(reqStore) if err != nil { return err } bodyStore := html.NewTokenizer(resStore.Body) // could also search over each #search_resultsRows element instead of regex regexAppid, err := regexp.Compile(`https://store\.steampowered\.com/app/(\d+)`) if err != nil { return err } var appIDs []string func() { for { tt := bodyStore.Next() switch { case tt == html.ErrorToken: // file end or error return case tt == html.StartTagToken: t := bodyStore.Token() if t.Data != "a" { continue } for _, a := range t.Attr { if a.Key != "href" { continue } appID := regexAppid.FindStringSubmatch(a.Val) if len(appID) < 1 { continue } appIDs = append(appIDs, appID[1]) } } } }() for _, appID := range appIDs { reqApi, err := http.NewRequest("GET", fmt.Sprintf("%v%v", e.apiUrl, appID), nil) if err != nil { return err } resApi, err := client.Do(reqApi) if err != nil { return err } bodyApi, err := io.ReadAll(resApi.Body) if err != nil { return err } var data steamApiBody err = json.Unmarshal(bodyApi, &data) if err != nil { return err } if game, ok := data[appID]; ok { if game.Data.Type != "game" { continue } id := fmt.Sprintf("%v%v", e.idPrefix, appID) title := game.Data.Name url := fmt.Sprintf("%v%v", e.baseUrl, appID) e.deals[id] = Deal{ Id: id, Title: title, Url: url, } } } return nil } func (e SteamStruct) get() []Deal { var deals []Deal for _, deal := range e.deals { deals = append(deals, deal) } return deals }