v1.0.0 beta 21

This commit is contained in:
2026-03-16 10:39:33 +03:00
parent fb81bb91bd
commit 389ec9f9d7
8 changed files with 125 additions and 86 deletions

View File

@@ -22,7 +22,7 @@ type RateLimiter struct {
chatLocks map[int64]time.Time // per-chat cooldown timestamps
chatLimiters map[int64]*rate.Limiter // per-chat token buckets (1 req/sec)
chatMu sync.Mutex // protects chatLocks and chatLimiters
chatMu sync.RWMutex // protects chatLocks and chatLimiters
}
// NewRateLimiter creates a new RateLimiter with default limits.
@@ -107,9 +107,9 @@ func (rl *RateLimiter) Allow(chatID int64) bool {
}
// Check chat cooldown
rl.chatMu.Lock()
rl.chatMu.RLock()
chatUntil, ok := rl.chatLocks[chatID]
rl.chatMu.Unlock()
rl.chatMu.RUnlock()
if ok && !chatUntil.IsZero() && time.Now().Before(chatUntil) {
return false
}
@@ -135,11 +135,15 @@ func (rl *RateLimiter) Allow(chatID int64) bool {
// chatID == 0 means no specific chat context (e.g., inline query, webhook without chat).
func (rl *RateLimiter) Check(ctx context.Context, dropOverflow bool, chatID int64) error {
if dropOverflow {
if chatID != 0 && !rl.Allow(chatID) {
return ErrDropOverflow
}
if !rl.GlobalAllow() {
return ErrDropOverflow
if chatID != 0 {
if !rl.Allow(chatID) {
return ErrDropOverflow
}
} else {
if !rl.GlobalAllow() {
return ErrDropOverflow
}
}
} else if chatID != 0 {
if err := rl.Wait(ctx, chatID); err != nil {
@@ -175,9 +179,9 @@ func (rl *RateLimiter) waitForGlobalUnlock(ctx context.Context) error {
// waitForChatUnlock blocks until the specified chat's cooldown expires or context is done.
// Does not check token bucket — only cooldown.
func (rl *RateLimiter) waitForChatUnlock(ctx context.Context, chatID int64) error {
rl.chatMu.Lock()
rl.chatMu.RLock()
until, ok := rl.chatLocks[chatID]
rl.chatMu.Unlock()
rl.chatMu.RUnlock()
if !ok || until.IsZero() || time.Now().After(until) {
return nil

View File

@@ -49,11 +49,9 @@ func Encode[T any](w *multipart.Writer, req T) error {
switch field.Kind() {
case reflect.String:
if !isEmpty {
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(field.String()))
}
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(field.String()))
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fw, err = w.CreateFormField(fieldName)
@@ -65,11 +63,17 @@ func Encode[T any](w *multipart.Writer, req T) error {
if err == nil {
_, err = fw.Write([]byte(strconv.FormatUint(field.Uint(), 10)))
}
case reflect.Float32, reflect.Float64:
case reflect.Float32:
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(strconv.FormatFloat(field.Float(), 'f', -1, 32)))
}
case reflect.Float64:
fw, err = w.CreateFormField(fieldName)
if err == nil {
_, err = fw.Write([]byte(strconv.FormatFloat(field.Float(), 'f', -1, 64)))
}
case reflect.Bool:
fw, err = w.CreateFormField(fieldName)
if err == nil {
@@ -103,8 +107,12 @@ func Encode[T any](w *multipart.Writer, req T) error {
_, err = fw.Write([]byte(strconv.FormatUint(elem.Uint(), 10)))
case reflect.Bool:
_, err = fw.Write([]byte(strconv.FormatBool(elem.Bool())))
case reflect.Float32, reflect.Float64:
case reflect.Float32:
_, err = fw.Write([]byte(strconv.FormatFloat(elem.Float(), 'f', -1, 32)))
case reflect.Float64:
_, err = fw.Write([]byte(strconv.FormatFloat(elem.Float(), 'f', -1, 64)))
default:
continue
}
if err != nil {
break

View File

@@ -1,9 +1,9 @@
package utils
const (
VersionString = "1.0.0-beta.20"
VersionString = "1.0.0-beta.21"
VersionMajor = 1
VersionMinor = 0
VersionPatch = 0
VersionBeta = 20
VersionBeta = 21
)