From 89072fafb412897e586d47726b0626c988a3bdc2 Mon Sep 17 00:00:00 2001 From: Chris Sexton Date: Mon, 27 Aug 2012 11:34:21 -0400 Subject: [PATCH] Added "what was that?", merged remember and factoid a bit, added fields to factoids, todo: stats on factoids, documentation. --- plugins/factoid.go | 118 ++++++++++++++++++++++++++++++++++---------- plugins/remember.go | 59 +++++++++++----------- 2 files changed, 121 insertions(+), 56 deletions(-) diff --git a/plugins/factoid.go b/plugins/factoid.go index 3b31c68..d8e14b6 100644 --- a/plugins/factoid.go +++ b/plugins/factoid.go @@ -14,18 +14,24 @@ import ( // This is a factoid plugin to serve as an example and quick copy/paste for new plugins. // factoid stores info about our factoid for lookup and later interaction -type factoid struct { - Id int - Trigger string - Operator string - FullText string - CreatedBy string +type Factoid struct { + _id bson.ObjectId `bson:"_id"` + Id int + Trigger string + Operator string + FullText string + Action string + CreatedBy string + DateCreated time.Time + LastAccessed time.Time + AccessCount int } type FactoidPlugin struct { Bot *bot.Bot Coll *mgo.Collection NotFound []string + LastFact Factoid } // NewFactoidPlugin creates a new FactoidPlugin with the Plugin interface @@ -43,6 +49,9 @@ func NewFactoidPlugin(bot *bot.Bot) *FactoidPlugin { }, } p.LoadData() + for _, channel := range bot.Config.Channels { + go p.factTimer(channel) + } return p } @@ -68,19 +77,28 @@ func (p *FactoidPlugin) learnFact(message bot.Message, trigger, operator, fact s return false } - newfact := factoid{ - Id: 0, + var funcres bson.M + err := p.Bot.Db.Run(bson.M{"eval": "return counter(\"factoid\");"}, &funcres) + if err != nil { + panic(err) + } + id := int(funcres["retval"].(float64)) + + newfact := Factoid{ + _id: bson.NewObjectId(), + Id: id, Trigger: trigger, Operator: operator, FullText: full, + Action: fact, CreatedBy: message.User.Name, } p.Coll.Insert(newfact) return true } -func (p *FactoidPlugin) findTrigger(message string) (bool, *factoid) { - var results []factoid +func (p *FactoidPlugin) findTrigger(message string) (bool, *Factoid) { + var results []Factoid iter := p.Coll.Find(bson.M{"trigger": strings.ToLower(message)}).Iter() err := iter.All(&results) if err != nil { @@ -96,22 +114,28 @@ func (p *FactoidPlugin) findTrigger(message string) (bool, *factoid) { return true, &fact } -func (p *FactoidPlugin) trigger(message bot.Message) bool { - if len(message.Body) > 4 || message.Command { - if ok, fact := p.findTrigger(message.Body); ok { - msg := p.Bot.Filter(message, fact.FullText) - for i, m := 0, strings.Split(msg, "$and"); i < len(m) && i < 4; i++ { - msg := strings.TrimSpace(m[i]) - if len(msg) == 0 { - continue - } +func (p *FactoidPlugin) sayFact(message bot.Message, fact Factoid) { + msg := p.Bot.Filter(message, fact.FullText) + for i, m := 0, strings.Split(msg, "$and"); i < len(m) && i < 4; i++ { + msg := strings.TrimSpace(m[i]) + if len(msg) == 0 { + continue + } - if fact.Operator == "action" { - p.Bot.SendAction(message.Channel, msg) - } else { - p.Bot.SendMessage(message.Channel, msg) - } - } + if fact.Operator == "action" { + p.Bot.SendAction(message.Channel, msg) + } else { + p.Bot.SendMessage(message.Channel, msg) + } + } + + p.LastFact = fact +} + +func (p *FactoidPlugin) trigger(message bot.Message) bool { + if len(message.Body) > 4 || message.Command || message.Body == "..." { + if ok, fact := p.findTrigger(message.Body); ok { + p.sayFact(message, *fact) return true } } @@ -126,6 +150,13 @@ func (p *FactoidPlugin) Message(message bot.Message) bool { // This bot does not reply to anything body := strings.TrimSpace(message.Body) + if strings.ToLower(message.Body) == "what was that?" { + fact := p.LastFact + msg := fmt.Sprintf("That was (#%d) '%s <%s> %s'", fact.Id, fact.Trigger, fact.Operator, fact.Action) + p.Bot.SendMessage(message.Channel, msg) + return true + } + // This plugin has no business with normal messages if !message.Command { // look for any triggers in the db matching this message @@ -193,3 +224,40 @@ func (p *FactoidPlugin) Help(channel string, parts []string) { func (p *FactoidPlugin) Event(kind string, message bot.Message) bool { return false } + +func (p *FactoidPlugin) randomFact() *Factoid { + var results []Factoid + iter := p.Coll.Find(bson.M{}).Iter() + err := iter.All(&results) + if err != nil { + panic(err) + } + + nfacts := len(results) + if nfacts == 0 { + return nil + } + + fact := results[rand.Intn(nfacts)] + return &fact +} + +func (p *FactoidPlugin) factTimer(channel string) { + for { + time.Sleep(time.Duration(p.Bot.Config.QuoteTime) * time.Minute) + chance := 1.0 / p.Bot.Config.QuoteChance + if rand.Intn(int(chance)) == 0 { + fact := p.randomFact() + if fact == nil { + continue + } + + // we need to fabricate a message so that bot.Filter can operate + message := bot.Message{ + User: &p.Bot.Users[rand.Intn(len(p.Bot.Users))], + Channel: channel, + } + p.sayFact(message, *fact) + } + } +} diff --git a/plugins/remember.go b/plugins/remember.go index a24e58f..32367f6 100644 --- a/plugins/remember.go +++ b/plugins/remember.go @@ -12,12 +12,6 @@ import ( // This is a skeleton plugin to serve as an example and quick copy/paste for new plugins. -type userRemember struct { - Nick string - Message string - Date time.Time -} - type RememberPlugin struct { Bot *bot.Bot Coll *mgo.Collection @@ -31,9 +25,9 @@ func NewRememberPlugin(b *bot.Bot) *RememberPlugin { Log: make(map[string][]bot.Message), } p.LoadData() - for _, channel := range b.Config.Channels { - go p.quoteTimer(channel) - } + // for _, channel := range b.Config.Channels { + // go p.quoteTimer(channel) + // } return &p } @@ -51,6 +45,7 @@ func (p *RememberPlugin) Message(message bot.Message) bool { return true } + user := message.User parts := strings.Fields(message.Body) if message.Command && len(parts) >= 3 && parts[0] == "remember" { // we have a remember! @@ -58,7 +53,7 @@ func (p *RememberPlugin) Message(message bot.Message) bool { nick := parts[1] snip := strings.Join(parts[2:], " ") - if nick == message.User.Name { + if nick == user.Name { msg := fmt.Sprintf("Don't try to quote yourself, %s.", nick) p.Bot.SendMessage(message.Channel, msg) return true @@ -66,8 +61,6 @@ func (p *RememberPlugin) Message(message bot.Message) bool { for i := len(p.Log[message.Channel]) - 1; i >= 0; i-- { entry := p.Log[message.Channel][i] - // find the entry we want - fmt.Printf("Comparing '%s' to '%s'\n", entry.Raw, snip) if entry.User.Name == nick && strings.Contains(entry.Body, snip) { // insert new remember entry var msg string @@ -78,12 +71,20 @@ func (p *RememberPlugin) Message(message bot.Message) bool { } else { msg = fmt.Sprintf("<%s> %s", entry.User.Name, entry.Raw) } - u := userRemember{ - Nick: entry.User.Name, - Message: msg, - Date: time.Now(), + + trigger := fmt.Sprintf("%s quotes", entry.User.Name) + + fact := Factoid{ + Trigger: trigger, + Operator: "", + FullText: msg, + Action: msg, + CreatedBy: user.Name, + DateCreated: time.Now(), + LastAccessed: time.Now(), + AccessCount: 0, } - p.Coll.Insert(u) + p.Coll.Insert(fact) // sorry, not creative with names so we're reusing msg msg = fmt.Sprintf("Okay, %s, remembering '%s'.", @@ -103,7 +104,7 @@ func (p *RememberPlugin) 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 *RememberPlugin) LoadData() { - p.Coll = p.Bot.Db.C("remember") + p.Coll = p.Bot.Db.C("factoid") rand.Seed(time.Now().Unix()) } @@ -115,21 +116,18 @@ func (p *RememberPlugin) Help(channel string, parts []string) { p.Bot.SendMessage(channel, msg) } -func (p *RememberPlugin) record(nick, msg string) { - message := userRemember{ - Nick: nick, - Message: msg, - Date: time.Now(), - } - p.Coll.Insert(message) -} - // deliver a random quote out of the db. // Note: this is the same cache for all channels joined. This plugin needs to be expanded // to have this function execute a quote for a particular channel func (p *RememberPlugin) randQuote() string { - var quotes []userRemember - iter := p.Coll.Find(bson.M{}).Iter() + var quotes []Factoid + // todo: find anything with the word "quotes" in the trigger + query := bson.M{ + "trigger": bson.M{ + "$regex": "quotes$", + }, + } + iter := p.Coll.Find(query).Iter() err := iter.All("es) if err != nil { panic(iter.Err()) @@ -141,7 +139,7 @@ func (p *RememberPlugin) randQuote() string { return "Sorry, I don't know any quotes." } quote := quotes[rand.Intn(nquotes)] - return quote.Message + return quote.FullText } func (p *RememberPlugin) quoteTimer(channel string) { @@ -152,7 +150,6 @@ func (p *RememberPlugin) quoteTimer(channel string) { chance := 1.0 / p.Bot.Config.QuoteChance if rand.Intn(int(chance)) == 0 { msg := p.randQuote() - fmt.Println("Delivering quote.") p.Bot.SendMessage(channel, msg) } }