464 lines
20 KiB
Go
464 lines
20 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки.
|
|
func ircMsgParser(channel string, nick string, user string, source string, msg string) { //nolint: revive
|
|
// nick - это выбранный пользователем nick (если он занят, то его "нарисует" сервер)
|
|
// user - это короткое имя пользователя, под которым его видит сервер
|
|
// source - это длинное имя пользователя, оно содержит в себе помимо user, ещё и ip с которого пришёл пользователь
|
|
if shutdown {
|
|
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
|
return
|
|
}
|
|
|
|
if channel == ircClient.GetNick() {
|
|
// В привате бот не отвечает, чтобы не было возможности DDoS-а, ratelimit-ы в irc слишком жёсткие
|
|
// TODO: возможно, это имеет смысл вынести в конфиг, но это если кому-то кроме меня бот будет интересен
|
|
return
|
|
}
|
|
|
|
// Ловим команды и обрабатываем их
|
|
if (len(msg) > len(config.Csign)) && (msg[:len(config.Csign)] == config.Csign) {
|
|
var outgoingMessage string
|
|
|
|
var message sMsg
|
|
message.From = config.Redis.MyChannel
|
|
message.Userid = user // как его видит сервер
|
|
message.Chatid = channel // чятик, в который написал user
|
|
message.Threadid = "" // тредиков в irc нету, поэтому это поле отправляем пустым
|
|
message.Plugin = config.Redis.MyChannel
|
|
message.Mode = "public"
|
|
// Предполагается, что на команды бот автоматом отвечает
|
|
message.Misc.Answer = 1
|
|
message.Misc.Fwdcnt = 0
|
|
message.Misc.Csign = config.Csign
|
|
message.Misc.Username = nick
|
|
message.Misc.Botnick = config.Irc.Nick
|
|
message.Misc.Msgformat = 0
|
|
|
|
var cmd = msg[len(config.Csign):]
|
|
|
|
switch {
|
|
case cmd == "help" || msg == "помощь":
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%shelp | %sпомощь - это сообщение", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sanek | %sанек | %sанекдот - рандомный анекдот с anekdot.ru", config.Csign, config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sbuni - комикс-стрип hapi buni", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sbunny - кролик", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%srabbit | %sкролик - кролик", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%scat | %sкис - кошечка", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sdice | %sroll | %sкости - бросить кости", config.Csign, config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sdig | %sкопать - заняться археологией", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sdrink | %sпраздник - какой сегодня праздник?", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfish | %sfisher - порыбачить", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sрыба | %sрыбка | %sрыбалка - порыбачить", config.Csign, config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sf | %sф - рандомная фраза из сборника цитат fortune_mod", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfortune | %sфортунка - рандомная фраза из сборника цитат fortune_mod", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfox | %sлис - лисичка", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfriday | %sпятница - а не пятница ли сегодня?", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfrog | %sлягушка - лягушка", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%shorse | %sлошадь | %sлошадка - лошадка", config.Csign, config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%skarma фраза - посмотреть карму фразы", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sкарма фраза - посмотреть карму фразы", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintln("фраза++ | фраза-- - повысить или понизить карму фразы")}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%slat | %sлат - сгенерировать фразу из крылатого латинского выражения", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%smonkeyuser - комикс-стрип MonkeyUser", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sowl | %sсова - сова", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sping | %sпинг - попинговать бота", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sproverb | %sпословица - рандомная русская пословица", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%ssnail | %sулитка - улитка", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%ssome_brew - выдать соответствующий напиток, бармен может налить rum, ром, vodka, водку, tequila, текила, whisky, виски, absinthe, абсент", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sver | %sversion - написать что-то про версию ПО", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sверсия - написать что-то про версию ПО", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sw <город> | %sп <город> - погода в городе", config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sxkcd - комикс-стрип с xkcb.ru", config.Csign)}
|
|
|
|
if userModeIsOped(channel, nick) {
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin - настройки некоторых плагинов бота для канала", config.Csign)}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin":
|
|
if userModeIsOped(channel, nick) {
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin oboobs # - где 1 - вкл, 0 - выкл плагина oboobs", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin oboobs показываем ли сисечки по просьбе участников чата (команды %stits, %stities, %sboobs, %sboobies, %sсиси, %sсисечки)", config.Csign, config.Csign, config.Csign, config.Csign, config.Csign, config.Csign, config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin obutts # - где 1 - вкл, 0 - выкл плагина obutts", config.Csign)}
|
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin obutts показываем ли попки по просьбе участников чата (команды %sass, %sbutt, %sbooty, %sпопа, %sпопка)", config.Csign, config.Csign, config.Csign, config.Csign, config.Csign, config.Csign)}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin oboobs":
|
|
if userModeIsOped(channel, nick) {
|
|
value := getSetting(channel, "oboobs")
|
|
|
|
switch value {
|
|
case "":
|
|
_ = saveSetting(channel, "oboobs", "0")
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
|
case "0":
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
|
case "1":
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
|
default:
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
|
}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin oboobs 1":
|
|
if userModeIsOped(channel, nick) {
|
|
err := saveSetting(channel, "oboobs", "1")
|
|
|
|
if err != nil {
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs всё ещё выключен"}
|
|
} else {
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
|
}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin oboobs 0":
|
|
if userModeIsOped(channel, nick) {
|
|
_ = saveSetting(channel, "oboobs", "0")
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin obutts":
|
|
if userModeIsOped(channel, nick) {
|
|
value := getSetting(channel, "obutts")
|
|
|
|
switch value {
|
|
case "":
|
|
_ = saveSetting(channel, "obutts", "0")
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
|
case "0":
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
|
case "1":
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
|
default:
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
|
}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin obutts 1":
|
|
if userModeIsOped(channel, nick) {
|
|
err := saveSetting(channel, "obutts", "1")
|
|
|
|
if err != nil {
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts всё ещё выключен"}
|
|
} else {
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
|
}
|
|
}
|
|
|
|
return
|
|
|
|
case cmd == "admin obutts 0":
|
|
if userModeIsOped(channel, nick) {
|
|
_ = saveSetting(channel, "obutts", "0")
|
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
|
}
|
|
|
|
return
|
|
|
|
default:
|
|
var done = false
|
|
|
|
// Общий список простых команд
|
|
cmds := []string{"ping", "пинг", "пинх", "pong", "понг", "понх", "coin", "монетка", "roll", "dice", "кости",
|
|
"ver", "version", "версия", "хэлп", "halp", "kde", "кде", "lat", "лат", "friday", "пятница", "proverb",
|
|
"пословица", "пословиться", "fortune", "фортунка", "f", "ф", "anek", "анек", "анекдот", "buni", "cat",
|
|
"кис", "drink", "праздник", "fox", "лис", "frog", "лягушка", "horse", "лошадь", "лошадка", "monkeyuser",
|
|
"owl", "сова", "сыч", "rabbit", "bunny", "кролик", "snail", "улитка", "xkcd", "dig", "копать", "fish",
|
|
"fishing", "рыба", "рыбка", "рыбалка", "karma", "карма", "w", "п", "weather", "погода", "погодка",
|
|
"погадка"}
|
|
|
|
for _, command := range cmds {
|
|
if cmd == command {
|
|
done = true
|
|
outgoingMessage = msg
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
// Список команд бармэна
|
|
if !done {
|
|
cmds := []string{"rum", "ром", "vodka", "водка", "beer", "пиво", "tequila", "текила", "whisky", "виски",
|
|
"absinthe", "абсент"}
|
|
|
|
// Тихо сам с собою я веду беседу...
|
|
for _, command := range cmds {
|
|
if cmd == command {
|
|
done = true
|
|
outgoingMessage = msg
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
// Заказываю выпивку кому-то ещё
|
|
if !done {
|
|
re := regexp.MustCompile(" +")
|
|
pile := re.Split(cmd, 2)
|
|
|
|
for _, command := range cmds {
|
|
if pile[0] == command {
|
|
userNick := strings.TrimSpace(pile[1])
|
|
|
|
if userNick != "" {
|
|
if userModeIsHere(channel, userNick) {
|
|
// Проставляем правильный в конкретно данном случае username, так как отвечать мы
|
|
// будем ему
|
|
message.Misc.Username = strings.TrimSpace(pile[1])
|
|
} else {
|
|
msg = fmt.Sprintf("Я тут не вижу участника с ником %s", userNick)
|
|
imChan <- iMsg{ChatID: channel, Text: msg}
|
|
|
|
return
|
|
}
|
|
} // в противном случае юзер просто поставил пробел на конце команды, интерпретируем это как
|
|
// стаканчик юзеру
|
|
|
|
outgoingMessage = msg
|
|
done = true
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Сложные команды, например, погода
|
|
if !done {
|
|
cmdLen := len(cmd)
|
|
|
|
cmds := []string{"w ", "п ", "погода ", "погодка ", "погадка ", "weather "}
|
|
|
|
for _, command := range cmds {
|
|
if cmdLen > len(command) && cmd[0:len(command)] == command {
|
|
done = true
|
|
outgoingMessage = msg
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Отключаемые команды
|
|
if !done {
|
|
value := getSetting(channel, "obutts")
|
|
|
|
if value == "1" {
|
|
cmds := []string{"butt", "booty", "ass", "попа", "попка"}
|
|
|
|
for _, command := range cmds {
|
|
cmdLen := len(command)
|
|
if cmd[:cmdLen] == command {
|
|
done = true
|
|
outgoingMessage = msg
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if !done {
|
|
value = getSetting(channel, "oboobs")
|
|
|
|
if value == "1" {
|
|
cmds := []string{"tits", "boobs", "tities", "boobies", "сиси", "сисечки"}
|
|
|
|
for _, command := range cmds {
|
|
cmdLen := len(command)
|
|
if cmd[:cmdLen] == command {
|
|
// done = true
|
|
outgoingMessage = msg
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if outgoingMessage != "" {
|
|
message.Message = outgoingMessage
|
|
data, err := json.Marshal(message)
|
|
|
|
if err != nil {
|
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
// Заталкиваем наш json в редиску
|
|
if err := redisClient.Publish(ctx, config.Redis.Channel, data).Err(); err != nil {
|
|
log.Warnf("Unable to send data to redis channel %s: %s", config.Redis.Channel, err)
|
|
} else {
|
|
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
|
}
|
|
}
|
|
} else {
|
|
// Это уже просто трёп в чятике
|
|
var message sMsg
|
|
message.From = config.Redis.MyChannel
|
|
message.Userid = user // как его видит сервер
|
|
message.Chatid = channel // чятик, в который написал user
|
|
message.Threadid = "" // тредиков в irc нету, поэтому это поле отправляем пустым
|
|
message.Message = msg
|
|
message.Plugin = config.Redis.MyChannel
|
|
message.Mode = "public"
|
|
|
|
// Если во фразе содержится ключевые слова, под это есть костыль в craniac-е - (предполагаем, что) он
|
|
// знает ставить или не ставить флажок answer
|
|
message.Misc.Answer = 0
|
|
|
|
// Предполагается что в канале бот должен отвечать, только если к нему обратились, либо это была команда
|
|
if regexp.MustCompile(ircClient.GetNick()).Match([]byte(message.Message)) {
|
|
message.Misc.Answer = 1
|
|
}
|
|
|
|
// Попробуем выискать изменение кармы
|
|
msgLen := len(msg)
|
|
|
|
// ++ или -- на конце фразы - это наш кандидат
|
|
if msgLen > len("++") {
|
|
if msg[msgLen-len("--"):msgLen] == "--" || msg[msgLen-len("++"):msgLen] == "++" {
|
|
// Предполагается, что менять карму мы будем для одной фразы, то есть для 1 строки
|
|
if len(strings.Split(msg, "\n")) == 1 {
|
|
// Костыль для кармы
|
|
message.Misc.Answer = 1
|
|
}
|
|
}
|
|
}
|
|
|
|
message.Misc.Fwdcnt = 0
|
|
message.Misc.Csign = config.Csign
|
|
message.Misc.Username = nick
|
|
message.Misc.Botnick = config.Irc.Nick
|
|
message.Misc.Msgformat = 0
|
|
|
|
data, err := json.Marshal(message)
|
|
|
|
if err != nil {
|
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
// Заталкиваем наш json в редиску
|
|
if err := redisClient.Publish(ctx, config.Redis.Channel, data).Err(); err != nil {
|
|
log.Warnf("Unable to send data to redis channel %s: %s", config.Redis.Channel, err)
|
|
} else {
|
|
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
|
}
|
|
}
|
|
}
|
|
|
|
// redisMsgParser парсит json-чики прилетевшие из REDIS-ки, причём, json-чики должны быть относительно валидными.
|
|
func redisMsgParser(msg string) {
|
|
if shutdown {
|
|
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
|
return
|
|
}
|
|
|
|
var j rMsg
|
|
|
|
log.Debugf("Incoming raw json: %s", msg)
|
|
|
|
if err := json.Unmarshal([]byte(msg), &j); err != nil {
|
|
log.Warnf("Unable to to parse message from redis channel: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
// Validate our j
|
|
if exist := j.From; exist == "" {
|
|
log.Warnf("Incorrect msg from redis, no from field: %s", msg)
|
|
|
|
return
|
|
}
|
|
|
|
if exist := j.Chatid; exist == "" {
|
|
log.Warnf("Incorrect msg from redis, no chatid field: %s", msg)
|
|
|
|
return
|
|
}
|
|
|
|
if exist := j.Userid; exist == "" {
|
|
log.Warnf("Incorrect msg from redis, no userid field: %s", msg)
|
|
|
|
return
|
|
}
|
|
|
|
if exist := j.Message; exist == "" {
|
|
log.Warnf("Incorrect msg from redis, no message field: %s", msg)
|
|
|
|
return
|
|
}
|
|
|
|
if exist := j.Plugin; exist == "" {
|
|
log.Warnf("Incorrect msg from redis, no plugin field: %s", msg)
|
|
|
|
return
|
|
}
|
|
|
|
if exist := j.Mode; exist == "" {
|
|
log.Warnf("Incorrect msg from redis, no mode field: %s", msg)
|
|
|
|
return
|
|
}
|
|
|
|
// j.Misc.Answer может и не быть, тогда ответа на такое сообщение не будет
|
|
if j.Misc.Answer == 0 {
|
|
log.Debug("Field Misc->Answer = 0, skipping message")
|
|
|
|
return
|
|
}
|
|
|
|
// j.Misc.BotNick тоже можно не передавать, тогда будет записана пустая строка
|
|
// j.Misc.CSign если нам его не передали, возьмём значение из конфига
|
|
if exist := j.Misc.Csign; exist == "" {
|
|
j.Misc.Csign = config.Csign
|
|
}
|
|
|
|
// j.Misc.FwdCnt если нам его не передали, то будет 0
|
|
if exist := j.Misc.Fwdcnt; exist == 0 {
|
|
j.Misc.Fwdcnt = 1
|
|
}
|
|
|
|
// j.Misc.GoodMorning может быть быть 1 или 0, по-умолчанию 0
|
|
// j.Misc.MsgFormat может быть быть 1 или 0, по-умолчанию 0
|
|
// j.Misc.Username можно не передавать, тогда будет пустая строка
|
|
|
|
// Отвалидировались, теперь вернёмся к нашим баранам.
|
|
lines := regexp.MustCompile("\r?\n").Split(j.Message, -1)
|
|
|
|
for _, message := range lines {
|
|
if userModeIsOped(j.Chatid, ircClient.GetNick()) || userModeIsVoiced(j.Chatid, ircClient.GetNick()) {
|
|
imChanUnrestricted <- iMsg{ChatID: j.Chatid, Text: message}
|
|
} else {
|
|
imChan <- iMsg{ChatID: j.Chatid, Text: message}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|