Merge pull request #16 from prosolis/fix/deal-source-error-handling

Improve error handling across deal sources
This commit is contained in:
prosolis
2026-03-20 01:50:21 -07:00
committed by GitHub
4 changed files with 50 additions and 17 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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"`