-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot.go
179 lines (162 loc) · 4.2 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
package main
import (
"fmt"
"log"
"strings"
"gopkg.in/telegram-bot-api.v4"
)
// Tokens in a message
type Tokens struct {
tokens []string
current int
}
// TokensFrom gets all tokens from message
func TokensFrom(msg *tgbotapi.Message) *Tokens {
return &Tokens{tokens: strings.Fields(msg.Text)}
}
// Next token in a message
func (tokens *Tokens) Next() string {
counter := tokens.current
if counter >= len(tokens.tokens) {
return ""
}
tokens.current++
return tokens.tokens[counter]
}
// Back one token
func (tokens *Tokens) Back() {
if tokens.current > 0 {
tokens.current--
}
}
// Remaining tokens
func (tokens *Tokens) Remaining() int {
return len(tokens.tokens) - tokens.current
}
// ReplyFunc models a function that can reply an order.
type ReplyFunc func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string
// Bot that manages the connection to Telegram
type Bot interface {
// Add a reply func
Add(key string, reply ReplyFunc)
// Add a master
AddMaster(master string)
// Loop through the messages
Loop()
}
type bot struct {
api *tgbotapi.BotAPI
replies map[string]ReplyFunc
masters []string
pin string
}
// NewBot creates a new Telegram Bot
func NewBot(token string) (Bot, error) {
api, err := tgbotapi.NewBotAPI(token)
if err != nil {
return nil, err
}
api.Debug = true
log.Printf("Bot username %s", api.Self.UserName)
result := &bot{
masters: make([]string, 0, 10),
api: api,
replies: make(map[string]ReplyFunc),
}
result.Add("master", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
if tokens.Remaining() <= 0 {
return "You must specify the PIN"
}
master := tokens.Next()
bot.AddMaster(master)
return fmt.Sprintf("Username %s added as master", master)
})
return result, nil
}
// Checks if the message comes from some of the masters
func (bot *bot) isMaster(msg *tgbotapi.Message) bool {
uname := msg.From.String()
// First message becomes master
if bot.masters == nil || len(bot.masters) == 0 {
bot.masters = append(bot.masters, uname)
bot.api.Send(tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("%s has become my first master", uname)))
return true
}
// Look in master list
found := false
for _, master := range bot.masters {
if master == uname {
found = true
break
}
}
if !found {
bot.api.Send(tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("%s is not my master", uname)))
}
return found
}
func (bot *bot) Add(key string, reply ReplyFunc) {
bot.replies[strings.ToLower(key)] = reply
}
func (bot *bot) AddMaster(master string) {
bot.masters = append(bot.masters, master)
}
// Messages coming from the robot
func (bot *bot) messages() <-chan *tgbotapi.Message {
// Update channel
result := make(chan *tgbotapi.Message)
go func(result chan *tgbotapi.Message) {
defer close(result)
updates := tgbotapi.NewUpdate(0)
updates.Timeout = 60
items, err := bot.api.GetUpdatesChan(updates)
if err != nil {
log.Print("Error: ", err)
return
}
for item := range items {
// Message can be new or edited
message := item.Message
if message == nil {
message = item.EditedMessage
}
if message != nil && bot.isMaster(message) {
result <- message
}
}
}(result)
return result
}
// Help string
func (bot *bot) help() string {
// Full list of handlers
orderlist := make([]string, 0, len(bot.replies))
for name := range bot.replies {
orderlist = append(orderlist, name)
}
return fmt.Sprintf("Known commands:\n - %s", strings.Join(orderlist, "\n - "))
}
func (bot *bot) Loop() {
for msg := range bot.messages() {
// Start parsing tokens
tokens := TokensFrom(msg)
for tokens.Remaining() > 0 {
result := ""
order := strings.ToLower(tokens.Next())
if reply, ok := bot.replies[order]; !ok {
result = fmt.Sprintf("Command %s is not known.\n%s", order, bot.help())
} else {
remaining := tokens.Remaining()
result = reply(bot, msg, tokens)
if tokens.Remaining() > remaining {
result = fmt.Sprintf("Possible loop in command %s, len(remainder) has grown", order)
bot.api.Send(tgbotapi.NewMessage(msg.Chat.ID, result))
return
}
}
newMsg := tgbotapi.NewMessage(msg.Chat.ID, result)
//newMsg.ReplyToMessageID = update.Message.MessageID
bot.api.Send(newMsg)
}
}
}