package qq_obv11 // This file implements the sliding-window send rate limiter that keeps QQ // OneBot proactive/reply traffic from bursting (see DevDocs/design/08 section // 3, "注意风控"). import ( "errors" "sync" "time" ) // ErrRateLimited is returned by SendMessage/SendProactive when the configured // per-window send quota is exhausted. var ErrRateLimited = errors.New("qq-obv11: send rate limit exceeded") // rateLimiter enforces a maximum number of events within a sliding window. It // is disabled when max is non-positive. type rateLimiter struct { max int window time.Duration mu sync.Mutex times []time.Time } func newRateLimiter(max int, window time.Duration) *rateLimiter { return &rateLimiter{max: max, window: window} } // allow records a send at now and reports whether it fits within the window // quota. A disabled limiter (max <= 0) always allows. The timestamp is only // recorded when the send is accepted. func (r *rateLimiter) allow(now time.Time) bool { if r == nil || r.max <= 0 { return true } r.mu.Lock() defer r.mu.Unlock() cutoff := now.Add(-r.window) i := 0 for i < len(r.times) && !r.times[i].After(cutoff) { i++ } r.times = r.times[i:] if len(r.times) >= r.max { return false } r.times = append(r.times, now) return true }