/
greetbot.go
163 lines (131 loc) · 4.12 KB
/
greetbot.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
// vim:ts=4:sw=4
// i3 - IRC bot to greet people and tell them to wait
// © 2012 Michael Stapelberg (see also: LICENSE)
package main
import (
"flag"
"log"
"math/rand"
"regexp"
"strings"
"time"
irc "github.com/fluffle/goirc/client"
"github.com/stapelberg/greetbot/histogram"
)
var (
ircChannel = flag.String(
"channel",
"#i3-build",
"In which channel this bot should be in")
ircPassword = flag.String(
"password",
"",
"Server password, if required. For FreeNode, can be <account>:<password> to identify to services (https://freenode.net/kb/answer/registration)")
greetings = flag.String(
"greetings",
"hello,hallo,hey,hi,yo,good morning,ohai",
"Greeting words on which the bot should react, comma-separated")
greetings_re []*regexp.Regexp
histogram_path = flag.String(
"histogram_path",
"./histogram.data",
"Serialized protobuf file containing the histogram data")
my_histogram histogram.Histogram
lastGreetingTime = time.Now()
)
// Returns true if any of the greeting words of the -greetings flag is
// contained in the given message.
func containsGreeting(msg string) bool {
lcMsg := []byte(strings.ToLower(msg))
for _, re := range greetings_re {
if re.Match(lcMsg) {
log.Printf("Message %s contains greeting %s\n", msg, re)
return true
}
}
log.Printf("Message %s does NOT contain a greeting\n", msg)
return false
}
func isBot(nickname string) bool {
// TODO
return false
}
// Called when somebody writes some message to the IRC channel.
func handleMessage(conn *irc.Conn, line *irc.Line) {
msg := line.Args[1]
if line.Args[0] != *ircChannel {
log.Printf(`Ignoring private message to me: "%s"`, msg)
return
}
log.Printf("Received: *%s* says: \n", line.Nick)
// Ignore messages from bots.
if isBot(line.Nick) {
log.Printf("Ignoring message, %s is a bot", line.Nick)
return
}
// Count every line as activity.
my_histogram.LogActivity(line.Nick)
// Check for greetings and say hello.
if len(msg) < 10 && containsGreeting(msg) {
if (time.Now().Unix() - lastGreetingTime.Unix()) < 30 {
log.Printf("Not replying, 30 seconds have not passed yet")
return
}
log.Printf(`Replying to line "%s"`, msg)
lastGreetingTime = time.Now()
go func() {
// Reply after a random time, at least 1s after the
// original line, at most 5s after the original line.
fuzz := rand.Int63n(4000)
time.Sleep((time.Duration)(1000+fuzz) * time.Millisecond)
conn.Privmsg(*ircChannel, "Hello! Please be patient, as it may be some time before someone is around who can answer your question. In the meantime, please remember to read the user guide, as well as the current /topic")
}()
return
}
// Otherwise, see if that was a question.
if !strings.HasSuffix(strings.TrimSpace(msg), "?") {
return
}
log.Printf(`Got question "%s" from "%s"`, msg, line.Nick)
// See if we have enough data points about the activity of this channel for
// this day and hour.
if my_histogram.IsActive() {
return
}
log.Printf("Telling user to be patient\n")
}
func main() {
flag.Parse()
// Compile regular expressions which match the greeting words if they
// appear as a standalone word.
for _, greetword := range strings.SplitN(*greetings, ",", -1) {
re := regexp.MustCompile(`\b` + greetword + `\b`)
greetings_re = append(greetings_re, re)
}
my_histogram = histogram.Load(*histogram_path)
quit := make(chan bool)
c := irc.SimpleClient("Eyo", "i3", "http://i3wm.org/")
c.HandleFunc(irc.CONNECTED,
func(conn *irc.Conn, line *irc.Line) {
log.Printf("Connected, joining channel %s\n", *ircChannel)
conn.Join(*ircChannel)
})
c.HandleFunc("disconnected",
func(conn *irc.Conn, line *irc.Line) { quit <- true })
c.HandleFunc("PRIVMSG", handleMessage)
log.Printf("Connecting...\n")
if err := c.ConnectTo("chat.freenode.net", *ircPassword); err != nil {
log.Printf("Connection error: %s\n", err.Error())
}
// program main loop
for {
select {
case <-quit:
log.Println("Disconnected. Reconnecting...")
if err := c.ConnectTo("chat.freenode.net", *ircPassword); err != nil {
log.Printf("Connection error: %s\n", err.Error())
}
}
}
log.Fatalln("Fell out of the main loop?!")
}