Initial revision of the godeepintir bot. This is a revised version of deepintir,

a python bot which used ii to connect. This version houses its own IRC
connection and therefore is most useful with a bouncer such as ZNC. This initial
version does not exhibit much functionality. It can only show off how plugins
might be written in later versions and currently mimics anything said to it.
There may be bugs, and it will most certainly be altered in signifigant ways
before the API is stable.
This commit is contained in:
Chris Sexton 2012-08-17 16:38:15 -04:00
commit 4b9b8fa682
7 changed files with 324 additions and 0 deletions

1
.hgignore Normal file
View File

@ -0,0 +1 @@
godeepintir

41
bot/bot.go Normal file
View File

@ -0,0 +1,41 @@
package bot
import irc "github.com/fluffle/goirc/client"
// Bot type provides storage for bot-wide information, configs, and database connections
type Bot struct {
Plugins []Handler
Users []User
Conn *irc.Conn
// mongodb connection will go here
}
// User type stores user history. This is a vehicle that will follow the user for the active
// session
type User struct {
// Current nickname known
Name string
// LastSeen DateTime
// Alternative nicknames seen
Alts []string
// Last N messages sent to the user
MessageLog []string
}
// NewBot creates a Bot for a given connection and set of handlers. The handlers must not
// require the bot as input for their creation (so use AddHandler instead to add handlers)
func NewBot(c *irc.Conn, p ...Handler) *Bot {
return &Bot{
Plugins: p,
Users: make([]User, 10),
Conn: c,
}
}
// Adds a constructed handler to the bots handlers list
func (b *Bot) AddHandler(h Handler) {
b.Plugins = append(b.Plugins, h)
}

61
bot/handlers.go Normal file
View File

@ -0,0 +1,61 @@
package bot
import (
"fmt"
)
import irc "github.com/fluffle/goirc/client"
// Interface used for compatibility with the Plugin interface
type Handler interface {
Message(user *User, channel, message string) bool
}
// 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
}
}
if user == nil {
fmt.Println("Making a new user")
user = &User{
Name: nick,
Alts: make([]string, 1),
MessageLog: make([]string, 50),
}
b.Users = append(b.Users, *user)
}
return user
}
// Handles incomming PRIVMSG requests
func (b *Bot) Msg_recieved(conn *irc.Conn, line *irc.Line) {
// Check for the user
user := b.checkuser(line.Nick)
channel := line.Args[0]
message := line.Args[1]
user.MessageLog = append(user.MessageLog, message)
fmt.Printf("In %s, %s said: '%s'\n", channel, line.Nick, message)
for _, p := range b.Plugins {
if p.Message(user, channel, message) {
break
}
}
}
// Take an input string and mutate it based on $vars in the string
func (b *Bot) filter(input string) string {
return input
}
// Sends message to channel
func (b *Bot) SendMessage(channel, message string) {
b.Conn.Privmsg(channel, message)
}

62
config.json Normal file
View File

@ -0,0 +1,62 @@
{
"Dbname": "deepintir",
"Dbserver": "127.0.0.1",
"Channels": ["#AlePaleTest"],
"Plugins": [],
"Server": "127.0.0.1:6666",
"Nick": "AlePaleTest",
"Pass": "AlePaleTest:test",
"comment": "Follows is the old bot",
"bot": {
"dbname": "deepintir",
"inchan": "out",
"outchan": "in",
"logchan": "logs",
"nick": "|AlePale|",
"channel": "#flagonslayers",
"command_char": "!",
"plugins": {
"help": "Help",
"talker": "Talker",
"remember": "Remember",
"beers": "Beers",
"first": "First",
"sudo": "Sudo"
},
"_plugins": {
"tama": "Tama"
},
"datastore": "datastore.dat"
},
"Talker": {
"names": ["fredfelps", "felps"],
"slogan": "GOD HATES %NICK%"
},
"Remember": {
"quotefile": "quotes.txt",
"loglength": 50,
"quotechance": 0.10,
"quotetimer": 1800
},
"Beers": {
"responses": [
"Stay thirsty, my friend!",
"HIC!",
"ZIGGY! ZAGGY!"
],
"countfile": "beers.txt",
"drinktriggers": [
"drink",
"imbibe"
],
"puketriggers": [
"puke",
"yack"
]
},
"Sudo": {
"chance": 0.01
}
}

31
config/config.go Normal file
View File

@ -0,0 +1,31 @@
package config
import "encoding/json"
import "fmt"
import "io/ioutil"
// Config stores any system-wide startup information that cannot be easily configured via
// the database
type Config struct {
Dbname string
Dbserver string
Channels []string
Plugins []string
Nick, Server, Pass string
}
// Readconfig loads the config data out of a JSON file located in cfile
func Readconfig(cfile string) *Config {
fmt.Printf("Using %s as config file.\n", cfile)
file, e := ioutil.ReadFile(cfile)
if e != nil {
panic("Couldn't read config file!")
}
var c Config
err := json.Unmarshal(file, &c)
if err != nil {
panic(err)
}
return &c
}

58
main.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"flag"
"fmt"
"godeepintir/config"
"godeepintir/bot"
"godeepintir/plugins"
)
import irc "github.com/fluffle/goirc/client"
func main() {
// These belong in the JSON file
// var server = flag.String("server", "irc.freenode.net", "Server to connect to.")
// var nick = flag.String("nick", "CrappyBot", "Nick to set upon connection.")
// var pass = flag.String("pass", "", "IRC server password to use")
// var channel = flag.String("channel", "#AlePaleTest", "Channel to connet to.")
var cfile = flag.String("config", "config.json", "Config file to load. (Defaults to config.json)")
flag.Parse() // parses the logging flags.
config := config.Readconfig(*cfile)
fmt.Println(config)
c := irc.SimpleClient(config.Nick)
// Optionally, enable SSL
c.SSL = false
// Add handlers to do things here!
// e.g. join a channel on connect.
c.AddHandler("connected",
func(conn *irc.Conn, line *irc.Line) {
for _, channel := range config.Channels {
conn.Join(channel)
}
})
// And a signal on disconnect
quit := make(chan bool)
c.AddHandler("disconnected",
func(conn *irc.Conn, line *irc.Line) { quit <- true })
b := bot.NewBot(c)
b.AddHandler(plugins.NewTestPlugin(b))
c.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
b.Msg_recieved(conn, line)
})
// Tell client to connect
if err := c.Connect(config.Server, config.Pass); err != nil {
fmt.Printf("Connection error: %s\n", err)
}
// Wait for disconnect
<-quit
}

70
plugins/plugins.go Normal file
View File

@ -0,0 +1,70 @@
package plugins
import "fmt"
import "godeepintir/bot"
// Plugin interface defines the methods needed to accept a plugin
type Plugin interface {
Message(user *bot.User, channel, message string) bool
LoadData()
}
// ---- Below are some example plugins
// Creates a new TestPlugin with the Plugin interface
func NewTestPlugin(bot *bot.Bot) *TestPlugin {
tp := TestPlugin{}
tp.LoadData()
tp.Bot = bot
return &tp
}
// TestPlugin type allows our plugin to store persistent state information
type TestPlugin struct {
Bot *bot.Bot
Responds []string
Name string
Feces string
}
func (p *TestPlugin) LoadData() {
config := GetPluginConfig("TestPlugin")
p.Name = config.Name
p.Feces = config.Values["Feces"].(string)
}
func (p *TestPlugin) Message(user *bot.User, channel, message string) bool {
fmt.Println(user, message)
fmt.Println("My plugin name is:", p.Name, " My feces are:", p.Feces)
p.Bot.SendMessage(channel, message)
return true
}
type PluginConfig struct {
Name string
Values map[string]interface{}
}
// Loads plugin config out of the DB
// Stored in db.plugins.find("name": name)
func GetPluginConfig(name string) PluginConfig {
return PluginConfig{
Name: "TestPlugin",
Values: map[string]interface{}{
"Feces": "test",
"Responds": "fucker",
},
}
}
// FalsePlugin shows how plugin fallthrough works for handling messages
type FalsePlugin struct{}
func (fp FalsePlugin) Message(user, message string) bool {
fmt.Println("FalsePlugin returning false.")
return false
}
func (fp FalsePlugin) LoadData() {
}