remember: fixed something? It works now.

This commit is contained in:
Chris Sexton 2019-02-15 13:22:54 -05:00
parent 6fb0990a11
commit 47a824e8da
10 changed files with 164 additions and 152 deletions

View File

@ -246,7 +246,7 @@ func (b *bot) RegisterFilter(name string, f func(string) string) {
// Register a callback
func (b *bot) Register(p Plugin, kind Kind, cb Callback) {
t := reflect.TypeOf(p)
t := reflect.TypeOf(p).String()
if _, ok := b.callbacks[t]; !ok {
b.callbacks[t] = make(map[Kind][]Callback)
}

View File

@ -25,6 +25,7 @@ func (b *bot) Receive(kind Kind, msg msg.Message, args ...interface{}) bool {
if kind == Message && strings.HasPrefix(msg.Body, "help") && msg.Command {
parts := strings.Fields(strings.ToLower(msg.Body))
b.checkHelp(msg.Channel, parts)
log.Println("Handled a help, returning")
goto RET
}
@ -40,7 +41,7 @@ RET:
}
func (b *bot) runCallback(plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool {
t := reflect.TypeOf(plugin)
t := reflect.TypeOf(plugin).String()
for _, cb := range b.callbacks[t][evt] {
if cb(evt, message, args...) {
return true

View File

@ -3,8 +3,6 @@
package bot
import (
"reflect"
"github.com/jmoiron/sqlx"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/bot/user"
@ -34,7 +32,7 @@ const (
type Kind int
type Callback func(Kind, msg.Message, ...interface{}) bool
type CallbackMap map[reflect.Type]map[Kind][]Callback
type CallbackMap map[string]map[Kind][]Callback
// Bot interface serves to allow mocking of the actual bot
type Bot interface {

View File

@ -48,7 +48,7 @@ func New(c *config.Config) *SlackApp {
log.Fatalf("No slack token found. Set SLACKTOKEN env.")
}
api := slack.New(token, slack.OptionDebug(true))
api := slack.New(token, slack.OptionDebug(false))
return &SlackApp{
api: api,
@ -263,7 +263,7 @@ func (s *SlackApp) populateEmojiList() {
log.Println("Cannot get emoji list without slack.usertoken")
return
}
api := slack.New(s.userToken, slack.OptionDebug(true))
api := slack.New(s.userToken, slack.OptionDebug(false))
em, err := api.GetEmoji()
if err != nil {
@ -339,7 +339,7 @@ func (s *SlackApp) Who(id string) []string {
log.Println("Cannot get emoji list without slack.usertoken")
return []string{s.config.Get("nick", "bot")}
}
api := slack.New(s.userToken, slack.OptionDebug(true))
api := slack.New(s.userToken, slack.OptionDebug(false))
log.Println("Who is queried for ", id)
// Not super sure this is the correct call

9
go.sum
View File

@ -2,7 +2,6 @@ github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff h1:+TEqaP0eO1unI7XHHFeMDhsxhLDIb0x8KYuZbqbAmxA=
github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff/go.mod h1:QCRjR0b4qiJiNjuP7RFM89bh4UExGJalcWmYeSvlnRc=
@ -20,7 +19,6 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mmcdole/gofeed v1.0.0-beta2 h1:CjQ0ADhAwNSb08zknAkGOEYqr8zfZKfrzgk9BxpWP2E=
github.com/mmcdole/gofeed v1.0.0-beta2/go.mod h1:/BF9JneEL2/flujm8XHoxUcghdTV6vvb3xx/vKyChFU=
@ -37,23 +35,16 @@ github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspo
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89 h1:3D3M900hEBJJAqyKl70QuRHi5weX9+ptlQI1v+FNcQ8=
github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89/go.mod h1:ejwOYCjnDMyO5LXFXRARQJGBZ6xQJZ3rgAHE5drSuMM=
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158 h1:p3rTUXxzuKsBOsHlkly7+rj9wagFBKeIsCDKkDII9sw=
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158/go.mod h1:Ojy3BTOiOTwpHpw7/HNi+TVTFppao191PQs+Qc3sqpE=
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs=
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8=
github.com/yuin/gopher-lua v0.0.0-20181214045814-db9ae37725ec h1:vpF8Kxql6/3OvGH4y2SKtpN3WsB17mvJ8f8H1o2vucQ=
github.com/yuin/gopher-lua v0.0.0-20181214045814-db9ae37725ec/go.mod h1:fFiAh+CowNFr0NK5VASokuwKwkbacRmHsVA7Yb1Tqac=
github.com/yuin/gopher-lua v0.0.0-20190125051437-7b9317363aa9/go.mod h1:fFiAh+CowNFr0NK5VASokuwKwkbacRmHsVA7Yb1Tqac=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4=
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=

View File

@ -29,6 +29,7 @@ import (
"github.com/velour/catbase/plugins/nerdepedia"
"github.com/velour/catbase/plugins/picker"
"github.com/velour/catbase/plugins/reaction"
"github.com/velour/catbase/plugins/remember"
"github.com/velour/catbase/plugins/reminder"
"github.com/velour/catbase/plugins/rpgORdie"
"github.com/velour/catbase/plugins/rss"
@ -89,7 +90,7 @@ func main() {
b.AddPlugin(dice.New(b))
b.AddPlugin(picker.New(b))
b.AddPlugin(beers.New(b))
b.AddPlugin(fact.NewRemember(b))
b.AddPlugin(remember.New(b))
b.AddPlugin(your.New(b))
b.AddPlugin(counter.New(b))
b.AddPlugin(reminder.New(b))

View File

@ -23,32 +23,10 @@ func makeMessage(nick, payload string) msg.Message {
}
}
func makePlugin(t *testing.T) (*RememberPlugin, *Factoid, *bot.MockBot) {
func makePlugin(t *testing.T) (*FactoidPlugin, *bot.MockBot) {
mb := bot.NewMockBot()
f := New(mb) // for DB table
p := NewRemember(mb)
assert.NotNil(t, p)
return p, f, mb
}
// Test case
func TestCornerCaseBug(t *testing.T) {
msgs := []msg.Message{
makeMessage("user1", "I dont want to personally touch a horse dick."),
makeMessage("user3", "idk my bff rose?"),
makeMessage("user2", "!remember user1 touch"),
}
p, _, mb := makePlugin(t)
for _, m := range msgs {
p.message(bot.Message, m)
}
assert.Len(t, mb.Messages, 1)
assert.Contains(t, mb.Messages[0], "horse dick")
q, err := getSingleFact(mb.DB(), "user1 quotes")
assert.Nil(t, err)
assert.Contains(t, q.Tidbit, "horse dick")
return f, mb
}
func TestReact(t *testing.T) {
@ -56,7 +34,7 @@ func TestReact(t *testing.T) {
makeMessage("user1", "!testing123 <react> jesus"),
makeMessage("user2", "testing123"),
}
_, p, mb := makePlugin(t)
p, mb := makePlugin(t)
for _, m := range msgs {
p.message(bot.Message, m)
@ -69,7 +47,7 @@ func TestReactCantLearnSpaces(t *testing.T) {
msgs := []msg.Message{
makeMessage("user1", "!test <react> jesus christ"),
}
_, p, mb := makePlugin(t)
p, mb := makePlugin(t)
for _, m := range msgs {
p.message(bot.Message, m)

View File

@ -22,14 +22,14 @@ import (
// respond to queries in a way that is unpredictable and fun
// factoid stores info about our factoid for lookup and later interaction
type factoid struct {
id sql.NullInt64
type Factoid struct {
ID sql.NullInt64
Fact string
Tidbit string
Verb string
Owner string
created time.Time
accessed time.Time
Created time.Time
Accessed time.Time
Count int
}
@ -38,14 +38,14 @@ type alias struct {
Next string
}
func (a *alias) resolve(db *sqlx.DB) (*factoid, error) {
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)
fact, err := GetSingleFact(db, a.Next)
if err != nil {
err := fmt.Errorf("Error resolvig alias %v: %v", a, err)
return nil, err
@ -55,7 +55,7 @@ func (a *alias) resolve(db *sqlx.DB) (*factoid, error) {
return next.resolve(db)
}
func findAlias(db *sqlx.DB, fact string) (bool, *factoid) {
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)
@ -89,9 +89,9 @@ func aliasFromStrings(from, to string) *alias {
return &alias{from, to}
}
func (f *factoid) save(db *sqlx.DB) error {
func (f *Factoid) Save(db *sqlx.DB) error {
var err error
if f.id.Valid {
if f.ID.Valid {
// update
_, err = db.Exec(`update factoid set
fact=?,
@ -105,12 +105,12 @@ func (f *factoid) save(db *sqlx.DB) error {
f.Tidbit,
f.Verb,
f.Owner,
f.accessed.Unix(),
f.Accessed.Unix(),
f.Count,
f.id.Int64)
f.ID.Int64)
} else {
f.created = time.Now()
f.accessed = time.Now()
f.Created = time.Now()
f.Accessed = time.Now()
// insert
res, err := db.Exec(`insert into factoid (
fact,
@ -125,8 +125,8 @@ func (f *factoid) save(db *sqlx.DB) error {
f.Tidbit,
f.Verb,
f.Owner,
f.created.Unix(),
f.accessed.Unix(),
f.Created.Unix(),
f.Accessed.Unix(),
f.Count,
)
if err != nil {
@ -134,23 +134,23 @@ func (f *factoid) save(db *sqlx.DB) error {
}
id, err := res.LastInsertId()
// hackhackhack?
f.id.Int64 = id
f.id.Valid = true
f.ID.Int64 = id
f.ID.Valid = true
}
return err
}
func (f *factoid) delete(db *sqlx.DB) error {
func (f *Factoid) delete(db *sqlx.DB) error {
var err error
if f.id.Valid {
_, err = db.Exec(`delete from factoid where id=?`, f.id)
if f.ID.Valid {
_, err = db.Exec(`delete from factoid where id=?`, f.ID)
}
f.id.Valid = false
f.ID.Valid = false
return err
}
func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*factoid, error) {
var fs []*factoid
func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*Factoid, error) {
var fs []*Factoid
query := `select
id,
fact,
@ -170,11 +170,11 @@ func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*factoid, error) {
return nil, err
}
for rows.Next() {
var f factoid
var f Factoid
var tmpCreated int64
var tmpAccessed int64
err := rows.Scan(
&f.id,
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
@ -186,15 +186,15 @@ func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*factoid, error) {
if err != nil {
return nil, err
}
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
fs = append(fs, &f)
}
return fs, err
}
func getSingle(db *sqlx.DB) (*factoid, error) {
var f factoid
func GetSingle(db *sqlx.DB) (*Factoid, error) {
var f Factoid
var tmpCreated int64
var tmpAccessed int64
err := db.QueryRow(`select
@ -208,7 +208,7 @@ func getSingle(db *sqlx.DB) (*factoid, error) {
count
from factoid
order by random() limit 1;`).Scan(
&f.id,
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
@ -217,13 +217,13 @@ func getSingle(db *sqlx.DB) (*factoid, error) {
&tmpAccessed,
&f.Count,
)
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
return &f, err
}
func getSingleFact(db *sqlx.DB, fact string) (*factoid, error) {
var f factoid
func GetSingleFact(db *sqlx.DB, fact string) (*Factoid, error) {
var f Factoid
var tmpCreated int64
var tmpAccessed int64
err := db.QueryRow(`select
@ -239,7 +239,7 @@ func getSingleFact(db *sqlx.DB, fact string) (*factoid, error) {
where fact like ?
order by random() limit 1;`,
fact).Scan(
&f.id,
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
@ -248,22 +248,22 @@ func getSingleFact(db *sqlx.DB, fact string) (*factoid, error) {
&tmpAccessed,
&f.Count,
)
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
return &f, err
}
// Factoid provides the necessary plugin-wide needs
type Factoid struct {
type FactoidPlugin struct {
Bot bot.Bot
NotFound []string
LastFact *factoid
LastFact *Factoid
db *sqlx.DB
}
// NewFactoid creates a new Factoid with the Plugin interface
func New(botInst bot.Bot) *Factoid {
p := &Factoid{
func New(botInst bot.Bot) *FactoidPlugin {
p := &FactoidPlugin{
Bot: botInst,
NotFound: []string{
"I don't know.",
@ -343,7 +343,7 @@ func findAction(message string) string {
// 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 {
func (p *FactoidPlugin) learnFact(message msg.Message, fact, verb, tidbit string) error {
verb = strings.ToLower(verb)
if verb == "react" {
// This would be a great place to check against the API for valid emojy
@ -366,17 +366,17 @@ func (p *Factoid) learnFact(message msg.Message, fact, verb, tidbit string) erro
return fmt.Errorf("Look, I already know that.")
}
n := factoid{
n := Factoid{
Fact: fact,
Tidbit: tidbit,
Verb: verb,
Owner: message.User.Name,
created: time.Now(),
accessed: time.Now(),
Created: time.Now(),
Accessed: time.Now(),
Count: 0,
}
p.LastFact = &n
err = n.save(p.db)
err = n.Save(p.db)
if err != nil {
log.Println("Error inserting fact: ", err)
return fmt.Errorf("My brain is overheating.")
@ -386,10 +386,10 @@ func (p *Factoid) learnFact(message msg.Message, fact, verb, tidbit string) erro
}
// findTrigger checks to see if a given string is a trigger or not
func (p *Factoid) findTrigger(fact string) (bool, *factoid) {
func (p *FactoidPlugin) findTrigger(fact string) (bool, *Factoid) {
fact = strings.ToLower(fact) // TODO: make sure this needs to be lowered here
f, err := getSingleFact(p.db, fact)
f, err := GetSingleFact(p.db, fact)
if err != nil {
return findAlias(p.db, fact)
}
@ -398,7 +398,7 @@ func (p *Factoid) findTrigger(fact string) (bool, *factoid) {
// 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) {
func (p *FactoidPlugin) sayFact(message msg.Message, fact Factoid) {
msg := p.Bot.Filter(message, fact.Tidbit)
full := p.Bot.Filter(message, fmt.Sprintf("%s %s %s",
fact.Fact, fact.Verb, fact.Tidbit,
@ -421,9 +421,9 @@ func (p *Factoid) sayFact(message msg.Message, fact factoid) {
}
// update fact tracking
fact.accessed = time.Now()
fact.Accessed = time.Now()
fact.Count += 1
err := fact.save(p.db)
err := fact.Save(p.db)
if err != nil {
log.Printf("Could not update fact.\n")
log.Printf("%#v\n", fact)
@ -434,7 +434,7 @@ func (p *Factoid) sayFact(message msg.Message, fact factoid) {
// 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 {
func (p *FactoidPlugin) trigger(message msg.Message) bool {
minLen := p.Bot.Config().GetInt("Factoid.MinLen", 4)
if len(message.Body) > minLen || message.Command || message.Body == "..." {
if ok, fact := p.findTrigger(message.Body); ok {
@ -453,20 +453,20 @@ func (p *Factoid) trigger(message msg.Message) bool {
}
// tellThemWhatThatWas is a hilarious name for a function.
func (p *Factoid) tellThemWhatThatWas(message msg.Message) bool {
func (p *FactoidPlugin) 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'",
fact.id.Int64, fact.Fact, fact.Verb, fact.Tidbit)
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 {
func (p *FactoidPlugin) learnAction(message msg.Message, action string) bool {
body := message.Body
parts := strings.SplitN(body, action, 2)
@ -512,7 +512,7 @@ func changeOperator(body string) string {
// 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 {
func (p *FactoidPlugin) forgetLastFact(message msg.Message) bool {
if p.LastFact == nil {
p.Bot.Send(bot.Message, message.Channel, "I refuse.")
return true
@ -522,7 +522,7 @@ func (p *Factoid) forgetLastFact(message msg.Message) bool {
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,
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
@ -531,7 +531,7 @@ func (p *Factoid) forgetLastFact(message msg.Message) bool {
}
// Allow users to change facts with a simple regexp
func (p *Factoid) changeFact(message msg.Message) bool {
func (p *FactoidPlugin) changeFact(message msg.Message) bool {
oper := changeOperator(message.Body)
parts := strings.SplitN(message.Body, oper, 2)
userexp := strings.TrimSpace(parts[1])
@ -571,8 +571,8 @@ func (p *Factoid) changeFact(message msg.Message) bool {
fact.Verb = reg.ReplaceAllString(fact.Verb, replace)
fact.Tidbit = reg.ReplaceAllString(fact.Tidbit, replace)
fact.Count += 1
fact.accessed = time.Now()
fact.save(p.db)
fact.Accessed = time.Now()
fact.Save(p.db)
}
} else if len(parts) == 3 {
// search for a factoid and print it
@ -614,7 +614,7 @@ func (p *Factoid) changeFact(message msg.Message) bool {
// 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 {
func (p *FactoidPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "what was that?" {
return p.tellThemWhatThatWas(message)
}
@ -674,15 +674,15 @@ func (p *Factoid) message(kind bot.Kind, message msg.Message, args ...interface{
}
// 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 {
func (p *FactoidPlugin) 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
}
// Pull a fact at random from the database
func (p *Factoid) randomFact() *factoid {
f, err := getSingle(p.db)
func (p *FactoidPlugin) randomFact() *Factoid {
f, err := GetSingle(p.db)
if err != nil {
fmt.Println("Error getting a fact: ", err)
return nil
@ -691,7 +691,7 @@ func (p *Factoid) randomFact() *factoid {
}
// factTimer spits out a fact at a given interval and with given probability
func (p *Factoid) factTimer(channel string) {
func (p *FactoidPlugin) factTimer(channel string) {
quoteTime := p.Bot.Config().GetInt("Factoid.QuoteTime", 30)
if quoteTime == 0 {
quoteTime = 30
@ -739,7 +739,7 @@ func (p *Factoid) factTimer(channel string) {
}
// Register any web URLs desired
func (p *Factoid) registerWeb() {
func (p *FactoidPlugin) registerWeb() {
http.HandleFunc("/factoid/req", p.serveQuery)
http.HandleFunc("/factoid", p.serveQuery)
p.Bot.RegisterWeb("/factoid", "Factoid")
@ -755,7 +755,7 @@ func linkify(text string) template.HTML {
return template.HTML(strings.Join(parts, " "))
}
func (p *Factoid) serveQuery(w http.ResponseWriter, r *http.Request) {
func (p *FactoidPlugin) serveQuery(w http.ResponseWriter, r *http.Request) {
context := make(map[string]interface{})
funcMap := template.FuncMap{
// The name "title" is what the function will be called in the template text.

View File

@ -1,6 +1,4 @@
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
package fact
package remember
import (
"fmt"
@ -11,38 +9,32 @@ import (
"github.com/jmoiron/sqlx"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/plugins/fact"
)
// This is a skeleton plugin to serve as an example and quick copy/paste for new
// plugins.
type RememberPlugin struct {
Bot bot.Bot
Log map[string][]msg.Message
bot bot.Bot
log map[string][]msg.Message
db *sqlx.DB
}
// NewRememberPlugin creates a new RememberPlugin with the Plugin interface
func NewRemember(b bot.Bot) *RememberPlugin {
p := RememberPlugin{
Bot: b,
Log: make(map[string][]msg.Message),
func New(b bot.Bot) *RememberPlugin {
p := &RememberPlugin{
bot: b,
log: make(map[string][]msg.Message),
db: b.DB(),
}
b.Register(p, bot.Message, p.message)
b.Register(p, bot.Message, p.help)
return &p
b.Register(p, bot.Help, p.help)
return p
}
// 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 *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "quote" && message.Command {
q := p.randQuote()
p.Bot.Send(bot.Message, message.Channel, q)
p.bot.Send(bot.Message, message.Channel, q)
// is it evil not to remember that the user said quote?
return true
@ -50,16 +42,16 @@ func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...int
user := message.User
parts := strings.Fields(message.Body)
if message.Command && len(parts) >= 3 &&
strings.ToLower(parts[0]) == "remember" {
// we have a remember!
// look through the logs and find parts[1] as a user, if not,
// fuck this hoser
nick := parts[1]
snip := strings.Join(parts[2:], " ")
for i := len(p.Log[message.Channel]) - 1; i >= 0; i-- {
entry := p.Log[message.Channel][i]
for i := len(p.log[message.Channel]) - 1; i >= 0; i-- {
entry := p.log[message.Channel][i]
log.Printf("Comparing %s:%s with %s:%s",
entry.User.Name, entry.Body, nick, snip)
if strings.ToLower(entry.User.Name) == strings.ToLower(nick) &&
@ -78,18 +70,18 @@ func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...int
trigger := fmt.Sprintf("%s quotes", entry.User.Name)
fact := factoid{
fact := fact.Factoid{
Fact: strings.ToLower(trigger),
Verb: "reply",
Tidbit: msg,
Owner: user.Name,
created: time.Now(),
accessed: time.Now(),
Created: time.Now(),
Accessed: time.Now(),
Count: 0,
}
if err := fact.save(p.db); err != nil {
if err := fact.Save(p.db); err != nil {
log.Println("ERROR!!!!:", err)
p.Bot.Send(bot.Message, message.Channel, "Tell somebody I'm broke.")
p.bot.Send(bot.Message, message.Channel, "Tell somebody I'm broke.")
}
log.Println("Remembering factoid:", msg)
@ -97,30 +89,28 @@ func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...int
// sorry, not creative with names so we're reusing msg
msg = fmt.Sprintf("Okay, %s, remembering '%s'.",
message.User.Name, msg)
p.Bot.Send(bot.Message, message.Channel, msg)
p.bot.Send(bot.Message, message.Channel, msg)
p.recordMsg(message)
return true
}
}
p.Bot.Send(bot.Message, message.Channel, "Sorry, I don't know that phrase.")
p.bot.Send(bot.Message, message.Channel, "Sorry, I don't know that phrase.")
p.recordMsg(message)
return true
}
p.recordMsg(message)
return false
}
// Help responds to help requests. Every plugin must implement a help function.
func (p *RememberPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool {
msg := "!remember will let you quote your idiot friends. Just type " +
msg := "remember will let you quote your idiot friends. Just type " +
"!remember <nick> <snippet> to remember what they said. Snippet can " +
"be any part of their message. Later on, you can ask for a random " +
"!quote."
p.Bot.Send(bot.Message, message.Channel, msg)
p.bot.Send(bot.Message, message.Channel, msg)
return true
}
@ -129,12 +119,12 @@ func (p *RememberPlugin) help(kind bot.Kind, message msg.Message, args ...interf
// expanded to have this function execute a quote for a particular channel
func (p *RememberPlugin) randQuote() string {
var f factoid
var f fact.Factoid
var tmpCreated int64
var tmpAccessed int64
err := p.db.QueryRow(`select * from factoid where fact like '%quotes'
order by random() limit 1;`).Scan(
&f.id,
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
@ -147,13 +137,13 @@ func (p *RememberPlugin) randQuote() string {
log.Println("Error getting quotes: ", err)
return "I had a problem getting your quote."
}
f.created = time.Unix(tmpCreated, 0)
f.accessed = time.Unix(tmpAccessed, 0)
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
return f.Tidbit
}
func (p *RememberPlugin) recordMsg(message msg.Message) {
log.Printf("Logging message: %s: %s", message.User.Name, message.Body)
p.Log[message.Channel] = append(p.Log[message.Channel], message)
p.log[message.Channel] = append(p.log[message.Channel], message)
}

View File

@ -0,0 +1,53 @@
package remember
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/bot/user"
"github.com/velour/catbase/plugins/fact"
)
func makeMessage(nick, payload string) msg.Message {
isCmd := strings.HasPrefix(payload, "!")
if isCmd {
payload = payload[1:]
}
return msg.Message{
User: &user.User{Name: nick},
Channel: "test",
Body: payload,
Command: isCmd,
}
}
func makePlugin(t *testing.T) (*RememberPlugin, *fact.FactoidPlugin, *bot.MockBot) {
mb := bot.NewMockBot()
f := fact.New(mb) // for DB table
p := New(mb)
assert.NotNil(t, p)
return p, f, mb
}
// Test case
func TestCornerCaseBug(t *testing.T) {
msgs := []msg.Message{
makeMessage("user1", "I dont want to personally touch a horse dick."),
makeMessage("user3", "idk my bff rose?"),
makeMessage("user2", "!remember user1 touch"),
}
p, _, mb := makePlugin(t)
for _, m := range msgs {
p.message(bot.Message, m)
}
assert.Len(t, mb.Messages, 1)
assert.Contains(t, mb.Messages[0], "horse dick")
q, err := fact.GetSingleFact(mb.DB(), "user1 quotes")
assert.Nil(t, err)
assert.Contains(t, q.Tidbit, "horse dick")
}