2022-04-26 00:02:51 +08:00
|
|
|
package bot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2025-01-10 17:00:32 +08:00
|
|
|
"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 (
|
2022-10-14 18:48:02 +08:00
|
|
|
defaultPollTimeout = time.Minute
|
2023-08-14 17:55:32 +08:00
|
|
|
defaultUpdatesChanCap = 1024
|
2022-10-14 18:48:02 +08:00
|
|
|
defaultCheckInitTimeout = time.Second * 5
|
2024-09-16 16:16:36 +08:00
|
|
|
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)
|
2022-12-26 17:27:35 +08:00
|
|
|
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
|
|
|
|
2024-06-28 17:13:43 +08:00
|
|
|
url string
|
|
|
|
token string
|
|
|
|
pollTimeout time.Duration
|
|
|
|
skipGetMe bool
|
|
|
|
webhookSecretToken string
|
2024-08-22 16:45:37 +08:00
|
|
|
testEnvironment bool
|
2024-09-16 16:16:36 +08:00
|
|
|
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
|
|
|
|
2022-10-14 18:48:02 +08:00
|
|
|
client HttpClient
|
|
|
|
isDebug bool
|
|
|
|
checkInitTimeout time.Duration
|
2022-05-03 00:39:04 +08:00
|
|
|
|
2024-03-05 00:06:02 +08:00
|
|
|
allowedUpdates AllowedUpdates
|
|
|
|
|
2022-05-03 00:39:04 +08:00
|
|
|
updates chan *models.Update
|
2022-04-26 00:02:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates new Bot instance
|
2022-10-14 18:48:02 +08:00
|
|
|
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,
|
2022-10-14 18:48:02 +08:00
|
|
|
checkInitTimeout: defaultCheckInitTimeout,
|
2024-09-16 16:16:36 +08:00
|
|
|
workers: defaultWorkers,
|
2022-05-03 00:39:04 +08:00
|
|
|
|
2022-05-06 17:47:43 +08:00
|
|
|
updates: make(chan *models.Update, defaultUpdatesChanCap),
|
2022-04-26 00:02:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, o := range options {
|
|
|
|
o(b)
|
|
|
|
}
|
|
|
|
|
2022-10-14 18:48:02 +08:00
|
|
|
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)
|
|
|
|
}
|
2022-10-14 18:48:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return b, nil
|
2022-04-26 00:02:51 +08:00
|
|
|
}
|
|
|
|
|
2025-01-10 17:00:32 +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
|
|
|
|
}
|
|
|
|
|
2025-01-10 17:00:32 +08:00
|
|
|
// 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
|
2022-05-06 17:47:43 +08:00
|
|
|
func (b *Bot) StartWebhook(ctx context.Context) {
|
2024-09-16 16:38:22 +08:00
|
|
|
wg := sync.WaitGroup{}
|
2022-05-06 17:47:43 +08:00
|
|
|
|
2024-09-16 16:16:36 +08:00
|
|
|
wg.Add(b.workers)
|
|
|
|
for i := 0; i < b.workers; i++ {
|
2024-09-16 16:38:22 +08:00
|
|
|
go b.waitUpdates(ctx, &wg)
|
2024-09-16 16:16:36 +08:00
|
|
|
}
|
2022-05-06 17:47:43 +08:00
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2023-01-19 16:33:13 +08:00
|
|
|
// Start the bot
|
2022-05-06 17:47:43 +08:00
|
|
|
func (b *Bot) Start(ctx context.Context) {
|
2024-09-16 16:38:22 +08:00
|
|
|
wg := sync.WaitGroup{}
|
2022-05-06 17:47:43 +08:00
|
|
|
|
2024-09-16 16:16:36 +08:00
|
|
|
wg.Add(1)
|
2024-09-16 16:38:22 +08:00
|
|
|
go b.getUpdates(ctx, &wg)
|
2022-05-06 17:47:43 +08:00
|
|
|
|
2024-09-16 16:16:36 +08:00
|
|
|
wg.Add(b.workers)
|
|
|
|
for i := 0; i < b.workers; i++ {
|
2024-09-16 16:38:22 +08:00
|
|
|
go b.waitUpdates(ctx, &wg)
|
2024-09-16 16:16:36 +08:00
|
|
|
}
|
|
|
|
|
2022-05-06 17:47:43 +08:00
|
|
|
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...)
|
|
|
|
}
|
|
|
|
|
2022-05-06 17:47:43 +08:00
|
|
|
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...))
|
|
|
|
}
|
|
|
|
|
2025-01-10 17:00:32 +08:00
|
|
|
// True and False returns the pointer to bool
|
2023-04-10 15:43:31 +08:00
|
|
|
func True() *bool {
|
|
|
|
b := true
|
|
|
|
return &b
|
|
|
|
}
|
|
|
|
|
2025-01-10 17:00:32 +08:00
|
|
|
// False and True returns the pointer to bool
|
2023-04-10 15:43:31 +08:00
|
|
|
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)
|
|
|
|
}
|