feat(channels/qq_obv11): OneBot v11 渠道插件(正/反 WS 双模式 + CQ 媒体 + 限频,stdlib 自研 WS)

This commit is contained in:
Aska committed 2026-09-06 06:46:43 +08:00
1 parent f9f5a78a02
commit 7831b0ea1b
11 files changed
+2526

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
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
}