-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathbot.go
182 lines (143 loc) · 3.62 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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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)
}