From 1efa7ebcd4700a5631f6d182d2c0caa2ac57b7fd Mon Sep 17 00:00:00 2001 From: Chris Sexton Date: Fri, 15 Jan 2016 01:12:26 -0500 Subject: [PATCH] Initial mongo->sqlite move Tons of bugs, I'm sure. This commit mostly gets the bot moving towards SQLite. It builds, but many plugins have a log.Fatal to prevent their use and it has not been tested. --- bot/bot.go | 67 ++++++++++++--- bot/handlers.go | 39 ++++++--- bot/users.go | 90 +++----------------- config/config.go | 1 + plugins/admin.go | 43 +++++----- plugins/beers.go | 178 +++++++++++++++++++++++++++------------- plugins/counter.go | 104 +++++++++++++++-------- plugins/downtime.go | 9 +- plugins/factoid.go | 6 +- plugins/feed.go | 6 +- plugins/first.go | 24 +++--- plugins/lisp.go | 13 ++- plugins/plugins.go | 3 +- plugins/remember.go | 28 ++++--- plugins/twitter.go | 143 -------------------------------- plugins/webTemplates.go | 2 + 16 files changed, 361 insertions(+), 395 deletions(-) delete mode 100644 plugins/twitter.go diff --git a/bot/bot.go b/bot/bot.go index 0f56bae..16689e7 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -3,6 +3,7 @@ package bot import ( + "database/sql" "html/template" "log" "net/http" @@ -11,7 +12,8 @@ import ( "code.google.com/p/velour/irc" "github.com/chrissexton/alepale/config" - "labix.org/v2/mgo" + + _ "github.com/mattn/go-sqlite3" ) const actionPrefix = "\x01ACTION" @@ -35,11 +37,9 @@ type Bot struct { Config *config.Config - // Mongo connection and db allow botwide access to the database - DbSession *mgo.Session - Db *mgo.Database - - varColl *mgo.Collection + // SQL DB + DB *sql.DB + DBVersion int64 logIn chan Message logOut chan Messages @@ -101,13 +101,11 @@ type Variable struct { // NewBot creates a Bot for a given connection and set of handlers. func NewBot(config *config.Config, c *irc.Client) *Bot { - session, err := mgo.Dial(config.DbServer) + sqlDB, err := sql.Open("sqlite3", config.DbFile) if err != nil { - panic(err) + log.Fatal(err) } - db := session.DB(config.DbName) - logIn := make(chan Message) logOut := make(chan Messages) @@ -126,15 +124,15 @@ func NewBot(config *config.Config, c *irc.Client) *Bot { Users: users, Me: users[0], Client: c, - DbSession: session, - Db: db, - varColl: db.C("variables"), + DB: sqlDB, logIn: logIn, logOut: logOut, Version: config.Version, httpEndPoints: make(map[string]string), } + bot.migrateDB() + http.HandleFunc("/", bot.serveRoot) if config.HttpAddr == "" { config.HttpAddr = "127.0.0.1:1337" @@ -144,6 +142,49 @@ func NewBot(config *config.Config, c *irc.Client) *Bot { return bot } +// Create any tables if necessary based on version of DB +// Plugins should create their own tables, these are only for official bot stuff +// Note: This does not return an error. Database issues are all fatal at this stage. +func (b *Bot) migrateDB() { + _, err := b.DB.Exec(`create table if not exists version (version integer);`) + if err != nil { + log.Fatal(err) + } + var version int64 + err = b.DB.QueryRow("select max(version) from version").Scan(&version) + switch { + case err == sql.ErrNoRows: + log.Printf("No versions, we're the first!.") + _, err := b.DB.Exec(`insert into version (version) values (1)`) + if err != nil { + log.Fatal(err) + } + case err != nil: + log.Fatal(err) + default: + b.DBVersion = version + log.Printf("Database version: %d\n", version) + } + + if version == 1 { + if _, err := b.DB.Exec(`create table if not exists variables ( + id integer primary key, + name string, + perms string, + type string + );`); err != nil { + log.Fatal(err) + } + if _, err := b.DB.Exec(`create table if not exists values ( + id integer primary key, + varId integer, + value string + );`); err != nil { + log.Fatal(err) + } + } +} + // Adds a constructed handler to the bots handlers list func (b *Bot) AddHandler(name string, h Handler) { b.Plugins[strings.ToLower(name)] = h diff --git a/bot/handlers.go b/bot/handlers.go index f700ad8..47dcbfd 100644 --- a/bot/handlers.go +++ b/bot/handlers.go @@ -3,15 +3,17 @@ package bot import ( - "code.google.com/p/velour/irc" + "database/sql" "errors" "fmt" - "labix.org/v2/mgo/bson" + "log" "math/rand" "regexp" "strconv" "strings" "time" + + "code.google.com/p/velour/irc" ) // Interface used for compatibility with the Plugin interface @@ -180,28 +182,43 @@ func (b *Bot) Filter(message Message, input string) string { 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 { + text, err := b.getVar(varname) + if err != nil { blacklist[varname] = true continue } - variable := result[rand.Intn(len(result))] - input = strings.Replace(input, varname, variable.Value, 1) + input = strings.Replace(input, varname, text, 1) varname = r.FindString(input) } return input } +func (b *Bot) getVar(varName string) (string, error) { + var text string + err := b.DB.QueryRow("select v.value from variables as va inner join values as v on va.id = va.id = v.varId order by random() limit 1").Scan(&text) + switch { + case err == sql.ErrNoRows: + return "", fmt.Errorf("No factoid found") + case err != nil: + log.Fatal(err) + } + return text, nil +} + func (b *Bot) listVars(channel string, parts []string) { - var result []string - err := b.varColl.Find(bson.M{}).Distinct("variable", &result) + rows, err := b.DB.Query(`select name from variables`) if err != nil { - panic(err) + log.Fatal(err) } msg := "I know: $who, $someone, $digit, $nonzero" - for _, variable := range result { + for rows.Next() { + var variable string + err := rows.Scan(&variable) + if err != nil { + log.Println("Error scanning variable.") + continue + } msg = fmt.Sprintf("%s, %s", msg, variable) } b.SendMessage(channel, msg) diff --git a/bot/users.go b/bot/users.go index ff38cd3..600bebf 100644 --- a/bot/users.go +++ b/bot/users.go @@ -2,13 +2,6 @@ package bot -import ( - // "labix.org/v2/mgo" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" - "log" -) - // User type stores user history. This is a vehicle that will follow the user for the active // session type User struct { @@ -26,6 +19,18 @@ type User struct { //bot *Bot } +var users map[string]*User + +func (b *Bot) GetUser(nick string) *User { + if _, ok := users[nick]; !ok { + users[nick] = &User{ + Name: nick, + Admin: b.checkAdmin(nick), + } + } + return users[nick] +} + func (b *Bot) NewUser(nick string) *User { return &User{ Name: nick, @@ -33,77 +38,6 @@ func (b *Bot) NewUser(nick string) *User { } } -func (b *Bot) GetUser(nick string) *User { - coll := b.Db.C("users") - query := coll.Find(bson.M{"name": nick}) - var user *User - - count, err := query.Count() - - if err != nil { - user = b.NewUser(nick) - coll.Insert(*user) - } else if count == 1 { - err = query.One(&user) - if err != nil { - log.Printf("ERROR adding user: %s -- %s\n", nick, err) - } - } else if count == 0 { - // create the user - log.Printf("Creating new user: %s\n", nick) - user = b.NewUser(nick) - coll.Insert(user) - } else { - log.Printf("Error: %s appears to have more than one user?\n", nick) - query.One(&user) - } - - // grab linked user, if any - if user.Parent != "" { - query := coll.Find(bson.M{"Name": user.Parent}) - if count, err := query.Count(); err != nil && count == 1 { - query.One(user) - } else { - log.Printf("Error: bad linkage on %s -> %s.\n", - user.Name, - user.Parent) - } - } - - found := false - for _, u := range b.Users { - if u.Name == user.Name { - found = true - } - } - if !found { - b.Users = append(b.Users, *user) - } - - return user -} - -// Modify user entry to be a link to other, return other -func (u *User) LinkUser(coll *mgo.Collection, other *User) *User { - user := u - - other.Alts = append(other.Alts, user.Alts...) - user.Alts = []string{} - user.Parent = other.Name - - err := coll.Update(bson.M{"name": u.Name}, u) - if err != nil { - log.Printf("Error updating user: %s\n", u.Name) - } - - err = coll.Update(bson.M{"name": other.Name}, other) - if err != nil { - log.Printf("Error updating other user: %s\n", other.Name) - } - - return other -} - func (b *Bot) checkAdmin(nick string) bool { for _, u := range b.Config.Admins { if nick == u { diff --git a/config/config.go b/config/config.go index 7fc044b..88bab45 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import "io/ioutil" // Config stores any system-wide startup information that cannot be easily configured via // the database type Config struct { + DbFile string DbName string DbServer string Channels []string diff --git a/plugins/admin.go b/plugins/admin.go index e00ac3f..7a58e72 100644 --- a/plugins/admin.go +++ b/plugins/admin.go @@ -3,26 +3,28 @@ package plugins import ( + "database/sql" "fmt" - "github.com/chrissexton/alepale/bot" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" + "log" "math/rand" "strings" "time" + + "github.com/chrissexton/alepale/bot" ) // This is a admin plugin to serve as an example and quick copy/paste for new plugins. type AdminPlugin struct { - Bot *bot.Bot - factC, remC, beerC, varC *mgo.Collection + Bot *bot.Bot + DB *sql.DB } // NewAdminPlugin creates a new AdminPlugin with the Plugin interface func NewAdminPlugin(bot *bot.Bot) *AdminPlugin { p := &AdminPlugin{ Bot: bot, + DB: bot.DB, } p.LoadData() return p @@ -56,19 +58,24 @@ func (p *AdminPlugin) handleVariables(message bot.Message) bool { variable := strings.TrimSpace(parts[0]) value := parts[1] - q := p.varC.Find(bson.M{"variable": variable, "value": value}) - if n, _ := q.Count(); n != 0 { - p.Bot.SendMessage(message.Channel, "I've already got that one.") + var count int64 + var varId int64 + err := p.DB.QueryRow(`select count(*), varId from variables vs inner join values v on vs.id = v.varId where vs.name = ? and v.value = ?`, variable, value).Scan(&count) + switch { + case err == sql.ErrNoRows: + _, err := p.DB.Exec(`insert into values (varId, value) values (?, ?)`, varId, value) + if err != nil { + log.Println(err) + } + msg := fmt.Sprintf("Added '%s' to %s.\n", value, variable) + p.Bot.SendMessage(message.Channel, msg) + return true + case err != nil: + p.Bot.SendMessage(message.Channel, "I'm broke and need attention in my variable creation code.") + log.Println(err) return true } - - p.varC.Insert(bot.Variable{ - Variable: variable, - Value: value, - }) - - msg := fmt.Sprintf("Added '%s' to %s.\n", value, variable) - p.Bot.SendMessage(message.Channel, msg) + p.Bot.SendMessage(message.Channel, "I've already got that one.") return true } @@ -78,10 +85,6 @@ func (p *AdminPlugin) handleVariables(message bot.Message) bool { func (p *AdminPlugin) LoadData() { // This bot has no data to load rand.Seed(time.Now().Unix()) - p.factC = p.Bot.Db.C("factoid") - p.remC = p.Bot.Db.C("remember") - p.beerC = p.Bot.Db.C("beers") - p.varC = p.Bot.Db.C("variables") } // Help responds to help requests. Every plugin must implement a help function. diff --git a/plugins/beers.go b/plugins/beers.go index faf8a28..4df02cf 100644 --- a/plugins/beers.go +++ b/plugins/beers.go @@ -3,6 +3,7 @@ package plugins import ( + "database/sql" "encoding/json" "errors" "fmt" @@ -15,21 +16,56 @@ import ( "time" "github.com/chrissexton/alepale/bot" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" ) // This is a skeleton plugin to serve as an example and quick copy/paste for new plugins. type BeersPlugin struct { - Bot *bot.Bot - Coll *mgo.Collection + Bot *bot.Bot + db *sql.DB +} + +type userBeers struct { + id int64 + nick string + count int + lastDrunk time.Time + saved bool +} + +type untappdUser struct { + id int64 + untappdUser string + channel string + lastCheckin int + chanNick string } // NewBeersPlugin creates a new BeersPlugin with the Plugin interface func NewBeersPlugin(bot *bot.Bot) *BeersPlugin { + if bot.DBVersion == 1 { + if _, err := bot.DB.Exec(`create table if not exists beers ( + id integer primary key, + nick string, + count integer, + lastDrunk integer + );`); err != nil { + log.Fatal(err) + } + + if _, err := bot.DB.Exec(`create table if not exists untappd ( + id integer primary key, + untappdUser string + channel string + lastCheckin integer + chanNick string + );`); err != nil { + log.Fatal(err) + } + } p := BeersPlugin{ Bot: bot, + db: bot.DB, } p.LoadData() for _, channel := range bot.Config.UntappdChannels { @@ -38,31 +74,39 @@ func NewBeersPlugin(bot *bot.Bot) *BeersPlugin { return &p } -type userBeers struct { - Nick string - Beercount int - Lastdrunk time.Time - Momentum float64 - New bool +func (u *userBeers) Save(db *sql.DB) error { + if !u.saved { + res, err := db.Exec(`insert into beers ( + nick string, + count integer, + lastDrunk integer + ) values (?, ?, ?)`, u.nick, u.count, u.lastDrunk) + if err != nil { + return err + } + id, err := res.LastInsertId() + if err != nil { + return err + } + u.id = id + } + return nil } -func (u *userBeers) Save(coll *mgo.Collection) { - _, err := coll.Upsert(bson.M{"nick": u.Nick}, u) - if err != nil { - panic(err) +func getUserBeers(db *sql.DB, nick string) *userBeers { + var ub userBeers + err := db.QueryRow(`select id, nick, count, lastDrunk from beers + where nick = ?`, nick).Scan( + &ub.id, + &ub.nick, + &ub.count, + &ub.lastDrunk, + ) + if err != nil && err != sql.ErrNoRows { + log.Println(err) + return nil } -} -func getUserBeers(coll *mgo.Collection, nick string) *userBeers { - ub := userBeers{New: true} - coll.Find(bson.M{"nick": nick}).One(&ub) - if ub.New == true { - ub.New = false - ub.Nick = nick - ub.Beercount = 0 - ub.Momentum = 0 - ub.Save(coll) - } return &ub } @@ -160,16 +204,33 @@ func (p *BeersPlugin) Message(message bot.Message) bool { channel = parts[3] } u := untappdUser{ - UntappdUser: parts[1], - ChanNick: chanNick, - Channel: channel, + untappdUser: parts[1], + chanNick: chanNick, + channel: channel, } - log.Println("Creating Untappd user:", u.UntappdUser, "nick:", u.ChanNick) + log.Println("Creating Untappd user:", u.untappdUser, "nick:", u.chanNick) - _, err := p.Coll.Upsert(bson.M{"untappduser": u.UntappdUser}, u) + var count int + err := p.db.QueryRow(`select count(*) from untappd + where untappdUser = ?`, u.untappdUser).Scan(&count) if err != nil { - log.Println("ERROR!!!:", err) + log.Println("Error registering untappd: ", err) + } + if count > 0 { + p.Bot.SendMessage(channel, "I'm already watching you.") + return true + } + _, err = p.db.Exec(`insert into untappd ( + untappdUser, + channel, + lastCheckin, + chanNick + ) values (?, ?, ?, ?);`) + if err != nil { + log.Println("Error registering untappd: ", err) + p.Bot.SendMessage(channel, "I can't see.") + return true } p.Bot.SendMessage(channel, "I'll be watching you.") @@ -196,7 +257,6 @@ func (p *BeersPlugin) Event(kind string, message bot.Message) bool { // than the fact that the Plugin interface demands it exist. This may be deprecated at a later // date. func (p *BeersPlugin) LoadData() { - p.Coll = p.Bot.Db.C("beers") rand.Seed(time.Now().Unix()) } @@ -209,10 +269,10 @@ func (p *BeersPlugin) Help(channel string, parts []string) { } func (p *BeersPlugin) setBeers(user string, amount int) { - ub := getUserBeers(p.Coll, user) - ub.Beercount = amount - ub.Lastdrunk = time.Now() - ub.Save(p.Coll) + ub := getUserBeers(p.db, user) + ub.count = amount + ub.lastDrunk = time.Now() + ub.Save(p.db) } func (p *BeersPlugin) addBeers(user string) { @@ -220,9 +280,8 @@ func (p *BeersPlugin) addBeers(user string) { } func (p *BeersPlugin) getBeers(nick string) int { - ub := getUserBeers(p.Coll, nick) - - return ub.Beercount + ub := getUserBeers(p.db, nick) + return ub.count } func (p *BeersPlugin) reportCount(nick, channel string, himself bool) { @@ -245,9 +304,10 @@ func (p *BeersPlugin) puke(user string, channel string) { } func (p *BeersPlugin) doIKnow(nick string) bool { - count, err := p.Coll.Find(bson.M{"nick": nick}).Count() + var count int + err := p.db.QueryRow(`select count(*) from beers where nick = ?`, nick).Scan(&count) if err != nil { - panic(err) + return false } return count > 0 } @@ -288,15 +348,6 @@ type Beers struct { Response resp } -type untappdUser struct { - Id bson.ObjectId `bson:"_id,omitempty"` - UntappdUser string - Channel string - LastCheckin int - ChanNick string - KnownCheckins [5]int -} - func (p *BeersPlugin) pullUntappd() ([]checkin, error) { access_token := "?access_token=" + p.Bot.Config.UntappdToken baseUrl := "https://api.untappd.com/v4/checkin/recent/" @@ -331,11 +382,20 @@ func (p *BeersPlugin) checkUntappd(channel string) { } var users []untappdUser - p.Coll.Find(bson.M{"untappduser": bson.M{"$exists": true}, "channel": channel}).All(&users) + rows, err := p.db.Query(`select *from untappd`) + if err != nil { + log.Println("Error getting untappd users: ", err) + return + } + for rows.Next() { + u := untappdUser{} + rows.Scan(&u.id, &u.untappdUser, &u.channel, &u.lastCheckin, &u.chanNick) + users = append(users, u) + } userMap := make(map[string]untappdUser) for _, u := range users { - userMap[u.UntappdUser] = u + userMap[u.untappdUser] = u } chks, err := p.pullUntappd() @@ -347,7 +407,7 @@ func (p *BeersPlugin) checkUntappd(channel string) { continue } - if checkin.Checkin_id <= userMap[checkin.User.User_name].LastCheckin { + if checkin.Checkin_id <= userMap[checkin.User.User_name].lastCheckin { continue } @@ -362,18 +422,20 @@ func (p *BeersPlugin) checkUntappd(channel string) { if !ok { continue } - p.addBeers(user.ChanNick) - drunken := p.getBeers(user.ChanNick) + p.addBeers(user.chanNick) + drunken := p.getBeers(user.chanNick) msg := fmt.Sprintf("%s just drank %s by %s%s, bringing his drunkeness to %d", - user.ChanNick, beerName, breweryName, venue, drunken) + user.chanNick, beerName, breweryName, venue, drunken) if checkin.Checkin_comment != "" { msg = fmt.Sprintf("%s -- %s", msg, checkin.Checkin_comment) } - user.LastCheckin = checkin.Checkin_id - err := p.Coll.Update(bson.M{"_id": user.Id}, user) + user.lastCheckin = checkin.Checkin_id + _, err := p.db.Exec(`update untappd set + lastCheckin = ? + where id = ?`, user.lastCheckin, user.id) if err != nil { log.Println("UPDATE ERROR!:", err) } diff --git a/plugins/counter.go b/plugins/counter.go index d1f471a..11afc62 100644 --- a/plugins/counter.go +++ b/plugins/counter.go @@ -3,21 +3,23 @@ package plugins import ( + "database/sql" "fmt" - "github.com/chrissexton/alepale/bot" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" + "log" "strings" + + "github.com/chrissexton/alepale/bot" ) // This is a counter plugin to count arbitrary things. type CounterPlugin struct { - Bot *bot.Bot - Coll *mgo.Collection + Bot *bot.Bot + DB *sql.DB } type Item struct { + ID int64 Nick string Item string Count int @@ -25,9 +27,19 @@ type Item struct { // NewCounterPlugin creates a new CounterPlugin with the Plugin interface func NewCounterPlugin(bot *bot.Bot) *CounterPlugin { + if bot.DBVersion == 1 { + if _, err := bot.DB.Exec(`create table if not exists counter ( + id integer primary key, + nick string, + item string, + count integer + );`); err != nil { + log.Fatal(err) + } + } return &CounterPlugin{ - Bot: bot, - Coll: bot.Db.C("counter"), + Bot: bot, + DB: bot.DB, } } @@ -55,38 +67,47 @@ func (p *CounterPlugin) Message(message bot.Message) bool { } // pull all of the items associated with "subject" - var items []Item - p.Coll.Find(bson.M{"nick": subject}).All(&items) - - if len(items) == 0 { - p.Bot.SendMessage(channel, fmt.Sprintf("%s has no counters.", subject)) - return true + rows, err := p.DB.Query(`select * from counter where nick = ?`, subject) + if err != nil { + log.Fatal(err) } resp := fmt.Sprintf("%s has the following counters:", subject) - for i, item := range items { - if i != 0 { - resp = fmt.Sprintf("%s,", resp) + count := 0 + for rows.Next() { + count += 1 + var it Item + rows.Scan(&it.Nick, &it.Item, &it.Count) + + if count > 1 { + resp = fmt.Sprintf("%s, ", resp) } - resp = fmt.Sprintf("%s %s: %d", resp, item.Item, item.Count) - if i > 20 { + resp = fmt.Sprintf("%s %s: %d", resp, it.Item, it.Count) + if count > 20 { fmt.Sprintf("%s, and a few others", resp) break } } resp = fmt.Sprintf("%s.", resp) + if count == 0 { + p.Bot.SendMessage(channel, fmt.Sprintf("%s has no counters.", subject)) + return true + } + p.Bot.SendMessage(channel, resp) return true } else if message.Command && len(parts) == 2 && parts[0] == "clear" { subject := strings.ToLower(nick) itemName := strings.ToLower(parts[1]) - p.Coll.Remove(bson.M{"nick": subject, "item": itemName}) + if _, err := p.DB.Exec(`delete from counters where nick = ? and item = ?`, subject, itemName); err != nil { + p.Bot.SendMessage(channel, "Something went wrong removing that counter;") + return true + } p.Bot.SendAction(channel, fmt.Sprintf("chops a few %s out of his brain", itemName)) - return true } else if message.Command && parts[0] == "count" { @@ -105,11 +126,18 @@ func (p *CounterPlugin) Message(message bot.Message) bool { } var item Item - err := p.Coll.Find(bson.M{"nick": subject, "item": itemName}).One(&item) - if err != nil { + err := p.DB.QueryRow(`select nick, item, count from counters + where nick = ? and item = ?`, subject, itemName).Scan( + &item.Nick, &item.Item, &item.Count, + ) + switch { + case err == sql.ErrNoRows: p.Bot.SendMessage(channel, fmt.Sprintf("I don't think %s has any %s.", subject, itemName)) return true + case err != nil: + log.Println(err) + return true } p.Bot.SendMessage(channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, @@ -149,21 +177,31 @@ func (p *CounterPlugin) Message(message bot.Message) bool { func (p *CounterPlugin) update(subject, itemName string, delta int) Item { var item Item - err := p.Coll.Find(bson.M{"nick": subject, "item": itemName}).One(&item) - if err != nil { + err := p.DB.QueryRow(`select id, nick, item, count from counter + where nick = ? and item = ?;`, subject, itemName).Scan( + &item.ID, &item.Nick, &item.Item, &item.Count, + ) + switch { + case err == sql.ErrNoRows: // insert it - item = Item{ - Nick: subject, - Item: itemName, - Count: delta, + res, err := p.DB.Exec(`insert into counter (nick, item, count) + values (?, ?, ?)`, subject, itemName, delta) + if err != nil { + log.Println(err) } - p.Coll.Insert(item) - } else { - // update it + id, err := res.LastInsertId() + return Item{id, subject, itemName, delta} + case err != nil: + log.Println(err) + return item + default: item.Count += delta - p.Coll.Update(bson.M{"nick": subject, "item": itemName}, item) + _, err := p.DB.Exec(`update counter set count = ? where id = ?`, item.Count, item.ID) + if err != nil { + log.Println(err) + } + return item } - return item } // LoadData imports any configuration data into the plugin. This is not diff --git a/plugins/downtime.go b/plugins/downtime.go index dec2f41..7150819 100644 --- a/plugins/downtime.go +++ b/plugins/downtime.go @@ -6,12 +6,13 @@ import "github.com/chrissexton/alepale/bot" import ( "fmt" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" "log" "sort" "strings" "time" + + "labix.org/v2/mgo" + "labix.org/v2/mgo/bson" ) // This is a downtime plugin to monitor how much our users suck @@ -123,7 +124,9 @@ func (p *DowntimePlugin) remove(user string) { // than the fact that the Plugin interface demands it exist. This may be deprecated at a later // date. func (p *DowntimePlugin) LoadData() { - p.Coll = p.Bot.Db.C("downtime") + // Mongo is removed, this plugin will crash if started + log.Fatal("The Downtime plugin has not been upgraded to SQL yet.") + // p.Coll = p.Bot.Db.C("downtime") } // Help responds to help requests. Every plugin must implement a help function. diff --git a/plugins/factoid.go b/plugins/factoid.go index e545b9f..0d0e076 100644 --- a/plugins/factoid.go +++ b/plugins/factoid.go @@ -420,7 +420,9 @@ func (p *FactoidPlugin) Message(message bot.Message) bool { // than the fact that the Plugin interface demands it exist. This may be deprecated at a later // date. func (p *FactoidPlugin) LoadData() { - p.Coll = p.Bot.Db.C("factoid") + // Mongo is removed, this plugin will crash if started + log.Fatal("The Factoid plugin has not been upgraded to SQL yet.") + // p.Coll = p.Bot.Db.C("factoid") rand.Seed(time.Now().Unix()) } @@ -517,7 +519,7 @@ func (p *FactoidPlugin) serveQuery(w http.ResponseWriter, r *http.Request) { } if e := r.FormValue("entry"); e != "" { var entries []Factoid - p.Coll.Find(bson.M{"trigger": bson.M{"$regex": strings.ToLower(e)}}).All(&entries) + p.Coll.Find(bson.M{"trigger": bson.M{"$regex": strings.ToLower(e)}}).All(&entries) context["Count"] = fmt.Sprintf("%d", len(entries)) context["Entries"] = entries context["Search"] = e diff --git a/plugins/feed.go b/plugins/feed.go index 9897506..509d018 100644 --- a/plugins/feed.go +++ b/plugins/feed.go @@ -23,9 +23,11 @@ type FeedPlugin struct { // NewFeedPlugin creates a new FeedPlugin with the Plugin interface func NewFeedPlugin(bot *bot.Bot) *FeedPlugin { + // Mongo is removed, this plugin will crash if started + log.Fatal("The Feed plugin has not been upgraded to SQL yet.") p := FeedPlugin{ - Bot: bot, - Coll: bot.Db.C("feed"), + Bot: bot, + // Coll: bot.Db.C("feed"), } go p.pollFeeds() return &p diff --git a/plugins/first.go b/plugins/first.go index 9d16a00..3cf1656 100644 --- a/plugins/first.go +++ b/plugins/first.go @@ -4,13 +4,14 @@ package plugins import ( "fmt" + "log" + "regexp" + "strings" + "time" + "github.com/chrissexton/alepale/bot" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" - "log" - "regexp" - "strings" - "time" ) // This is a first plugin to serve as an example and quick copy/paste for new plugins. @@ -30,7 +31,10 @@ type FirstEntry struct { // NewFirstPlugin creates a new FirstPlugin with the Plugin interface func NewFirstPlugin(b *bot.Bot) *FirstPlugin { - coll := b.Db.C("first") + // Mongo is removed, this plugin will crash if started + log.Fatal("The First plugin has not been upgraded to SQL yet.") + var coll *mgo.Collection + // coll := b.Db.C("first") var firsts []FirstEntry query := bson.M{"day": midnight(time.Now())} log.Println("Day:", midnight(time.Now())) @@ -91,11 +95,11 @@ func (p *FirstPlugin) Message(message bot.Message) bool { func (p *FirstPlugin) allowed(message bot.Message) bool { for _, msg := range p.Bot.Config.Bad.Msgs { - match, err := regexp.MatchString(msg, strings.ToLower(message.Body)) - if err != nil { - log.Println("Bad regexp: ", err) - } - if match { + match, err := regexp.MatchString(msg, strings.ToLower(message.Body)) + if err != nil { + log.Println("Bad regexp: ", err) + } + if match { log.Println("Disallowing first: ", message.User.Name, ":", message.Body) return false } diff --git a/plugins/lisp.go b/plugins/lisp.go index b3511a5..91aa1ca 100644 --- a/plugins/lisp.go +++ b/plugins/lisp.go @@ -4,17 +4,16 @@ package plugins import ( "fmt" - "github.com/chrissexton/alepale/bot" - "github.com/chrissexton/kakapo/lisp" - "labix.org/v2/mgo" "log" "strings" "time" + + "github.com/chrissexton/alepale/bot" + "github.com/chrissexton/kakapo/lisp" ) type LispPlugin struct { - Bot *bot.Bot - Coll *mgo.Collection + Bot *bot.Bot } type Program struct { @@ -26,8 +25,7 @@ type Program struct { // NewLispPlugin creates a new LispPlugin with the Plugin interface func NewLispPlugin(bot *bot.Bot) *LispPlugin { return &LispPlugin{ - Bot: bot, - Coll: bot.Db.C("lisp"), + Bot: bot, } } @@ -43,7 +41,6 @@ func (p *LispPlugin) Message(message bot.Message) bool { Contents: strings.Replace(message.Body, "lisp:", "", 1), } log.Println("Evaluating:", prog) - p.Coll.Insert(prog) defer func() { if r := recover(); r != nil { diff --git a/plugins/plugins.go b/plugins/plugins.go index 4398b4d..57fdc09 100644 --- a/plugins/plugins.go +++ b/plugins/plugins.go @@ -75,8 +75,7 @@ type PluginConfig struct { Values map[string]interface{} } -// Loads plugin config out of the DB -// Stored in db.plugins.find("name": name) +// Loads plugin config (could be out of a DB or something) func GetPluginConfig(name string) PluginConfig { return PluginConfig{ Name: "TestPlugin", diff --git a/plugins/remember.go b/plugins/remember.go index 7473153..f7baca1 100644 --- a/plugins/remember.go +++ b/plugins/remember.go @@ -4,13 +4,14 @@ package plugins import ( "fmt" - "github.com/chrissexton/alepale/bot" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" "log" "math/rand" "strings" "time" + + "github.com/chrissexton/alepale/bot" + "labix.org/v2/mgo" + "labix.org/v2/mgo/bson" ) // This is a skeleton plugin to serve as an example and quick copy/paste for new @@ -94,14 +95,15 @@ func (p *RememberPlugin) Message(message bot.Message) bool { if len(msgs) == len(snips) { msg := strings.Join(msgs, "$and") var funcres bson.M - err := p.Bot.Db.Run( - bson.M{"eval": "return counter(\"factoid\");"}, - &funcres, - ) + // Needs to be upgraded to SQL + // err := p.Bot.Db.Run( + // bson.M{"eval": "return counter(\"factoid\");"}, + // &funcres, + // ) - if err != nil { - panic(err) - } + // if err != nil { + // panic(err) + // } id := int(funcres["retval"].(float64)) fact := Factoid{ @@ -116,7 +118,7 @@ func (p *RememberPlugin) Message(message bot.Message) bool { LastAccessed: time.Now(), AccessCount: 0, } - if err = p.Coll.Insert(fact); err != nil { + if err := p.Coll.Insert(fact); err != nil { log.Println("ERROR!!!!:", err) } @@ -142,7 +144,9 @@ func (p *RememberPlugin) Message(message bot.Message) bool { // necessary other than the fact that the Plugin interface demands it exist. // This may be deprecated at a later date. func (p *RememberPlugin) LoadData() { - p.Coll = p.Bot.Db.C("factoid") + // Mongo is removed, this plugin will crash if started + log.Fatal("The Remember plugin has not been upgraded to SQL yet.") + // p.Coll = p.Bot.Db.C("factoid") rand.Seed(time.Now().Unix()) } diff --git a/plugins/twitter.go b/plugins/twitter.go deleted file mode 100644 index 001e0cc..0000000 --- a/plugins/twitter.go +++ /dev/null @@ -1,143 +0,0 @@ -// © 2013 the AlePale Authors under the WTFPL. See AUTHORS for the list of authors. - -package plugins - -import ( - "bitbucket.org/phlyingpenguin/twitter" - "github.com/chrissexton/alepale/bot" - "github.com/garyburd/go-oauth/oauth" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" - "log" - "net/url" - "strings" - "time" -) - -// Plugin to squak tweets at the channel. - -type TwitterPlugin struct { - Bot *bot.Bot - Coll *mgo.Collection - Client *twitter.Client -} - -type twitterUser struct { - Id bson.ObjectId `bson:"_id,omitempty"` - User string - Tweets []string -} - -// NewTwitterPlugin creates a new TwitterPlugin with the Plugin interface -func NewTwitterPlugin(bot *bot.Bot) *TwitterPlugin { - return &TwitterPlugin{ - Bot: bot, - Coll: bot.Db.C("twitter"), - } -} - -func (p *TwitterPlugin) say(message bot.Message, body string) bool { - p.Bot.SendMessage(message.Channel, body) - return true -} - -// Check for commands accepted -// follow, unfollow -func (p *TwitterPlugin) checkCommand(message bot.Message) bool { - parts := strings.Split(message.Body, " ") - if parts[0] == "follow" { - if len(parts) != 2 { - return p.say(message, "I don't get it.") - } - - user := parts[1] - res := p.Coll.Find(bson.M{"user": user}) - - if count, _ := res.Count(); count > 0 { - return p.say(message, "I'm already following "+user+"!") - } - - p.Coll.Insert(twitterUser{User: user}) - } else if parts[0] == "unfollow" { - if len(parts) != 2 { - return p.say(message, "I don't get it.") - } - - user := parts[1] - - p.Coll.Remove(bson.M{"user": user}) - return p.say(message, "Fuck "+user) - } - - return false -} - -// Message responds to the bot hook on recieving messages. -// This function returns true if the plugin responds in a meaningful way to the users message. -// Otherwise, the function returns false and the bot continues execution of other plugins. -func (p *TwitterPlugin) Message(message bot.Message) bool { - - if message.Command { - return p.checkCommand(message) - } - - return false -} - -// LoadData imports any configuration data into the plugin. This is not strictly necessary other -// than the fact that the Plugin interface demands it exist. This may be deprecated at a later -// date. -func (p *TwitterPlugin) LoadData() { - // This bot has no data to load - p.Client = twitter.New(&oauth.Credentials{ - Token: p.Bot.Config.TwitterConsumerKey, - Secret: p.Bot.Config.TwitterConsumerSecret, - }) - - p.Client.SetAuth(&oauth.Credentials{ - Token: p.Bot.Config.TwitterUserKey, - Secret: p.Bot.Config.TwitterUserSecret, - }) - - _, err := p.Client.VerifyCredentials(nil) - - if err != nil { - log.Println("Could not auth with twitter:", err) - } else { - go p.checkMessages() - } -} - -// Help responds to help requests. Every plugin must implement a help function. -func (p *TwitterPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Tell me to follow or unfollow twitter users!") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *TwitterPlugin) Event(kind string, message bot.Message) bool { - return false -} - -func (p *TwitterPlugin) checkMessages() { - for { - time.Sleep(time.Minute * 30) - var u url.Values - u.Set("screen_name", "phlyingpenguin") - data, err := p.Client.UserTimeline(u) - if err != nil { - log.Println(err) - } else { - log.Println(data) - } - } -} - -// Handler for bot's own messages -func (p *TwitterPlugin) BotMessage(message bot.Message) bool { - return false -} - -// Register any web URLs desired -func (p *TwitterPlugin) RegisterWeb() *string { - return nil -} diff --git a/plugins/webTemplates.go b/plugins/webTemplates.go index 48099be..8dd4e9f 100644 --- a/plugins/webTemplates.go +++ b/plugins/webTemplates.go @@ -5,6 +5,8 @@ package plugins // I hate this, but I'm creating strings of the templates to avoid having to // track where templates reside. +// 2016-01-15 Later note, why are these in plugins and the server is in bot? + var factoidIndex string = `