discordBots/cmd/dealsbot/api/steam.go

163 lines
3.5 KiB
Go
Raw Normal View History

2023-03-06 19:53:07 +00:00
package api
2023-03-04 11:54:08 +00:00
import (
"encoding/json"
"fmt"
"golang.org/x/net/html"
"io"
2023-11-05 16:03:52 +00:00
"log/slog"
2023-03-04 11:54:08 +00:00
"net/http"
"regexp"
)
type SteamStruct struct {
url string
baseUrl string
apiUrl string
idPrefix string
2023-03-06 18:02:40 +00:00
headers map[string]string
2023-03-04 11:54:08 +00:00
deals DealsMap
2023-11-05 16:03:52 +00:00
logger *slog.Logger
2023-03-04 11:54:08 +00:00
}
2023-11-05 16:03:52 +00:00
func NewSteamApi(logger *slog.Logger) SteamStruct {
2023-03-06 18:02:40 +00:00
steam := SteamStruct{
2023-03-04 11:54:08 +00:00
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-",
2023-03-06 18:02:40 +00:00
headers: make(map[string]string),
2023-03-04 11:54:08 +00:00
deals: make(map[string]Deal),
2023-11-05 16:03:52 +00:00
logger: logger,
2023-03-04 11:54:08 +00:00
}
2023-03-06 18:02:40 +00:00
steam.headers["Accept-Language"] = "en"
steam.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0"
return steam
2023-03-04 11:54:08 +00:00
}
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
2023-03-06 19:53:07 +00:00
func (e SteamStruct) Load() error {
2023-03-04 11:54:08 +00:00
client := &http.Client{}
reqStore, err := http.NewRequest("GET", e.url, nil)
if err != nil {
return err
}
2023-03-06 18:02:40 +00:00
for key, value := range e.headers {
reqStore.Header.Set(key, value)
}
2023-03-04 11:54:08 +00:00
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
}
2023-03-06 18:02:40 +00:00
for key, value := range e.headers {
reqApi.Header.Set(key, value)
}
2023-03-04 11:54:08 +00:00
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
}
2023-03-06 19:53:07 +00:00
func (e SteamStruct) Get() []Deal {
2023-03-04 11:54:08 +00:00
var deals []Deal
for _, deal := range e.deals {
deals = append(deals, deal)
}
return deals
}