release: 1.0.0 beta 22

Implemented full tgapi method coverage from Telegram docs, aligned numeric ID/file_size types, and fixed method signatures/JSON tags.; Standardized GoDoc across exported APIs with Telegram links and refreshed README sections for MsgContext plus API/Uploader usage.
This commit is contained in:
2026-03-17 13:21:06 +03:00
parent 389ec9f9d7
commit 1e043da05d
48 changed files with 921 additions and 284 deletions

View File

@@ -36,6 +36,17 @@ func NewRateLimiter() *RateLimiter {
}
}
// SetGlobalRate overrides global request-per-second limit and burst.
// If rps <= 0, current settings are kept.
func (rl *RateLimiter) SetGlobalRate(rps int) {
if rps <= 0 {
return
}
rl.globalMu.Lock()
defer rl.globalMu.Unlock()
rl.globalLimiter = rate.NewLimiter(rate.Limit(rps), rps)
}
// SetGlobalLock sets a global cooldown period (e.g., after receiving 429 from Telegram).
// If retryAfter <= 0, no lock is applied.
func (rl *RateLimiter) SetGlobalLock(retryAfter int) {
@@ -64,7 +75,11 @@ func (rl *RateLimiter) GlobalWait(ctx context.Context) error {
if err := rl.waitForGlobalUnlock(ctx); err != nil {
return err
}
return rl.globalLimiter.Wait(ctx)
limiter := rl.getGlobalLimiter()
if limiter == nil {
return nil
}
return limiter.Wait(ctx)
}
// Wait blocks until a request for the given chat can be made.
@@ -77,8 +92,21 @@ func (rl *RateLimiter) Wait(ctx context.Context, chatID int64) error {
if err := rl.waitForGlobalUnlock(ctx); err != nil {
return err
}
limiter := rl.getChatLimiter(chatID)
return limiter.Wait(ctx)
limiter := rl.getGlobalLimiter()
if limiter != nil {
if err := limiter.Wait(ctx); err != nil {
return err
}
}
chatLimiter := rl.getChatLimiter(chatID)
return chatLimiter.Wait(ctx)
}
// getGlobalLimiter returns the global limiter safely under read lock.
func (rl *RateLimiter) getGlobalLimiter() *rate.Limiter {
rl.globalMu.RLock()
defer rl.globalMu.RUnlock()
return rl.globalLimiter
}
// GlobalAllow checks if a global request can be made without blocking.
@@ -91,7 +119,11 @@ func (rl *RateLimiter) GlobalAllow() bool {
if !until.IsZero() && time.Now().Before(until) {
return false
}
return rl.globalLimiter.Allow()
limiter := rl.getGlobalLimiter()
if limiter == nil {
return true
}
return limiter.Allow()
}
// Allow checks if a request for the given chat can be made without blocking.
@@ -115,13 +147,14 @@ func (rl *RateLimiter) Allow(chatID int64) bool {
}
// Check global token bucket
if !rl.globalLimiter.Allow() {
limiter := rl.getGlobalLimiter()
if limiter != nil && !limiter.Allow() {
return false
}
// Check chat token bucket
limiter := rl.getChatLimiter(chatID)
return limiter.Allow()
chatLimiter := rl.getChatLimiter(chatID)
return chatLimiter.Allow()
}
// Check applies rate limiting based on configuration.

View File

@@ -10,6 +10,7 @@ import (
"strings"
)
// Encode writes struct fields into multipart form-data using json tags as field names.
func Encode[T any](w *multipart.Writer, req T) error {
v := reflect.ValueOf(req)
if v.Kind() == reflect.Ptr {

View File

@@ -6,6 +6,7 @@ import (
"git.nix13.pw/scuroneko/slog"
)
// GetLoggerLevel returns DEBUG when DEBUG=true in env, otherwise FATAL.
func GetLoggerLevel() slog.LogLevel {
level := slog.FATAL
if os.Getenv("DEBUG") == "true" {

View File

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