1
bot/bot.go

183 lines
3.6 KiB
Go
Raw Permalink Normal View History

2022-04-26 00:02:51 +08:00
package bot
import (
"context"
"fmt"
"log"
"net/http"
"strconv"
2023-06-13 21:47:20 +08:00
"strings"
2022-04-26 00:02:51 +08:00
"sync"
"time"
"github.com/go-telegram/bot/models"
)
const (
defaultPollTimeout = time.Minute
defaultUpdatesChanCap = 1024
defaultCheckInitTimeout = time.Second * 5
defaultWorkers = 1
2022-04-26 00:02:51 +08:00
)
type HttpClient interface {
Do(*http.Request) (*http.Response, error)
}
type ErrorsHandler func(err error)
2023-04-13 16:39:58 +08:00
type DebugHandler func(format string, args ...any)
2022-04-26 00:02:51 +08:00
type Middleware func(next HandlerFunc) HandlerFunc
type HandlerFunc func(ctx context.Context, bot *Bot, update *models.Update)
type MatchFunc func(update *models.Update) bool
2022-04-26 00:02:51 +08:00
// Bot represents Telegram Bot main object
type Bot struct {
2024-11-08 17:17:20 +08:00
lastUpdateID int64
2024-11-08 16:01:22 +08:00
url string
token string
pollTimeout time.Duration
skipGetMe bool
webhookSecretToken string
testEnvironment bool
workers int
2024-10-17 16:17:15 +08:00
notAsyncHandlers bool
2022-04-26 00:02:51 +08:00
defaultHandlerFunc HandlerFunc
errorsHandler ErrorsHandler
2023-04-13 16:39:58 +08:00
debugHandler DebugHandler
2022-04-26 00:02:51 +08:00
2024-09-16 16:58:56 +08:00
middlewares []Middleware
2022-04-26 00:02:51 +08:00
2024-09-16 16:38:22 +08:00
handlersMx sync.RWMutex
handlers []handler
2022-04-26 00:02:51 +08:00
client HttpClient
isDebug bool
checkInitTimeout time.Duration
allowedUpdates AllowedUpdates
updates chan *models.Update
2022-04-26 00:02:51 +08:00
}
// New creates new Bot instance
func New(token string, options ...Option) (*Bot, error) {
2023-06-13 21:47:20 +08:00
if strings.TrimSpace(token) == "" {
return nil, fmt.Errorf("empty token")
}
2022-04-26 00:02:51 +08:00
b := &Bot{
2024-09-16 16:38:22 +08:00
url: "https://api.telegram.org",
token: token,
pollTimeout: defaultPollTimeout,
2022-04-26 00:02:51 +08:00
client: &http.Client{
Timeout: defaultPollTimeout,
},
defaultHandlerFunc: defaultHandler,
errorsHandler: defaultErrorsHandler,
2023-04-13 16:39:58 +08:00
debugHandler: defaultDebugHandler,
checkInitTimeout: defaultCheckInitTimeout,
workers: defaultWorkers,
updates: make(chan *models.Update, defaultUpdatesChanCap),
2022-04-26 00:02:51 +08:00
}
for _, o := range options {
o(b)
}
ctx, cancel := context.WithTimeout(context.Background(), b.checkInitTimeout)
defer cancel()
2023-05-16 15:38:28 +08:00
if !b.skipGetMe {
_, err := b.GetMe(ctx)
if err != nil {
return nil, fmt.Errorf("error call getMe, %w", err)
}
}
return b, nil
2022-04-26 00:02:51 +08:00
}
// ID returns the bot ID
func (b *Bot) ID() int64 {
id, err := strconv.ParseInt(strings.Split(b.token, ":")[0], 10, 64)
if err != nil {
return 0
}
return id
}
2024-10-07 16:11:11 +08:00
// SetToken sets the bot token
func (b *Bot) SetToken(token string) {
2024-10-07 16:01:49 +08:00
b.token = token
}
// Token returns the bot token
func (b *Bot) Token() string {
return b.token
}
2023-01-19 16:33:13 +08:00
// StartWebhook starts the Bot with webhook mode
func (b *Bot) StartWebhook(ctx context.Context) {
2024-09-16 16:38:22 +08:00
wg := sync.WaitGroup{}
wg.Add(b.workers)
for i := 0; i < b.workers; i++ {
2024-09-16 16:38:22 +08:00
go b.waitUpdates(ctx, &wg)
}
wg.Wait()
}
2023-01-19 16:33:13 +08:00
// Start the bot
func (b *Bot) Start(ctx context.Context) {
2024-09-16 16:38:22 +08:00
wg := sync.WaitGroup{}
wg.Add(1)
2024-09-16 16:38:22 +08:00
go b.getUpdates(ctx, &wg)
wg.Add(b.workers)
for i := 0; i < b.workers; i++ {
2024-09-16 16:38:22 +08:00
go b.waitUpdates(ctx, &wg)
}
wg.Wait()
}
2022-04-26 00:02:51 +08:00
func defaultErrorsHandler(err error) {
log.Printf("[TGBOT] [ERROR] %v", err)
}
2023-04-13 16:39:58 +08:00
func defaultDebugHandler(format string, args ...interface{}) {
log.Printf("[TGBOT] [DEBUG] "+format, args...)
}
func defaultHandler(_ context.Context, _ *Bot, update *models.Update) {
log.Printf("[TGBOT] [UPDATE] %+v", update)
2022-04-26 00:02:51 +08:00
}
func (b *Bot) error(format string, args ...interface{}) {
b.errorsHandler(fmt.Errorf(format, args...))
}
// True and False returns the pointer to bool
func True() *bool {
b := true
return &b
}
// False and True returns the pointer to bool
func False() *bool {
b := false
return &b
}
2023-12-07 15:51:53 +08:00
// FileDownloadLink returns the file download link
func (b *Bot) FileDownloadLink(f *models.File) string {
return fmt.Sprintf("%s/file/bot%s/%s", b.url, b.token, f.FilePath)
}