mirror of https://github.com/velour/catbase.git
Make testing great again! Add examples in counter
* Made bot.Bot an interface and added a mock with an in-memory database for plugins to use. * Remove logger nonsense * Rename Counter New
This commit is contained in:
parent
a34afa97ad
commit
ef40d335eb
89
bot/bot.go
89
bot/bot.go
|
@ -16,33 +16,33 @@ import (
|
||||||
"github.com/velour/catbase/config"
|
"github.com/velour/catbase/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bot type provides storage for bot-wide information, configs, and database connections
|
// bot type provides storage for bot-wide information, configs, and database connections
|
||||||
type Bot struct {
|
type bot struct {
|
||||||
// Each plugin must be registered in our plugins handler. To come: a map so that this
|
// Each plugin must be registered in our plugins handler. To come: a map so that this
|
||||||
// will allow plugins to respond to specific kinds of events
|
// will allow plugins to respond to specific kinds of events
|
||||||
Plugins map[string]Handler
|
plugins map[string]Handler
|
||||||
PluginOrdering []string
|
pluginOrdering []string
|
||||||
|
|
||||||
// Users holds information about all of our friends
|
// Users holds information about all of our friends
|
||||||
Users []User
|
users []User
|
||||||
// Represents the bot
|
// Represents the bot
|
||||||
Me User
|
me User
|
||||||
|
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
|
|
||||||
Conn Connector
|
conn Connector
|
||||||
|
|
||||||
// SQL DB
|
// SQL DB
|
||||||
// TODO: I think it'd be nice to use https://github.com/jmoiron/sqlx so that
|
// TODO: I think it'd be nice to use https://github.com/jmoiron/sqlx so that
|
||||||
// the select/update/etc statements could be simplified with struct
|
// the select/update/etc statements could be simplified with struct
|
||||||
// marshalling.
|
// marshalling.
|
||||||
DB *sqlx.DB
|
db *sqlx.DB
|
||||||
DBVersion int64
|
dbVersion int64
|
||||||
|
|
||||||
logIn chan Message
|
logIn chan Message
|
||||||
logOut chan Messages
|
logOut chan Messages
|
||||||
|
|
||||||
Version string
|
version string
|
||||||
|
|
||||||
// The entries to the bot's HTTP interface
|
// The entries to the bot's HTTP interface
|
||||||
httpEndPoints map[string]string
|
httpEndPoints map[string]string
|
||||||
|
@ -109,8 +109,8 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBot creates a Bot for a given connection and set of handlers.
|
// Newbot creates a bot for a given connection and set of handlers.
|
||||||
func NewBot(config *config.Config, connector Connector) *Bot {
|
func New(config *config.Config, connector Connector) Bot {
|
||||||
sqlDB, err := sqlx.Open("sqlite3_custom", config.DB.File)
|
sqlDB, err := sqlx.Open("sqlite3_custom", config.DB.File)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -127,17 +127,17 @@ func NewBot(config *config.Config, connector Connector) *Bot {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
bot := &Bot{
|
bot := &bot{
|
||||||
Config: config,
|
config: config,
|
||||||
Plugins: make(map[string]Handler),
|
plugins: make(map[string]Handler),
|
||||||
PluginOrdering: make([]string, 0),
|
pluginOrdering: make([]string, 0),
|
||||||
Conn: connector,
|
conn: connector,
|
||||||
Users: users,
|
users: users,
|
||||||
Me: users[0],
|
me: users[0],
|
||||||
DB: sqlDB,
|
db: sqlDB,
|
||||||
logIn: logIn,
|
logIn: logIn,
|
||||||
logOut: logOut,
|
logOut: logOut,
|
||||||
Version: config.Version,
|
version: config.Version,
|
||||||
httpEndPoints: make(map[string]string),
|
httpEndPoints: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,32 +155,45 @@ func NewBot(config *config.Config, connector Connector) *Bot {
|
||||||
return bot
|
return bot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Config gets the configuration that the bot is using
|
||||||
|
func (b *bot) Config() *config.Config {
|
||||||
|
return b.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bot) DBVersion() int64 {
|
||||||
|
return b.dbVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bot) DB() *sqlx.DB {
|
||||||
|
return b.db
|
||||||
|
}
|
||||||
|
|
||||||
// Create any tables if necessary based on version of DB
|
// Create any tables if necessary based on version of DB
|
||||||
// Plugins should create their own tables, these are only for official bot stuff
|
// Plugins should create their own tables, these are only for official bot stuff
|
||||||
// Note: This does not return an error. Database issues are all fatal at this stage.
|
// Note: This does not return an error. Database issues are all fatal at this stage.
|
||||||
func (b *Bot) migrateDB() {
|
func (b *bot) migrateDB() {
|
||||||
_, err := b.DB.Exec(`create table if not exists version (version integer);`)
|
_, err := b.db.Exec(`create table if not exists version (version integer);`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Initial DB migration create version table: ", err)
|
log.Fatal("Initial DB migration create version table: ", err)
|
||||||
}
|
}
|
||||||
var version sql.NullInt64
|
var version sql.NullInt64
|
||||||
err = b.DB.QueryRow("select max(version) from version").Scan(&version)
|
err = b.db.QueryRow("select max(version) from version").Scan(&version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Initial DB migration get version: ", err)
|
log.Fatal("Initial DB migration get version: ", err)
|
||||||
}
|
}
|
||||||
if version.Valid {
|
if version.Valid {
|
||||||
b.DBVersion = version.Int64
|
b.dbVersion = version.Int64
|
||||||
log.Printf("Database version: %v\n", b.DBVersion)
|
log.Printf("Database version: %v\n", b.dbVersion)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("No versions, we're the first!.")
|
log.Printf("No versions, we're the first!.")
|
||||||
_, err := b.DB.Exec(`insert into version (version) values (1)`)
|
_, err := b.db.Exec(`insert into version (version) values (1)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Initial DB migration insert: ", err)
|
log.Fatal("Initial DB migration insert: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.DBVersion == 1 {
|
if b.dbVersion == 1 {
|
||||||
if _, err := b.DB.Exec(`create table if not exists variables (
|
if _, err := b.db.Exec(`create table if not exists variables (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
name string,
|
name string,
|
||||||
perms string,
|
perms string,
|
||||||
|
@ -188,7 +201,7 @@ func (b *Bot) migrateDB() {
|
||||||
);`); err != nil {
|
);`); err != nil {
|
||||||
log.Fatal("Initial DB migration create variables table: ", err)
|
log.Fatal("Initial DB migration create variables table: ", err)
|
||||||
}
|
}
|
||||||
if _, err := b.DB.Exec(`create table if not exists 'values' (
|
if _, err := b.db.Exec(`create table if not exists 'values' (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
varId integer,
|
varId integer,
|
||||||
value string
|
value string
|
||||||
|
@ -199,18 +212,18 @@ func (b *Bot) migrateDB() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a constructed handler to the bots handlers list
|
// Adds a constructed handler to the bots handlers list
|
||||||
func (b *Bot) AddHandler(name string, h Handler) {
|
func (b *bot) AddHandler(name string, h Handler) {
|
||||||
b.Plugins[strings.ToLower(name)] = h
|
b.plugins[strings.ToLower(name)] = h
|
||||||
b.PluginOrdering = append(b.PluginOrdering, name)
|
b.pluginOrdering = append(b.pluginOrdering, name)
|
||||||
if entry := h.RegisterWeb(); entry != nil {
|
if entry := h.RegisterWeb(); entry != nil {
|
||||||
b.httpEndPoints[name] = *entry
|
b.httpEndPoints[name] = *entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) Who(channel string) []User {
|
func (b *bot) Who(channel string) []User {
|
||||||
out := []User{}
|
out := []User{}
|
||||||
for _, u := range b.Users {
|
for _, u := range b.users {
|
||||||
if u.Name != b.Config.Nick {
|
if u.Name != b.Config().Nick {
|
||||||
out = append(out, u)
|
out = append(out, u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +260,7 @@ var rootIndex string = `
|
||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
|
|
||||||
func (b *Bot) serveRoot(w http.ResponseWriter, r *http.Request) {
|
func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
context := make(map[string]interface{})
|
context := make(map[string]interface{})
|
||||||
context["EndPoints"] = b.httpEndPoints
|
context["EndPoints"] = b.httpEndPoints
|
||||||
t, err := template.New("rootIndex").Parse(rootIndex)
|
t, err := template.New("rootIndex").Parse(rootIndex)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handles incomming PRIVMSG requests
|
// Handles incomming PRIVMSG requests
|
||||||
func (b *Bot) MsgReceived(msg Message) {
|
func (b *bot) MsgReceived(msg Message) {
|
||||||
log.Println("Received message: ", msg)
|
log.Println("Received message: ", msg)
|
||||||
|
|
||||||
// msg := b.buildMessage(client, inMsg)
|
// msg := b.buildMessage(client, inMsg)
|
||||||
|
@ -27,8 +27,8 @@ func (b *Bot) MsgReceived(msg Message) {
|
||||||
goto RET
|
goto RET
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range b.PluginOrdering {
|
for _, name := range b.pluginOrdering {
|
||||||
p := b.Plugins[name]
|
p := b.plugins[name]
|
||||||
if p.Message(msg) {
|
if p.Message(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -40,31 +40,31 @@ RET:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle incoming events
|
// Handle incoming events
|
||||||
func (b *Bot) EventReceived(msg Message) {
|
func (b *bot) EventReceived(msg Message) {
|
||||||
log.Println("Received event: ", msg)
|
log.Println("Received event: ", msg)
|
||||||
//msg := b.buildMessage(conn, inMsg)
|
//msg := b.buildMessage(conn, inMsg)
|
||||||
for _, name := range b.PluginOrdering {
|
for _, name := range b.pluginOrdering {
|
||||||
p := b.Plugins[name]
|
p := b.plugins[name]
|
||||||
if p.Event(msg.Body, msg) { // TODO: could get rid of msg.Body
|
if p.Event(msg.Body, msg) { // TODO: could get rid of msg.Body
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) SendMessage(channel, message string) {
|
func (b *bot) SendMessage(channel, message string) {
|
||||||
b.Conn.SendMessage(channel, message)
|
b.conn.SendMessage(channel, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) SendAction(channel, message string) {
|
func (b *bot) SendAction(channel, message string) {
|
||||||
b.Conn.SendAction(channel, message)
|
b.conn.SendAction(channel, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks to see if the user is asking for help, returns true if so and handles the situation.
|
// Checks to see if the user is asking for help, returns true if so and handles the situation.
|
||||||
func (b *Bot) checkHelp(channel string, parts []string) {
|
func (b *bot) checkHelp(channel string, parts []string) {
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
// just print out a list of help topics
|
// just print out a list of help topics
|
||||||
topics := "Help topics: about variables"
|
topics := "Help topics: about variables"
|
||||||
for name, _ := range b.Plugins {
|
for name, _ := range b.plugins {
|
||||||
topics = fmt.Sprintf("%s, %s", topics, name)
|
topics = fmt.Sprintf("%s, %s", topics, name)
|
||||||
}
|
}
|
||||||
b.SendMessage(channel, topics)
|
b.SendMessage(channel, topics)
|
||||||
|
@ -78,7 +78,7 @@ func (b *Bot) checkHelp(channel string, parts []string) {
|
||||||
b.listVars(channel, parts)
|
b.listVars(channel, parts)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
plugin := b.Plugins[parts[1]]
|
plugin := b.plugins[parts[1]]
|
||||||
if plugin != nil {
|
if plugin != nil {
|
||||||
plugin.Help(channel, parts)
|
plugin.Help(channel, parts)
|
||||||
} else {
|
} else {
|
||||||
|
@ -88,7 +88,7 @@ func (b *Bot) checkHelp(channel string, parts []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) LastMessage(channel string) (Message, error) {
|
func (b *bot) LastMessage(channel string) (Message, error) {
|
||||||
log := <-b.logOut
|
log := <-b.logOut
|
||||||
if len(log) == 0 {
|
if len(log) == 0 {
|
||||||
return Message{}, errors.New("No messages found.")
|
return Message{}, errors.New("No messages found.")
|
||||||
|
@ -103,7 +103,7 @@ func (b *Bot) LastMessage(channel string) (Message, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take an input string and mutate it based on $vars in the string
|
// Take an input string and mutate it based on $vars in the string
|
||||||
func (b *Bot) Filter(message Message, input string) string {
|
func (b *bot) Filter(message Message, input string) string {
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
|
|
||||||
if strings.Contains(input, "$NICK") {
|
if strings.Contains(input, "$NICK") {
|
||||||
|
@ -155,9 +155,9 @@ func (b *Bot) Filter(message Message, input string) string {
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) getVar(varName string) (string, error) {
|
func (b *bot) getVar(varName string) (string, error) {
|
||||||
var text string
|
var text string
|
||||||
err := b.DB.QueryRow(`select v.value from variables as va inner join "values" as v on va.id = va.id = v.varId order by random() limit 1`).Scan(&text)
|
err := b.db.QueryRow(`select v.value from variables as va inner join "values" as v on va.id = va.id = v.varId order by random() limit 1`).Scan(&text)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
return "", fmt.Errorf("No factoid found")
|
return "", fmt.Errorf("No factoid found")
|
||||||
|
@ -167,8 +167,8 @@ func (b *Bot) getVar(varName string) (string, error) {
|
||||||
return text, nil
|
return text, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) listVars(channel string, parts []string) {
|
func (b *bot) listVars(channel string, parts []string) {
|
||||||
rows, err := b.DB.Query(`select name from variables`)
|
rows, err := b.db.Query(`select name from variables`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -185,17 +185,17 @@ func (b *Bot) listVars(channel string, parts []string) {
|
||||||
b.SendMessage(channel, msg)
|
b.SendMessage(channel, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) Help(channel string, parts []string) {
|
func (b *bot) Help(channel string, parts []string) {
|
||||||
msg := fmt.Sprintf("Hi, I'm based on godeepintir version %s. I'm written in Go, and you "+
|
msg := fmt.Sprintf("Hi, I'm based on godeepintir version %s. I'm written in Go, and you "+
|
||||||
"can find my source code on the internet here: "+
|
"can find my source code on the internet here: "+
|
||||||
"http://github.com/velour/catbase", b.Version)
|
"http://github.com/velour/catbase", b.version)
|
||||||
b.SendMessage(channel, msg)
|
b.SendMessage(channel, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send our own musings to the plugins
|
// Send our own musings to the plugins
|
||||||
func (b *Bot) selfSaid(channel, message string, action bool) {
|
func (b *bot) selfSaid(channel, message string, action bool) {
|
||||||
msg := Message{
|
msg := Message{
|
||||||
User: &b.Me, // hack
|
User: &b.me, // hack
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
Body: message,
|
Body: message,
|
||||||
Raw: message, // hack
|
Raw: message, // hack
|
||||||
|
@ -205,8 +205,8 @@ func (b *Bot) selfSaid(channel, message string, action bool) {
|
||||||
Host: "0.0.0.0", // hack
|
Host: "0.0.0.0", // hack
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range b.PluginOrdering {
|
for _, name := range b.pluginOrdering {
|
||||||
p := b.Plugins[name]
|
p := b.plugins[name]
|
||||||
if p.BotMessage(msg) {
|
if p.BotMessage(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,25 @@
|
||||||
|
|
||||||
package bot
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/velour/catbase/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bot interface {
|
||||||
|
Config() *config.Config
|
||||||
|
DBVersion() int64
|
||||||
|
DB() *sqlx.DB
|
||||||
|
Who(string) []User
|
||||||
|
AddHandler(string, Handler)
|
||||||
|
SendMessage(string, string)
|
||||||
|
SendAction(string, string)
|
||||||
|
MsgReceived(Message)
|
||||||
|
EventReceived(Message)
|
||||||
|
Filter(Message, string) string
|
||||||
|
LastMessage(string) (Message, error)
|
||||||
|
}
|
||||||
|
|
||||||
type Connector interface {
|
type Connector interface {
|
||||||
RegisterEventReceived(func(message Message))
|
RegisterEventReceived(func(message Message))
|
||||||
RegisterMessageReceived(func(message Message))
|
RegisterMessageReceived(func(message Message))
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
// © 2016 the CatBase Authors under the WTFPL license. See AUTHORS for the list of authors.
|
||||||
|
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/velour/catbase/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockBot struct {
|
||||||
|
mock.Mock
|
||||||
|
db *sqlx.DB
|
||||||
|
|
||||||
|
Messages []string
|
||||||
|
Actions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mb *MockBot) Config() *config.Config { return &config.Config{} }
|
||||||
|
func (mb *MockBot) DBVersion() int64 { return 1 }
|
||||||
|
func (mb *MockBot) DB() *sqlx.DB { return mb.db }
|
||||||
|
func (mb *MockBot) Who(string) []User { return []User{} }
|
||||||
|
func (mb *MockBot) AddHandler(name string, f Handler) {}
|
||||||
|
func (mb *MockBot) SendMessage(ch string, msg string) {
|
||||||
|
mb.Messages = append(mb.Messages, msg)
|
||||||
|
}
|
||||||
|
func (mb *MockBot) SendAction(ch string, msg string) {
|
||||||
|
mb.Actions = append(mb.Actions, msg)
|
||||||
|
}
|
||||||
|
func (mb *MockBot) MsgReceived(msg Message) {}
|
||||||
|
func (mb *MockBot) EventReceived(msg Message) {}
|
||||||
|
func (mb *MockBot) Filter(msg Message, s string) string { return "" }
|
||||||
|
func (mb *MockBot) LastMessage(ch string) (Message, error) { return Message{}, nil }
|
||||||
|
|
||||||
|
func NewMockBot() *MockBot {
|
||||||
|
db, err := sqlx.Open("sqlite3_custom", ":memory:")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to open database:", err)
|
||||||
|
}
|
||||||
|
b := MockBot{
|
||||||
|
db: db,
|
||||||
|
Messages: make([]string, 0),
|
||||||
|
Actions: make([]string, 0),
|
||||||
|
}
|
||||||
|
return &b
|
||||||
|
}
|
10
bot/users.go
10
bot/users.go
|
@ -16,12 +16,12 @@ type User struct {
|
||||||
|
|
||||||
Admin bool
|
Admin bool
|
||||||
|
|
||||||
//bot *Bot
|
//bot *bot
|
||||||
}
|
}
|
||||||
|
|
||||||
var users = map[string]*User{}
|
var users = map[string]*User{}
|
||||||
|
|
||||||
func (b *Bot) GetUser(nick string) *User {
|
func (b *bot) GetUser(nick string) *User {
|
||||||
if _, ok := users[nick]; !ok {
|
if _, ok := users[nick]; !ok {
|
||||||
users[nick] = &User{
|
users[nick] = &User{
|
||||||
Name: nick,
|
Name: nick,
|
||||||
|
@ -31,15 +31,15 @@ func (b *Bot) GetUser(nick string) *User {
|
||||||
return users[nick]
|
return users[nick]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) NewUser(nick string) *User {
|
func (b *bot) NewUser(nick string) *User {
|
||||||
return &User{
|
return &User{
|
||||||
Name: nick,
|
Name: nick,
|
||||||
Admin: b.checkAdmin(nick),
|
Admin: b.checkAdmin(nick),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) checkAdmin(nick string) bool {
|
func (b *bot) checkAdmin(nick string) bool {
|
||||||
for _, u := range b.Config.Admins {
|
for _, u := range b.Config().Admins {
|
||||||
if nick == u {
|
if nick == u {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
20
main.go
20
main.go
|
@ -9,10 +9,8 @@ import (
|
||||||
"github.com/velour/catbase/bot"
|
"github.com/velour/catbase/bot"
|
||||||
"github.com/velour/catbase/config"
|
"github.com/velour/catbase/config"
|
||||||
"github.com/velour/catbase/irc"
|
"github.com/velour/catbase/irc"
|
||||||
"github.com/velour/catbase/plugins"
|
|
||||||
"github.com/velour/catbase/plugins/admin"
|
"github.com/velour/catbase/plugins/admin"
|
||||||
"github.com/velour/catbase/plugins/beers"
|
"github.com/velour/catbase/plugins/beers"
|
||||||
"github.com/velour/catbase/plugins/counter"
|
|
||||||
"github.com/velour/catbase/plugins/dice"
|
"github.com/velour/catbase/plugins/dice"
|
||||||
"github.com/velour/catbase/plugins/downtime"
|
"github.com/velour/catbase/plugins/downtime"
|
||||||
"github.com/velour/catbase/plugins/fact"
|
"github.com/velour/catbase/plugins/fact"
|
||||||
|
@ -39,22 +37,20 @@ func main() {
|
||||||
log.Fatalf("Unknown connection type: %s", c.Type)
|
log.Fatalf("Unknown connection type: %s", c.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
b := bot.NewBot(c, client)
|
b := bot.New(c, client)
|
||||||
|
|
||||||
// b.AddHandler(plugins.NewTestPlugin(b))
|
// b.AddHandler(plugins.NewTestPlugin(b))
|
||||||
b.AddHandler("admin", admin.NewAdminPlugin(b))
|
b.AddHandler("admin", admin.New(b))
|
||||||
// b.AddHandler("first", plugins.NewFirstPlugin(b))
|
// b.AddHandler("first", plugins.NewFirstPlugin(b))
|
||||||
b.AddHandler("leftpad", leftpad.New(b))
|
b.AddHandler("leftpad", leftpad.New(b))
|
||||||
b.AddHandler("downtime", downtime.NewDowntimePlugin(b))
|
b.AddHandler("downtime", downtime.New(b))
|
||||||
b.AddHandler("talker", talker.New(b))
|
b.AddHandler("talker", talker.New(b))
|
||||||
b.AddHandler("dice", dice.NewDicePlugin(b))
|
b.AddHandler("dice", dice.New(b))
|
||||||
b.AddHandler("beers", beers.NewBeersPlugin(b))
|
b.AddHandler("beers", beers.New(b))
|
||||||
b.AddHandler("counter", counter.NewCounterPlugin(b))
|
b.AddHandler("remember", fact.NewRemember(b))
|
||||||
b.AddHandler("remember", fact.NewRememberPlugin(b))
|
b.AddHandler("your", your.New(b))
|
||||||
b.AddHandler("skeleton", plugins.NewSkeletonPlugin(b))
|
|
||||||
b.AddHandler("your", your.NewYourPlugin(b))
|
|
||||||
// catches anything left, will always return true
|
// catches anything left, will always return true
|
||||||
b.AddHandler("factoid", fact.NewFactoidPlugin(b))
|
b.AddHandler("factoid", fact.New(b))
|
||||||
|
|
||||||
client.Serve()
|
client.Serve()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,15 @@ import (
|
||||||
// This is a admin plugin to serve as an example and quick copy/paste for new plugins.
|
// This is a admin plugin to serve as an example and quick copy/paste for new plugins.
|
||||||
|
|
||||||
type AdminPlugin struct {
|
type AdminPlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminPlugin creates a new AdminPlugin with the Plugin interface
|
// NewAdminPlugin creates a new AdminPlugin with the Plugin interface
|
||||||
func NewAdminPlugin(bot *bot.Bot) *AdminPlugin {
|
func New(bot bot.Bot) *AdminPlugin {
|
||||||
p := &AdminPlugin{
|
p := &AdminPlugin{
|
||||||
Bot: bot,
|
Bot: bot,
|
||||||
DB: bot.DB,
|
DB: bot.DB(),
|
||||||
}
|
}
|
||||||
p.LoadData()
|
p.LoadData()
|
||||||
return p
|
return p
|
||||||
|
|
|
@ -22,7 +22,7 @@ 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 BeersPlugin struct {
|
type BeersPlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,9 +35,9 @@ type untappdUser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBeersPlugin creates a new BeersPlugin with the Plugin interface
|
// NewBeersPlugin creates a new BeersPlugin with the Plugin interface
|
||||||
func NewBeersPlugin(bot *bot.Bot) *BeersPlugin {
|
func New(bot bot.Bot) *BeersPlugin {
|
||||||
if bot.DBVersion == 1 {
|
if bot.DBVersion() == 1 {
|
||||||
if _, err := bot.DB.Exec(`create table if not exists untappd (
|
if _, err := bot.DB().Exec(`create table if not exists untappd (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
untappdUser string,
|
untappdUser string,
|
||||||
channel string,
|
channel string,
|
||||||
|
@ -49,10 +49,10 @@ func NewBeersPlugin(bot *bot.Bot) *BeersPlugin {
|
||||||
}
|
}
|
||||||
p := BeersPlugin{
|
p := BeersPlugin{
|
||||||
Bot: bot,
|
Bot: bot,
|
||||||
db: bot.DB,
|
db: bot.DB(),
|
||||||
}
|
}
|
||||||
p.LoadData()
|
p.LoadData()
|
||||||
for _, channel := range bot.Config.Untappd.Channels {
|
for _, channel := range bot.Config().Untappd.Channels {
|
||||||
go p.untappdLoop(channel)
|
go p.untappdLoop(channel)
|
||||||
}
|
}
|
||||||
return &p
|
return &p
|
||||||
|
@ -313,7 +313,7 @@ type Beers struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BeersPlugin) pullUntappd() ([]checkin, error) {
|
func (p *BeersPlugin) pullUntappd() ([]checkin, error) {
|
||||||
access_token := "?access_token=" + p.Bot.Config.Untappd.Token
|
access_token := "?access_token=" + p.Bot.Config().Untappd.Token
|
||||||
baseUrl := "https://api.untappd.com/v4/checkin/recent/"
|
baseUrl := "https://api.untappd.com/v4/checkin/recent/"
|
||||||
|
|
||||||
url := baseUrl + access_token + "&limit=25"
|
url := baseUrl + access_token + "&limit=25"
|
||||||
|
@ -343,7 +343,7 @@ func (p *BeersPlugin) pullUntappd() ([]checkin, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BeersPlugin) checkUntappd(channel string) {
|
func (p *BeersPlugin) checkUntappd(channel string) {
|
||||||
token := p.Bot.Config.Untappd.Token
|
token := p.Bot.Config().Untappd.Token
|
||||||
if token == "" || token == "<Your Token>" {
|
if token == "" || token == "<Your Token>" {
|
||||||
log.Println("No Untappd token, cannot enable plugin.")
|
log.Println("No Untappd token, cannot enable plugin.")
|
||||||
return
|
return
|
||||||
|
@ -418,7 +418,7 @@ func (p *BeersPlugin) checkUntappd(channel string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BeersPlugin) untappdLoop(channel string) {
|
func (p *BeersPlugin) untappdLoop(channel string) {
|
||||||
frequency := p.Bot.Config.Untappd.Freq
|
frequency := p.Bot.Config().Untappd.Freq
|
||||||
|
|
||||||
log.Println("Checking every ", frequency, " seconds")
|
log.Println("Checking every ", frequency, " seconds")
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
// This is a counter plugin to count arbitrary things.
|
// This is a counter plugin to count arbitrary things.
|
||||||
|
|
||||||
type CounterPlugin struct {
|
type CounterPlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,9 +102,9 @@ func (i *Item) Delete() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCounterPlugin creates a new CounterPlugin with the Plugin interface
|
// NewCounterPlugin creates a new CounterPlugin with the Plugin interface
|
||||||
func NewCounterPlugin(bot *bot.Bot) *CounterPlugin {
|
func New(bot bot.Bot) *CounterPlugin {
|
||||||
if bot.DBVersion == 1 {
|
if bot.DBVersion() == 1 {
|
||||||
if _, err := bot.DB.Exec(`create table if not exists counter (
|
if _, err := bot.DB().Exec(`create table if not exists counter (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
nick string,
|
nick string,
|
||||||
item string,
|
item string,
|
||||||
|
@ -115,7 +115,7 @@ func NewCounterPlugin(bot *bot.Bot) *CounterPlugin {
|
||||||
}
|
}
|
||||||
return &CounterPlugin{
|
return &CounterPlugin{
|
||||||
Bot: bot,
|
Bot: bot,
|
||||||
DB: bot.DB,
|
DB: bot.DB(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,13 +271,6 @@ func (p *CounterPlugin) Message(message bot.Message) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadData imports any configuration data into the plugin. This is not
|
|
||||||
// strictly necessary other than the fact that the Plugin interface demands it
|
|
||||||
// exist. This may be deprecated at a later date.
|
|
||||||
func (p *CounterPlugin) LoadData() {
|
|
||||||
// This bot has no data to load
|
|
||||||
}
|
|
||||||
|
|
||||||
// Help responds to help requests. Every plugin must implement a help function.
|
// Help responds to help requests. Every plugin must implement a help function.
|
||||||
func (p *CounterPlugin) Help(channel string, parts []string) {
|
func (p *CounterPlugin) Help(channel string, parts []string) {
|
||||||
p.Bot.SendMessage(channel, "You can set counters incrementally by using "+
|
p.Bot.SendMessage(channel, "You can set counters incrementally by using "+
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
// © 2016 the CatBase Authors under the WTFPL license. See AUTHORS for the list of authors.
|
||||||
|
|
||||||
|
package counter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/velour/catbase/bot"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeMessage(payload string) bot.Message {
|
||||||
|
isCmd := strings.HasPrefix(payload, "!")
|
||||||
|
if isCmd {
|
||||||
|
payload = payload[1:]
|
||||||
|
}
|
||||||
|
return bot.Message{
|
||||||
|
User: &bot.User{Name: "tester"},
|
||||||
|
Channel: "test",
|
||||||
|
Body: payload,
|
||||||
|
Command: isCmd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterOne(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
assert.Len(t, mb.Messages, 1)
|
||||||
|
assert.Equal(t, mb.Messages[0], "tester has 1 test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterFour(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
}
|
||||||
|
assert.Len(t, mb.Messages, 4)
|
||||||
|
assert.Equal(t, mb.Messages[3], "tester has 4 test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterDecrement(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1))
|
||||||
|
}
|
||||||
|
c.Message(makeMessage("test--"))
|
||||||
|
assert.Len(t, mb.Messages, 5)
|
||||||
|
assert.Equal(t, mb.Messages[4], "tester has 3 test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFriendCounterDecrement(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("other.test++"))
|
||||||
|
assert.Equal(t, mb.Messages[i], fmt.Sprintf("other has %d test.", i+1))
|
||||||
|
}
|
||||||
|
c.Message(makeMessage("other.test--"))
|
||||||
|
assert.Len(t, mb.Messages, 5)
|
||||||
|
assert.Equal(t, mb.Messages[4], "other has 3 test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecrementZero(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1))
|
||||||
|
}
|
||||||
|
j := 4
|
||||||
|
for i := 4; i > 0; i-- {
|
||||||
|
c.Message(makeMessage("test--"))
|
||||||
|
assert.Equal(t, mb.Messages[j], fmt.Sprintf("tester has %d test.", i-1))
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
assert.Len(t, mb.Messages, 8)
|
||||||
|
assert.Equal(t, mb.Messages[7], "tester has 0 test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClear(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1))
|
||||||
|
}
|
||||||
|
res := c.Message(makeMessage("!clear test"))
|
||||||
|
assert.True(t, res)
|
||||||
|
assert.Len(t, mb.Actions, 1)
|
||||||
|
assert.Equal(t, mb.Actions[0], "chops a few test out of his brain")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCount(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1))
|
||||||
|
}
|
||||||
|
res := c.Message(makeMessage("!count test"))
|
||||||
|
assert.True(t, res)
|
||||||
|
assert.Len(t, mb.Messages, 5)
|
||||||
|
assert.Equal(t, mb.Messages[4], "tester has 4 test.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInspectMe(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c.Message(makeMessage("test++"))
|
||||||
|
assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1))
|
||||||
|
}
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
c.Message(makeMessage("fucks++"))
|
||||||
|
assert.Equal(t, mb.Messages[i+4], fmt.Sprintf("tester has %d fucks.", i+1))
|
||||||
|
}
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
c.Message(makeMessage("cheese++"))
|
||||||
|
assert.Equal(t, mb.Messages[i+6], fmt.Sprintf("tester has %d cheese.", i+1))
|
||||||
|
}
|
||||||
|
res := c.Message(makeMessage("!inspect me"))
|
||||||
|
assert.True(t, res)
|
||||||
|
assert.Len(t, mb.Messages, 27)
|
||||||
|
assert.Equal(t, mb.Messages[26], "tester has the following counters: test: 4, fucks: 2, cheese: 20.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelp(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
c.Help("channel", []string{})
|
||||||
|
assert.Len(t, mb.Messages, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBotMessage(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
assert.False(t, c.BotMessage(makeMessage("test")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvent(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
assert.False(t, c.Event("dummy", makeMessage("test")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterWeb(t *testing.T) {
|
||||||
|
mb := bot.NewMockBot()
|
||||||
|
c := New(mb)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
assert.Nil(t, c.RegisterWeb())
|
||||||
|
}
|
|
@ -15,11 +15,11 @@ import (
|
||||||
// This is a dice plugin to serve as an example and quick copy/paste for new plugins.
|
// This is a dice plugin to serve as an example and quick copy/paste for new plugins.
|
||||||
|
|
||||||
type DicePlugin struct {
|
type DicePlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDicePlugin creates a new DicePlugin with the Plugin interface
|
// NewDicePlugin creates a new DicePlugin with the Plugin interface
|
||||||
func NewDicePlugin(bot *bot.Bot) *DicePlugin {
|
func New(bot bot.Bot) *DicePlugin {
|
||||||
return &DicePlugin{
|
return &DicePlugin{
|
||||||
Bot: bot,
|
Bot: bot,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
// This is a downtime plugin to monitor how much our users suck
|
// This is a downtime plugin to monitor how much our users suck
|
||||||
|
|
||||||
type DowntimePlugin struct {
|
type DowntimePlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,13 +100,13 @@ func (ie idleEntries) Swap(i, j int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDowntimePlugin creates a new DowntimePlugin with the Plugin interface
|
// NewDowntimePlugin creates a new DowntimePlugin with the Plugin interface
|
||||||
func NewDowntimePlugin(bot *bot.Bot) *DowntimePlugin {
|
func New(bot bot.Bot) *DowntimePlugin {
|
||||||
p := DowntimePlugin{
|
p := DowntimePlugin{
|
||||||
Bot: bot,
|
Bot: bot,
|
||||||
db: bot.DB,
|
db: bot.DB(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if bot.DBVersion == 1 {
|
if bot.DBVersion() == 1 {
|
||||||
_, err := p.db.Exec(`create table if not exists downtime (
|
_, err := p.db.Exec(`create table if not exists downtime (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
nick string,
|
nick string,
|
||||||
|
@ -160,7 +160,7 @@ func (p *DowntimePlugin) Message(message bot.Message) bool {
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
|
|
||||||
// filter out ZNC entries and ourself
|
// filter out ZNC entries and ourself
|
||||||
if strings.HasPrefix(e.nick, "*") || strings.ToLower(p.Bot.Config.Nick) == e.nick {
|
if strings.HasPrefix(e.nick, "*") || strings.ToLower(p.Bot.Config().Nick) == e.nick {
|
||||||
p.remove(e.nick)
|
p.remove(e.nick)
|
||||||
} else {
|
} else {
|
||||||
tops = fmt.Sprintf("%s%s: %s ", tops, e.nick, time.Now().Sub(e.lastSeen))
|
tops = fmt.Sprintf("%s%s: %s ", tops, e.nick, time.Now().Sub(e.lastSeen))
|
||||||
|
@ -204,7 +204,7 @@ func (p *DowntimePlugin) Help(channel string, parts []string) {
|
||||||
// Empty event handler because this plugin does not do anything on event recv
|
// Empty event handler because this plugin does not do anything on event recv
|
||||||
func (p *DowntimePlugin) Event(kind string, message bot.Message) bool {
|
func (p *DowntimePlugin) Event(kind string, message bot.Message) bool {
|
||||||
log.Println(kind, "\t", message)
|
log.Println(kind, "\t", message)
|
||||||
if kind != "PART" && message.User.Name != p.Bot.Config.Nick {
|
if kind != "PART" && message.User.Name != p.Bot.Config().Nick {
|
||||||
// user joined, let's nail them for it
|
// user joined, let's nail them for it
|
||||||
if kind == "NICK" {
|
if kind == "NICK" {
|
||||||
p.record(strings.ToLower(message.Channel))
|
p.record(strings.ToLower(message.Channel))
|
||||||
|
|
|
@ -196,14 +196,14 @@ func getSingleFact(db *sqlx.DB, fact string) (*factoid, error) {
|
||||||
|
|
||||||
// FactoidPlugin provides the necessary plugin-wide needs
|
// FactoidPlugin provides the necessary plugin-wide needs
|
||||||
type FactoidPlugin struct {
|
type FactoidPlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
NotFound []string
|
NotFound []string
|
||||||
LastFact *factoid
|
LastFact *factoid
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFactoidPlugin creates a new FactoidPlugin with the Plugin interface
|
// NewFactoidPlugin creates a new FactoidPlugin with the Plugin interface
|
||||||
func NewFactoidPlugin(botInst *bot.Bot) *FactoidPlugin {
|
func New(botInst bot.Bot) *FactoidPlugin {
|
||||||
p := &FactoidPlugin{
|
p := &FactoidPlugin{
|
||||||
Bot: botInst,
|
Bot: botInst,
|
||||||
NotFound: []string{
|
NotFound: []string{
|
||||||
|
@ -214,7 +214,7 @@ func NewFactoidPlugin(botInst *bot.Bot) *FactoidPlugin {
|
||||||
"NOPE! NOPE! NOPE!",
|
"NOPE! NOPE! NOPE!",
|
||||||
"One time, I learned how to jump rope.",
|
"One time, I learned how to jump rope.",
|
||||||
},
|
},
|
||||||
db: botInst.DB,
|
db: botInst.DB(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := p.db.Exec(`create table if not exists factoid (
|
_, err := p.db.Exec(`create table if not exists factoid (
|
||||||
|
@ -231,13 +231,13 @@ func NewFactoidPlugin(botInst *bot.Bot) *FactoidPlugin {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, channel := range botInst.Config.Channels {
|
for _, channel := range botInst.Config().Channels {
|
||||||
go p.factTimer(channel)
|
go p.factTimer(channel)
|
||||||
|
|
||||||
go func(ch string) {
|
go func(ch string) {
|
||||||
// Some random time to start up
|
// Some random time to start up
|
||||||
time.Sleep(time.Duration(15) * time.Second)
|
time.Sleep(time.Duration(15) * time.Second)
|
||||||
if ok, fact := p.findTrigger(p.Bot.Config.StartupFact); ok {
|
if ok, fact := p.findTrigger(p.Bot.Config().StartupFact); ok {
|
||||||
p.sayFact(bot.Message{
|
p.sayFact(bot.Message{
|
||||||
Channel: ch,
|
Channel: ch,
|
||||||
Body: "speed test", // BUG: This is defined in the config too
|
Body: "speed test", // BUG: This is defined in the config too
|
||||||
|
@ -596,7 +596,7 @@ func (p *FactoidPlugin) randomFact() *factoid {
|
||||||
|
|
||||||
// factTimer spits out a fact at a given interval and with given probability
|
// factTimer spits out a fact at a given interval and with given probability
|
||||||
func (p *FactoidPlugin) factTimer(channel string) {
|
func (p *FactoidPlugin) factTimer(channel string) {
|
||||||
duration := time.Duration(p.Bot.Config.QuoteTime) * time.Minute
|
duration := time.Duration(p.Bot.Config().QuoteTime) * time.Minute
|
||||||
myLastMsg := time.Now()
|
myLastMsg := time.Now()
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Duration(5) * time.Second)
|
time.Sleep(time.Duration(5) * time.Second)
|
||||||
|
@ -609,7 +609,7 @@ func (p *FactoidPlugin) factTimer(channel string) {
|
||||||
tdelta := time.Since(lastmsg.Time)
|
tdelta := time.Since(lastmsg.Time)
|
||||||
earlier := time.Since(myLastMsg) > tdelta
|
earlier := time.Since(myLastMsg) > tdelta
|
||||||
chance := rand.Float64()
|
chance := rand.Float64()
|
||||||
success := chance < p.Bot.Config.QuoteChance
|
success := chance < p.Bot.Config().QuoteChance
|
||||||
|
|
||||||
if success && tdelta > duration && earlier {
|
if success && tdelta > duration && earlier {
|
||||||
fact := p.randomFact()
|
fact := p.randomFact()
|
||||||
|
@ -617,9 +617,11 @@ func (p *FactoidPlugin) factTimer(channel string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
users := p.Bot.Who(channel)
|
||||||
|
|
||||||
// we need to fabricate a message so that bot.Filter can operate
|
// we need to fabricate a message so that bot.Filter can operate
|
||||||
message := bot.Message{
|
message := bot.Message{
|
||||||
User: &p.Bot.Users[rand.Intn(len(p.Bot.Users))],
|
User: &users[rand.Intn(len(users))],
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
}
|
}
|
||||||
p.sayFact(message, *fact)
|
p.sayFact(message, *fact)
|
||||||
|
|
|
@ -17,17 +17,17 @@ import (
|
||||||
// plugins.
|
// plugins.
|
||||||
|
|
||||||
type RememberPlugin struct {
|
type RememberPlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
Log map[string][]bot.Message
|
Log map[string][]bot.Message
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRememberPlugin creates a new RememberPlugin with the Plugin interface
|
// NewRememberPlugin creates a new RememberPlugin with the Plugin interface
|
||||||
func NewRememberPlugin(b *bot.Bot) *RememberPlugin {
|
func NewRemember(b bot.Bot) *RememberPlugin {
|
||||||
p := RememberPlugin{
|
p := RememberPlugin{
|
||||||
Bot: b,
|
Bot: b,
|
||||||
Log: make(map[string][]bot.Message),
|
Log: make(map[string][]bot.Message),
|
||||||
db: b.DB,
|
db: b.DB(),
|
||||||
}
|
}
|
||||||
return &p
|
return &p
|
||||||
}
|
}
|
||||||
|
@ -167,8 +167,8 @@ func (p *RememberPlugin) quoteTimer(channel string) {
|
||||||
for {
|
for {
|
||||||
// this pisses me off: You can't multiply int * time.Duration so it
|
// this pisses me off: You can't multiply int * time.Duration so it
|
||||||
// has to look ugly as shit.
|
// has to look ugly as shit.
|
||||||
time.Sleep(time.Duration(p.Bot.Config.QuoteTime) * time.Minute)
|
time.Sleep(time.Duration(p.Bot.Config().QuoteTime) * time.Minute)
|
||||||
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()
|
||||||
p.Bot.SendMessage(channel, msg)
|
p.Bot.SendMessage(channel, msg)
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
|
|
||||||
type FirstPlugin struct {
|
type FirstPlugin struct {
|
||||||
First *FirstEntry
|
First *FirstEntry
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ func (fe *FirstEntry) save(db *sqlx.DB) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFirstPlugin creates a new FirstPlugin with the Plugin interface
|
// NewFirstPlugin creates a new FirstPlugin with the Plugin interface
|
||||||
func NewFirstPlugin(b *bot.Bot) *FirstPlugin {
|
func New(b bot.Bot) *FirstPlugin {
|
||||||
if b.DBVersion == 1 {
|
if b.DBVersion() == 1 {
|
||||||
_, err := b.DB.Exec(`create table if not exists first (
|
_, err := b.DB().Exec(`create table if not exists first (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
day integer,
|
day integer,
|
||||||
time integer,
|
time integer,
|
||||||
|
@ -62,14 +62,14 @@ func NewFirstPlugin(b *bot.Bot) *FirstPlugin {
|
||||||
|
|
||||||
log.Println("First plugin initialized with day:", midnight(time.Now()))
|
log.Println("First plugin initialized with day:", midnight(time.Now()))
|
||||||
|
|
||||||
first, err := getLastFirst(b.DB)
|
first, err := getLastFirst(b.DB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not initialize first plugin: ", err)
|
log.Fatal("Could not initialize first plugin: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &FirstPlugin{
|
return &FirstPlugin{
|
||||||
Bot: b,
|
Bot: b,
|
||||||
db: b.DB,
|
db: b.DB(),
|
||||||
First: first,
|
First: first,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ func (p *FirstPlugin) Message(message bot.Message) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *FirstPlugin) allowed(message bot.Message) bool {
|
func (p *FirstPlugin) allowed(message bot.Message) bool {
|
||||||
for _, msg := range p.Bot.Config.Bad.Msgs {
|
for _, msg := range p.Bot.Config().Bad.Msgs {
|
||||||
match, err := regexp.MatchString(msg, strings.ToLower(message.Body))
|
match, err := regexp.MatchString(msg, strings.ToLower(message.Body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Bad regexp: ", err)
|
log.Println("Bad regexp: ", err)
|
||||||
|
@ -161,13 +161,13 @@ func (p *FirstPlugin) allowed(message bot.Message) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, host := range p.Bot.Config.Bad.Hosts {
|
for _, host := range p.Bot.Config().Bad.Hosts {
|
||||||
if host == message.Host {
|
if host == message.Host {
|
||||||
log.Println("Disallowing first: ", message.User.Name, ":", message.Body)
|
log.Println("Disallowing first: ", message.User.Name, ":", message.Body)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, nick := range p.Bot.Config.Bad.Nicks {
|
for _, nick := range p.Bot.Config().Bad.Nicks {
|
||||||
if nick == message.User.Name {
|
if nick == message.User.Name {
|
||||||
log.Println("Disallowing first: ", message.User.Name, ":", message.Body)
|
log.Println("Disallowing first: ", message.User.Name, ":", message.Body)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -15,11 +15,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type LeftpadPlugin struct {
|
type LeftpadPlugin struct {
|
||||||
bot *bot.Bot
|
bot bot.Bot
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new LeftpadPlugin with the Plugin interface
|
// New creates a new LeftpadPlugin with the Plugin interface
|
||||||
func New(bot *bot.Bot) *LeftpadPlugin {
|
func New(bot bot.Bot) *LeftpadPlugin {
|
||||||
p := LeftpadPlugin{
|
p := LeftpadPlugin{
|
||||||
bot: bot,
|
bot: bot,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
import "github.com/velour/catbase/bot"
|
import "github.com/velour/catbase/bot"
|
||||||
|
|
||||||
// Plugin interface defines the methods needed to accept a plugin
|
// Plugin interface defines the methods needed to accept a plugin
|
||||||
|
@ -14,96 +13,3 @@ type Plugin interface {
|
||||||
Help()
|
Help()
|
||||||
RegisterWeb()
|
RegisterWeb()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Below are some example plugins
|
|
||||||
|
|
||||||
// Creates a new TestPlugin with the Plugin interface
|
|
||||||
func NewTestPlugin(bot *bot.Bot) *TestPlugin {
|
|
||||||
tp := TestPlugin{}
|
|
||||||
tp.LoadData()
|
|
||||||
tp.Bot = bot
|
|
||||||
return &tp
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestPlugin type allows our plugin to store persistent state information
|
|
||||||
type TestPlugin struct {
|
|
||||||
Bot *bot.Bot
|
|
||||||
Responds []string
|
|
||||||
Name string
|
|
||||||
Feces string
|
|
||||||
helpmsg []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *TestPlugin) LoadData() {
|
|
||||||
config := GetPluginConfig("TestPlugin")
|
|
||||||
p.Name = config.Name
|
|
||||||
p.Feces = config.Values["Feces"].(string)
|
|
||||||
p.helpmsg = []string{
|
|
||||||
"TestPlugin just shows off how shit works.",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *TestPlugin) Message(message bot.Message) bool {
|
|
||||||
user := message.User
|
|
||||||
channel := message.Channel
|
|
||||||
body := message.Body
|
|
||||||
|
|
||||||
fmt.Println(user, body)
|
|
||||||
fmt.Println("My plugin name is:", p.Name, " My feces are:", p.Feces)
|
|
||||||
p.Bot.SendMessage(channel, body)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *TestPlugin) Help(message bot.Message) {
|
|
||||||
for _, msg := range p.helpmsg {
|
|
||||||
p.Bot.SendMessage(message.Channel, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty event handler because this plugin does not do anything on event recv
|
|
||||||
func (p *TestPlugin) Event(kind string, message bot.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler for bot's own messages
|
|
||||||
func (p *TestPlugin) BotMessage(message bot.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginConfig struct {
|
|
||||||
Name string
|
|
||||||
Values map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loads plugin config (could be out of a DB or something)
|
|
||||||
func GetPluginConfig(name string) PluginConfig {
|
|
||||||
return PluginConfig{
|
|
||||||
Name: "TestPlugin",
|
|
||||||
Values: map[string]interface{}{
|
|
||||||
"Feces": "test",
|
|
||||||
"Responds": "fucker",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FalsePlugin shows how plugin fallthrough works for handling messages
|
|
||||||
type FalsePlugin struct{}
|
|
||||||
|
|
||||||
func (fp FalsePlugin) Message(user, message string) bool {
|
|
||||||
fmt.Println("FalsePlugin returning false.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fp FalsePlugin) LoadData() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty event handler because this plugin does not do anything on event recv
|
|
||||||
func (p *FalsePlugin) Event(kind string, message bot.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler for bot's own messages
|
|
||||||
func (p *FalsePlugin) BotMessage(message bot.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
|
|
||||||
|
|
||||||
package plugins
|
|
||||||
|
|
||||||
import "github.com/velour/catbase/bot"
|
|
||||||
|
|
||||||
// This is a skeleton plugin to serve as an example and quick copy/paste for new plugins.
|
|
||||||
|
|
||||||
type SkeletonPlugin struct {
|
|
||||||
Bot *bot.Bot
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSkeletonPlugin creates a new SkeletonPlugin with the Plugin interface
|
|
||||||
func NewSkeletonPlugin(bot *bot.Bot) *SkeletonPlugin {
|
|
||||||
return &SkeletonPlugin{
|
|
||||||
Bot: bot,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 *SkeletonPlugin) Message(message bot.Message) bool {
|
|
||||||
// This bot does not reply to anything
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadData imports any configuration data into the plugin. This is not strictly necessary other
|
|
||||||
// than the fact that the Plugin interface demands it exist. This may be deprecated at a later
|
|
||||||
// date.
|
|
||||||
func (p *SkeletonPlugin) LoadData() {
|
|
||||||
// This bot has no data to load
|
|
||||||
}
|
|
||||||
|
|
||||||
// Help responds to help requests. Every plugin must implement a help function.
|
|
||||||
func (p *SkeletonPlugin) Help(channel string, parts []string) {
|
|
||||||
p.Bot.SendMessage(channel, "Sorry, Skeleton does not do a goddamn thing.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty event handler because this plugin does not do anything on event recv
|
|
||||||
func (p *SkeletonPlugin) Event(kind string, message bot.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler for bot's own messages
|
|
||||||
func (p *SkeletonPlugin) BotMessage(message bot.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register any web URLs desired
|
|
||||||
func (p *SkeletonPlugin) RegisterWeb() *string {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -40,14 +40,14 @@ var goatse []string = []string{
|
||||||
}
|
}
|
||||||
|
|
||||||
type TalkerPlugin struct {
|
type TalkerPlugin struct {
|
||||||
Bot *bot.Bot
|
Bot bot.Bot
|
||||||
enforceNicks bool
|
enforceNicks bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(bot *bot.Bot) *TalkerPlugin {
|
func New(bot bot.Bot) *TalkerPlugin {
|
||||||
return &TalkerPlugin{
|
return &TalkerPlugin{
|
||||||
Bot: bot,
|
Bot: bot,
|
||||||
enforceNicks: bot.Config.EnforceNicks,
|
enforceNicks: bot.Config().EnforceNicks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +105,11 @@ func (p *TalkerPlugin) Help(channel string, parts []string) {
|
||||||
|
|
||||||
// Empty event handler because this plugin does not do anything on event recv
|
// Empty event handler because this plugin does not do anything on event recv
|
||||||
func (p *TalkerPlugin) Event(kind string, message bot.Message) bool {
|
func (p *TalkerPlugin) Event(kind string, message bot.Message) bool {
|
||||||
sayings := p.Bot.Config.WelcomeMsgs
|
sayings := p.Bot.Config().WelcomeMsgs
|
||||||
if len(sayings) == 0 {
|
if len(sayings) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if kind == "JOIN" && strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config.Nick) {
|
if kind == "JOIN" && strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Nick) {
|
||||||
msg := fmt.Sprintf(sayings[rand.Intn(len(sayings))], message.User.Name)
|
msg := fmt.Sprintf(sayings[rand.Intn(len(sayings))], message.User.Name)
|
||||||
p.Bot.SendMessage(message.Channel, msg)
|
p.Bot.SendMessage(message.Channel, msg)
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -12,11 +12,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type YourPlugin struct {
|
type YourPlugin struct {
|
||||||
bot *bot.Bot
|
bot bot.Bot
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewYourPlugin creates a new YourPlugin with the Plugin interface
|
// NewYourPlugin creates a new YourPlugin with the Plugin interface
|
||||||
func NewYourPlugin(bot *bot.Bot) *YourPlugin {
|
func New(bot bot.Bot) *YourPlugin {
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
return &YourPlugin{
|
return &YourPlugin{
|
||||||
bot: bot,
|
bot: bot,
|
||||||
|
@ -28,7 +28,7 @@ func NewYourPlugin(bot *bot.Bot) *YourPlugin {
|
||||||
// Otherwise, the function returns false and the bot continues execution of other plugins.
|
// Otherwise, the function returns false and the bot continues execution of other plugins.
|
||||||
func (p *YourPlugin) Message(message bot.Message) bool {
|
func (p *YourPlugin) Message(message bot.Message) bool {
|
||||||
lower := strings.ToLower(message.Body)
|
lower := strings.ToLower(message.Body)
|
||||||
config := p.bot.Config.Your
|
config := p.bot.Config().Your
|
||||||
if len(message.Body) > config.MaxLength {
|
if len(message.Body) > config.MaxLength {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue