Merge pull request #16 from prosolis/fix/deal-source-error-handling
Improve error handling across deal sources
This commit is contained in:
@@ -228,7 +228,7 @@ func main() {
|
|||||||
func populateInitialState(cfg *config.Config, db *database.DB) {
|
func populateInitialState(cfg *config.Config, db *database.DB) {
|
||||||
// Record CheapShark deals without posting
|
// Record CheapShark deals without posting
|
||||||
if cfg.HasSource("cheapshark") {
|
if cfg.HasSource("cheapshark") {
|
||||||
rawDeals, err := deals.FetchCheapSharkDeals(cfg.MaxPriceUSD, 10)
|
rawDeals, err := deals.FetchCheapSharkDeals(cfg.MaxPriceUSD, 60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Warn("first run: cheapshark fetch failed", "error", err)
|
slog.Warn("first run: cheapshark fetch failed", "error", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -290,7 +290,7 @@ func checkCheapShark(cfg *config.Config, db *database.DB, mx *matrix.Client, con
|
|||||||
slog.Debug("checking cheapshark deals")
|
slog.Debug("checking cheapshark deals")
|
||||||
conv.EnsureRates()
|
conv.EnsureRates()
|
||||||
|
|
||||||
rawDeals, err := deals.FetchCheapSharkDeals(cfg.MaxPriceUSD, 10)
|
rawDeals, err := deals.FetchCheapSharkDeals(cfg.MaxPriceUSD, 60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("cheapshark fetch failed", "error", err)
|
slog.Error("cheapshark fetch failed", "error", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ type cheapSharkRaw struct {
|
|||||||
|
|
||||||
// FetchCheapSharkDeals fetches deals from CheapShark API.
|
// FetchCheapSharkDeals fetches deals from CheapShark API.
|
||||||
func FetchCheapSharkDeals(maxPrice float64, pageSize int) ([]CheapSharkDeal, error) {
|
func FetchCheapSharkDeals(maxPrice float64, pageSize int) ([]CheapSharkDeal, error) {
|
||||||
url := fmt.Sprintf(
|
reqURL := fmt.Sprintf(
|
||||||
"https://www.cheapshark.com/api/1.0/deals?storeID=1,7,11,23&upperPrice=%d&sortBy=recent&desc=1&pageSize=%d",
|
"https://www.cheapshark.com/api/1.0/deals?storeID=1,7,11,23&upperPrice=%d&sortBy=recent&desc=1&pageSize=%d",
|
||||||
int(maxPrice), pageSize,
|
int(maxPrice), pageSize,
|
||||||
)
|
)
|
||||||
|
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(reqURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cheapshark request failed: %w", err)
|
return nil, fmt.Errorf("cheapshark request failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -71,11 +71,17 @@ func FetchCheapSharkDeals(maxPrice float64, pageSize int) ([]CheapSharkDeal, err
|
|||||||
|
|
||||||
var deals []CheapSharkDeal
|
var deals []CheapSharkDeal
|
||||||
for _, d := range raw {
|
for _, d := range raw {
|
||||||
sale, _ := strconv.ParseFloat(d.SalePrice, 64)
|
sale, err1 := strconv.ParseFloat(d.SalePrice, 64)
|
||||||
normal, _ := strconv.ParseFloat(d.NormalPrice, 64)
|
normal, err2 := strconv.ParseFloat(d.NormalPrice, 64)
|
||||||
savings, _ := strconv.ParseFloat(d.Savings, 64)
|
savings, err3 := strconv.ParseFloat(d.Savings, 64)
|
||||||
rating, _ := strconv.ParseFloat(d.DealRating, 64)
|
rating, _ := strconv.ParseFloat(d.DealRating, 64)
|
||||||
|
|
||||||
|
if err1 != nil || err2 != nil || err3 != nil {
|
||||||
|
slog.Warn("cheapshark: skipping deal with unparseable prices", "title", d.Title,
|
||||||
|
"salePrice", d.SalePrice, "normalPrice", d.NormalPrice, "savings", d.Savings)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
storeName := storeNames[d.StoreID]
|
storeName := storeNames[d.StoreID]
|
||||||
if storeName == "" {
|
if storeName == "" {
|
||||||
storeName = "Unknown"
|
storeName = "Unknown"
|
||||||
@@ -103,12 +109,12 @@ func FetchCheapSharkDeals(maxPrice float64, pageSize int) ([]CheapSharkDeal, err
|
|||||||
|
|
||||||
// SearchCheapSharkDeals searches for current deals matching a title query.
|
// SearchCheapSharkDeals searches for current deals matching a title query.
|
||||||
func SearchCheapSharkDeals(query string, maxResults int) ([]CheapSharkDeal, error) {
|
func SearchCheapSharkDeals(query string, maxResults int) ([]CheapSharkDeal, error) {
|
||||||
url := fmt.Sprintf(
|
reqURL := fmt.Sprintf(
|
||||||
"https://www.cheapshark.com/api/1.0/deals?storeID=1,7,11,23&sortBy=savings&desc=1&pageSize=%d&title=%s",
|
"https://www.cheapshark.com/api/1.0/deals?storeID=1,7,11,23&sortBy=savings&desc=1&pageSize=%d&title=%s",
|
||||||
maxResults, url.QueryEscape(query),
|
maxResults, url.QueryEscape(query),
|
||||||
)
|
)
|
||||||
|
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(reqURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cheapshark search failed: %w", err)
|
return nil, fmt.Errorf("cheapshark search failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -125,9 +131,14 @@ func SearchCheapSharkDeals(query string, maxResults int) ([]CheapSharkDeal, erro
|
|||||||
|
|
||||||
var results []CheapSharkDeal
|
var results []CheapSharkDeal
|
||||||
for _, d := range raw {
|
for _, d := range raw {
|
||||||
sale, _ := strconv.ParseFloat(d.SalePrice, 64)
|
sale, err1 := strconv.ParseFloat(d.SalePrice, 64)
|
||||||
normal, _ := strconv.ParseFloat(d.NormalPrice, 64)
|
normal, err2 := strconv.ParseFloat(d.NormalPrice, 64)
|
||||||
savings, _ := strconv.ParseFloat(d.Savings, 64)
|
savings, err3 := strconv.ParseFloat(d.Savings, 64)
|
||||||
|
|
||||||
|
if err1 != nil || err2 != nil || err3 != nil {
|
||||||
|
slog.Warn("cheapshark: skipping search result with unparseable prices", "title", d.Title)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if savings <= 0 {
|
if savings <= 0 {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package deals
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -88,6 +89,10 @@ func FetchEpicFreeGames() ([]EpicFreeGame, error) {
|
|||||||
if slug == "" && len(elem.CatalogNs.Mappings) > 0 {
|
if slug == "" && len(elem.CatalogNs.Mappings) > 0 {
|
||||||
slug = elem.CatalogNs.Mappings[0].PageSlug
|
slug = elem.CatalogNs.Mappings[0].PageSlug
|
||||||
}
|
}
|
||||||
|
if slug == "" {
|
||||||
|
slog.Warn("epic: skipping game with no slug", "title", elem.Title, "id", elem.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
storeURL := fmt.Sprintf("https://store.epicgames.com/en-US/p/%s", slug)
|
storeURL := fmt.Sprintf("https://store.epicgames.com/en-US/p/%s", slug)
|
||||||
|
|
||||||
// Check current free offers
|
// Check current free offers
|
||||||
@@ -96,8 +101,12 @@ func FetchEpicFreeGames() ([]EpicFreeGame, error) {
|
|||||||
if offer.DiscountSetting.DiscountPercentage != 0 {
|
if offer.DiscountSetting.DiscountPercentage != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
start, _ := time.Parse(time.RFC3339, offer.StartDate)
|
start, errS := time.Parse(time.RFC3339, offer.StartDate)
|
||||||
end, _ := time.Parse(time.RFC3339, offer.EndDate)
|
end, errE := time.Parse(time.RFC3339, offer.EndDate)
|
||||||
|
if errS != nil || errE != nil {
|
||||||
|
slog.Warn("epic: failed to parse offer dates", "title", elem.Title, "start", offer.StartDate, "end", offer.EndDate)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if start.After(now) || end.Before(now) {
|
if start.After(now) || end.Before(now) {
|
||||||
continue
|
continue
|
||||||
@@ -109,7 +118,7 @@ func FetchEpicFreeGames() ([]EpicFreeGame, error) {
|
|||||||
Desc: elem.Description,
|
Desc: elem.Description,
|
||||||
URL: storeURL,
|
URL: storeURL,
|
||||||
Upcoming: false,
|
Upcoming: false,
|
||||||
DedupID: fmt.Sprintf("epic-%s", elem.ID),
|
DedupID: fmt.Sprintf("epic-current-%s", elem.ID),
|
||||||
}
|
}
|
||||||
if !end.IsZero() {
|
if !end.IsZero() {
|
||||||
game.EndDate = &end
|
game.EndDate = &end
|
||||||
@@ -131,7 +140,7 @@ func FetchEpicFreeGames() ([]EpicFreeGame, error) {
|
|||||||
Desc: elem.Description,
|
Desc: elem.Description,
|
||||||
URL: storeURL,
|
URL: storeURL,
|
||||||
Upcoming: true,
|
Upcoming: true,
|
||||||
DedupID: fmt.Sprintf("epic-%s", elem.ID),
|
DedupID: fmt.Sprintf("epic-upcoming-%s", elem.ID),
|
||||||
}
|
}
|
||||||
if offer.EndDate != "" {
|
if offer.EndDate != "" {
|
||||||
if end, err := time.Parse(time.RFC3339, offer.EndDate); err == nil {
|
if end, err := time.Parse(time.RFC3339, offer.EndDate); err == nil {
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ func LookupHistoricalLows(apiKey string, steamAppIDs []string) (map[string]bool,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
slog.Warn("itad lookup returned non-200", "appid", appID, "status", resp.StatusCode)
|
||||||
|
resp.Body.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var lookup struct {
|
var lookup struct {
|
||||||
Found bool `json:"found"`
|
Found bool `json:"found"`
|
||||||
Game struct {
|
Game struct {
|
||||||
@@ -195,7 +201,10 @@ func LookupHistoricalLows(apiKey string, steamAppIDs []string) (map[string]bool,
|
|||||||
itadToSteam[itadID] = steamID
|
itadToSteam[itadID] = steamID
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := json.Marshal(gameIDs)
|
body, err := json.Marshal(gameIDs)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("itad overview marshal failed: %w", err)
|
||||||
|
}
|
||||||
url := fmt.Sprintf("https://api.isthereanydeal.com/games/overview/v2?key=%s", apiKey)
|
url := fmt.Sprintf("https://api.isthereanydeal.com/games/overview/v2?key=%s", apiKey)
|
||||||
resp, err := http.Post(url, "application/json", bytes.NewReader(body))
|
resp, err := http.Post(url, "application/json", bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -203,6 +212,10 @@ func LookupHistoricalLows(apiKey string, steamAppIDs []string) (map[string]bool,
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return result, fmt.Errorf("itad overview returned status %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
var overview struct {
|
var overview struct {
|
||||||
Prices []struct {
|
Prices []struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|||||||
Reference in New Issue
Block a user