2016-01-17 18:00:44 +00:00
|
|
|
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
|
2013-12-10 23:37:07 +00:00
|
|
|
|
2012-08-17 20:38:15 +00:00
|
|
|
package bot
|
|
|
|
|
2012-10-11 20:28:00 +00:00
|
|
|
import (
|
2019-06-13 14:04:06 +00:00
|
|
|
"fmt"
|
2022-08-04 13:17:41 +00:00
|
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
|
|
"github.com/go-chi/httprate"
|
2019-06-13 14:04:06 +00:00
|
|
|
"math/rand"
|
2013-06-01 17:10:15 +00:00
|
|
|
"net/http"
|
2022-07-22 21:04:51 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2019-02-05 20:13:32 +00:00
|
|
|
"reflect"
|
2020-04-21 15:46:29 +00:00
|
|
|
"regexp"
|
2012-10-11 20:28:00 +00:00
|
|
|
"strings"
|
2019-06-13 14:04:06 +00:00
|
|
|
"time"
|
2014-04-20 19:08:24 +00:00
|
|
|
|
2021-07-21 18:52:45 +00:00
|
|
|
"github.com/go-chi/chi/v5"
|
2016-03-19 18:02:46 +00:00
|
|
|
"github.com/jmoiron/sqlx"
|
2019-03-07 16:35:42 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2021-10-05 23:05:27 +00:00
|
|
|
"github.com/velour/catbase/bot/history"
|
2016-04-01 14:20:03 +00:00
|
|
|
"github.com/velour/catbase/bot/msg"
|
|
|
|
"github.com/velour/catbase/bot/msglog"
|
|
|
|
"github.com/velour/catbase/bot/user"
|
2016-01-17 18:00:44 +00:00
|
|
|
"github.com/velour/catbase/config"
|
2021-07-21 13:54:29 +00:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2012-10-11 20:28:00 +00:00
|
|
|
)
|
2012-08-17 20:38:15 +00:00
|
|
|
|
2016-03-30 14:00:20 +00:00
|
|
|
// bot type provides storage for bot-wide information, configs, and database connections
|
|
|
|
type bot struct {
|
2012-08-17 22:09:29 +00:00
|
|
|
// 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
|
2019-02-05 15:54:13 +00:00
|
|
|
plugins map[string]Plugin
|
2016-03-30 14:00:20 +00:00
|
|
|
pluginOrdering []string
|
2012-08-17 22:09:29 +00:00
|
|
|
|
2020-06-09 17:48:56 +00:00
|
|
|
// channel -> plugin
|
|
|
|
pluginBlacklist map[string]bool
|
|
|
|
|
2020-10-09 16:00:10 +00:00
|
|
|
// plugin, this is bot-wide
|
|
|
|
pluginWhitelist map[string]bool
|
|
|
|
|
2012-08-17 22:09:29 +00:00
|
|
|
// Users holds information about all of our friends
|
2016-04-01 14:20:03 +00:00
|
|
|
users []user.User
|
2013-05-08 00:08:18 +00:00
|
|
|
// Represents the bot
|
2016-04-01 14:20:03 +00:00
|
|
|
me user.User
|
2012-08-17 22:09:29 +00:00
|
|
|
|
2016-03-30 14:00:20 +00:00
|
|
|
config *config.Config
|
2012-08-17 22:09:29 +00:00
|
|
|
|
2016-03-30 14:00:20 +00:00
|
|
|
conn Connector
|
2016-03-10 18:37:07 +00:00
|
|
|
|
2016-04-01 14:20:03 +00:00
|
|
|
logIn chan msg.Message
|
|
|
|
logOut chan msg.Messages
|
2012-10-11 20:28:00 +00:00
|
|
|
|
2016-03-30 14:00:20 +00:00
|
|
|
version string
|
2013-06-01 17:10:15 +00:00
|
|
|
|
|
|
|
// The entries to the bot's HTTP interface
|
2019-06-09 04:15:06 +00:00
|
|
|
httpEndPoints []EndPoint
|
2017-09-29 04:58:21 +00:00
|
|
|
|
|
|
|
// filters registered by plugins
|
|
|
|
filters map[string]func(string) string
|
2019-02-05 15:54:13 +00:00
|
|
|
|
2019-02-05 17:25:31 +00:00
|
|
|
callbacks CallbackMap
|
2019-06-13 14:04:06 +00:00
|
|
|
|
|
|
|
password string
|
|
|
|
passwordCreated time.Time
|
2020-04-29 21:45:53 +00:00
|
|
|
|
|
|
|
quiet bool
|
2021-07-21 18:52:45 +00:00
|
|
|
|
|
|
|
router *chi.Mux
|
2021-10-05 23:05:27 +00:00
|
|
|
|
|
|
|
history *history.History
|
2012-08-17 20:38:15 +00:00
|
|
|
}
|
|
|
|
|
2019-06-09 04:15:06 +00:00
|
|
|
type EndPoint struct {
|
2021-01-09 18:46:28 +00:00
|
|
|
Name string `json:"name"`
|
|
|
|
URL string `json:"url"`
|
2019-06-09 04:15:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-05 15:54:13 +00:00
|
|
|
// Variable represents a $var replacement
|
2012-08-26 23:23:51 +00:00
|
|
|
type Variable struct {
|
|
|
|
Variable, Value string
|
|
|
|
}
|
|
|
|
|
2019-02-05 15:54:13 +00:00
|
|
|
// New creates a bot for a given connection and set of handlers.
|
2016-03-30 14:00:20 +00:00
|
|
|
func New(config *config.Config, connector Connector) Bot {
|
2016-04-01 14:20:03 +00:00
|
|
|
logIn := make(chan msg.Message)
|
|
|
|
logOut := make(chan msg.Messages)
|
2012-10-11 20:28:00 +00:00
|
|
|
|
2016-04-01 14:20:03 +00:00
|
|
|
msglog.RunNew(logIn, logOut)
|
2012-10-11 20:28:00 +00:00
|
|
|
|
2021-10-05 23:05:27 +00:00
|
|
|
historySz := config.GetInt("bot.historysz", 100)
|
|
|
|
|
2016-04-01 14:20:03 +00:00
|
|
|
users := []user.User{
|
2019-05-27 23:21:53 +00:00
|
|
|
{
|
2019-01-22 00:16:57 +00:00
|
|
|
Name: config.Get("Nick", "bot"),
|
2013-05-07 23:05:40 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-03-30 14:00:20 +00:00
|
|
|
bot := &bot{
|
2020-06-09 17:48:56 +00:00
|
|
|
config: config,
|
|
|
|
plugins: make(map[string]Plugin),
|
|
|
|
pluginOrdering: make([]string, 0),
|
|
|
|
pluginBlacklist: make(map[string]bool),
|
2020-10-09 16:00:10 +00:00
|
|
|
pluginWhitelist: make(map[string]bool),
|
2020-06-09 17:48:56 +00:00
|
|
|
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),
|
2021-07-21 18:52:45 +00:00
|
|
|
router: chi.NewRouter(),
|
2021-10-05 23:05:27 +00:00
|
|
|
history: history.New(historySz),
|
2012-08-17 20:38:15 +00:00
|
|
|
}
|
2013-06-01 17:10:15 +00:00
|
|
|
|
2016-01-15 06:12:26 +00:00
|
|
|
bot.migrateDB()
|
|
|
|
|
2020-06-09 17:48:56 +00:00
|
|
|
bot.RefreshPluginBlacklist()
|
2020-10-09 16:00:10 +00:00
|
|
|
bot.RefreshPluginWhitelist()
|
2020-06-09 17:48:56 +00:00
|
|
|
|
2021-07-21 18:52:45 +00:00
|
|
|
log.Debug().Msgf("created web router")
|
|
|
|
|
2022-08-04 13:17:41 +00:00
|
|
|
bot.setupHTTP()
|
|
|
|
|
|
|
|
connector.RegisterEvent(bot.Receive)
|
|
|
|
|
|
|
|
return bot
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bot) setupHTTP() {
|
2022-07-22 13:44:42 +00:00
|
|
|
// Make the http logger optional
|
|
|
|
// It has never served a purpose in production and with the emojy page, can make a rather noisy log
|
2022-08-04 13:17:41 +00:00
|
|
|
if b.Config().GetInt("bot.useLogger", 0) == 1 {
|
|
|
|
b.router.Use(middleware.Logger)
|
2022-07-22 13:44:42 +00:00
|
|
|
}
|
2021-07-21 18:52:45 +00:00
|
|
|
|
2022-08-04 13:17:41 +00:00
|
|
|
reqCount := b.Config().GetInt("bot.httprate.requests", 500)
|
|
|
|
reqTime := time.Duration(b.Config().GetInt("bot.httprate.seconds", 5))
|
|
|
|
if reqCount > 0 && reqTime > 0 {
|
|
|
|
b.router.Use(httprate.LimitByIP(reqCount, reqTime*time.Second))
|
|
|
|
}
|
2013-06-01 17:10:15 +00:00
|
|
|
|
2022-08-04 13:17:41 +00:00
|
|
|
b.router.Use(middleware.RequestID)
|
|
|
|
b.router.Use(middleware.Recoverer)
|
|
|
|
b.router.Use(middleware.StripSlashes)
|
2016-03-10 18:37:07 +00:00
|
|
|
|
2022-08-04 13:17:41 +00:00
|
|
|
b.router.HandleFunc("/", b.serveRoot)
|
|
|
|
b.router.HandleFunc("/nav", b.serveNav)
|
2023-08-17 19:43:27 +00:00
|
|
|
b.router.HandleFunc("/navHTML", b.serveNavHTML)
|
|
|
|
b.router.HandleFunc("/navHTML/{currentPage}", b.serveNavHTML)
|
2012-08-17 20:38:15 +00:00
|
|
|
}
|
|
|
|
|
2021-07-21 18:52:45 +00:00
|
|
|
func (b *bot) ListenAndServe() {
|
|
|
|
addr := b.config.Get("HttpAddr", "127.0.0.1:1337")
|
2022-07-22 21:04:51 +00:00
|
|
|
stop := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(stop, os.Interrupt)
|
|
|
|
go func() {
|
|
|
|
log.Debug().Msgf("starting web service at %s", addr)
|
|
|
|
log.Fatal().Err(http.ListenAndServe(addr, b.router)).Msg("bot killed")
|
|
|
|
}()
|
|
|
|
<-stop
|
|
|
|
b.DefaultConnector().Shutdown()
|
2023-01-04 16:16:49 +00:00
|
|
|
b.Receive(b.DefaultConnector(), Shutdown, msg.Message{})
|
2021-07-21 18:52:45 +00:00
|
|
|
}
|
|
|
|
|
2021-07-28 15:32:59 +00:00
|
|
|
func (b *bot) RegisterWeb(r http.Handler, root string) {
|
|
|
|
b.router.Mount(root, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bot) RegisterWebName(r http.Handler, root, name string) {
|
2021-07-21 18:52:45 +00:00
|
|
|
b.httpEndPoints = append(b.httpEndPoints, EndPoint{name, root})
|
|
|
|
b.router.Mount(root, r)
|
|
|
|
}
|
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// DefaultConnector is the main connector used for the bot
|
|
|
|
// If more than one connector is on, some users may not see all messages if this is used.
|
|
|
|
// Usage should be limited to out-of-band communications such as timed messages.
|
2019-05-27 23:21:53 +00:00
|
|
|
func (b *bot) DefaultConnector() Connector {
|
|
|
|
return b.conn
|
|
|
|
}
|
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// WhoAmI returns the bot's current registered name
|
2019-05-27 23:21:53 +00:00
|
|
|
func (b *bot) WhoAmI() string {
|
|
|
|
return b.me.Name
|
|
|
|
}
|
|
|
|
|
2016-03-30 14:00:20 +00:00
|
|
|
// Config gets the configuration that the bot is using
|
|
|
|
func (b *bot) Config() *config.Config {
|
|
|
|
return b.config
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bot) DB() *sqlx.DB {
|
2019-01-20 20:21:26 +00:00
|
|
|
return b.config.DB
|
2016-03-30 14:00:20 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 06:12:26 +00:00
|
|
|
// Create any tables if necessary based on version of DB
|
|
|
|
// 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.
|
2016-03-30 14:00:20 +00:00
|
|
|
func (b *bot) migrateDB() {
|
2019-01-20 20:21:26 +00:00
|
|
|
if _, err := b.DB().Exec(`create table if not exists variables (
|
2016-01-15 06:12:26 +00:00
|
|
|
id integer primary key,
|
|
|
|
name string,
|
|
|
|
value string
|
|
|
|
);`); err != nil {
|
2021-07-21 18:52:45 +00:00
|
|
|
log.Fatal().Err(err).Msgf("Initial db migration create variables table")
|
2016-01-15 06:12:26 +00:00
|
|
|
}
|
2020-06-09 17:48:56 +00:00
|
|
|
if _, err := b.DB().Exec(`create table if not exists pluginBlacklist (
|
|
|
|
channel string,
|
|
|
|
name string,
|
|
|
|
primary key (channel, name)
|
|
|
|
);`); err != nil {
|
2021-07-21 18:52:45 +00:00
|
|
|
log.Fatal().Err(err).Msgf("Initial db migration create blacklist table")
|
2020-10-09 16:00:10 +00:00
|
|
|
}
|
|
|
|
if _, err := b.DB().Exec(`create table if not exists pluginWhitelist (
|
|
|
|
name string primary key
|
|
|
|
);`); err != nil {
|
2021-07-21 18:52:45 +00:00
|
|
|
log.Fatal().Err(err).Msgf("Initial db migration create whitelist table")
|
2020-06-09 17:48:56 +00:00
|
|
|
}
|
2016-01-15 06:12:26 +00:00
|
|
|
}
|
|
|
|
|
2012-08-17 20:38:15 +00:00
|
|
|
// Adds a constructed handler to the bots handlers list
|
2019-02-05 21:10:36 +00:00
|
|
|
func (b *bot) AddPlugin(h Plugin) {
|
|
|
|
name := reflect.TypeOf(h).String()
|
2017-10-31 10:22:36 +00:00
|
|
|
b.plugins[name] = h
|
2016-03-30 14:00:20 +00:00
|
|
|
b.pluginOrdering = append(b.pluginOrdering, name)
|
2012-08-17 21:37:49 +00:00
|
|
|
}
|
2012-08-26 20:15:50 +00:00
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// Who returns users for a channel the bot is in
|
|
|
|
// Check the particular connector for channel values
|
2016-04-01 14:20:03 +00:00
|
|
|
func (b *bot) Who(channel string) []user.User {
|
2016-04-21 15:19:38 +00:00
|
|
|
names := b.conn.Who(channel)
|
|
|
|
users := []user.User{}
|
|
|
|
for _, n := range names {
|
|
|
|
users = append(users, user.New(n))
|
2014-04-21 01:06:42 +00:00
|
|
|
}
|
2016-04-21 15:19:38 +00:00
|
|
|
return users
|
2013-06-02 01:59:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 15:46:29 +00:00
|
|
|
var suffixRegex *regexp.Regexp
|
|
|
|
|
2019-02-05 15:54:13 +00:00
|
|
|
// IsCmd checks if message is a command and returns its curtailed version
|
2016-03-11 02:11:52 +00:00
|
|
|
func IsCmd(c *config.Config, message string) (bool, string) {
|
2019-01-22 00:16:57 +00:00
|
|
|
cmdcs := c.GetArray("CommandChar", []string{"!"})
|
|
|
|
botnick := strings.ToLower(c.Get("Nick", "bot"))
|
2019-01-21 19:24:03 +00:00
|
|
|
if botnick == "" {
|
2019-03-07 16:35:42 +00:00
|
|
|
log.Fatal().
|
|
|
|
Msgf(`You must run catbase -set nick -val <your bot nick>`)
|
2019-01-21 19:24:03 +00:00
|
|
|
}
|
2016-03-11 02:11:52 +00:00
|
|
|
iscmd := false
|
|
|
|
lowerMessage := strings.ToLower(message)
|
|
|
|
|
2016-09-27 16:42:00 +00:00
|
|
|
if strings.HasPrefix(lowerMessage, botnick) &&
|
2016-03-11 02:11:52 +00:00
|
|
|
len(lowerMessage) > len(botnick) &&
|
|
|
|
(lowerMessage[len(botnick)] == ',' || lowerMessage[len(botnick)] == ':') {
|
|
|
|
|
|
|
|
iscmd = true
|
|
|
|
message = message[len(botnick):]
|
|
|
|
|
|
|
|
// trim off the customary addressing punctuation
|
|
|
|
if message[0] == ':' || message[0] == ',' {
|
|
|
|
message = message[1:]
|
|
|
|
}
|
2016-09-27 16:42:00 +00:00
|
|
|
} else {
|
|
|
|
for _, cmdc := range cmdcs {
|
|
|
|
if strings.HasPrefix(lowerMessage, cmdc) && len(cmdc) > 0 {
|
|
|
|
iscmd = true
|
|
|
|
message = message[len(cmdc):]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2016-03-11 02:11:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// trim off any whitespace left on the message
|
|
|
|
message = strings.TrimSpace(message)
|
|
|
|
|
|
|
|
return iscmd, message
|
|
|
|
}
|
2016-04-01 14:20:03 +00:00
|
|
|
|
2022-07-29 11:00:29 +00:00
|
|
|
func (b *bot) CheckAdmin(ID string) bool {
|
2022-05-30 20:53:15 +00:00
|
|
|
admins := b.Config().GetArray("Admins", []string{})
|
2022-07-29 11:00:29 +00:00
|
|
|
log.Info().Interface("admins", admins).Msgf("Checking admin for %s", ID)
|
2022-05-30 20:53:15 +00:00
|
|
|
for _, u := range admins {
|
2022-07-29 11:00:29 +00:00
|
|
|
if ID == u {
|
2022-08-04 01:09:04 +00:00
|
|
|
log.Info().Msgf("%s admin check: passed", u)
|
2016-04-01 14:20:03 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2022-05-30 20:53:15 +00:00
|
|
|
log.Info().Msg("%s admin check: failed")
|
2016-04-01 14:20:03 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-09-29 04:58:21 +00:00
|
|
|
// Register a text filter which every outgoing message is passed through
|
|
|
|
func (b *bot) RegisterFilter(name string, f func(string) string) {
|
|
|
|
b.filters[name] = f
|
|
|
|
}
|
2019-02-05 15:54:13 +00:00
|
|
|
|
2021-02-01 15:45:41 +00:00
|
|
|
// RegisterTable registers multiple regex handlers at a time
|
|
|
|
func (b *bot) RegisterTable(p Plugin, handlers HandlerTable) {
|
|
|
|
for _, h := range handlers {
|
|
|
|
if h.IsCmd {
|
|
|
|
b.RegisterRegexCmd(p, h.Kind, h.Regex, h.Handler)
|
|
|
|
} else {
|
|
|
|
b.RegisterRegex(p, h.Kind, h.Regex, h.Handler)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-31 21:48:53 +00:00
|
|
|
// RegisterRegex does what register does, but with a matcher
|
|
|
|
func (b *bot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, resp ResponseHandler) {
|
2021-04-27 16:36:34 +00:00
|
|
|
t := PluginName(p)
|
2019-02-05 20:13:32 +00:00
|
|
|
if _, ok := b.callbacks[t]; !ok {
|
2021-02-02 02:25:19 +00:00
|
|
|
b.callbacks[t] = make(map[Kind][]HandlerSpec)
|
2019-02-05 18:33:18 +00:00
|
|
|
}
|
2019-02-05 20:13:32 +00:00
|
|
|
if _, ok := b.callbacks[t][kind]; !ok {
|
2021-02-02 02:25:19 +00:00
|
|
|
b.callbacks[t][kind] = []HandlerSpec{}
|
2021-01-31 21:48:53 +00:00
|
|
|
}
|
2021-02-02 02:25:19 +00:00
|
|
|
spec := HandlerSpec{
|
|
|
|
Kind: kind,
|
|
|
|
Regex: r,
|
|
|
|
Handler: resp,
|
2021-01-31 21:48:53 +00:00
|
|
|
}
|
2021-02-02 02:25:19 +00:00
|
|
|
b.callbacks[t][kind] = append(b.callbacks[t][kind], spec)
|
2021-01-31 21:48:53 +00:00
|
|
|
}
|
|
|
|
|
2021-01-31 23:08:25 +00:00
|
|
|
// RegisterRegexCmd is a shortcut to filter non-command messages from a registration
|
|
|
|
func (b *bot) RegisterRegexCmd(p Plugin, kind Kind, r *regexp.Regexp, resp ResponseHandler) {
|
|
|
|
newResp := func(req Request) bool {
|
|
|
|
if !req.Msg.Command {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return resp(req)
|
|
|
|
}
|
|
|
|
b.RegisterRegex(p, kind, r, newResp)
|
|
|
|
}
|
|
|
|
|
2021-01-31 21:48:53 +00:00
|
|
|
// Register a callback
|
|
|
|
// This function should be considered deprecated.
|
|
|
|
func (b *bot) Register(p Plugin, kind Kind, cb Callback) {
|
|
|
|
r := regexp.MustCompile(`.*`)
|
|
|
|
resp := func(r Request) bool {
|
|
|
|
return cb(r.Conn, r.Kind, r.Msg, r.Args...)
|
2019-02-05 18:33:18 +00:00
|
|
|
}
|
2021-01-31 21:48:53 +00:00
|
|
|
b.RegisterRegex(p, kind, r, resp)
|
2019-02-05 17:25:31 +00:00
|
|
|
}
|
2019-02-07 16:30:42 +00:00
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// GetPassword returns a random password generated for the bot
|
|
|
|
// Passwords expire in 24h and are used for the web interface
|
2019-06-13 14:04:06 +00:00
|
|
|
func (b *bot) GetPassword() string {
|
2022-07-17 17:46:44 +00:00
|
|
|
if override := b.config.Get("bot.password", ""); override != "" {
|
|
|
|
return override
|
|
|
|
}
|
2019-06-13 14:04:06 +00:00
|
|
|
if b.passwordCreated.Before(time.Now().Add(-24 * time.Hour)) {
|
|
|
|
adjs := b.config.GetArray("bot.passwordAdjectives", []string{"very"})
|
|
|
|
nouns := b.config.GetArray("bot.passwordNouns", []string{"noun"})
|
|
|
|
verbs := b.config.GetArray("bot.passwordVerbs", []string{"do"})
|
|
|
|
a, n, v := adjs[rand.Intn(len(adjs))], nouns[rand.Intn(len(nouns))], verbs[rand.Intn(len(verbs))]
|
|
|
|
b.passwordCreated = time.Now()
|
|
|
|
b.password = fmt.Sprintf("%s-%s-%s", a, n, v)
|
|
|
|
}
|
|
|
|
return b.password
|
|
|
|
}
|
2020-04-29 21:45:53 +00:00
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// SetQuiet is called to silence the bot from sending channel messages
|
2020-04-29 21:45:53 +00:00
|
|
|
func (b *bot) SetQuiet(status bool) {
|
|
|
|
b.quiet = status
|
|
|
|
}
|
2020-06-09 17:48:56 +00:00
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// RefreshPluginBlacklist loads data for which plugins are disabled for particular channels
|
2020-06-09 17:48:56 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-10-09 16:00:10 +00:00
|
|
|
// RefreshPluginWhitelist loads data for which plugins are enabled
|
|
|
|
func (b *bot) RefreshPluginWhitelist() error {
|
|
|
|
whitelistItems := []struct {
|
|
|
|
Name string
|
|
|
|
}{
|
|
|
|
{Name: "admin"}, // we must always ensure admin is on!
|
|
|
|
}
|
|
|
|
if err := b.DB().Select(&whitelistItems, `select name from pluginWhitelist`); err != nil {
|
|
|
|
return fmt.Errorf("%w", err)
|
|
|
|
}
|
|
|
|
b.pluginWhitelist = make(map[string]bool)
|
|
|
|
for _, i := range whitelistItems {
|
|
|
|
b.pluginWhitelist[i.Name] = true
|
|
|
|
}
|
|
|
|
log.Debug().Interface("whitelist", b.pluginWhitelist).Msgf("Refreshed plugin whitelist")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-17 18:24:34 +00:00
|
|
|
// GetPluginNames returns an ordered list of plugins loaded (used for blacklisting)
|
2020-06-09 17:48:56 +00:00
|
|
|
func (b *bot) GetPluginNames() []string {
|
|
|
|
names := []string{}
|
|
|
|
for _, name := range b.pluginOrdering {
|
|
|
|
names = append(names, pluginNameStem(name))
|
|
|
|
}
|
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
2020-10-09 16:00:10 +00:00
|
|
|
func (b *bot) GetWhitelist() []string {
|
|
|
|
list := []string{}
|
|
|
|
for k := range b.pluginWhitelist {
|
|
|
|
list = append(list, k)
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2021-04-27 16:36:34 +00:00
|
|
|
func (b *bot) OnBlacklist(channel, plugin string) bool {
|
2020-06-09 17:48:56 +00:00
|
|
|
return b.pluginBlacklist[channel+plugin]
|
|
|
|
}
|
|
|
|
|
2020-10-09 16:00:10 +00:00
|
|
|
func (b *bot) onWhitelist(plugin string) bool {
|
|
|
|
return b.pluginWhitelist[plugin]
|
|
|
|
}
|
|
|
|
|
2020-06-09 17:48:56 +00:00
|
|
|
func pluginNameStem(name string) string {
|
|
|
|
return strings.Split(strings.TrimPrefix(name, "*"), ".")[0]
|
|
|
|
}
|
2021-04-27 16:36:34 +00:00
|
|
|
|
|
|
|
func PluginName(p Plugin) string {
|
|
|
|
t := reflect.TypeOf(p).String()
|
|
|
|
return t
|
|
|
|
}
|
2021-07-21 13:54:29 +00:00
|
|
|
|
|
|
|
func (b *bot) CheckPassword(secret, password string) bool {
|
2022-07-17 17:46:44 +00:00
|
|
|
log.Debug().Msgf("CheckPassword(%s, %s) => b.password=%s, b.GetPassword()=%s", secret, password, b.password, b.GetPassword())
|
2021-07-28 15:32:59 +00:00
|
|
|
if password == "" {
|
|
|
|
return false
|
|
|
|
}
|
2022-07-17 17:46:44 +00:00
|
|
|
if b.GetPassword() == password {
|
2021-07-21 13:54:29 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
parts := strings.SplitN(password, ":", 2)
|
|
|
|
if len(parts) == 2 {
|
|
|
|
secret = parts[0]
|
|
|
|
password = parts[1]
|
|
|
|
}
|
|
|
|
q := `select encoded_pass from apppass where secret = ?`
|
|
|
|
encodedPasswords := [][]byte{}
|
|
|
|
b.DB().Select(&encodedPasswords, q, secret)
|
|
|
|
for _, p := range encodedPasswords {
|
|
|
|
if err := bcrypt.CompareHashAndPassword(p, []byte(password)); err == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|