150 lines
3.3 KiB
Go
150 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"golang.org/x/net/html"
|
|
"log/slog"
|
|
"net/http"
|
|
"regexp"
|
|
)
|
|
|
|
type HumbleBundleStruct struct {
|
|
url string
|
|
baseUrl string
|
|
idPrefix string
|
|
headers map[string]string
|
|
deals DealsMap
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewHumbleBundleApi(logger *slog.Logger) HumbleBundleStruct {
|
|
humbleBundle := HumbleBundleStruct{
|
|
url: "https://www.humblebundle.com/",
|
|
baseUrl: "https://www.humblebundle.com/store/",
|
|
idPrefix: "humblebundle-",
|
|
headers: make(map[string]string),
|
|
deals: make(map[string]Deal),
|
|
logger: logger,
|
|
}
|
|
humbleBundle.headers["Accept-Language"] = "en"
|
|
humbleBundle.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0"
|
|
return humbleBundle
|
|
|
|
}
|
|
|
|
type humblebundleJsonBody struct {
|
|
Mosaic []struct {
|
|
Products []struct {
|
|
ProductUrl string `json:"product_url,omitempty"`
|
|
Highlights []string `json:"highlights,omitempty"`
|
|
TileName string `json:"tile_name,omitempty"`
|
|
ProductTitle *string `json:"product_title,omitempty"`
|
|
Type string `json:"type"`
|
|
Category string `json:"category,omitempty"`
|
|
EndDateDatetime string `json:"end_date|datetime,omitempty"`
|
|
OperatingSystems []string `json:"operating_systems,omitempty"`
|
|
Platforms []string `json:"platforms,omitempty"`
|
|
} `json:"products"`
|
|
} `json:"mosaic"`
|
|
}
|
|
|
|
func (e HumbleBundleStruct) Load() error {
|
|
client := &http.Client{}
|
|
// might have to add a cookie at a later time but currently works without
|
|
// "Cookie", "gog_lc=GB_GBP_en-US" or "Accept-Language", "en"
|
|
reqStore, err := http.NewRequest("GET", e.url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for key, value := range e.headers {
|
|
reqStore.Header.Set(key, value)
|
|
}
|
|
|
|
resStore, err := client.Do(reqStore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bodyStore := html.NewTokenizer(resStore.Body)
|
|
|
|
var data humblebundleJsonBody
|
|
err = func() error {
|
|
for {
|
|
tt := bodyStore.Next()
|
|
|
|
switch {
|
|
case tt == html.ErrorToken:
|
|
// file end or error
|
|
return nil
|
|
case tt == html.StartTagToken:
|
|
t := bodyStore.Token()
|
|
if t.Data != "script" {
|
|
continue
|
|
}
|
|
for _, a := range t.Attr {
|
|
if !(a.Key == "id" && a.Val == "webpack-json-data") {
|
|
continue
|
|
}
|
|
if tt = bodyStore.Next(); tt != html.TextToken {
|
|
continue
|
|
}
|
|
err = json.Unmarshal([]byte(bodyStore.Token().Data), &data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
regexAppid, err := regexp.Compile(`/store/([-\w]+)`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, products := range data.Mosaic {
|
|
for _, product := range products.Products {
|
|
if !contains(product.Highlights, "FREE WHILE SUPPLIES LAST") {
|
|
continue
|
|
}
|
|
|
|
appID := regexAppid.FindStringSubmatch(product.ProductUrl)
|
|
if len(appID) < 1 {
|
|
continue
|
|
}
|
|
id := fmt.Sprintf("%v%v", e.idPrefix, appID[1])
|
|
title := product.TileName
|
|
url := fmt.Sprintf("%v%v", e.baseUrl, appID[1])
|
|
|
|
e.deals[id] = Deal{
|
|
Id: id,
|
|
Title: title,
|
|
Url: url,
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func contains(s []string, str string) bool {
|
|
for _, v := range s {
|
|
if v == str {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (e HumbleBundleStruct) Get() []Deal {
|
|
var deals []Deal
|
|
for _, deal := range e.deals {
|
|
deals = append(deals, deal)
|
|
}
|
|
return deals
|
|
}
|