go-telegram / bot

Telegram Bot API Go framework
MIT License
502 stars 46 forks source link

Added error handling for specific error codes #69

Closed icoder-new closed 3 months ago

icoder-new commented 3 months ago

Added error handling for specific HTTP status codes in accordance with the Telegram API specifications.

Changes:

Introduced error handling logic for HTTP status codes 403, 400, 401, 404, 409, and 429.
New error variables added for each handled status code.
Error messages now provide clearer feedback for developers.

Benefits:

Improves error handling and readability of error messages.
Simplifies debugging and development process.
qulaz commented 3 months ago

It would be great if the retry_after value could be retrieved from the ErrorTooManyRequests error. Like custom error type + errors.As or smth

icoder-new commented 3 months ago

Added parameters field to API response with retry_after information for rate limiting. Implemented error handling for HTTP status code 429 (Too Many Requests) in the rawRequest function, extracting the retry_after value for proper handling of rate-limiting scenarios.

Here is a brief example demonstrating how TooManyRequestsError works.

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "strings"
)

var (
    ErrorTooManyRequests = fmt.Errorf("too many requests")
)

type TooManyRequestsError struct {
    Message    string
    RetryAfter int
}

func (e *TooManyRequestsError) Error() string {
    return fmt.Sprintf("%s: retry_after %d", e.Message, e.RetryAfter)
}

func IsTooManyRequestsError(err error) bool {
    _, ok := err.(*TooManyRequestsError)
    return ok
}

type apiResponse struct {
    OK          bool            `json:"ok"`
    Result      json.RawMessage `json:"result,omitempty"`
    Description string          `json:"description,omitempty"`
    ErrorCode   int             `json:"error_code,omitempty"`
    Parameters  struct {
        RetryAfter int `json:"retry_after"`
    } `json:"parameters,omitempty"`
}

func rawRequest() error {
    mockResponse := `{
        "ok": false,
        "error_code": 429,
        "description": "Too Many Requests: retry after 35",
        "parameters": {
            "retry_after": 35
        }
    }`
    body := strings.NewReader(mockResponse)

    // Mocking an HTTP response
    r := &http.Response{
        StatusCode: http.StatusTooManyRequests,
        Body:       io.NopCloser(body),
    }

    // Parse API response
    var apiResp apiResponse
    errDecode := json.NewDecoder(r.Body).Decode(&apiResp)
    if errDecode != nil {
        return fmt.Errorf("error decoding API response: %w", errDecode)
    }

    if apiResp.ErrorCode == 429 {
        err := &TooManyRequestsError{
            Message:    fmt.Sprintf("%w, %s", ErrorTooManyRequests, apiResp.Description),
            RetryAfter: apiResp.Parameters.RetryAfter,
        }
        return err
    }

    return nil
}

func main() {
    err := rawRequest()
    if err != nil {
        if IsTooManyRequestsError(err) {
            fmt.Println("Received TooManyRequestsError with retry_after:", err.(*TooManyRequestsError).RetryAfter)
        } else {
            fmt.Println("Received error:", err)
        }
    } else {
        fmt.Println("No error received.")
    }
}