catbase/plugins/fact/factoid.go

782 lines
19 KiB
Go
Raw Normal View History

2016-01-17 18:00:44 +00:00
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
package fact
2012-08-26 19:15:04 +00:00
import (
2016-01-17 15:29:30 +00:00
"database/sql"
2012-08-26 19:15:04 +00:00
"fmt"
2013-06-01 17:10:15 +00:00
"html/template"
"log"
2012-08-26 19:15:04 +00:00
"math/rand"
2013-06-01 17:10:15 +00:00
"net/http"
2012-08-26 19:15:04 +00:00
"regexp"
"strings"
"time"
2016-03-19 18:02:46 +00:00
"github.com/jmoiron/sqlx"
2016-01-17 18:00:44 +00:00
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
2012-08-26 19:15:04 +00:00
)
2013-01-22 17:39:27 +00:00
// The factoid plugin provides a learning system to the bot so that it can
// respond to queries in a way that is unpredictable and fun
2012-08-26 19:15:04 +00:00
// factoid stores info about our factoid for lookup and later interaction
2016-01-17 15:29:30 +00:00
type factoid struct {
id sql.NullInt64
2016-03-29 14:20:44 +00:00
Fact string
Tidbit string
Verb string
Owner string
2016-01-17 15:29:30 +00:00
created time.Time
accessed time.Time
2016-03-29 14:20:44 +00:00
Count int
2016-01-17 15:29:30 +00:00
}
type alias struct {
Fact string
Next string
}
func (a *alias) resolve(db *sqlx.DB) (*factoid, error) {
// perform DB query to fill the To field
q := `select fact, next from factoid_alias where fact=?`
var next alias
err := db.Get(&next, q, a.Next)
if err != nil {
// we hit the end of the chain, get a factoid named Next
fact, err := getSingleFact(db, a.Next)
if err != nil {
err := fmt.Errorf("Error resolvig alias %v: %v", a, err)
return nil, err
}
return fact, nil
}
return next.resolve(db)
}
func findAlias(db *sqlx.DB, fact string) (bool, *factoid) {
q := `select * from factoid_alias where fact=?`
var a alias
err := db.Get(&a, q, fact)
if err != nil {
return false, nil
}
f, err := a.resolve(db)
return err == nil, f
}
func (a *alias) save(db *sqlx.DB) error {
2017-10-25 22:16:33 +00:00
q := `select * from factoid_alias where fact=?`
var offender alias
err := db.Get(&offender, q, a.Next)
if err == nil {
return fmt.Errorf("DANGER: an opposite alias already exists")
}
2017-10-25 22:16:33 +00:00
_, err = a.resolve(db)
if err != nil {
return fmt.Errorf("there is no fact at that destination")
}
2017-10-25 22:16:33 +00:00
q = `insert or replace into factoid_alias (fact, next) values (?, ?)`
_, err = db.Exec(q, a.Fact, a.Next)
if err != nil {
return err
}
return nil
}
func aliasFromStrings(from, to string) *alias {
return &alias{from, to}
}
2016-03-19 18:02:46 +00:00
func (f *factoid) save(db *sqlx.DB) error {
2016-01-17 15:29:30 +00:00
var err error
if f.id.Valid {
// update
_, err = db.Exec(`update factoid set
fact=?,
tidbit=?,
verb=?,
owner=?,
accessed=?,
count=?
where id=?`,
2016-03-29 14:20:44 +00:00
f.Fact,
f.Tidbit,
f.Verb,
f.Owner,
2016-01-17 15:29:30 +00:00
f.accessed.Unix(),
2016-03-29 14:20:44 +00:00
f.Count,
2016-01-17 15:29:30 +00:00
f.id.Int64)
} else {
f.created = time.Now()
f.accessed = time.Now()
// insert
res, err := db.Exec(`insert into factoid (
fact,
tidbit,
verb,
owner,
created,
accessed,
count
) values (?, ?, ?, ?, ?, ?, ?);`,
2016-03-29 14:20:44 +00:00
f.Fact,
f.Tidbit,
f.Verb,
f.Owner,
2016-01-17 15:29:30 +00:00
f.created.Unix(),
f.accessed.Unix(),
2016-03-29 14:20:44 +00:00
f.Count,
2016-01-17 15:29:30 +00:00
)
if err != nil {
return err
}
id, err := res.LastInsertId()
// hackhackhack?
f.id.Int64 = id
f.id.Valid = true
}
return err
}
2016-03-19 18:02:46 +00:00
func (f *factoid) delete(db *sqlx.DB) error {
2016-01-17 15:29:30 +00:00
var err error
if f.id.Valid {
_, err = db.Exec(`delete from factoid where id=?`, f.id)
}
f.id.Valid = false
return err
}
func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*factoid, error) {
2016-01-17 15:29:30 +00:00
var fs []*factoid
query := `select
2016-01-17 15:29:30 +00:00
id,
fact,
tidbit,
verb,
owner,
created,
accessed,
count
from factoid
where fact like ?
and tidbit like ?;`
rows, err := db.Query(query,
"%"+fact+"%", "%"+tidbit+"%")
2016-03-29 16:34:04 +00:00
if err != nil {
log.Printf("Error regexping for facts: %s", err)
return nil, err
}
2016-01-17 15:29:30 +00:00
for rows.Next() {
var f factoid
var tmpCreated int64
var tmpAccessed int64
err := rows.Scan(
&f.id,
2016-03-29 14:20:44 +00:00
&f.Fact,
&f.Tidbit,
&f.Verb,
&f.Owner,
2016-01-17 15:29:30 +00:00
&tmpCreated,
&tmpAccessed,
2016-03-29 14:20:44 +00:00
&f.Count,
2016-01-17 15:29:30 +00:00
)
if err != nil {
return nil, err
}
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
fs = append(fs, &f)
}
return fs, err
}
2016-03-19 18:02:46 +00:00
func getSingle(db *sqlx.DB) (*factoid, error) {
2016-01-17 15:29:30 +00:00
var f factoid
var tmpCreated int64
var tmpAccessed int64
err := db.QueryRow(`select
id,
fact,
tidbit,
verb,
owner,
created,
accessed,
count
from factoid
order by random() limit 1;`).Scan(
&f.id,
2016-03-29 14:20:44 +00:00
&f.Fact,
&f.Tidbit,
&f.Verb,
&f.Owner,
2016-01-17 15:29:30 +00:00
&tmpCreated,
&tmpAccessed,
2016-03-29 14:20:44 +00:00
&f.Count,
2016-01-17 15:29:30 +00:00
)
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
return &f, err
}
2016-03-19 18:02:46 +00:00
func getSingleFact(db *sqlx.DB, fact string) (*factoid, error) {
2016-01-17 15:29:30 +00:00
var f factoid
var tmpCreated int64
var tmpAccessed int64
err := db.QueryRow(`select
id,
fact,
tidbit,
verb,
owner,
created,
accessed,
count
from factoid
where fact like ?
2016-01-17 15:29:30 +00:00
order by random() limit 1;`,
fact).Scan(
&f.id,
2016-03-29 14:20:44 +00:00
&f.Fact,
&f.Tidbit,
&f.Verb,
&f.Owner,
2016-01-17 15:29:30 +00:00
&tmpCreated,
&tmpAccessed,
2016-03-29 14:20:44 +00:00
&f.Count,
2016-01-17 15:29:30 +00:00
)
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
return &f, err
2012-08-26 19:15:04 +00:00
}
// Factoid provides the necessary plugin-wide needs
type Factoid struct {
Bot bot.Bot
NotFound []string
2016-01-17 15:29:30 +00:00
LastFact *factoid
2016-03-19 18:02:46 +00:00
db *sqlx.DB
2012-08-26 19:15:04 +00:00
}
// NewFactoid creates a new Factoid with the Plugin interface
func New(botInst bot.Bot) *Factoid {
p := &Factoid{
2016-01-17 15:29:30 +00:00
Bot: botInst,
NotFound: []string{
"I don't know.",
"NONONONO",
"((",
"*pukes*",
"NOPE! NOPE! NOPE!",
"One time, I learned how to jump rope.",
},
db: botInst.DB(),
2016-01-17 15:29:30 +00:00
}
if _, err := p.db.Exec(`create table if not exists factoid (
2016-01-17 15:29:30 +00:00
id integer primary key,
fact string,
tidbit string,
verb string,
owner string,
created integer,
accessed integer,
count integer
);`); err != nil {
log.Fatal(err)
}
if _, err := p.db.Exec(`create table if not exists factoid_alias (
fact string,
next string,
primary key (fact, next)
);`); err != nil {
2016-01-17 15:29:30 +00:00
log.Fatal(err)
2012-08-26 19:15:04 +00:00
}
2016-01-17 15:29:30 +00:00
2019-01-22 00:16:57 +00:00
for _, channel := range botInst.Config().GetArray("channels", []string{}) {
go p.factTimer(channel)
2016-03-19 18:02:46 +00:00
go func(ch string) {
// Some random time to start up
2013-09-01 02:29:13 +00:00
time.Sleep(time.Duration(15) * time.Second)
2019-01-22 00:16:57 +00:00
if ok, fact := p.findTrigger(p.Bot.Config().Get("Factoid.StartupFact", "speed test")); ok {
p.sayFact(msg.Message{
2016-03-19 15:38:18 +00:00
Channel: ch,
2016-01-17 15:29:30 +00:00
Body: "speed test", // BUG: This is defined in the config too
Command: true,
Action: false,
}, *fact)
}
2016-03-19 15:38:18 +00:00
}(channel)
}
botInst.Register(p, bot.Message, p.message)
botInst.Register(p, bot.Help, p.help)
2019-02-07 16:30:42 +00:00
p.registerWeb()
2012-08-26 19:15:04 +00:00
return p
}
// findAction simply regexes a string for the action verb
func findAction(message string) string {
r, err := regexp.Compile("<.+?>")
2012-08-26 19:15:04 +00:00
if err != nil {
panic(err)
}
action := r.FindString(message)
2013-09-05 01:56:03 +00:00
if action == "" {
if strings.Contains(message, " is ") {
return "is"
} else if strings.Contains(message, " are ") {
return "are"
}
}
2012-08-26 19:15:04 +00:00
return action
}
// learnFact assumes we have a learning situation and inserts a new fact
// into the database
func (p *Factoid) learnFact(message msg.Message, fact, verb, tidbit string) error {
2016-01-17 15:29:30 +00:00
verb = strings.ToLower(verb)
if verb == "react" {
// This would be a great place to check against the API for valid emojy
// I'm too lazy for that
tidbit = strings.Replace(tidbit, ":", "", -1)
if len(strings.Split(tidbit, " ")) > 1 {
return fmt.Errorf("That's not a valid emojy.")
}
}
2016-01-17 15:29:30 +00:00
var count sql.NullInt64
err := p.db.QueryRow(`select count(*) from factoid
where fact=? and verb=? and tidbit=?`,
fact, verb, tidbit).Scan(&count)
if err != nil {
log.Println("Error counting facts: ", err)
return fmt.Errorf("What?")
2016-01-17 15:29:30 +00:00
} else if count.Valid && count.Int64 != 0 {
log.Println("User tried to relearn a fact.")
return fmt.Errorf("Look, I already know that.")
}
2016-01-17 15:29:30 +00:00
n := factoid{
2016-03-29 14:20:44 +00:00
Fact: fact,
Tidbit: tidbit,
Verb: verb,
Owner: message.User.Name,
2016-01-17 15:29:30 +00:00
created: time.Now(),
accessed: time.Now(),
2016-03-29 14:20:44 +00:00
Count: 0,
2016-01-17 15:29:30 +00:00
}
p.LastFact = &n
err = n.save(p.db)
if err != nil {
2016-01-17 15:29:30 +00:00
log.Println("Error inserting fact: ", err)
return fmt.Errorf("My brain is overheating.")
}
return nil
2012-08-26 19:15:04 +00:00
}
// findTrigger checks to see if a given string is a trigger or not
func (p *Factoid) findTrigger(fact string) (bool, *factoid) {
2016-01-17 15:29:30 +00:00
fact = strings.ToLower(fact) // TODO: make sure this needs to be lowered here
2012-08-26 19:15:04 +00:00
2016-01-17 15:29:30 +00:00
f, err := getSingleFact(p.db, fact)
if err != nil {
return findAlias(p.db, fact)
2012-08-26 19:15:04 +00:00
}
2016-01-17 15:29:30 +00:00
return true, f
}
// sayFact spits out a fact to the channel and updates the fact in the database
// with new time and count information
func (p *Factoid) sayFact(message msg.Message, fact factoid) {
2016-03-29 14:20:44 +00:00
msg := p.Bot.Filter(message, fact.Tidbit)
2016-01-17 15:29:30 +00:00
full := p.Bot.Filter(message, fmt.Sprintf("%s %s %s",
2016-03-29 14:20:44 +00:00
fact.Fact, fact.Verb, fact.Tidbit,
2016-01-17 15:29:30 +00:00
))
for i, m := 0, strings.Split(msg, "$and"); i < len(m) && i < 4; i++ {
msg := strings.TrimSpace(m[i])
if len(msg) == 0 {
continue
}
2016-03-29 14:20:44 +00:00
if fact.Verb == "action" {
p.Bot.Send(bot.Action, message.Channel, msg)
} else if fact.Verb == "react" {
p.Bot.Send(bot.Reaction, message.Channel, msg, message)
2016-03-29 14:20:44 +00:00
} else if fact.Verb == "reply" {
p.Bot.Send(bot.Message, message.Channel, msg)
2016-01-17 15:29:30 +00:00
} else {
p.Bot.Send(bot.Message, message.Channel, full)
}
}
2012-08-29 14:24:34 +00:00
// update fact tracking
2016-01-17 15:29:30 +00:00
fact.accessed = time.Now()
2016-03-29 14:20:44 +00:00
fact.Count += 1
2016-01-17 15:29:30 +00:00
err := fact.save(p.db)
2012-08-29 14:24:34 +00:00
if err != nil {
2016-01-17 15:29:30 +00:00
log.Printf("Could not update fact.\n")
log.Printf("%#v\n", fact)
log.Println(err)
2012-08-29 14:24:34 +00:00
}
p.LastFact = &fact
}
// trigger checks the message for its fitness to be a factoid and then hauls
// the message off to sayFact for processing if it is in fact a trigger
func (p *Factoid) trigger(message msg.Message) bool {
2019-01-22 00:16:57 +00:00
minLen := p.Bot.Config().GetInt("Factoid.MinLen", 4)
2016-04-01 14:45:45 +00:00
if len(message.Body) > minLen || message.Command || message.Body == "..." {
if ok, fact := p.findTrigger(message.Body); ok {
p.sayFact(message, *fact)
return true
}
2012-12-30 16:26:26 +00:00
r := strings.NewReplacer("'", "", "\"", "", ",", "", ".", "", ":", "",
"?", "", "!", "")
2012-11-11 16:14:40 +00:00
if ok, fact := p.findTrigger(r.Replace(message.Body)); ok {
p.sayFact(message, *fact)
return true
}
}
return false
2012-08-26 19:15:04 +00:00
}
// tellThemWhatThatWas is a hilarious name for a function.
func (p *Factoid) tellThemWhatThatWas(message msg.Message) bool {
fact := p.LastFact
var msg string
if fact == nil {
msg = "Nope."
} else {
msg = fmt.Sprintf("That was (#%d) '%s <%s> %s'",
2016-03-29 14:20:44 +00:00
fact.id.Int64, fact.Fact, fact.Verb, fact.Tidbit)
}
p.Bot.Send(bot.Message, message.Channel, msg)
return true
}
func (p *Factoid) learnAction(message msg.Message, action string) bool {
body := message.Body
parts := strings.SplitN(body, action, 2)
// This could fail if is were the last word or it weren't in the sentence (like no spaces)
if len(parts) != 2 {
return false
}
trigger := strings.TrimSpace(parts[0])
fact := strings.TrimSpace(parts[1])
action = strings.TrimSpace(action)
if len(trigger) == 0 || len(fact) == 0 || len(action) == 0 {
p.Bot.Send(bot.Message, message.Channel, "I don't want to learn that.")
return true
2012-08-26 19:15:04 +00:00
}
if len(strings.Split(fact, "$and")) > 4 {
p.Bot.Send(bot.Message, message.Channel, "You can't use more than 4 $and operators.")
return true
}
2012-08-26 19:15:04 +00:00
strippedaction := strings.Replace(strings.Replace(action, "<", "", 1), ">", "", 1)
2012-08-26 19:15:04 +00:00
if err := p.learnFact(message, trigger, strippedaction, fact); err != nil {
p.Bot.Send(bot.Message, message.Channel, err.Error())
} else {
p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("Okay, %s.", message.User.Name))
}
2012-08-26 20:35:13 +00:00
return true
}
2013-03-18 21:44:49 +00:00
// Checks body for the ~= operator returns it
func changeOperator(body string) string {
if strings.Contains(body, "=~") {
return "=~"
} else if strings.Contains(body, "~=") {
return "~="
}
return ""
}
2012-08-26 19:15:04 +00:00
// If the user requesting forget is either the owner of the last learned fact or
// an admin, it may be deleted
func (p *Factoid) forgetLastFact(message msg.Message) bool {
if p.LastFact == nil {
p.Bot.Send(bot.Message, message.Channel, "I refuse.")
return true
}
err := p.LastFact.delete(p.db)
if err != nil {
log.Println("Error removing fact: ", p.LastFact, err)
}
fmt.Printf("Forgot #%d: %s %s %s\n", p.LastFact.id.Int64, p.LastFact.Fact,
p.LastFact.Verb, p.LastFact.Tidbit)
p.Bot.Send(bot.Action, message.Channel, "hits himself over the head with a skillet")
p.LastFact = nil
2012-08-26 19:15:04 +00:00
return true
}
// Allow users to change facts with a simple regexp
func (p *Factoid) changeFact(message msg.Message) bool {
2013-03-18 21:44:49 +00:00
oper := changeOperator(message.Body)
parts := strings.SplitN(message.Body, oper, 2)
userexp := strings.TrimSpace(parts[1])
trigger := strings.TrimSpace(parts[0])
parts = strings.Split(userexp, "/")
log.Printf("changeFact: %s %s %#v", trigger, userexp, parts)
if len(parts) == 4 {
// replacement
if parts[0] != "s" {
p.Bot.Send(bot.Message, message.Channel, "Nah.")
}
find := parts[1]
replace := parts[2]
// replacement
result, err := getFacts(p.db, trigger, parts[1])
2016-01-17 15:29:30 +00:00
if err != nil {
log.Println("Error getting facts: ", trigger, err)
}
2017-06-08 14:02:10 +00:00
if userexp[len(userexp)-1] != 'g' {
2017-06-07 18:56:14 +00:00
result = result[:1]
}
// make the changes
msg := fmt.Sprintf("Changing %d facts.", len(result))
p.Bot.Send(bot.Message, message.Channel, msg)
reg, err := regexp.Compile(find)
if err != nil {
p.Bot.Send(bot.Message, message.Channel, "I don't really want to.")
return false
}
for _, fact := range result {
2016-03-29 14:20:44 +00:00
fact.Fact = reg.ReplaceAllString(fact.Fact, replace)
fact.Fact = strings.ToLower(fact.Fact)
fact.Verb = reg.ReplaceAllString(fact.Verb, replace)
fact.Tidbit = reg.ReplaceAllString(fact.Tidbit, replace)
fact.Count += 1
2016-01-17 15:29:30 +00:00
fact.accessed = time.Now()
fact.save(p.db)
}
} else if len(parts) == 3 {
// search for a factoid and print it
result, err := getFacts(p.db, trigger, parts[1])
2016-01-17 15:29:30 +00:00
if err != nil {
log.Println("Error getting facts: ", trigger, err)
p.Bot.Send(bot.Message, message.Channel, "bzzzt")
return true
2016-01-17 15:29:30 +00:00
}
count := len(result)
if count == 0 {
p.Bot.Send(bot.Message, message.Channel, "I didn't find any facts like that.")
return true
}
if parts[2] == "g" && len(result) > 4 {
// summarize
2016-01-17 15:29:30 +00:00
result = result[:4]
} else {
2016-01-17 15:29:30 +00:00
p.sayFact(message, *result[0])
return true
}
msg := fmt.Sprintf("%s ", trigger)
for i, fact := range result {
if i != 0 {
msg = fmt.Sprintf("%s |", msg)
}
2016-03-29 14:20:44 +00:00
msg = fmt.Sprintf("%s <%s> %s", msg, fact.Verb, fact.Tidbit)
}
if count > 4 {
msg = fmt.Sprintf("%s | ...and %d others", msg, count)
}
p.Bot.Send(bot.Message, message.Channel, msg)
} else {
p.Bot.Send(bot.Message, message.Channel, "I don't know what you mean.")
}
return true
}
// 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 *Factoid) message(kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "what was that?" {
return p.tellThemWhatThatWas(message)
}
// This plugin has no business with normal messages
if !message.Command {
// look for any triggers in the db matching this message
return p.trigger(message)
}
if strings.HasPrefix(strings.ToLower(message.Body), "alias") {
log.Printf("Trying to learn an alias: %s", message.Body)
m := strings.TrimPrefix(message.Body, "alias ")
parts := strings.SplitN(m, "->", 2)
if len(parts) != 2 {
p.Bot.Send(bot.Message, message.Channel, "If you want to alias something, use: `alias this -> that`")
return true
}
a := aliasFromStrings(strings.TrimSpace(parts[1]), strings.TrimSpace(parts[0]))
if err := a.save(p.db); err != nil {
p.Bot.Send(bot.Message, message.Channel, err.Error())
} else {
p.Bot.Send(bot.Action, message.Channel, "learns a new synonym")
}
return true
}
if strings.ToLower(message.Body) == "factoid" {
if fact := p.randomFact(); fact != nil {
p.sayFact(message, *fact)
return true
}
log.Println("Got a nil fact.")
}
if strings.ToLower(message.Body) == "forget that" {
return p.forgetLastFact(message)
}
2013-03-18 21:44:49 +00:00
if changeOperator(message.Body) != "" {
return p.changeFact(message)
}
action := findAction(message.Body)
if action != "" {
return p.learnAction(message, action)
2012-08-26 19:15:04 +00:00
}
// look for any triggers in the db matching this message
if p.trigger(message) {
2012-08-26 19:15:04 +00:00
return true
}
// We didn't find anything, panic!
p.Bot.Send(bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))])
2012-08-26 19:15:04 +00:00
return true
}
// Help responds to help requests. Every plugin must implement a help function.
func (p *Factoid) help(kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.Bot.Send(bot.Message, message.Channel, "I can learn facts and spit them back out. You can say \"this is that\" or \"he <has> $5\". Later, trigger the factoid by just saying the trigger word, \"this\" or \"he\" in these examples.")
p.Bot.Send(bot.Message, message.Channel, "I can also figure out some variables including: $nonzero, $digit, $nick, and $someone.")
return true
2012-08-26 19:15:04 +00:00
}
// Pull a fact at random from the database
func (p *Factoid) randomFact() *factoid {
2016-01-17 15:29:30 +00:00
f, err := getSingle(p.db)
if err != nil {
2016-01-17 15:29:30 +00:00
fmt.Println("Error getting a fact: ", err)
return nil
}
2016-01-17 15:29:30 +00:00
return f
}
// factTimer spits out a fact at a given interval and with given probability
func (p *Factoid) factTimer(channel string) {
2019-01-22 00:16:57 +00:00
quoteTime := p.Bot.Config().GetInt("Factoid.QuoteTime", 30)
if quoteTime == 0 {
quoteTime = 30
p.Bot.Config().Set("Factoid.QuoteTime", "30")
}
duration := time.Duration(quoteTime) * time.Minute
myLastMsg := time.Now()
for {
2016-04-21 15:19:38 +00:00
time.Sleep(time.Duration(5) * time.Second) // why 5?
2013-06-17 01:03:43 +00:00
lastmsg, err := p.Bot.LastMessage(channel)
if err != nil {
2016-04-21 15:19:38 +00:00
// Probably no previous message to time off of
continue
}
tdelta := time.Since(lastmsg.Time)
earlier := time.Since(myLastMsg) > tdelta
chance := rand.Float64()
2019-01-22 00:16:57 +00:00
quoteChance := p.Bot.Config().GetFloat64("Factoid.QuoteChance", 0.99)
if quoteChance == 0.0 {
quoteChance = 0.99
p.Bot.Config().Set("Factoid.QuoteChance", "0.99")
}
success := chance < quoteChance
if success && tdelta > duration && earlier {
fact := p.randomFact()
if fact == nil {
2016-04-21 15:19:38 +00:00
log.Println("Didn't find a random fact to say")
continue
}
users := p.Bot.Who(channel)
// we need to fabricate a message so that bot.Filter can operate
message := msg.Message{
User: &users[rand.Intn(len(users))],
Channel: channel,
}
p.sayFact(message, *fact)
myLastMsg = time.Now()
}
}
}
2013-06-01 17:10:15 +00:00
// Register any web URLs desired
2019-02-07 16:30:42 +00:00
func (p *Factoid) registerWeb() {
2013-06-01 17:10:15 +00:00
http.HandleFunc("/factoid/req", p.serveQuery)
2013-06-01 18:40:06 +00:00
http.HandleFunc("/factoid", p.serveQuery)
2019-02-07 16:30:42 +00:00
p.Bot.RegisterWeb("/factoid", "Factoid")
2013-06-01 17:10:15 +00:00
}
2013-06-01 21:24:05 +00:00
func linkify(text string) template.HTML {
parts := strings.Fields(text)
2013-06-01 21:24:05 +00:00
for i, word := range parts {
if strings.HasPrefix(word, "http") {
parts[i] = fmt.Sprintf("<a href=\"%s\">%s</a>", word, word)
}
}
return template.HTML(strings.Join(parts, " "))
}
func (p *Factoid) serveQuery(w http.ResponseWriter, r *http.Request) {
2013-06-01 17:10:15 +00:00
context := make(map[string]interface{})
2013-06-01 21:24:05 +00:00
funcMap := template.FuncMap{
// The name "title" is what the function will be called in the template text.
"linkify": linkify,
}
2013-06-01 20:20:45 +00:00
if e := r.FormValue("entry"); e != "" {
entries, err := getFacts(p.db, e, "")
if err != nil {
log.Println("Web error searching: ", err)
}
2013-06-01 18:17:13 +00:00
context["Count"] = fmt.Sprintf("%d", len(entries))
2013-06-01 17:10:15 +00:00
context["Entries"] = entries
2013-06-01 17:29:12 +00:00
context["Search"] = e
2013-06-01 17:10:15 +00:00
}
2013-06-01 21:24:05 +00:00
t, err := template.New("factoidIndex").Funcs(funcMap).Parse(factoidIndex)
2013-06-01 17:10:15 +00:00
if err != nil {
log.Println(err)
}
2016-03-29 14:20:44 +00:00
err = t.Execute(w, context)
if err != nil {
log.Println(err)
}
2013-06-01 17:10:15 +00:00
}