diff --git a/go.mod b/go.mod index 55feec0..8defaad 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/beevik/etree v1.5.0 github.com/cavaliergopher/grab/v3 v3.0.1 github.com/go-chi/chi/v5 v5.1.0 + github.com/go-co-op/gocron/v2 v2.16.1 github.com/goccy/go-json v0.10.5 github.com/google/uuid v1.6.0 github.com/gorilla/sessions v1.4.0 @@ -26,7 +27,6 @@ require ( github.com/anacrolix/missinggo/v2 v2.7.3 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/go-co-op/gocron/v2 v2.16.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect @@ -36,6 +36,5 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect - github.com/stretchr/testify v1.10.0 // indirect golang.org/x/sys v0.30.0 // indirect ) diff --git a/go.sum b/go.sum index 5d7399d..f24598e 100644 --- a/go.sum +++ b/go.sum @@ -220,6 +220,8 @@ github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPy go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= diff --git a/internal/request/request.go b/internal/request/request.go index 53524b8..1dd0271 100644 --- a/internal/request/request.go +++ b/internal/request/request.go @@ -17,7 +17,6 @@ import ( "net" "net/http" "net/url" - "regexp" "strconv" "strings" "sync" @@ -340,29 +339,34 @@ func New(options ...ClientOption) *Client { } func ParseRateLimit(rateStr string) *rate.Limiter { - if rateStr == "" { - return nil - } - re := regexp.MustCompile(`(\d+)/(minute|second)`) - matches := re.FindStringSubmatch(rateStr) - if len(matches) != 3 { + parts := strings.SplitN(rateStr, "/", 2) + if len(parts) != 2 { return nil } - count, err := strconv.Atoi(matches[1]) - if err != nil { + // parse count + count, err := strconv.Atoi(strings.TrimSpace(parts[0])) + if err != nil || count <= 0 { return nil } - unit := matches[2] + // normalize unit + unit := strings.ToLower(strings.TrimSpace(parts[1])) + unit = strings.TrimSuffix(unit, "s") + burstSize := int(math.Ceil(float64(count) * 0.1)) + if burstSize < 1 { + burstSize = 1 + } + if burstSize > count { + burstSize = count + } switch unit { - case "minute": - reqsPerSecond := float64(count) / 60.0 - burstSize := int(math.Max(30, float64(count)*0.25)) - return rate.NewLimiter(rate.Limit(reqsPerSecond), burstSize) - case "second": - burstSize := int(math.Max(30, float64(count)*5)) + case "minute", "min": + return rate.NewLimiter(rate.Limit(float64(count)/60.0), burstSize) + case "second", "sec": return rate.NewLimiter(rate.Limit(float64(count)), burstSize) + case "hour", "hr": + return rate.NewLimiter(rate.Limit(float64(count)/3600.0), burstSize) default: return nil }