catbase/bot/handlers.go

296 lines
7.4 KiB
Go
Raw Normal View History

2016-01-17 18:00:44 +00:00
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
package bot
import (
2021-11-16 01:34:09 +00:00
"bytes"
"database/sql"
2021-11-16 01:34:09 +00:00
"encoding/json"
"errors"
"fmt"
2021-11-16 01:34:09 +00:00
"io/ioutil"
"math/rand"
2021-11-16 01:34:09 +00:00
"net/http"
"reflect"
2013-03-04 15:43:23 +00:00
"regexp"
"strconv"
"strings"
"time"
2019-03-07 16:35:42 +00:00
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot/msg"
)
func (b *bot) Receive(conn Connector, kind Kind, msg msg.Message, args ...any) bool {
2016-03-10 18:37:07 +00:00
// msg := b.buildMessage(client, inMsg)
// do need to look up user and fix it
2021-10-05 23:05:27 +00:00
if kind == Edit {
b.history.Edit(msg.ID, &msg)
} else {
b.history.Append(&msg)
}
if kind == Message && strings.HasPrefix(msg.Body, "help") && msg.Command {
parts := strings.Fields(strings.ToLower(msg.Body))
2019-05-27 23:21:53 +00:00
b.checkHelp(conn, msg.Channel, parts)
2019-03-07 16:35:42 +00:00
log.Debug().Msg("Handled a help, returning")
goto RET
}
for _, name := range b.pluginOrdering {
2021-04-27 16:36:34 +00:00
if b.OnBlacklist(msg.Channel, pluginNameStem(name)) || !b.onWhitelist(pluginNameStem(name)) {
continue
}
2019-05-27 23:21:53 +00:00
if b.runCallback(conn, b.plugins[name], kind, msg, args...) {
goto RET
}
}
RET:
b.logIn <- msg
return true
}
2021-02-01 02:51:28 +00:00
func ParseValues(r *regexp.Regexp, body string) RegexValues {
out := RegexValues{}
subs := r.FindStringSubmatch(body)
if len(subs) == 0 {
return out
}
for i, n := range r.SubexpNames() {
out[n] = subs[i]
}
return out
}
func (b *bot) runCallback(conn Connector, plugin Plugin, evt Kind, message msg.Message, args ...any) bool {
t := reflect.TypeOf(plugin).String()
2021-02-02 02:25:19 +00:00
for _, spec := range b.callbacks[t][evt] {
if spec.Regex.MatchString(message.Body) {
2021-02-01 15:45:41 +00:00
req := Request{
Conn: conn,
Kind: evt,
Msg: message,
2021-02-02 02:25:19 +00:00
Values: ParseValues(spec.Regex, message.Body),
2021-02-01 15:45:41 +00:00
Args: args,
}
2021-02-02 02:25:19 +00:00
if spec.Handler(req) {
return true
}
}
}
return false
}
// Send a message to the connection
func (b *bot) Send(conn Connector, kind Kind, args ...any) (string, error) {
2020-04-29 21:45:53 +00:00
if b.quiet {
return "", nil
}
2019-05-27 23:21:53 +00:00
return conn.Send(kind, args...)
}
func (b *bot) GetEmojiList() map[string]string {
return b.conn.GetEmojiList()
}
// Checks to see if the user is asking for help, returns true if so and handles the situation.
2019-05-27 23:21:53 +00:00
func (b *bot) checkHelp(conn Connector, channel string, parts []string) {
if len(parts) == 1 {
// just print out a list of help topics
2012-08-27 00:30:23 +00:00
topics := "Help topics: about variables"
for _, name := range b.GetWhitelist() {
name = pluginNameStem(name)
topics = fmt.Sprintf("%s, %s", topics, name)
}
2019-05-27 23:21:53 +00:00
b.Send(conn, Message, channel, topics)
} else {
// trigger the proper plugin's help response
if parts[1] == "about" {
2019-05-27 23:21:53 +00:00
b.Help(conn, channel, parts)
return
}
2012-08-27 00:30:23 +00:00
if parts[1] == "variables" {
2019-05-27 23:21:53 +00:00
b.listVars(conn, channel, parts)
2012-08-27 00:30:23 +00:00
return
}
for name, plugin := range b.plugins {
if strings.HasPrefix(name, "*"+parts[1]) {
2019-05-27 23:21:53 +00:00
if b.runCallback(conn, plugin, Help, msg.Message{Channel: channel}, channel, parts) {
return
} else {
msg := fmt.Sprintf("I'm sorry, I don't know how to help you with %s.", parts[1])
2019-05-27 23:21:53 +00:00
b.Send(conn, Message, channel, msg)
return
}
}
}
msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", strings.Join(parts, " "))
2019-05-27 23:21:53 +00:00
b.Send(conn, Message, channel, msg)
}
}
func msgIsMsg(m msg.Message) bool {
return m.Kind == Message || m.Kind == Action || m.Kind == Reply
}
func (b *bot) LastMessage(channel string) (msg.Message, error) {
log := <-b.logOut
if len(log) == 0 {
return msg.Message{}, errors.New("No messages found.")
}
for i := len(log) - 1; i >= 0; i-- {
msg := log[i]
if msgIsMsg(msg) && strings.ToLower(msg.Channel) == strings.ToLower(channel) {
return msg, nil
2013-06-17 01:03:43 +00:00
}
}
return msg.Message{}, errors.New("No messages found.")
}
// Take an input string and mutate it based on $vars in the string
func (b *bot) Filter(message msg.Message, input string) string {
if strings.Contains(input, "$NICK") {
nick := strings.ToUpper(message.User.Name)
input = strings.Replace(input, "$NICK", nick, -1)
}
// Let's be bucket compatible for this var
2012-08-27 00:33:33 +00:00
input = strings.Replace(input, "$who", "$nick", -1)
if strings.Contains(input, "$nick") {
nick := message.User.Name
input = strings.Replace(input, "$nick", nick, -1)
}
2013-08-31 02:09:45 +00:00
for strings.Contains(input, "$someone") {
nicks := b.Who(message.Channel)
someone := nicks[rand.Intn(len(nicks))].Name
2013-08-31 02:09:45 +00:00
input = strings.Replace(input, "$someone", someone, 1)
}
for strings.Contains(input, "$digit") {
num := strconv.Itoa(rand.Intn(9))
input = strings.Replace(input, "$digit", num, 1)
}
for strings.Contains(input, "$nonzero") {
num := strconv.Itoa(rand.Intn(8) + 1)
input = strings.Replace(input, "$nonzero", num, 1)
}
for strings.Contains(input, "$time") {
hrs := rand.Intn(23)
mins := rand.Intn(59)
input = strings.Replace(input, "$time", fmt.Sprintf("%0.2d:%0.2d", hrs, mins), 1)
}
for strings.Contains(input, "$now") {
now := time.Now().Format("15:04")
input = strings.Replace(input, "$now", now, 1)
}
for strings.Contains(input, "$msg") {
msg := message.Body
input = strings.Replace(input, "$msg", msg, 1)
}
r, err := regexp.Compile("\\$[A-z]+")
if err != nil {
panic(err)
}
2017-09-29 04:58:21 +00:00
for _, f := range b.filters {
input = f(input)
}
varname := r.FindString(input)
blacklist := make(map[string]bool)
blacklist["$and"] = true
for len(varname) > 0 && !blacklist[varname] {
text, err := b.getVar(varname)
if err != nil {
blacklist[varname] = true
continue
}
input = strings.Replace(input, varname, text, 1)
varname = r.FindString(input)
}
return input
}
func (b *bot) getVar(varName string) (string, error) {
var text string
err := b.DB().Get(&text, `select value from variables where name=? order by random() limit 1`, varName)
switch {
case err == sql.ErrNoRows:
return "", fmt.Errorf("No factoid found")
case err != nil:
2019-03-07 16:35:42 +00:00
log.Fatal().Err(err).Msg("getVar error")
}
return text, nil
}
2019-05-27 23:21:53 +00:00
func (b *bot) listVars(conn Connector, channel string, parts []string) {
2016-05-20 20:28:48 +00:00
var variables []string
err := b.DB().Select(&variables, `select name from variables group by name`)
2012-08-27 00:30:23 +00:00
if err != nil {
2019-03-07 16:35:42 +00:00
log.Fatal().Err(err)
2012-08-27 00:30:23 +00:00
}
msg := "I know: $who, $someone, $digit, $nonzero, $time, $now, $msg"
2016-05-20 20:28:48 +00:00
if len(variables) > 0 {
msg += ", " + strings.Join(variables, ", ")
2012-08-27 00:30:23 +00:00
}
2019-05-27 23:21:53 +00:00
b.Send(conn, Message, channel, msg)
2012-08-27 00:30:23 +00:00
}
2019-05-27 23:21:53 +00:00
func (b *bot) Help(conn Connector, channel string, parts []string) {
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: "+
"http://github.com/velour/catbase", b.version)
2019-05-27 23:21:53 +00:00
b.Send(conn, Message, channel, msg)
}
// Send our own musings to the plugins
2019-05-27 23:21:53 +00:00
func (b *bot) selfSaid(conn Connector, channel, message string, action bool) {
msg := msg.Message{
User: &b.me, // hack
Channel: channel,
Body: message,
Raw: message, // hack
Action: action,
Command: false,
Time: time.Now(),
Host: "0.0.0.0", // hack
}
for _, name := range b.pluginOrdering {
2019-05-27 23:21:53 +00:00
if b.runCallback(conn, b.plugins[name], SelfMessage, msg) {
return
}
}
}
2021-11-16 01:34:09 +00:00
// PubToASub sends updates to subscribed URLs
func (b *bot) PubToASub(subject string, payload any) {
2021-11-16 01:34:09 +00:00
key := fmt.Sprintf("pubsub.%s.url", subject)
subs := b.config.GetArray(key, []string{})
if len(subs) == 0 {
return
}
encodedBody, _ := json.Marshal(struct {
Payload any `json:"payload"`
2021-11-16 01:34:09 +00:00
}{payload})
body := bytes.NewBuffer(encodedBody)
for _, url := range subs {
r, err := http.Post(url, "text/json", body)
if err != nil {
log.Error().Err(err).Msg("")
} else {
body, _ := ioutil.ReadAll(r.Body)
log.Debug().Msgf("Response body: %s", string(body))
}
}
}