-
Notifications
You must be signed in to change notification settings - Fork 68
/
bot.go
164 lines (128 loc) · 3.28 KB
/
bot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package bot
import (
"context"
"fmt"
"log"
"net/http"
"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
}
// SetToken sets the bot token
func (b *Bot) SetToken(token string) {
b.token = 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...))
}
func True() *bool {
b := true
return &b
}
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)
}