2012-08-17 20:38:15 +00:00
|
|
|
package bot
|
|
|
|
|
|
|
|
import (
|
2012-10-11 20:28:00 +00:00
|
|
|
"errors"
|
2012-08-17 20:38:15 +00:00
|
|
|
"fmt"
|
2013-03-04 15:43:23 +00:00
|
|
|
"labix.org/v2/mgo/bson"
|
2012-08-26 19:14:09 +00:00
|
|
|
"math/rand"
|
2013-03-04 15:43:23 +00:00
|
|
|
"regexp"
|
2012-08-26 19:14:09 +00:00
|
|
|
"strconv"
|
2012-08-17 22:09:29 +00:00
|
|
|
"strings"
|
2012-08-26 19:14:09 +00:00
|
|
|
"time"
|
2012-08-17 20:38:15 +00:00
|
|
|
)
|
|
|
|
import irc "github.com/fluffle/goirc/client"
|
|
|
|
|
|
|
|
// Interface used for compatibility with the Plugin interface
|
|
|
|
type Handler interface {
|
2012-08-17 22:09:29 +00:00
|
|
|
Message(message Message) bool
|
2012-08-25 04:46:13 +00:00
|
|
|
Event(kind string, message Message) bool
|
2013-05-08 00:08:18 +00:00
|
|
|
BotMessage(message Message) bool
|
2012-08-17 22:56:44 +00:00
|
|
|
Help(channel string, parts []string)
|
2012-08-17 20:38:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Checks to see if our user exists and if any changes have occured to it
|
|
|
|
// This uses a linear scan for now, oh well.
|
|
|
|
func (b *Bot) checkuser(nick string) *User {
|
|
|
|
var user *User = nil
|
|
|
|
for _, usr := range b.Users {
|
|
|
|
if usr.Name == nick {
|
|
|
|
user = &usr
|
2012-08-25 01:43:53 +00:00
|
|
|
break
|
2012-08-17 20:38:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if user == nil {
|
2012-08-26 23:23:51 +00:00
|
|
|
isadmin := false
|
|
|
|
for _, u := range b.Config.Admins {
|
|
|
|
if nick == u {
|
|
|
|
isadmin = true
|
|
|
|
}
|
|
|
|
}
|
2012-08-17 20:38:15 +00:00
|
|
|
user = &User{
|
2012-08-17 21:37:49 +00:00
|
|
|
Name: nick,
|
|
|
|
Alts: make([]string, 1),
|
2012-08-17 20:38:15 +00:00
|
|
|
MessageLog: make([]string, 50),
|
2012-08-26 23:23:51 +00:00
|
|
|
Admin: isadmin,
|
2012-08-17 20:38:15 +00:00
|
|
|
}
|
|
|
|
b.Users = append(b.Users, *user)
|
|
|
|
}
|
|
|
|
|
|
|
|
return user
|
|
|
|
}
|
|
|
|
|
2012-08-18 01:39:26 +00:00
|
|
|
// Checks to see if the user is asking for help, returns true if so and handles the situation.
|
|
|
|
func (b *Bot) checkHelp(channel string, parts []string) {
|
2012-08-18 02:30:04 +00:00
|
|
|
if len(parts) == 1 {
|
2012-08-18 01:39:26 +00:00
|
|
|
// just print out a list of help topics
|
2012-08-27 00:30:23 +00:00
|
|
|
topics := "Help topics: about variables"
|
2012-08-18 01:39:26 +00:00
|
|
|
for name, _ := range b.Plugins {
|
|
|
|
topics = fmt.Sprintf("%s, %s", topics, name)
|
|
|
|
}
|
|
|
|
b.SendMessage(channel, topics)
|
|
|
|
} else {
|
|
|
|
// trigger the proper plugin's help response
|
|
|
|
if parts[1] == "about" {
|
|
|
|
b.Help(channel, parts)
|
|
|
|
return
|
|
|
|
}
|
2012-08-27 00:30:23 +00:00
|
|
|
if parts[1] == "variables" {
|
|
|
|
b.listVars(channel, parts)
|
|
|
|
return
|
|
|
|
}
|
2012-08-18 01:39:26 +00:00
|
|
|
plugin := b.Plugins[parts[1]]
|
|
|
|
if plugin != nil {
|
|
|
|
plugin.Help(channel, parts)
|
|
|
|
} else {
|
|
|
|
msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", parts[1])
|
|
|
|
b.SendMessage(channel, msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks if message is a command and returns its curtailed version
|
|
|
|
func (b *Bot) isCmd(message string) (bool, string) {
|
|
|
|
cmdc := b.Config.CommandChar
|
2013-01-22 14:45:31 +00:00
|
|
|
botnick := strings.ToLower(b.Conn.Me.Nick)
|
2012-08-18 01:39:26 +00:00
|
|
|
iscmd := false
|
2013-02-07 22:37:38 +00:00
|
|
|
lowerMessage := strings.ToLower(message)
|
2012-08-18 01:39:26 +00:00
|
|
|
|
2013-05-07 23:30:37 +00:00
|
|
|
rex := fmt.Sprintf(`^%s\S\s.+`, botnick)
|
|
|
|
|
2013-02-07 22:37:38 +00:00
|
|
|
if strings.HasPrefix(lowerMessage, cmdc) && len(cmdc) > 0 {
|
2012-08-18 01:39:26 +00:00
|
|
|
iscmd = true
|
|
|
|
message = message[len(cmdc):]
|
2013-05-08 00:56:09 +00:00
|
|
|
// } else if match, _ := regexp.MatchString(rex, lowerMessage); match {
|
|
|
|
} else if strings.HasPrefix(lowerMessage, botnick) &&
|
|
|
|
len(lowerMessage) > len(botnick) &&
|
|
|
|
(lowerMessage[len(botnick)] == ',' ||
|
|
|
|
lowerMessage[len(botnick)] == ':') &&
|
|
|
|
lowerMessage[len(botnick)+1] == ' ' {
|
|
|
|
|
2012-08-18 01:39:26 +00:00
|
|
|
iscmd = true
|
|
|
|
message = message[len(botnick):]
|
|
|
|
|
|
|
|
// trim off the customary addressing punctuation
|
|
|
|
if message[0] == ':' || message[0] == ',' {
|
|
|
|
message = message[1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim off any whitespace left on the message
|
|
|
|
message = strings.TrimSpace(message)
|
|
|
|
|
|
|
|
return iscmd, message
|
|
|
|
}
|
|
|
|
|
2012-08-25 04:46:13 +00:00
|
|
|
// Builds our internal message type out of a Conn & Line from irc
|
2012-08-25 04:49:48 +00:00
|
|
|
func (b *Bot) buildMessage(conn *irc.Conn, line *irc.Line) Message {
|
2012-08-17 20:38:15 +00:00
|
|
|
// Check for the user
|
|
|
|
user := b.checkuser(line.Nick)
|
|
|
|
|
|
|
|
channel := line.Args[0]
|
2012-08-18 03:11:36 +00:00
|
|
|
if channel == conn.Me.Nick {
|
|
|
|
channel = line.Nick
|
|
|
|
}
|
2012-08-23 20:28:45 +00:00
|
|
|
|
|
|
|
isaction := line.Cmd == "ACTION"
|
|
|
|
|
2012-08-25 04:46:13 +00:00
|
|
|
var message string
|
|
|
|
if len(line.Args) > 1 {
|
|
|
|
message = line.Args[1]
|
|
|
|
}
|
2013-01-23 00:22:32 +00:00
|
|
|
|
2012-08-18 01:39:26 +00:00
|
|
|
iscmd := false
|
2012-08-23 20:28:45 +00:00
|
|
|
filteredMessage := message
|
|
|
|
if !isaction {
|
|
|
|
iscmd, filteredMessage = b.isCmd(message)
|
|
|
|
}
|
2012-08-17 20:38:15 +00:00
|
|
|
|
|
|
|
user.MessageLog = append(user.MessageLog, message)
|
|
|
|
|
2012-08-23 20:28:45 +00:00
|
|
|
msg := Message{
|
|
|
|
User: user,
|
|
|
|
Channel: channel,
|
|
|
|
Body: filteredMessage,
|
2012-08-25 04:49:48 +00:00
|
|
|
Raw: message,
|
2012-08-23 20:28:45 +00:00
|
|
|
Command: iscmd,
|
2012-08-25 04:49:48 +00:00
|
|
|
Action: isaction,
|
2012-10-11 20:28:00 +00:00
|
|
|
Time: time.Now(),
|
2013-04-21 20:49:00 +00:00
|
|
|
Host: line.Host,
|
2012-08-23 20:28:45 +00:00
|
|
|
}
|
2013-02-07 22:37:38 +00:00
|
|
|
|
2012-08-25 04:46:13 +00:00
|
|
|
return msg
|
|
|
|
}
|
|
|
|
|
2012-10-11 20:28:00 +00:00
|
|
|
func (b *Bot) LastMessage() (Message, error) {
|
|
|
|
log := <-b.logOut
|
|
|
|
if len(log) == 0 {
|
|
|
|
return Message{}, errors.New("No messages found.")
|
|
|
|
}
|
|
|
|
return log[len(log)-1], nil
|
|
|
|
}
|
|
|
|
|
2012-08-17 20:38:15 +00:00
|
|
|
// Take an input string and mutate it based on $vars in the string
|
2012-08-17 22:09:29 +00:00
|
|
|
func (b *Bot) Filter(message Message, input string) string {
|
2012-08-26 19:14:09 +00:00
|
|
|
rand.Seed(time.Now().Unix())
|
|
|
|
|
2012-08-17 22:09:29 +00:00
|
|
|
if strings.Contains(input, "$NICK") {
|
|
|
|
nick := strings.ToUpper(message.User.Name)
|
|
|
|
input = strings.Replace(input, "$NICK", nick, -1)
|
2012-08-26 19:14:09 +00:00
|
|
|
}
|
|
|
|
|
2012-08-26 23:23:51 +00:00
|
|
|
// Let's be bucket compatible for this var
|
2012-08-27 00:33:33 +00:00
|
|
|
input = strings.Replace(input, "$who", "$nick", -1)
|
2012-08-26 19:14:09 +00:00
|
|
|
if strings.Contains(input, "$nick") {
|
2012-08-17 22:09:29 +00:00
|
|
|
nick := message.User.Name
|
|
|
|
input = strings.Replace(input, "$nick", nick, -1)
|
|
|
|
}
|
2012-08-26 19:14:09 +00:00
|
|
|
|
|
|
|
if strings.Contains(input, "$someone") {
|
|
|
|
someone := b.Users[rand.Intn(len(b.Users))].Name
|
|
|
|
input = strings.Replace(input, "$someone", someone, -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
for strings.Contains(input, "$digit") {
|
|
|
|
num := strconv.Itoa(rand.Intn(9))
|
|
|
|
input = strings.Replace(input, "$digit", num, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
for strings.Contains(input, "$nonzero") {
|
|
|
|
num := strconv.Itoa(rand.Intn(8) + 1)
|
|
|
|
input = strings.Replace(input, "$nonzero", num, 1)
|
|
|
|
}
|
|
|
|
|
2012-08-26 23:23:51 +00:00
|
|
|
r, err := regexp.Compile("\\$[A-z]+")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
varname := r.FindString(input)
|
|
|
|
blacklist := make(map[string]bool)
|
|
|
|
blacklist["$and"] = true
|
|
|
|
for len(varname) > 0 && !blacklist[varname] {
|
|
|
|
var result []Variable
|
|
|
|
b.varColl.Find(bson.M{"variable": varname}).All(&result)
|
|
|
|
if len(result) == 0 {
|
|
|
|
blacklist[varname] = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
variable := result[rand.Intn(len(result))]
|
|
|
|
input = strings.Replace(input, varname, variable.Value, 1)
|
|
|
|
varname = r.FindString(input)
|
|
|
|
}
|
|
|
|
|
2012-08-17 20:38:15 +00:00
|
|
|
return input
|
|
|
|
}
|
|
|
|
|
2012-08-27 00:30:23 +00:00
|
|
|
func (b *Bot) listVars(channel string, parts []string) {
|
|
|
|
var result []string
|
|
|
|
err := b.varColl.Find(bson.M{}).Distinct("variable", &result)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
msg := "I know: $who, $someone, $digit, $nonzero"
|
|
|
|
for _, variable := range result {
|
|
|
|
msg = fmt.Sprintf("%s, %s", msg, variable)
|
|
|
|
}
|
|
|
|
b.SendMessage(channel, msg)
|
|
|
|
}
|
|
|
|
|
2012-08-17 22:56:44 +00:00
|
|
|
func (b *Bot) Help(channel string, parts []string) {
|
|
|
|
msg := fmt.Sprintf("Hi, I'm based on godeepintir version %s. I'm written in Go, and you "+
|
|
|
|
"can find my source code on the internet here: "+
|
2013-05-06 04:29:13 +00:00
|
|
|
"http://github.com/chrissexton/alepale", b.Version)
|
2012-08-17 22:56:44 +00:00
|
|
|
b.SendMessage(channel, msg)
|
|
|
|
}
|
2012-08-25 04:46:13 +00:00
|
|
|
|
2013-01-23 00:22:32 +00:00
|
|
|
func (b *Bot) ActionRecieved(conn *irc.Conn, line *irc.Line) {
|
2012-08-25 04:46:13 +00:00
|
|
|
msg := b.buildMessage(conn, line)
|
2012-08-26 16:15:26 +00:00
|
|
|
for _, name := range b.PluginOrdering {
|
|
|
|
p := b.Plugins[name]
|
2012-08-25 04:46:13 +00:00
|
|
|
if p.Event(line.Cmd, msg) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2012-08-25 04:49:48 +00:00
|
|
|
}
|
2013-05-08 00:08:18 +00:00
|
|
|
|
|
|
|
// Send our own musings to the plugins
|
|
|
|
func (b *Bot) selfSaid(channel, message string) {
|
|
|
|
msg := Message{
|
|
|
|
User: &b.Me, // hack
|
|
|
|
Channel: channel,
|
|
|
|
Body: message,
|
|
|
|
Raw: message, // hack
|
|
|
|
Command: false,
|
|
|
|
Time: time.Now(),
|
|
|
|
Host: "0.0.0.0", // hack
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range b.PluginOrdering {
|
|
|
|
p := b.Plugins[name]
|
|
|
|
if p.BotMessage(msg) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|