This repository has been archived by the owner on May 26, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcallbackquery.go
259 lines (235 loc) · 8.18 KB
/
callbackquery.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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
package busetabot
import (
"context"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/yi-jiayu/telegram-bot-api"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"github.com/yi-jiayu/bus-eta-bot/v4/telegram"
)
var callbackQueryHandlers = map[string]CallbackQueryHandler{
"refresh": RefreshCallbackHandler,
"resend": NewEtaHandler,
"eta": EtaCallbackHandler,
"eta_demo": EtaDemoCallbackHandler,
"new_eta": NewEtaHandler,
"addf": ToggleFavouritesHandler,
"togf": ToggleFavouritesHandler,
}
// CallbackQueryHandler is a handler for callback queries
type CallbackQueryHandler func(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)
func updateETAMessage(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, req ETARequest, formatter string, responses chan<- Response) {
eta := NewETA(ctx, bot.BusStops, bot.Datamall, req)
var f Formatter
var ok_ bool
if f, ok_ = Formatters[formatter]; !ok_ {
f = summaryFormatter
}
text, err := f.Format(eta)
if err != nil {
responses <- notOk(err)
return
}
editMessageTextRequest := telegram.EditMessageTextRequest{
Text: text,
ParseMode: "markdown",
}
if cbq.InlineMessageID != "" {
editMessageTextRequest.InlineMessageID = cbq.InlineMessageID
editMessageTextRequest.ReplyMarkup = NewETAMessageReplyMarkup(req.Code, req.Services, formatter, true)
} else {
editMessageTextRequest.ChatID = cbq.Message.Chat.ID
editMessageTextRequest.MessageID = cbq.Message.MessageID
editMessageTextRequest.ReplyMarkup = NewETAMessageReplyMarkup(req.Code, req.Services, formatter, false)
}
responses <- ok(editMessageTextRequest)
answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
CallbackQueryID: cbq.ID,
Text: "ETAs updated!",
}
responses <- ok(answerCallbackQueryRequest)
}
func sendETAMessage(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, code string, services []string, responses chan<- Response) {
eta := NewETA(ctx, bot.BusStops, bot.Datamall, ETARequest{
UserID: cbq.From.ID,
Time: bot.NowFunc(),
Code: code,
Services: services,
})
text, err := summaryFormatter.Format(eta)
if err != nil {
responses <- notOk(err)
return
}
markup := NewETAMessageReplyMarkup(code, services, "", false)
sendMessageRequest := telegram.SendMessageRequest{
Text: text,
ChatID: cbq.Message.Chat.ID,
ParseMode: "markdown",
ReplyMarkup: markup,
}
responses <- ok(sendMessageRequest)
answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
CallbackQueryID: cbq.ID,
Text: "ETAs sent!",
}
responses <- ok(answerCallbackQueryRequest)
}
// RefreshCallbackHandler handles the callback for the Refresh button on an eta message.
func RefreshCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
defer close(responses)
var data CallbackData
err := json.Unmarshal([]byte(cbq.Data), &data)
if err != nil {
responses <- notOk(errors.Wrap(err, "error unmarshalling callback data"))
return
}
req := ETARequest{
UserID: cbq.From.ID,
Time: bot.NowFunc(),
Code: data.BusStopID,
Services: data.ServiceNos,
}
var format string
switch data.Formatter {
case FormatterSummary:
format = "summary"
case FormatterFeatures:
format = "features"
}
go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionRefreshCallback, format)
updateETAMessage(ctx, bot, cbq, req, data.Formatter, responses)
}
// EtaCallbackHandler handles callback queries from eta messages from old versions of the bot for
// backwards-compatibility.
func EtaCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
defer close(responses)
var data CallbackData
err := json.Unmarshal([]byte(cbq.Data), &data)
if err != nil {
responses <- notOk(errors.Wrap(err, "error unmarshalling callback data"))
return
}
var code string
var services []string
if data.Argstr != "" {
code, services, _ = InferEtaQuery(data.Argstr)
} else {
code = data.BusStopID
services = data.ServiceNos
}
req := ETARequest{
UserID: cbq.From.ID,
Time: bot.NowFunc(),
Code: code,
Services: services,
}
updateETAMessage(ctx, bot, cbq, req, "", responses)
}
// EtaDemoCallbackHandler handles an eta_demo callback from a start command.
func EtaDemoCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionEtaDemoCallback, cbq.Message.Chat.Type)
sendETAMessage(ctx, bot, cbq, "96049", nil, responses)
close(responses)
}
// NewEtaHandler sends etas for a bus stop when a user taps "Get etas" on a bus stop location returned from a
// location query
func NewEtaHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionEtaFromLocationCallback, "")
var data CallbackData
err := json.Unmarshal([]byte(cbq.Data), &data)
if err != nil {
responses <- notOk(err)
return
}
code, services := data.BusStopID, data.ServiceNos
sendETAMessage(ctx, bot, cbq, code, services, responses)
close(responses)
}
func stringInSlice(a string, list []string) (bool, int) {
for i, b := range list {
if b == a {
return true, i
}
}
return false, 0
}
func newShowFavouritesMarkup(favourites []string) telegram.ReplyKeyboardMarkup {
var keyboard [][]telegram.KeyboardButton
for _, fav := range favourites {
button := telegram.KeyboardButton{
Text: fav,
}
row := []telegram.KeyboardButton{button}
keyboard = append(keyboard, row)
}
return telegram.ReplyKeyboardMarkup{
Keyboard: keyboard,
ResizeKeyboard: true,
}
}
// ToggleFavouritesHandler handles the toggle favourite callback button on etas
func ToggleFavouritesHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
defer close(responses)
var data CallbackData
err := json.Unmarshal([]byte(cbq.Data), &data)
if err != nil {
responses <- notOk(errors.Wrap(err, fmt.Sprintf("error unmarshalling callback data: %s", cbq.Data)))
return
}
userID := cbq.From.ID
favourites, err := bot.Users.GetUserFavourites(ctx, userID)
if err != nil {
responses <- notOk(errors.Wrap(err, "could not retrieve user favourites"))
return
}
var action string
// if the entry is already in the favourites, we remove it
if exists, pos := stringInSlice(data.Argstr, favourites); exists {
// remove item from slice
copy(favourites[pos:], favourites[pos+1:])
favourites[len(favourites)-1] = ""
favourites = favourites[:len(favourites)-1]
action = "removed from"
} else {
favourites = append(favourites, data.Argstr)
action = "added to"
}
err = bot.Users.SetUserFavourites(ctx, userID, favourites)
if err != nil {
responses <- notOk(errors.Wrap(err, "error updating user favourites"))
}
sendMessageRequest := telegram.SendMessageRequest{
ChatID: cbq.Message.Chat.ID,
Text: fmt.Sprintf("ETA query `%s` %s favourites!", data.Argstr, action),
ParseMode: "markdown",
}
if len(favourites) > 0 {
sendMessageRequest.ReplyMarkup = newShowFavouritesMarkup(favourites)
} else {
sendMessageRequest.ReplyMarkup = telegram.ReplyKeyboardRemove{}
}
responses <- ok(sendMessageRequest)
answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
CallbackQueryID: cbq.ID,
}
responses <- ok(answerCallbackQueryRequest)
if action == "removed from" {
go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionRemoveFavouriteCalback, cbq.Message.Chat.Type)
} else {
go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionAddFavouriteCalback, cbq.Message.Chat.Type)
}
}
// callbackErrorHandler is for informing the user about an error while processing a callback query.
func callbackErrorHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, err error) {
log.Errorf(ctx, "%+v", err)
text := fmt.Sprintf("Oh no! Something went wrong. \n\nRequest ID: `%s`", appengine.RequestID(ctx))
answer := tgbotapi.NewCallbackWithAlert(cbq.ID, text)
_, err = bot.Telegram.AnswerCallbackQuery(answer)
if err != nil {
err := errors.Wrap(err, fmt.Sprintf("%#v", answer))
log.Errorf(ctx, "%+v", err)
}
}