1
bot/bot.go
negasus 6317bb0bb5 - add method bot.ID() int64
- add method `bot.Token() string`
2025-01-10 12:00:32 +03:00

183 lines
3.6 KiB
Go

package bot
import (
"context"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"sync"
"time"
"github.com/go-telegram/bot/models"
)
const (
defaultPollTimeout = time.Minute
defaultUpdatesChanCap = 1024
defaultCheckInitTimeout = time.Second * 5
defaultWorkers = 1
)
type HttpClient interface {
Do(*http.Request) (*http.Response, error)
}
type ErrorsHandler func(err error)
type DebugHandler func(format string, args ...any)
type Middleware func(next HandlerFunc) HandlerFunc
type HandlerFunc func(ctx context.Context, bot *Bot, update *models.Update)
type MatchFunc func(update *models.Update) bool
// Bot represents Telegram Bot main object
type Bot struct {
lastUpdateID int64
url string
token string
pollTimeout time.Duration
skipGetMe bool
webhookSecretToken string
testEnvironment bool
workers int
notAsyncHandlers bool
defaultHandlerFunc HandlerFunc
errorsHandler ErrorsHandler
debugHandler DebugHandler
middlewares []Middleware
handlersMx sync.RWMutex
handlers []handler
client HttpClient
isDebug bool
checkInitTimeout time.Duration
allowedUpdates AllowedUpdates
updates chan *models.Update
}
// New creates new Bot instance
func New(token string, options ...Option) (*Bot, error) {
if strings.TrimSpace(token) == "" {
return nil, fmt.Errorf("empty token")
}
b := &Bot{
url: "https://api.telegram.org",
token: token,
pollTimeout: defaultPollTimeout,
client: &http.Client{
Timeout: defaultPollTimeout,
},
defaultHandlerFunc: defaultHandler,
errorsHandler: defaultErrorsHandler,
debugHandler: defaultDebugHandler,
checkInitTimeout: defaultCheckInitTimeout,
workers: defaultWorkers,
updates: make(chan *models.Update, defaultUpdatesChanCap),
}
for _, o := range options {
o(b)
}
ctx, cancel := context.WithTimeout(context.Background(), b.checkInitTimeout)
defer cancel()
if !b.skipGetMe {
_, err := b.GetMe(ctx)
if err != nil {
return nil, fmt.Errorf("error call getMe, %w", err)
}
}
return b, nil
}
// 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
}
// SetToken sets the bot token
func (b *Bot) SetToken(token string) {
b.token = token
}
// Token returns the bot token
func (b *Bot) Token() string {
return b.token
}
// StartWebhook starts the Bot with webhook mode
func (b *Bot) StartWebhook(ctx context.Context) {
wg := sync.WaitGroup{}
wg.Add(b.workers)
for i := 0; i < b.workers; i++ {
go b.waitUpdates(ctx, &wg)
}
wg.Wait()
}
// Start the bot
func (b *Bot) Start(ctx context.Context) {
wg := sync.WaitGroup{}
wg.Add(1)
go b.getUpdates(ctx, &wg)
wg.Add(b.workers)
for i := 0; i < b.workers; i++ {
go b.waitUpdates(ctx, &wg)
}
wg.Wait()
}
func defaultErrorsHandler(err error) {
log.Printf("[TGBOT] [ERROR] %v", err)
}
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)
}
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
}
// 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)
}