Compare commits

..

5 Commits

Author SHA1 Message Date
Chris Sexton 4e0c308253 refactor: nerdepedia the lazy way 2021-02-07 15:12:45 -05:00
Chris Sexton 7dfa5bf891 meme: refactor
impossible: fix bug where it was eating all messages
2021-02-07 15:09:52 -05:00
Chris Sexton 0891713523 leftpad: refactor 2021-02-07 14:23:18 -05:00
Chris Sexton 353f289cae impossible: refactor 2021-02-07 14:03:34 -05:00
Chris Sexton ca97a07a4d goals: refactor 2021-02-07 13:33:30 -05:00
9 changed files with 217 additions and 170 deletions

View File

@ -65,6 +65,7 @@ type HandlerSpec struct {
Kind Kind
IsCmd bool
Regex *regexp.Regexp
HelpText string
Handler ResponseHandler
}
type HandlerTable []HandlerSpec

View File

@ -5,7 +5,6 @@ import (
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/jmoiron/sqlx"
@ -21,6 +20,7 @@ type GoalsPlugin struct {
b bot.Bot
cfg *config.Config
db *sqlx.DB
handlers bot.HandlerTable
}
func New(b bot.Bot) *GoalsPlugin {
@ -30,7 +30,7 @@ func New(b bot.Bot) *GoalsPlugin {
db: b.DB(),
}
p.mkDB()
b.Register(p, bot.Message, p.message)
p.registerCmds()
b.Register(p, bot.Help, p.help)
counter.RegisterUpdate(p.update)
return p
@ -51,42 +51,54 @@ func (p *GoalsPlugin) mkDB() {
}
}
var registerSelf = regexp.MustCompile(`(?i)^register (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)\s?(?P<amount>[[:digit:]]+)?`)
var registerOther = regexp.MustCompile(`(?i)^register (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>[[:punct:][:alnum:]]+)\s?(?P<amount>[[:digit:]]+)?`)
var deRegisterSelf = regexp.MustCompile(`(?i)^deregister (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)`)
var deRegisterOther = regexp.MustCompile(`(?i)^deregister (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>.*)`)
var checkSelf = regexp.MustCompile(`(?i)^check (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)`)
var checkOther = regexp.MustCompile(`(?i)^check (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>[[:punct:][:alnum:]]+)`)
func (p *GoalsPlugin) message(conn bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
body := strings.TrimSpace(message.Body)
who := message.User.Name
ch := message.Channel
if registerOther.MatchString(body) {
c := parseCmd(registerOther, body)
amount, _ := strconv.Atoi(c["amount"])
p.register(conn, ch, c["type"], c["what"], c["who"], amount)
} else if registerSelf.MatchString(body) {
c := parseCmd(registerSelf, body)
amount, _ := strconv.Atoi(c["amount"])
p.register(conn, ch, c["type"], c["what"], who, amount)
} else if deRegisterOther.MatchString(body) {
c := parseCmd(deRegisterOther, body)
p.deregister(conn, ch, c["type"], c["what"], c["who"])
} else if deRegisterSelf.MatchString(body) {
c := parseCmd(deRegisterSelf, body)
p.deregister(conn, ch, c["type"], c["what"], who)
} else if checkOther.MatchString(body) {
c := parseCmd(checkOther, body)
p.check(conn, ch, c["type"], c["what"], c["who"])
} else if checkSelf.MatchString(body) {
c := parseCmd(checkSelf, body)
p.check(conn, ch, c["type"], c["what"], who)
} else {
return false
}
func (p *GoalsPlugin) registerCmds() {
p.handlers = bot.HandlerTable{
{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^register (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+) (?P<amount>[[:digit:]]+)?`),
HelpText: "Register with `%s` for yourself",
Handler: func(r bot.Request) bool {
amount, _ := strconv.Atoi(r.Values["amount"])
p.register(r.Conn, r.Msg.Channel, r.Values["type"], r.Values["what"], r.Msg.User.Name, amount)
return true
}},
{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^register (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>[[:punct:][:alnum:]]+) (?P<amount>[[:digit:]]+)?`),
HelpText: "Register with `%s` for other people",
Handler: func(r bot.Request) bool {
amount, _ := strconv.Atoi(r.Values["amount"])
p.register(r.Conn, r.Msg.Channel, r.Values["type"], r.Values["what"], r.Values["who"], amount)
return true
}},
{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^deregister (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)`),
HelpText: "Deregister with `%s` for yourself",
Handler: func(r bot.Request) bool {
p.deregister(r.Conn, r.Msg.Channel, r.Values["type"], r.Values["what"], r.Msg.User.Name)
return true
}},
{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^deregister (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>.*)`),
HelpText: "Deregister with `%s` for other people",
Handler: func(r bot.Request) bool {
p.deregister(r.Conn, r.Msg.Channel, r.Values["type"], r.Values["what"], r.Values["who"])
return true
}},
{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^check (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)`),
HelpText: "Check with `%s` for yourself",
Handler: func(r bot.Request) bool {
p.check(r.Conn, r.Msg.Channel, r.Values["type"], r.Values["what"], r.Msg.User.Name)
return true
}},
{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^check (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>[[:punct:][:alnum:]]+)`),
HelpText: "Check with `%s` for other people",
Handler: func(r bot.Request) bool {
p.check(r.Conn, r.Msg.Channel, r.Values["type"], r.Values["what"], r.Values["who"])
return true
}},
}
p.b.RegisterTable(p, p.handlers)
}
func (p *GoalsPlugin) register(c bot.Connector, ch, kind, what, who string, howMuch int) {
@ -201,12 +213,9 @@ func (p *GoalsPlugin) checkGoal(c bot.Connector, ch, what, who string) {
func (p *GoalsPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
ch := message.Channel
msg := "Goals can set goals and competition for your counters."
msg += fmt.Sprintf("\nRegister with `%s` for yourself", registerSelf)
msg += fmt.Sprintf("\nRegister with `%s` for other people", registerOther)
msg += fmt.Sprintf("\nDeregister with `%s` for yourself", deRegisterSelf)
msg += fmt.Sprintf("\nDeregister with `%s` for other people", deRegisterOther)
msg += fmt.Sprintf("\nCheck with `%s` for yourself", checkSelf)
msg += fmt.Sprintf("\nCheck with `%s` for other people", checkOther)
for _, cmd := range p.handlers {
msg += fmt.Sprintf("\n"+cmd.HelpText, cmd.Regex)
}
p.b.Send(c, bot.Message, ch, msg)
return true
}

View File

@ -21,6 +21,7 @@ import (
type Impossible struct {
b bot.Bot
c *config.Config
handlers bot.HandlerTable
title string
content []string
@ -39,7 +40,7 @@ func New(b bot.Bot) *Impossible {
}
b.Register(i, bot.Help, i.help)
b.Register(i, bot.Message, i.message)
i.register()
return i
}
@ -55,7 +56,7 @@ func newTesting(b bot.Bot) *Impossible {
}
b.Register(i, bot.Help, i.help)
b.Register(i, bot.Message, i.message)
i.register()
return i
}
@ -65,39 +66,62 @@ func (p *Impossible) help(c bot.Connector, kind bot.Kind, message msg.Message, a
return true
}
func (p *Impossible) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
messaged := false
func (p *Impossible) tryRefresh(r bot.Request) (sent bool) {
if p.updated.Before(time.Now()) {
if p.title != "" {
p.b.Send(c, bot.Message, message.Channel, fmt.Sprintf("The last impossible wikipedia article was: \"%s\"", p.title))
messaged = true
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("The last impossible wikipedia article was: \"%s\"", p.title))
sent = true
}
for !p.refreshImpossible() {
}
if p.testing {
p.b.Send(c, bot.Message, message.Channel, p.title)
messaged = true
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, p.title)
sent = true
}
}
return sent
}
lowercase := strings.ToLower(message.Body)
if lowercase == "hint" || lowercase == "clue" {
messaged = true
p.b.Send(c, bot.Message, message.Channel, p.content[rand.Intn(len(p.content))])
} else if strings.Contains(lowercase, strings.ToLower(p.title)) {
messaged = true
p.b.Send(c, bot.Message, message.Channel, fmt.Sprintf("You guessed the last impossible wikipedia article: \"%s\"", p.title))
for !p.refreshImpossible() {
}
} else if strings.Contains(lowercase, "i friggin give up") {
messaged = true
p.b.Send(c, bot.Message, message.Channel, fmt.Sprintf("You're a failure the last impossible wikipedia article: \"%s\"", p.title))
func (p *Impossible) register() {
p.handlers = bot.HandlerTable{
{Kind: bot.Message, IsCmd: false,
Regex: regexp.MustCompile(`(?i)^hint|clue$`),
Handler: func(r bot.Request) bool {
if p.tryRefresh(r) {
return true
}
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, p.content[rand.Intn(len(p.content))])
return true
}},
{Kind: bot.Message, IsCmd: false,
Regex: regexp.MustCompile(`(?i)^i friggin give up.?$`),
Handler: func(r bot.Request) bool {
if p.tryRefresh(r) {
return true
}
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("You guessed the last impossible wikipedia article: \"%s\"", p.title))
for !p.refreshImpossible() {
}
return true
}},
{Kind: bot.Message, IsCmd: false,
Regex: regexp.MustCompile(`.*`),
Handler: func(r bot.Request) bool {
if p.tryRefresh(r) {
return true
}
return messaged
if strings.Contains(strings.ToLower(r.Msg.Body), strings.ToLower(p.title)) {
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("You guessed the last impossible wikipedia article: \"%s\"", p.title))
for !p.refreshImpossible() {
}
return true
}
return false
}},
}
p.b.RegisterTable(p, p.handlers)
}
func (p *Impossible) refreshImpossible() bool {

View File

@ -2,10 +2,12 @@ package impossible
import (
"fmt"
"github.com/velour/catbase/plugins/cli"
"regexp"
"strings"
"testing"
"github.com/velour/catbase/plugins/cli"
"github.com/stretchr/testify/assert"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
@ -13,16 +15,22 @@ import (
"github.com/velour/catbase/plugins/counter"
)
func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
func makeMessage(payload string, r *regexp.Regexp) bot.Request {
isCmd := strings.HasPrefix(payload, "!")
if isCmd {
payload = payload[1:]
}
return &cli.CliPlugin{}, bot.Message, msg.Message{
values := bot.ParseValues(r, payload)
return bot.Request{
Conn: &cli.CliPlugin{},
Kind: bot.Message,
Values: values,
Msg: msg.Message{
User: &user.User{Name: "tester"},
Channel: "test",
Body: payload,
Command: isCmd,
},
}
}
@ -34,24 +42,32 @@ func makePlugin(t *testing.T) (*Impossible, *bot.MockBot) {
return p, mb
}
func testMessage(p *Impossible, body string) {
for _, h := range p.handlers {
if h.Regex.MatchString(body) && h.Handler(makeMessage(body, h.Regex)) {
return
}
}
}
func TestNothing(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("hi"))
p.message(makeMessage("nothing"))
testMessage(p, "hi")
testMessage(p, "nothing")
assert.Len(t, mb.Messages, 1)
}
func TestHint(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("hi"))
p.message(makeMessage("!hint"))
testMessage(p, "hi")
testMessage(p, "hint")
assert.Len(t, mb.Messages, 2)
}
func TestCorrect(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("hi"))
p.message(makeMessage(mb.Messages[0]))
testMessage(p, "hi")
testMessage(p, mb.Messages[0])
congrats := fmt.Sprintf("You guessed the last impossible wikipedia article: \"%s\"", mb.Messages[0])

View File

@ -5,12 +5,11 @@ package leftpad
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/chrissexton/leftpad"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/config"
)
@ -25,7 +24,7 @@ func New(b bot.Bot) *LeftpadPlugin {
bot: b,
config: b.Config(),
}
b.Register(p, bot.Message, p.message)
b.RegisterRegexCmd(p, bot.Message, leftpadRegex, p.leftpadCmd)
return p
}
@ -33,32 +32,25 @@ type leftpadResp struct {
Str string
}
func (p *LeftpadPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if !message.Command {
return false
}
var leftpadRegex = regexp.MustCompile(`(?i)^leftpad (?P<padstr>\S+) (?P<padding>\d+) (?P<text>.+)$`)
parts := strings.Fields(message.Body)
if len(parts) > 3 && parts[0] == "leftpad" {
padchar := parts[1]
length, err := strconv.Atoi(parts[2])
func (p *LeftpadPlugin) leftpadCmd(r bot.Request) bool {
padchar := r.Values["padstr"]
length, err := strconv.Atoi(r.Values["padding"])
if err != nil {
p.bot.Send(c, bot.Message, message.Channel, "Invalid padding number")
p.bot.Send(r.Conn, bot.Message, r.Msg.Channel, "Invalid padding number")
return true
}
maxLen, who := p.config.GetInt("LeftPad.MaxLen", 50), p.config.Get("LeftPad.Who", "Putin")
if length > maxLen && maxLen > 0 {
msg := fmt.Sprintf("%s would kill me if I did that.", who)
p.bot.Send(c, bot.Message, message.Channel, msg)
p.bot.Send(r.Conn, bot.Message, r.Msg.Channel, msg)
return true
}
text := strings.Join(parts[3:], " ")
text := r.Values["text"]
res := leftpad.LeftPad(text, length, padchar)
p.bot.Send(c, bot.Message, message.Channel, res)
p.bot.Send(r.Conn, bot.Message, r.Msg.Channel, res)
return true
}
return false
}

View File

@ -3,10 +3,10 @@
package leftpad
import (
"github.com/velour/catbase/plugins/cli"
"strings"
"testing"
"github.com/velour/catbase/plugins/cli"
"github.com/stretchr/testify/assert"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
@ -14,16 +14,24 @@ import (
"github.com/velour/catbase/plugins/counter"
)
func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!")
if isCmd {
payload = payload[1:]
}
return &cli.CliPlugin{}, bot.Message, msg.Message{
func makeMessage(payload string) bot.Request {
values := bot.ParseValues(leftpadRegex, payload)
return bot.Request{
Kind: bot.Message,
Conn: &cli.CliPlugin{},
Values: values,
Msg: msg.Message{
User: &user.User{Name: "tester"},
Channel: "test",
Body: payload,
Command: isCmd,
},
}
}
func testMessage(p *LeftpadPlugin, body string) {
if leftpadRegex.MatchString(body) {
p.leftpadCmd(makeMessage(body))
}
}
@ -38,51 +46,48 @@ func makePlugin(t *testing.T) (*LeftpadPlugin, *bot.MockBot) {
func TestLeftpad(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("!leftpad test 8 test"))
testMessage(p, "leftpad test 8 test")
if assert.Len(t, mb.Messages, 1) {
assert.Contains(t, mb.Messages[0], "testtest")
assert.Len(t, mb.Messages, 1)
}
func TestBadNumber(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("!leftpad test fuck test"))
assert.Contains(t, mb.Messages[0], "Invalid")
assert.Len(t, mb.Messages, 1)
}
func TestNotCommand(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("leftpad test fuck test"))
testMessage(p, "leftpad test fuck test")
assert.Len(t, mb.Messages, 0)
}
func TestNoMaxLen(t *testing.T) {
p, mb := makePlugin(t)
p.config.Set("LeftPad.MaxLen", "0")
p.message(makeMessage("!leftpad dicks 100 dicks"))
assert.Len(t, mb.Messages, 1)
testMessage(p, "leftpad dicks 100 dicks")
if assert.Len(t, mb.Messages, 1) {
assert.Contains(t, mb.Messages[0], "dicks")
}
}
func Test50Padding(t *testing.T) {
p, mb := makePlugin(t)
p.config.Set("LeftPad.MaxLen", "50")
assert.Equal(t, 50, p.config.GetInt("LeftPad.MaxLen", 100))
p.message(makeMessage("!leftpad dicks 100 dicks"))
assert.Len(t, mb.Messages, 1)
testMessage(p, "leftpad dicks 100 dicks")
if assert.Len(t, mb.Messages, 1) {
assert.Contains(t, mb.Messages[0], "kill me")
}
}
func TestUnder50Padding(t *testing.T) {
p, mb := makePlugin(t)
p.config.Set("LeftPad.MaxLen", "50")
p.message(makeMessage("!leftpad dicks 49 dicks"))
assert.Len(t, mb.Messages, 1)
testMessage(p, "leftpad dicks 49 dicks")
if assert.Len(t, mb.Messages, 1) {
assert.Contains(t, mb.Messages[0], "dicks")
}
}
func TestNotPadding(t *testing.T) {
p, mb := makePlugin(t)
p.message(makeMessage("!lololol"))
testMessage(p, "lololol")
assert.Len(t, mb.Messages, 0)
}

View File

@ -66,28 +66,21 @@ func New(b bot.Bot) *MemePlugin {
horizon = mp.c.GetInt("meme.horizon", horizon)
b.Register(mp, bot.Message, mp.message)
b.RegisterRegexCmd(mp, bot.Message, cmdMatch, mp.message)
b.Register(mp, bot.Help, mp.help)
mp.registerWeb(b.DefaultConnector())
return mp
}
var cmdMatch = regexp.MustCompile(`(?i)meme (.+)`)
var cmdMatch = regexp.MustCompile(`(?i)^meme (?P<content>.+)$`)
func (p *MemePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if message.Command && cmdMatch.MatchString(message.Body) {
subs := cmdMatch.FindStringSubmatch(message.Body)
if len(subs) != 2 {
p.bot.Send(c, bot.Message, message.Channel, "Invalid meme request.")
func (p *MemePlugin) message(r bot.Request) bool {
minusMeme := r.Values["content"]
log.Debug().Msgf("Calling sendMeme with text: %s", minusMeme)
p.sendMeme(r.Conn, r.Msg.Channel, r.Msg.ChannelName, r.Msg.ID, r.Msg.User, minusMeme)
return true
}
minusMeme := subs[1]
p.sendMeme(c, message.Channel, message.ChannelName, message.ID, message.User, minusMeme)
return true
}
return false
}
func (p *MemePlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
webRoot := p.c.Get("BaseURL", "https://catbase.velour.ninja")

View File

@ -7,6 +7,7 @@ import (
"fmt"
"html"
"net/http"
"regexp"
"strings"
"github.com/velour/catbase/bot"
@ -44,7 +45,7 @@ func New(b bot.Bot) *NerdepediaPlugin {
bot: b,
config: b.Config(),
}
b.Register(np, bot.Message, np.message)
b.RegisterRegex(np, bot.Message, regexp.MustCompile(`.*`), np.message)
b.Register(np, bot.Help, np.help)
return np
}
@ -79,7 +80,9 @@ func defaultSites() map[string]string {
// 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 *NerdepediaPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
func (p *NerdepediaPlugin) message(r bot.Request) bool {
c := r.Conn
message := r.Msg
lowerCase := strings.ToLower(message.Body)
query := ""
queries := p.config.GetMap("nerdepedia.sites", defaultSites())

View File

@ -39,16 +39,20 @@ func (cl MockClient) Do(req *http.Request) (*http.Response, error) {
}, cl.Err
}
func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
func makeMessage(payload string) bot.Request {
isCmd := strings.HasPrefix(payload, "!")
if isCmd {
payload = payload[1:]
}
return &cli.CliPlugin{}, bot.Message, msg.Message{
return bot.Request{
Conn: &cli.CliPlugin{},
Kind: bot.Message,
Msg: msg.Message{
User: &user.User{Name: "tester"},
Channel: "test",
Body: payload,
Command: isCmd,
},
}
}