Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added error handling for specific error codes #69

Merged
merged 12 commits into from
Apr 3, 2024
Merged
36 changes: 32 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,42 @@ See [documentation(https://core.telegram.org/bots/api#getfile)

## Errors

The library provides error `ErrorForbidden` for error code 403. That code returns when the bot has no access to the action.
For example, when the user blocked the bot.
This library includes error handling. It provides the following error types:

- **ErrorForbidden (403):** This error occurs when the bot has no access to the action, such as when the user has blocked the bot.
- **ErrorBadRequest (400):** This error indicates a bad request made to the bot's API.
- **ErrorUnauthorized (401):** This error occurs when the bot's access is unauthorized for the requested action.
- **TooManyRequestsError: (429)** This error indicates that the bot has received too many requests within a short period. It includes a RetryAfter value indicating when to retry the request.
- **ErrorNotFound (404):** This error indicates that the requested resource was not found.
- **ErrorConflict (409):** This error indicates a conflict occurred during the request.

Usage:
```go
_, err := b.SendMessage(...)

if errors.Is(err, bot.ErrorForbidden) {
// your code
if errors.Is(err, mybot.ErrorForbidden) {
// Handle the ErrorForbidden (403) case here
}

icoder-new marked this conversation as resolved.
Show resolved Hide resolved
if errors.Is(err, mybot.ErrorBadRequest) {
// Handle the ErrorBadRequest (400) case here
}

if errors.Is(err, mybot.ErrorUnauthorized) {
// Handle the ErrorUnauthorized (401) case here
}

if mybot.IsTooManyRequestsError(err) {
// Handle the TooManyRequestsError (429) case here
fmt.Println("Received TooManyRequestsError with retry_after:", err.(*mybot.TooManyRequestsError).RetryAfter)
}

if errors.Is(err, mybot.ErrorNotFound) {
// Handle the ErrorNotFound (404) case here
}

if errors.Is(err, mybot.ErrorConflict) {
// Handle the ErrorConflict (409) case here
}
```

Expand Down
2 changes: 1 addition & 1 deletion bot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func TestNew_error_getMe(t *testing.T) {
if err == nil {
t.Fatal("unexpected nil error")
}
if err.Error() != "error call getMe, error response from telegram for method getMe, 400 err1" {
if err.Error() != "error call getMe, bad request, err1" {
t.Fatalf("wrong error message %q", err.Error())
}
}
Expand Down
43 changes: 43 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package bot

import (
"errors"
"fmt"
)

var (
ErrorForbidden = errors.New("forbidden")
ErrorBadRequest = errors.New("bad request")
ErrorUnauthorized = errors.New("unauthorized")
ErrorTooManyRequests = errors.New("too many requests")
ErrorNotFound = errors.New("not found")
ErrorConflict = errors.New("conflict")
)

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 MigrateError struct {
Message string
MigrateToChatID int
}

func (e *MigrateError) Error() string {
return fmt.Sprintf("%s: migrate_to_chat_id %d", e.Message, e.MigrateToChatID)
}

func IsMigrateError(err error) bool {
_, ok := err.(*MigrateError)
return ok
}
35 changes: 30 additions & 5 deletions raw_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
Expand All @@ -18,10 +17,12 @@ type apiResponse struct {
Result json.RawMessage `json:"result,omitempty"`
Description string `json:"description,omitempty"`
ErrorCode int `json:"error_code,omitempty"`
Parameters struct {
RetryAfter int `json:"retry_after,omitempty"`
MigrateToChatID int `json:"migrate_to_chat_id,omitempty"`
} `json:"parameters,omitempty"`
negasus marked this conversation as resolved.
Show resolved Hide resolved
}

var ErrorForbidden = errors.New("forbidden")

func (b *Bot) rawRequest(ctx context.Context, method string, params any, dest any) error {
var httpBody io.Reader = http.NoBody
var contentType string
Expand Down Expand Up @@ -81,10 +82,34 @@ func (b *Bot) rawRequest(ctx context.Context, method string, params any, dest an
}

if !r.OK {
if r.ErrorCode == 403 {
switch r.ErrorCode {
case 403:
return fmt.Errorf("%w, %s", ErrorForbidden, r.Description)
case 400:
if r.Parameters.MigrateToChatID != 0 {
err := &MigrateError{
Message: fmt.Sprintf("%s: %s", ErrorBadRequest, r.Description),
MigrateToChatID: r.Parameters.MigrateToChatID,
}

return err
}
return fmt.Errorf("%w, %s", ErrorBadRequest, r.Description)
case 401:
return fmt.Errorf("%w, %s", ErrorUnauthorized, r.Description)
case 404:
return fmt.Errorf("%w, %s", ErrorNotFound, r.Description)
case 409:
return fmt.Errorf("%w, %s", ErrorConflict, r.Description)
case 429:
err := &TooManyRequestsError{
Message: fmt.Sprintf("%s, %s", ErrorTooManyRequests, r.Description),
RetryAfter: r.Parameters.RetryAfter,
}
return err
default:
return fmt.Errorf("error response from telegram for method %s, %d %s", method, r.ErrorCode, r.Description)
}
return fmt.Errorf("error response from telegram for method %s, %d %s", method, r.ErrorCode, r.Description)
}

if !bytes.Equal(r.Result, []byte("[]")) {
Expand Down
Loading