-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbotstate.go
More file actions
164 lines (130 loc) · 4.06 KB
/
botstate.go
File metadata and controls
164 lines (130 loc) · 4.06 KB
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 botstate
import (
"encoding/json"
"errors"
)
//State are used to save states
//Name is the identifier of each state, so it must be unique.
//Executes is the method to be executed when bot call state.
//Callback is the method to be executed in the next state, commonly used to receive input data and validate it.
//Next is the identifier to next state, it is added to user data after the call of Executes method.
type State struct {
Name string
Executes func(bot *Bot) bool
Callback func(bot *Bot) bool
Next string
}
//Bot are used to initialize states and control bot flow.
//The methods in Executes and Callback in the states receive an instance of Bot struct
type Bot struct {
States []State
Data *BotData
}
//New returns new Bot struct with BotData
func New(states []State) *Bot {
return &Bot{
States: states,
Data: &BotData{},
}
}
//ExecuteState define the current_state using argument name in the user's data.
//
//If exists callback in user's data (state_with_callback), execute it first with the executeCallback method.
//Terminates execution if callback returns false.
//
//If there is no callback, the flow continues and if the current state has a method in the item State.Callback, this value will be defined in the user's current data (state_with_callback) to be executed later.
//
//After all checks, the method in State.Executes is executed.
//The current state is defined using the value of State.Next if execution return true.
//
//Return execution boolean and error if exists.
func (b *Bot) ExecuteState(name string) (bool, error) {
for _, state := range b.States {
if state.Name == name {
if b.Data.UserID == "" {
return false, errors.New("Undefined user to execute state " + state.Name + ".")
}
if state.Executes == nil {
return false, errors.New("Method to execute in the " + state.Name + " state is nil.")
}
b.Data.SetCurrentState(state.Name)
callbackResp, err := b.executeCallback()
if err != nil {
return false, err
}
if callbackResp == false {
return false, nil
}
if state.Callback != nil {
err := b.Data.SetStateWithCallback(state.Name)
if err != nil {
return false, err
}
}
execute := state.Executes(b)
if execute == true && state.Next != "" {
b.Data.SetCurrentState(state.Next)
}
return execute, nil
}
}
return false, errors.New("No state to execute with name " + name + ".")
}
//executeCallback will get state_with_callback from user's data.
//And execute the executeCallbackFromState method passing state name as argument.
//Return callback execution boolean and error if exists.
func (b *Bot) executeCallback() (bool, error) {
stateWithCallback, _ := b.Data.GetStateWithCallback()
if stateWithCallback != "" {
return b.executeCallbackFromState(stateWithCallback), nil
}
return true, nil
}
//executeCallbackFromState will loop through all states to find the state with argument name.
//Checks if the state has method in State.Callback.
//Execute method in State.Callback.
//Return callback boolean response.
func (b *Bot) executeCallbackFromState(name string) bool {
for _, state := range b.States {
if state.Name == name {
if state.Callback != nil {
return state.Callback(b)
}
}
}
return true
}
//AddMessage save messages to user data
//Can be used to return messages after bot execution
func (b *Bot) AddMessage(messages []string) error {
if len(messages) <= 0 {
return errors.New("undefined messages")
}
messages = append(b.GetMessages(), messages...)
j, err := json.Marshal(messages)
if err != nil {
return err
}
err = b.Data.SetData(Data{
"messages": string(j),
})
if err != nil {
return err
}
return nil
}
//GetMessages return all messages saved in user data
//This messages is from bot, saved during execution flow
//Calling GetMessages will return messages and remove from data
func (b *Bot) GetMessages() []string {
var messages []string
m, _ := b.Data.Current["messages"]
err := json.Unmarshal([]byte(m), &messages)
b.Data.SetData(Data{
"messages": "",
})
if err == nil {
return messages
}
return []string{}
}