Added "what was that?", merged remember and factoid a bit, added fields to factoids, todo: stats on factoids, documentation.

This commit is contained in:
Chris Sexton 2012-08-27 11:34:21 -04:00
parent 0970ee980f
commit 89072fafb4
2 changed files with 121 additions and 56 deletions

View File

@ -14,18 +14,24 @@ import (
// This is a factoid plugin to serve as an example and quick copy/paste for new plugins. // 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 // factoid stores info about our factoid for lookup and later interaction
type factoid struct { type Factoid struct {
Id int _id bson.ObjectId `bson:"_id"`
Trigger string Id int
Operator string Trigger string
FullText string Operator string
CreatedBy string FullText string
Action string
CreatedBy string
DateCreated time.Time
LastAccessed time.Time
AccessCount int
} }
type FactoidPlugin struct { type FactoidPlugin struct {
Bot *bot.Bot Bot *bot.Bot
Coll *mgo.Collection Coll *mgo.Collection
NotFound []string NotFound []string
LastFact Factoid
} }
// NewFactoidPlugin creates a new FactoidPlugin with the Plugin interface // NewFactoidPlugin creates a new FactoidPlugin with the Plugin interface
@ -43,6 +49,9 @@ func NewFactoidPlugin(bot *bot.Bot) *FactoidPlugin {
}, },
} }
p.LoadData() p.LoadData()
for _, channel := range bot.Config.Channels {
go p.factTimer(channel)
}
return p return p
} }
@ -68,19 +77,28 @@ func (p *FactoidPlugin) learnFact(message bot.Message, trigger, operator, fact s
return false return false
} }
newfact := factoid{ var funcres bson.M
Id: 0, 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, Trigger: trigger,
Operator: operator, Operator: operator,
FullText: full, FullText: full,
Action: fact,
CreatedBy: message.User.Name, CreatedBy: message.User.Name,
} }
p.Coll.Insert(newfact) p.Coll.Insert(newfact)
return true return true
} }
func (p *FactoidPlugin) findTrigger(message string) (bool, *factoid) { func (p *FactoidPlugin) findTrigger(message string) (bool, *Factoid) {
var results []factoid var results []Factoid
iter := p.Coll.Find(bson.M{"trigger": strings.ToLower(message)}).Iter() iter := p.Coll.Find(bson.M{"trigger": strings.ToLower(message)}).Iter()
err := iter.All(&results) err := iter.All(&results)
if err != nil { if err != nil {
@ -96,22 +114,28 @@ func (p *FactoidPlugin) findTrigger(message string) (bool, *factoid) {
return true, &fact return true, &fact
} }
func (p *FactoidPlugin) trigger(message bot.Message) bool { func (p *FactoidPlugin) sayFact(message bot.Message, fact Factoid) {
if len(message.Body) > 4 || message.Command { msg := p.Bot.Filter(message, fact.FullText)
if ok, fact := p.findTrigger(message.Body); ok { for i, m := 0, strings.Split(msg, "$and"); i < len(m) && i < 4; i++ {
msg := p.Bot.Filter(message, fact.FullText) msg := strings.TrimSpace(m[i])
for i, m := 0, strings.Split(msg, "$and"); i < len(m) && i < 4; i++ { if len(msg) == 0 {
msg := strings.TrimSpace(m[i]) continue
if len(msg) == 0 { }
continue
}
if fact.Operator == "action" { if fact.Operator == "action" {
p.Bot.SendAction(message.Channel, msg) p.Bot.SendAction(message.Channel, msg)
} else { } else {
p.Bot.SendMessage(message.Channel, msg) 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 return true
} }
} }
@ -126,6 +150,13 @@ func (p *FactoidPlugin) Message(message bot.Message) bool {
// This bot does not reply to anything // This bot does not reply to anything
body := strings.TrimSpace(message.Body) 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 // This plugin has no business with normal messages
if !message.Command { if !message.Command {
// look for any triggers in the db matching this message // 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 { func (p *FactoidPlugin) Event(kind string, message bot.Message) bool {
return false 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)
}
}
}

View File

@ -12,12 +12,6 @@ import (
// This is a skeleton plugin to serve as an example and quick copy/paste for new plugins. // 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 { type RememberPlugin struct {
Bot *bot.Bot Bot *bot.Bot
Coll *mgo.Collection Coll *mgo.Collection
@ -31,9 +25,9 @@ func NewRememberPlugin(b *bot.Bot) *RememberPlugin {
Log: make(map[string][]bot.Message), Log: make(map[string][]bot.Message),
} }
p.LoadData() p.LoadData()
for _, channel := range b.Config.Channels { // for _, channel := range b.Config.Channels {
go p.quoteTimer(channel) // go p.quoteTimer(channel)
} // }
return &p return &p
} }
@ -51,6 +45,7 @@ func (p *RememberPlugin) Message(message bot.Message) bool {
return true return true
} }
user := message.User
parts := strings.Fields(message.Body) parts := strings.Fields(message.Body)
if message.Command && len(parts) >= 3 && parts[0] == "remember" { if message.Command && len(parts) >= 3 && parts[0] == "remember" {
// we have a remember! // we have a remember!
@ -58,7 +53,7 @@ func (p *RememberPlugin) Message(message bot.Message) bool {
nick := parts[1] nick := parts[1]
snip := strings.Join(parts[2:], " ") 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) msg := fmt.Sprintf("Don't try to quote yourself, %s.", nick)
p.Bot.SendMessage(message.Channel, msg) p.Bot.SendMessage(message.Channel, msg)
return true 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-- { for i := len(p.Log[message.Channel]) - 1; i >= 0; i-- {
entry := p.Log[message.Channel][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) { if entry.User.Name == nick && strings.Contains(entry.Body, snip) {
// insert new remember entry // insert new remember entry
var msg string var msg string
@ -78,12 +71,20 @@ func (p *RememberPlugin) Message(message bot.Message) bool {
} else { } else {
msg = fmt.Sprintf("<%s> %s", entry.User.Name, entry.Raw) msg = fmt.Sprintf("<%s> %s", entry.User.Name, entry.Raw)
} }
u := userRemember{
Nick: entry.User.Name, trigger := fmt.Sprintf("%s quotes", entry.User.Name)
Message: msg,
Date: time.Now(), fact := Factoid{
Trigger: trigger,
Operator: "<reply>",
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 // sorry, not creative with names so we're reusing msg
msg = fmt.Sprintf("Okay, %s, remembering '%s'.", 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 // than the fact that the Plugin interface demands it exist. This may be deprecated at a later
// date. // date.
func (p *RememberPlugin) LoadData() { func (p *RememberPlugin) LoadData() {
p.Coll = p.Bot.Db.C("remember") p.Coll = p.Bot.Db.C("factoid")
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
} }
@ -115,21 +116,18 @@ func (p *RememberPlugin) Help(channel string, parts []string) {
p.Bot.SendMessage(channel, msg) 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. // deliver a random quote out of the db.
// Note: this is the same cache for all channels joined. This plugin needs to be expanded // 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 // to have this function execute a quote for a particular channel
func (p *RememberPlugin) randQuote() string { func (p *RememberPlugin) randQuote() string {
var quotes []userRemember var quotes []Factoid
iter := p.Coll.Find(bson.M{}).Iter() // 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(&quotes) err := iter.All(&quotes)
if err != nil { if err != nil {
panic(iter.Err()) panic(iter.Err())
@ -141,7 +139,7 @@ func (p *RememberPlugin) randQuote() string {
return "Sorry, I don't know any quotes." return "Sorry, I don't know any quotes."
} }
quote := quotes[rand.Intn(nquotes)] quote := quotes[rand.Intn(nquotes)]
return quote.Message return quote.FullText
} }
func (p *RememberPlugin) quoteTimer(channel string) { func (p *RememberPlugin) quoteTimer(channel string) {
@ -152,7 +150,6 @@ func (p *RememberPlugin) quoteTimer(channel string) {
chance := 1.0 / p.Bot.Config.QuoteChance chance := 1.0 / p.Bot.Config.QuoteChance
if rand.Intn(int(chance)) == 0 { if rand.Intn(int(chance)) == 0 {
msg := p.randQuote() msg := p.randQuote()
fmt.Println("Delivering quote.")
p.Bot.SendMessage(channel, msg) p.Bot.SendMessage(channel, msg)
} }
} }