-
Notifications
You must be signed in to change notification settings - Fork 0
/
bot.go
215 lines (158 loc) · 5.14 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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// Telebot is a Go wrapper for the Telegram Bot API.
// It provides a convenient way to write a Telegram in Go whitout diving in Telegram API.
// Updates are receveived from Telegram either with Telegram Webhook or with calls to Telegram /getUpdates API endpoint.
// In order to build a bot, follow the steps :
// * Create a bot with the appropriate config
// * Write handlers and link it to your bot with the OnText function
// * And last but not least, start the bot
//
// In addition to update reception, telebot has some functions designed to make your bot send content. You can use it in your handlers.
package telebot
import (
"encoding/json"
"log"
"net/http"
"net/url"
"time"
)
// Create a bog with the appropriate config.
func CreateBot(apiToken string, config map[string]string) Bot {
// handerMap is a map to make the correspondance between events and handlers.
handlerMap := make(map[string]map[string]func(u *Update))
// Create the bot.
return Bot{apiToken: apiToken, config: config, handlerMap: handlerMap}
}
// Start the bot.
func (b *Bot) Start() {
// Set the commands of the bot
b.setBotCommands()
// Determine the type of the bot.
isWebook := b.config != nil
// If the bot uses webhook to get Updates.
if isWebook {
// Set up webhook.
_, err := b.setWebhook()
if err != nil {
panic(err)
}
// Start a webhook bot and handle incoming updates.
u, err := url.Parse(b.config["WebhookUrl"])
if err != nil {
panic(err)
}
http.HandleFunc(u.Path+b.apiToken, func(w http.ResponseWriter, r *http.Request) {
b.handleTelegramWebHook(w, r)
})
http.ListenAndServeTLS(":8443", b.config["SslCertificate"], b.config["SslPrivkey"], nil)
// Else, the bot uses getUpdates.
} else {
// Deactivate previous webhooks if exists.
b.deleteWebhook()
// Start with no offset.
offset := 0
for {
// Fetch updates twice a second.
time.Sleep(500 * time.Millisecond)
// Get and dispatch updates. Set a new offset.
offset = b.getUpdates(offset)
}
}
}
// Call the handler corresponding to a pair (event, filter) id it exists.
func (b *Bot) dispatchEvent(event Event, filter string, u *Update) {
eventMap := b.handlerMap[event.Identifier]
// Get all keys of the eventMap
keys := make([]string, len(eventMap))
i := 0
for k := range eventMap {
keys[i] = k
i += 1
}
// Dispatch handler if checker is true for each key
for _, k := range keys {
if event.Checker(k, filter) {
eventMap[k](u)
}
}
}
// Dispatch an update to the corresponding handler based on the detected event.
func (b *Bot) dispatchUpdate(u *Update) {
// Find the corresponding event and dispatch it.
switch {
case u.Message.Text != "":
b.dispatchEvent(ONCOMMAND, u.Message.Text, u)
b.dispatchEvent(ONTEXT, u.Message.Text, u)
case u.CallbackQuery.Data != "":
b.dispatchEvent(ONCALLBACK, u.CallbackQuery.Data, u)
b.dispatchEvent(ONPAYLOAD, u.CallbackQuery.Data, u)
}
}
// Register the handler corresponding to the pair (event, filter)
func (b *Bot) registerHandler(event Event, filter string, handler func(u *Update)) {
// Check if event is already registered.
_, exists := b.handlerMap[event.Identifier]
// If the event doesn't exist, create a new eventMap and register the handler.
if !exists {
eventMap := make(map[string]func(u *Update))
eventMap[filter] = handler
b.handlerMap[event.Identifier] = eventMap
// Otherwise, register the handler.
} else {
b.handlerMap[event.Identifier][filter] = handler
}
}
// Register a command in the bot commands
func (b *Bot) registerInCommands(command string, description string) {
// Only append commands with description >= 3 (otherwise Telegram will ignore it)
if len(description) >= 3 {
b.commands = append(b.commands, BotCommand{command, description})
}
}
// Set the bot commands with Telegram API
func (b *Bot) setBotCommands() {
// If no commands are specified, reset bot commands
commands := "[]"
if len(b.commands) > 0 {
jsonCommands, err := json.Marshal(b.commands)
if err != nil {
log.Println(err)
}
commands = string(jsonCommands)
}
val := url.Values{
"commands": {commands},
}
_, err := b.makeAPICall(setMyCommandsEndpoint, val)
if err != nil {
log.Println(err)
}
}
//
// Below are defined the module API functions used to link handlers to events.
//
// Trigger handler if the text of the update matches the variable text.
func (b *Bot) OnText(text string, handler func(u *Update)) {
event := ONTEXT
// Register handler.
b.registerHandler(event, text, handler)
}
// Match commands (i.e. when text starts with the filter but can contain more text)
func (b *Bot) OnCommand(text string, description string, handler func(u *Update)) {
event := ONCOMMAND
// Register handler.
b.registerHandler(event, text, handler)
// Register the command in the commandMap
b.registerInCommands(text, description)
}
// Match CallbackQuery
func (b *Bot) OnCallback(data string, handler func(u *Update)) {
event := ONCALLBACK
// Register handler.
b.registerHandler(event, data, handler)
}
// Match CallbackQuery with payload
func (b *Bot) OnPayload(data string, handler func(u *Update)) {
event := ONPAYLOAD
// Register handler.
b.registerHandler(event, data, handler)
}