bot: add ability to disable plugins per channel

admin: use 'enable/disable plugin <name>'
This commit is contained in:
Chris Sexton 2020-06-09 13:48:56 -04:00 committed by Chris Sexton
parent c68ca70964
commit 12ca34006f
5 changed files with 124 additions and 12 deletions

View File

@ -26,6 +26,9 @@ type bot struct {
plugins map[string]Plugin
pluginOrdering []string
// channel -> plugin
pluginBlacklist map[string]bool
// Users holds information about all of our friends
users []user.User
// Represents the bot
@ -77,21 +80,24 @@ func New(config *config.Config, connector Connector) Bot {
}
bot := &bot{
config: config,
plugins: make(map[string]Plugin),
pluginOrdering: make([]string, 0),
conn: connector,
users: users,
me: users[0],
logIn: logIn,
logOut: logOut,
httpEndPoints: make([]EndPoint, 0),
filters: make(map[string]func(string) string),
callbacks: make(CallbackMap),
config: config,
plugins: make(map[string]Plugin),
pluginOrdering: make([]string, 0),
pluginBlacklist: make(map[string]bool),
conn: connector,
users: users,
me: users[0],
logIn: logIn,
logOut: logOut,
httpEndPoints: make([]EndPoint, 0),
filters: make(map[string]func(string) string),
callbacks: make(CallbackMap),
}
bot.migrateDB()
bot.RefreshPluginBlacklist()
http.HandleFunc("/", bot.serveRoot)
connector.RegisterEvent(bot.Receive)
@ -127,6 +133,13 @@ func (b *bot) migrateDB() {
);`); err != nil {
log.Fatal().Err(err).Msgf("Initial DB migration create variables table")
}
if _, err := b.DB().Exec(`create table if not exists pluginBlacklist (
channel string,
name string,
primary key (channel, name)
);`); err != nil {
log.Fatal().Err(err).Msgf("Initial DB migration create variables table")
}
}
// Adds a constructed handler to the bots handlers list
@ -265,3 +278,35 @@ func (b *bot) GetPassword() string {
func (b *bot) SetQuiet(status bool) {
b.quiet = status
}
func (b *bot) RefreshPluginBlacklist() error {
blacklistItems := []struct {
Channel string
Name string
}{}
if err := b.DB().Select(&blacklistItems, `select channel, name from pluginBlacklist`); err != nil {
return fmt.Errorf("%w", err)
}
b.pluginBlacklist = make(map[string]bool)
for _, i := range blacklistItems {
b.pluginBlacklist[i.Channel+i.Name] = true
}
log.Debug().Interface("blacklist", b.pluginBlacklist).Msgf("Refreshed plugin blacklist")
return nil
}
func (b *bot) GetPluginNames() []string {
names := []string{}
for _, name := range b.pluginOrdering {
names = append(names, pluginNameStem(name))
}
return names
}
func (b *bot) onBlacklist(channel, plugin string) bool {
return b.pluginBlacklist[channel+plugin]
}
func pluginNameStem(name string) string {
return strings.Split(strings.TrimPrefix(name, "*"), ".")[0]
}

View File

@ -31,7 +31,11 @@ func (b *bot) Receive(conn Connector, kind Kind, msg msg.Message, args ...interf
goto RET
}
log.Debug().Msgf("checking blacklist %v", b.pluginBlacklist)
for _, name := range b.pluginOrdering {
if b.onBlacklist(msg.Channel, pluginNameStem(name)) {
continue
}
if b.runCallback(conn, b.plugins[name], kind, msg, args...) {
goto RET
}
@ -70,7 +74,7 @@ func (b *bot) checkHelp(conn Connector, channel string, parts []string) {
// just print out a list of help topics
topics := "Help topics: about variables"
for name := range b.plugins {
name = strings.Split(strings.TrimPrefix(name, "*"), ".")[0]
name = pluginNameStem(name)
topics = fmt.Sprintf("%s, %s", topics, name)
}
b.Send(conn, Message, channel, topics)

View File

@ -4,6 +4,7 @@ package bot
import (
"github.com/jmoiron/sqlx"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/bot/user"
"github.com/velour/catbase/config"
@ -69,6 +70,8 @@ type Bot interface {
GetWebNavigation() []EndPoint
GetPassword() string
SetQuiet(bool)
GetPluginNames() []string
RefreshPluginBlacklist() error
}
// Connector represents a server connection to a chat service

View File

@ -112,3 +112,6 @@ func NewMockBot() *MockBot {
http.DefaultServeMux = new(http.ServeMux)
return &b
}
func (mb *MockBot) GetPluginNames() []string { return nil }
func (mb *MockBot) RefreshPluginBlacklist() error { return nil }

View File

@ -8,6 +8,7 @@ import (
"html/template"
"net/http"
"os"
"regexp"
"strings"
"time"
@ -51,6 +52,9 @@ var forbiddenKeys = map[string]bool{
"meme.memes": true,
}
var addBlacklist = regexp.MustCompile(`(?i)disable plugin (.*)`)
var rmBlacklist = regexp.MustCompile(`(?i)enable plugin (.*)`)
// 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.
@ -103,6 +107,30 @@ func (p *AdminPlugin) message(conn bot.Connector, k bot.Kind, message msg.Messag
return true
}
if addBlacklist.MatchString(body) {
submatches := addBlacklist.FindStringSubmatch(message.Body)
plugin := submatches[1]
if err := p.addBlacklist(message.Channel, plugin); err != nil {
p.bot.Send(conn, bot.Message, message.Channel, fmt.Sprintf("I couldn't add that item: %s", err))
log.Error().Err(err).Msgf("error adding blacklist item")
return true
}
p.bot.Send(conn, bot.Message, message.Channel, fmt.Sprintf("%s disabled. Use `!enable plugin %s` to re-enable it.", plugin, plugin))
return true
}
if rmBlacklist.MatchString(body) {
submatches := rmBlacklist.FindStringSubmatch(message.Body)
plugin := submatches[1]
if err := p.rmBlacklist(message.Channel, plugin); err != nil {
p.bot.Send(conn, bot.Message, message.Channel, fmt.Sprintf("I couldn't remove that item: %s", err))
log.Error().Err(err).Msgf("error removing blacklist item")
return true
}
p.bot.Send(conn, bot.Message, message.Channel, fmt.Sprintf("%s enabled. Use `!disable plugin %s` to disable it.", plugin, plugin))
return true
}
if strings.ToLower(body) == "password" {
p.bot.Send(conn, bot.Message, message.Channel, p.bot.GetPassword())
return true
@ -254,3 +282,32 @@ func (p *AdminPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) {
j, _ := json.Marshal(configEntries)
fmt.Fprintf(w, "%s", j)
}
func (p *AdminPlugin) addBlacklist(channel, plugin string) error {
if plugin == "admin" {
return fmt.Errorf("you cannot disable the admin plugin")
}
return p.modBlacklist(`insert or replace into pluginBlacklist values (?, ?)`, channel, plugin)
}
func (p *AdminPlugin) rmBlacklist(channel, plugin string) error {
return p.modBlacklist(`delete from pluginBlacklist where channel=? and name=?`, channel, plugin)
}
func (p *AdminPlugin) modBlacklist(query, channel, plugin string) error {
plugins := p.bot.GetPluginNames()
for _, pp := range plugins {
if pp == plugin {
if _, err := p.db.Exec(query, channel, plugin); err != nil {
return fmt.Errorf("%w", err)
}
err := p.bot.RefreshPluginBlacklist()
if err != nil {
return fmt.Errorf("%w", err)
}
return nil
}
}
err := fmt.Errorf("unknown plugin named '%s'", plugin)
return err
}