diff --git a/internal/plugin/dnd_expedition_camp.go b/internal/plugin/dnd_expedition_camp.go index e6cbbf8..0b8f876 100644 --- a/internal/plugin/dnd_expedition_camp.go +++ b/internal/plugin/dnd_expedition_camp.go @@ -2,6 +2,7 @@ package plugin import ( "fmt" + "log/slog" "math/rand/v2" "strings" "time" @@ -369,7 +370,13 @@ func campLocationCheck(exp *Expedition) (cleared bool, problem string) { encID := encounterIDForRoom(run.CurrentRoom) sess, err := getCombatSessionForEncounter(run.RunID, encID) if err != nil { - return false, "" + // Can't read the encounter's combat state. Fail open like the + // run-lookup path above rather than blocking standard camp with an + // empty (misleading "not cleared") rejection — a DB hiccup shouldn't + // strand a player who only wants to rest. + slog.Warn("camp: combat session lookup failed; allowing camp", + "run", run.RunID, "encounter", encID, "err", err) + return true, "" } if sess != nil && sess.Status == CombatStatusActive { return false, "You can't camp mid-fight — finish the encounter first." diff --git a/internal/plugin/forex_crypto.go b/internal/plugin/forex_crypto.go index e71c094..29b3595 100644 --- a/internal/plugin/forex_crypto.go +++ b/internal/plugin/forex_crypto.go @@ -4,7 +4,9 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" + "net/url" "strings" "sync" "time" @@ -82,8 +84,11 @@ func (p *ForexPlugin) cgSpotUSD(sym string) (float64, error) { ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) defer cancel() - url := fmt.Sprintf("%s/simple/price?ids=%s&vs_currencies=usd", coinGeckoBaseURL, info.CoinGeckoID) - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + q := url.Values{} + q.Set("ids", info.CoinGeckoID) + q.Set("vs_currencies", "usd") + reqURL := coinGeckoBaseURL + "/simple/price?" + q.Encode() + req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil) if err != nil { return 0, err } @@ -99,7 +104,7 @@ func (p *ForexPlugin) cgSpotUSD(sym string) (float64, error) { } var data map[string]map[string]float64 - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + if err := json.NewDecoder(io.LimitReader(resp.Body, 64<<10)).Decode(&data); err != nil { return 0, fmt.Errorf("coingecko decode error: %w", err) } entry, ok := data[info.CoinGeckoID]