fix(vk): определять ошибки авторизации по sentinel, а не по тексту

This commit is contained in:
samosvalishe
2026-09-21 13:40:43 +03:00
parent dd0bc211b7
commit 3f9b3b6cd0
5 changed files with 35 additions and 27 deletions
@@ -2,7 +2,6 @@ package vkauth
import (
"errors"
"strings"
"sync"
"sync/atomic"
@@ -88,11 +87,6 @@ func IsAuthError(err error) bool {
return false
}
}
// Ошибки не от TURN-сервера (получение кредов у провайдера) типа не несут.
s := err.Error()
return strings.Contains(s, "401") ||
strings.Contains(s, "Unauthorized") ||
strings.Contains(s, "authentication") ||
strings.Contains(s, "invalid credential") ||
strings.Contains(s, "stale nonce")
return errors.Is(err, ErrVKAuthFailed)
}
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"net"
"strings"
"sync"
"sync/atomic"
"time"
@@ -280,8 +279,7 @@ func (c *Client) fetch(ctx context.Context, link string, streamID int) (string,
errors.Is(err, ErrCallFull) || errors.Is(err, captcha.ErrUnavailable) {
return "", "", nil, err
}
es := err.Error()
if strings.Contains(es, "error_code:29") || strings.Contains(es, "error_code: 29") || strings.Contains(es, "Rate limit") {
if errors.Is(err, ErrVKRateLimit) {
c.log.Warnf("[STREAM %d] [VK Auth] Rate limit detected, trying next credentials", streamID)
}
}
@@ -31,20 +31,17 @@ func newTestClient(t *testing.T, fake tokenChainFn, opts ...func(*Client)) *Clie
func TestIsAuthError(t *testing.T) {
t.Parallel()
cases := map[string]bool{
"401 Unauthorized": true,
"alloc failed: 401": true,
"stale nonce": true,
"invalid credential": true,
"authentication required": true,
"connection refused": false,
"dial tcp 10.0.0.1:48612: i/o timeout": false,
"": false,
cases := map[error]bool{
fmt.Errorf("%w: map[error_code:5]", ErrVKAuthFailed): true,
fmt.Errorf("%w: map[error_code:29]", ErrVKRateLimit): false,
// Порт с "401" внутри не делает сетевую ошибку ошибкой авторизации.
errors.New("dial udp 77.88.8.8:10401: i/o timeout"): false,
errors.New("401 Unauthorized"): false,
errors.New("connection refused"): false,
}
for msg, want := range cases {
got := IsAuthError(errors.New(msg))
if got != want {
t.Errorf("IsAuthError(%q) = %v, want %v", msg, got, want)
for err, want := range cases {
if got := IsAuthError(err); got != want {
t.Errorf("IsAuthError(%v) = %v, want %v", err, got, want)
}
}
if IsAuthError(nil) {
@@ -46,6 +46,9 @@ func (c *Client) fetchCallToken(
c.log.Errorf("[STREAM %d] [VK Auth] terminal link error: %v", streamID, termErr)
return "", termErr
}
if sentinel := apiErrSentinel(errObj); sentinel != nil {
return "", fmt.Errorf("%w: %v", sentinel, errObj)
}
return "", fmt.Errorf("VK API error: %v", errObj)
}
@@ -61,11 +64,25 @@ func (c *Client) fetchCallToken(
}
}
func classifyLinkError(errObj map[string]any) error {
code := 0
func apiErrCode(errObj map[string]any) int {
if f, ok := errObj["error_code"].(float64); ok {
code = int(f)
return int(f)
}
return 0
}
func apiErrSentinel(errObj map[string]any) error {
switch apiErrCode(errObj) {
case 5:
return ErrVKAuthFailed
case 29:
return ErrVKRateLimit
}
return nil
}
func classifyLinkError(errObj map[string]any) error {
code := apiErrCode(errObj)
if code == 9000 || code == 9008 {
return ErrInvalidJoinLink
@@ -50,4 +50,6 @@ var (
ErrInvalidJoinLink = errors.Join(provider.ErrFatalNoStreams, errors.New("INVALID_JOIN_LINK"))
ErrAnonymousBlocked = errors.Join(provider.ErrFatalNoStreams, errors.New("ANON_BLOCKED"))
ErrCallFull = errors.Join(provider.ErrFatalNoStreams, errors.New("CALL_FULL"))
ErrVKAuthFailed = errors.New("VK authorization failed")
ErrVKRateLimit = errors.New("VK rate limit")
)