-
Notifications
You must be signed in to change notification settings - Fork 1
/
withvalidation.go
151 lines (137 loc) · 5.43 KB
/
withvalidation.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
package readers
import (
"context"
"regexp"
"time"
"github.com/ufy-it/go-telegram-bot/handlers/buttons"
"github.com/ufy-it/go-telegram-bot/logger"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
// InputTextValidation is a type for function that validates messaged recieved from a user
type InputTextValidation func(text string) bool // predicat that accepts only valid strings
// BotUpdateValidator validates an update recieved from a user.
// If the update does not pass the criteria, the validator can return a string with the clarification
type BotUpdateValidator func(update *tgbotapi.Update) (bool, string)
// AskGenericMessageReplyWithValidation continuously asks a user for reply until reply passes validation functor
func AskGenericMessageReplyWithValidation(ctx context.Context,
conversation BotConversation,
message tgbotapi.Chattable,
bs buttons.ButtonSet,
validator BotUpdateValidator,
repeatOriginalOnIncorrect bool) (*tgbotapi.Update, bool, error) {
sendOriginalMessage := func() (int, error) {
if message != nil { //do nothing on empty message
msgID, err := conversation.SendGeneralMessageWithKeyboardRemoveOnExit(message)
if err != nil {
return -1, err
}
if !bs.IsEmpty() {
err = conversation.EditReplyMarkup(msgID, bs.GetInlineKeyboard())
}
return msgID, err
}
return -1, nil
}
sentID, err := sendOriginalMessage()
defer func() { //hide buttons before exiting
if bs.IsEmpty() {
return
}
err := conversation.RemoveReplyMarkup(sentID)
if err != nil {
logger.Warning("failed to hide reply markup in message: %v", err)
}
}()
for {
if err != nil {
return nil, false, err
}
reply, exit := conversation.GetUpdateFromUser(ctx) // get reply from user
if exit {
return nil, true, nil
}
if reply.CallbackQuery != nil && reply.CallbackQuery.Data != "" {
var berr error
reply.CallbackQuery.Data, berr = bs.FindButtonData(reply.CallbackQuery.Data) // convert data from guid to an original
if berr == nil {
return reply, false, nil
}
logger.Warning("unknown button pressed: %v", berr) //log unknown button press
continue
}
if isValid, messageOnIncorrect := validator(reply); !isValid {
if messageOnIncorrect != "" {
if reply.Message != nil {
_, err = conversation.ReplyWithText(messageOnIncorrect, reply.Message.MessageID)
} else {
_, err = conversation.SendText(messageOnIncorrect)
}
if err != nil {
return nil, false, err
}
}
} else {
return reply, false, nil
}
if sentID != -1 && repeatOriginalOnIncorrect { // no need to delete a message
err = conversation.DeleteMessage(sentID) // delete and resend original message
if err != nil {
logger.Warning("failed to delete old mesage: %v", err)
}
sentID, err = sendOriginalMessage()
}
}
}
// AskTextMessageReplyWithValidation is a complex conversation that keep asking a question to user
// until they give information that pass the validation or press a button
func AskTextMessageReplyWithValidation(ctx context.Context,
conversation BotConversation,
message string, bs buttons.ButtonSet,
validator InputTextValidation, messageOnIncorrect string) (UserTextAndDataReply, error) {
outerValidator := func(update *tgbotapi.Update) (bool, string) {
if update != nil && update.Message != nil && validator(update.Message.Text) {
return true, ""
}
return false, messageOnIncorrect
}
reply, exit, err := AskGenericMessageReplyWithValidation(ctx, conversation, conversation.NewMessage(message), bs, outerValidator, true)
return ParseUserTextAndDataReply(reply, exit), err
}
//AskOnlyButtonReply sends message to a user and accepts only buttons
func AskOnlyButtonReply(ctx context.Context, conversation BotConversation, message tgbotapi.Chattable, bs buttons.ButtonSet, messageOnText string) (UserTextAndDataReply, error) {
validator := func(update *tgbotapi.Update) (bool, string) {
return false, messageOnText
}
return ParseUserTextDataAndErrorReply(AskGenericMessageReplyWithValidation(ctx, conversation, message, bs, validator, true))
}
// AskReplyDate asks a user to enter date in format dd.mm.yyyy
func AskReplyDate(ctx context.Context, conversation BotConversation, message string, bs buttons.ButtonSet, messageOnIncorrectInput string, location *time.Location) (UserTimeAndDataReply, error) {
validator := func(text string) bool {
_, err := time.ParseInLocation("02.01.2006", text, location)
return err == nil
}
preResult, err := AskTextMessageReplyWithValidation(ctx, conversation, message, bs, validator, messageOnIncorrectInput)
result := UserTimeAndDataReply{
MessageID: preResult.MessageID,
Data: preResult.Data,
Exit: preResult.Exit,
}
if err != nil {
return result, err
}
if preResult.Text != "" {
result.Time, err = time.ParseInLocation("02.01.2006", preResult.Text, location)
}
return result, err
}
// AskReplyEmail asks user to enter a vaid email address
func AskReplyEmail(ctx context.Context, conversation BotConversation, message string, bs buttons.ButtonSet, messageOnIncorrectInput string) (UserTextAndDataReply, error) {
validator := func(text string) bool {
if len(text) < 3 || len(text) > 254 {
return false
}
// regexp by W3C
return regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$").MatchString(text)
}
return AskTextMessageReplyWithValidation(ctx, conversation, message, bs, validator, messageOnIncorrectInput)
}