Implement help, помощь, rehash commands, restrict presence collection precedure
This commit is contained in:
7
Makefile
7
Makefile
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env gmake -f
|
#!/usr/bin/env gmake -f
|
||||||
|
|
||||||
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath -pgo=auto
|
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath -pgo=auto
|
||||||
|
FILELIST=collection.go types.go globals.go lib.go event_parser.go buny.go commands.go main.go
|
||||||
|
|
||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
@@ -9,15 +10,15 @@ ifeq ($(OS),Windows_NT)
|
|||||||
# powershell
|
# powershell
|
||||||
ifeq ($(SHELL),sh.exe)
|
ifeq ($(SHELL),sh.exe)
|
||||||
SET CGO_ENABLED=0
|
SET CGO_ENABLED=0
|
||||||
go build ${BUILDOPTS} -o "buny-jabber-bot" collection.go types.go globals.go lib.go event_parser.go buny.go main.go
|
go build ${BUILDOPTS} -o "buny-jabber-bot" ${FILELIST}
|
||||||
else
|
else
|
||||||
# jetbrains golang
|
# jetbrains golang
|
||||||
CGO_ENABLED=0
|
CGO_ENABLED=0
|
||||||
go build ${BUILDOPTS} -o "buny-jabber-bot" collection.go types.go globals.go lib.go event_parser.go buny.go main.go
|
go build ${BUILDOPTS} -o "buny-jabber-bot" ${FILELIST}
|
||||||
endif
|
endif
|
||||||
# bash/git bash
|
# bash/git bash
|
||||||
else
|
else
|
||||||
CGO_ENABLED=0 go build ${BUILDOPTS} -o "buny-jabber-bot" collection.go types.go globals.go lib.go event_parser.go buny.go main.go
|
CGO_ENABLED=0 go build ${BUILDOPTS} -o "buny-jabber-bot" ${FILELIST}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
141
commands.go
Normal file
141
commands.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/eleksir/go-xmpp"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cmd(v xmpp.Chat) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case v.Text == fmt.Sprintf("%shelp", config.CSign) || v.Text == fmt.Sprintf("%sпомощь", config.CSign):
|
||||||
|
var (
|
||||||
|
answer string
|
||||||
|
chosenOneTalks = false
|
||||||
|
)
|
||||||
|
|
||||||
|
realJID := getRealJIDfromNick(v.Remote)
|
||||||
|
|
||||||
|
for _, master := range config.Jabber.BotMasters {
|
||||||
|
if (strings.SplitN(realJID, "/", 2))[0] == master {
|
||||||
|
chosenOneTalks = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if chosenOneTalks {
|
||||||
|
answer = fmt.Sprintf("%sпомощь - этот список команд\n", config.CSign)
|
||||||
|
answer += fmt.Sprintf("%shelp - this commands list\n", config.CSign)
|
||||||
|
answer += fmt.Sprintf("%srehash - reload white and black lists", config.CSign)
|
||||||
|
} else {
|
||||||
|
answer = "Ничем помочь не могу. Луна не светит на тебя."
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := v.Remote
|
||||||
|
|
||||||
|
if v.Type == "groupchat" {
|
||||||
|
dest = (strings.SplitN(v.Remote, "/", 2))[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := talk.Send(
|
||||||
|
xmpp.Chat{ //nolint:exhaustruct
|
||||||
|
Remote: dest,
|
||||||
|
Text: strings.TrimSpace(answer),
|
||||||
|
Type: v.Type,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
err = fmt.Errorf("unable to send message to %s: %w", v.Remote, err)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case v.Text == fmt.Sprintf("%srehash", config.CSign):
|
||||||
|
/* Эта команда может прилетать из приватной беседы с realjid-ом, из приватной беседы с chat-nick-ом, а также из
|
||||||
|
* чятика.
|
||||||
|
* Groupchat мы можем определить по v.Type (groupchat) и тогда понятно что делать.
|
||||||
|
* А вот, например, приватные притязания определить несколько сложно-вато. Но можно попробовать, например,
|
||||||
|
* пытаться по списку presence-ов находить персонажа и его real jid и уже из этого определять master он или нет.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Попробуем определить каков алгоритм наших действий, в зависимости от типа сообщения
|
||||||
|
switch v.Type {
|
||||||
|
case "groupchat":
|
||||||
|
// Проверим, является ли "командир" избранным или это самозванец?
|
||||||
|
var (
|
||||||
|
chosenOneTalks = false
|
||||||
|
listsLoaded = true
|
||||||
|
)
|
||||||
|
|
||||||
|
realJID := getRealJIDfromNick(v.Remote)
|
||||||
|
|
||||||
|
for _, master := range config.Jabber.BotMasters {
|
||||||
|
if (strings.SplitN(realJID, "/", 2))[0] == master {
|
||||||
|
chosenOneTalks = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chosenOneTalks {
|
||||||
|
log.Infof(
|
||||||
|
"Command %srehash given by non-bot_master user %s(%s), ignoring",
|
||||||
|
config.CSign,
|
||||||
|
realJID,
|
||||||
|
v.Remote,
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := readWhitelist(); err != nil {
|
||||||
|
listsLoaded = false
|
||||||
|
|
||||||
|
var msg xmpp.Chat
|
||||||
|
msg.Remote = v.Remote
|
||||||
|
msg.Type = "chat" // private message
|
||||||
|
msg.Text = fmt.Sprint(err)
|
||||||
|
|
||||||
|
if _, err := talk.Send(msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := readBlacklist(); err != nil {
|
||||||
|
listsLoaded = false
|
||||||
|
|
||||||
|
var msg xmpp.Chat
|
||||||
|
msg.Remote = v.Remote
|
||||||
|
msg.Type = "chat" // private message
|
||||||
|
msg.Text = fmt.Sprint(err)
|
||||||
|
|
||||||
|
if _, err := talk.Send(msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if listsLoaded {
|
||||||
|
room := (strings.SplitN(v.Remote, "/", 2))[0]
|
||||||
|
|
||||||
|
var msg xmpp.Chat
|
||||||
|
msg.Remote = room
|
||||||
|
msg.Type = v.Type
|
||||||
|
msg.Text = "Сделано"
|
||||||
|
|
||||||
|
if _, err := talk.Send(msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "chat":
|
||||||
|
default:
|
||||||
|
log.Infof("Rehash command from outer space: %s", spew.Sdump(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -3,46 +3,80 @@
|
|||||||
"jabber": {
|
"jabber": {
|
||||||
# Если не задан, будет localhost
|
# Если не задан, будет localhost
|
||||||
"server": "jabber.tld",
|
"server": "jabber.tld",
|
||||||
|
|
||||||
# Если не задано - 5222 для нешифрованного соединения или для starttls и 5223 для голого (старорежимного) ssl
|
# Если не задано - 5222 для нешифрованного соединения или для starttls и 5223 для голого (старорежимного) ssl
|
||||||
"port": 5222,
|
"port": 5222,
|
||||||
|
|
||||||
# Включить шифрование
|
# Включить шифрование
|
||||||
"ssl": true,
|
"ssl": true,
|
||||||
|
|
||||||
# Это более современный протокол и как раз на нём работет большинство jabber-серверов, порт 5222,
|
# Это более современный протокол и как раз на нём работет большинство jabber-серверов, порт 5222,
|
||||||
# jabber.ru по этому протоколу как-то странно работает, если его включить, я получаю
|
# jabber.ru по этому протоколу как-то странно работает, если его включить, я получаю
|
||||||
# tls: first record does not look like a TLS handshake
|
# tls: first record does not look like a TLS handshake
|
||||||
"starttls" : true,
|
"starttls" : true,
|
||||||
|
|
||||||
# Не проверяем сертификаты по-умолчанию.
|
# Не проверяем сертификаты по-умолчанию.
|
||||||
# Вам правда хочется всё это проверять? Там по идее могут быть всякие Lets Encrypt, которые хоть и работают, но
|
# Вам правда хочется всё это проверять? Там по идее могут быть всякие Lets Encrypt, которые хоть и работают, но
|
||||||
# создают кучу проблем
|
# создают кучу проблем
|
||||||
"ssl_verify": false,
|
"ssl_verify": false,
|
||||||
|
|
||||||
# Позволять авторизацию логин-паролем по незашифрованному каналу связи
|
# Позволять авторизацию логин-паролем по незашифрованному каналу связи
|
||||||
"insecureallowunencryptedauth": false,
|
"insecureallowunencryptedauth": false,
|
||||||
|
|
||||||
# Таймаут установления соединения с сервером, в секундах. 10 секунд, если не задано.
|
# Таймаут установления соединения с сервером, в секундах. 10 секунд, если не задано.
|
||||||
"connection_timeout": 10,
|
"connection_timeout": 10,
|
||||||
|
|
||||||
# Задержка перед пере-коннекчиванием. 3 секунды, если не задано.
|
# Задержка перед пере-коннекчиванием. 3 секунды, если не задано.
|
||||||
"reconnect_delay": 3,
|
"reconnect_delay": 3,
|
||||||
|
|
||||||
# Промежуток между пингами сервера, для определения его живости. По-умолчанию 60 секунд.
|
# Промежуток между пингами сервера, для определения его живости. По-умолчанию 60 секунд.
|
||||||
# Пинги посылаются, если в течение этого времени не было активности со стороны сервера.
|
# Пинги посылаются, если в течение этого времени не было активности со стороны сервера.
|
||||||
"server_ping_delay": 60,
|
"server_ping_delay": 60,
|
||||||
|
|
||||||
# Промежуток между пингами muc-ов. По-умолчанию 900 секунд. (Согласно рекомендации в xep-0410.)
|
# Промежуток между пингами muc-ов. По-умолчанию 900 секунд. (Согласно рекомендации в xep-0410.)
|
||||||
# Пинги посылаются, если в течение этого времени не было активности со стороны muc-а.
|
# Пинги посылаются, если в течение этого времени не было активности со стороны muc-а.
|
||||||
"muc_ping_delay": 900,
|
"muc_ping_delay": 900,
|
||||||
|
|
||||||
# Сколько ждать перед переджоиванием к комнате. По-умолчанию, 3 секунды.
|
# Сколько ждать перед переджоиванием к комнате. По-умолчанию, 3 секунды.
|
||||||
"muc_rejoin_delay": 3,
|
"muc_rejoin_delay": 3,
|
||||||
|
|
||||||
# Ping-to-ping splay delay. Варьируемая задержка между пингами, чтобы не набирать высокий message rate.
|
# Ping-to-ping splay delay. Варьируемая задержка между пингами, чтобы не набирать высокий message rate.
|
||||||
# По-умолчанию, 3 секунды. Под капотом преобразуются в миллисекунды. Важна для пингов MUC-ов, особенно,
|
# По-умолчанию, 3 секунды. Под капотом преобразуются в миллисекунды. Важна для пингов MUC-ов, особенно,
|
||||||
# если их МНОГО.
|
# если их МНОГО.
|
||||||
"ping_splay_delay": 3,
|
"ping_splay_delay": 3,
|
||||||
|
|
||||||
# Ник бота, с которым он будет заходить в комнаты
|
# Ник бота, с которым он будет заходить в комнаты
|
||||||
"nick": "buny",
|
"nick": "buny",
|
||||||
|
|
||||||
# Уникальная часть jid-а, специфичная для клиента
|
# Уникальная часть jid-а, специфичная для клиента
|
||||||
"resource" : "Лунная призма",
|
"resource" : "Лунная призма",
|
||||||
|
|
||||||
|
# Имя пользователя для авторизации на сервере
|
||||||
|
"user": "buny@jabber.tld",
|
||||||
|
|
||||||
|
# Пароль для авторизации
|
||||||
|
"password": "secret",
|
||||||
|
|
||||||
|
# Список JID-ов, которым позволено управлять ботом (например, давать отмашку на rehash).
|
||||||
|
# В списке jid-ы без resource-а.
|
||||||
|
# Если не задано ни одного мастера, то бот сам себе мастер.
|
||||||
|
"bot_masters": [
|
||||||
|
"luna@earth.tld"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Jabber-каналы, к которым бот попробует присоединиться при старте
|
||||||
|
"channels": [
|
||||||
|
"my_channel@conference.jabber.tld",
|
||||||
|
"another_channel@conference.jabber.tld"
|
||||||
|
],
|
||||||
|
|
||||||
# Статус, с которым бот запускается. (если всё нормально, этот статус будет видать только в логах сервера и
|
# Статус, с которым бот запускается. (если всё нормально, этот статус будет видать только в логах сервера и
|
||||||
# на долю секунды он промелькнёт у всех в ростере, у кого бот в знакомых. Может отсуствовать, тогда статус будет
|
# на долю секунды он промелькнёт у всех в ростере, у кого бот в знакомых. Может отсуствовать, тогда статус будет
|
||||||
# пустой строкой
|
# пустой строкой
|
||||||
"startup_status": [
|
"startup_status": [
|
||||||
"bunny bot is starting"
|
"bunny bot is starting"
|
||||||
],
|
],
|
||||||
|
|
||||||
# Статус, который демонстрируется во время работы бота, как xa
|
# Статус, который демонстрируется во время работы бота, как xa
|
||||||
# (по задумке - extended away, на практике, к away обычно отношения не имеет)
|
# (по задумке - extended away, на практике, к away обычно отношения не имеет)
|
||||||
"runtime_status": {
|
"runtime_status": {
|
||||||
@@ -62,15 +96,19 @@
|
|||||||
"Звезда нашей надежды никогда не исчезнет. Пока она горит, всё будет в порядке. Мы не сможем проиграть",
|
"Звезда нашей надежды никогда не исчезнет. Пока она горит, всё будет в порядке. Мы не сможем проиграть",
|
||||||
"Голова с хвостиками… Не вмешивайся в наши дела! Играть в войну — это опасно"
|
"Голова с хвостиками… Не вмешивайся в наши дела! Играть в войну — это опасно"
|
||||||
],
|
],
|
||||||
|
|
||||||
# Период смены статуса, сек. Если параметра нет или он выставлен в 0, ротации не будет
|
# Период смены статуса, сек. Если параметра нет или он выставлен в 0, ротации не будет
|
||||||
# 3 дня
|
# 3 дня
|
||||||
"rotation_time": 259200,
|
"rotation_time": 259200,
|
||||||
|
|
||||||
# Рандомная составляющая, добавляемая к периоду смены статуса, сек.
|
# Рандомная составляющая, добавляемая к периоду смены статуса, сек.
|
||||||
# 4 дня
|
# 4 дня
|
||||||
"rotation_splay_time": 345600
|
"rotation_splay_time": 345600
|
||||||
},
|
},
|
||||||
|
|
||||||
# Произносим ли что-то пафосное в момоент бана
|
# Произносим ли что-то пафосное в момоент бана
|
||||||
"ban_phrases_enable": false,
|
"ban_phrases_enable": false,
|
||||||
|
|
||||||
# Пафосные фраза, произносимые перед баном
|
# Пафосные фраза, произносимые перед баном
|
||||||
"ban_phrases": [
|
"ban_phrases": [
|
||||||
"Во имя Луны, я накажу тебя!",
|
"Во имя Луны, я накажу тебя!",
|
||||||
@@ -103,18 +141,12 @@
|
|||||||
"Мы сражаемся за мир и равенство!",
|
"Мы сражаемся за мир и равенство!",
|
||||||
"Мы — надежда для всех, кто нуждается в защите!",
|
"Мы — надежда для всех, кто нуждается в защите!",
|
||||||
"Мы — воительницы справедливости, и ничто не остановит нас!"
|
"Мы — воительницы справедливости, и ничто не остановит нас!"
|
||||||
],
|
|
||||||
# Имя пользователя для авторизации на сервере
|
|
||||||
"user": "buny@jabber.tld",
|
|
||||||
# Пароль для авторизации
|
|
||||||
"password": "secret",
|
|
||||||
# Jabber-каналы, к которым бот попробует присоединиться при старте
|
|
||||||
"channels": [
|
|
||||||
"my_channel@conference.jabber.tld",
|
|
||||||
"another_channel@conference.jabber.tld"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# command sign символ-суффикс команды, например !rehash или !help. По-умолчанию "!".
|
||||||
|
"csign" : "!",
|
||||||
|
|
||||||
# Многословность логов. Если не задано, то info. Debug - ДЕЙСТВИТЕЛЬНО вербозный уровень логгирования.
|
# Многословность логов. Если не задано, то info. Debug - ДЕЙСТВИТЕЛЬНО вербозный уровень логгирования.
|
||||||
# Есть ещё trace :)
|
# Есть ещё trace :)
|
||||||
"loglevel": "debug",
|
"loglevel": "debug",
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := cmd(v); err != nil {
|
||||||
|
gTomb.Kill(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
lastActivity = lastServerActivity
|
lastActivity = lastServerActivity
|
||||||
|
|
||||||
if muc, _ := strings.CutSuffix(v.Remote, "/"); muc != "" {
|
if muc, _ := strings.CutSuffix(v.Remote, "/"); muc != "" {
|
||||||
@@ -55,6 +61,12 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
// eleksir@jabber.ru/array.lan - это если мы работаем через ростер
|
// eleksir@jabber.ru/array.lan - это если мы работаем через ростер
|
||||||
log.Debugf("Private message: %s", v.Text)
|
log.Debugf("Private message: %s", v.Text)
|
||||||
|
|
||||||
|
if err := cmd(v); err != nil {
|
||||||
|
gTomb.Kill(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
lastActivity = lastServerActivity
|
lastActivity = lastServerActivity
|
||||||
|
|
||||||
// Внезапно, ошибки. По идее они должны ассоциироваться с отправляемыми сообщениями, но по ходу это
|
// Внезапно, ошибки. По идее они должны ассоциироваться с отправляемыми сообщениями, но по ходу это
|
||||||
@@ -539,7 +551,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
var p xmpp.Presence
|
var p xmpp.Presence
|
||||||
_ = json.Unmarshal([]byte(presenceJSONstring), &p)
|
_ = json.Unmarshal([]byte(presenceJSONstring), &p)
|
||||||
|
|
||||||
if p.JID == v.JID {
|
if p.From == v.From {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,15 +563,29 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
// Участник пришёл
|
// Участник пришёл
|
||||||
default:
|
default:
|
||||||
var presenceJSONStrings []string
|
var presenceJSONStrings []string
|
||||||
|
var newPresenceJSONStrings []string
|
||||||
var presenceJSONBytes []byte
|
var presenceJSONBytes []byte
|
||||||
|
|
||||||
if presenceJSONInterface, present := roomPresences.Get(room); present {
|
if presenceJSONInterface, present := roomPresences.Get(room); present {
|
||||||
presenceJSONStrings = interfaceToStringSlice(presenceJSONInterface)
|
presenceJSONStrings = interfaceToStringSlice(presenceJSONInterface)
|
||||||
}
|
}
|
||||||
|
|
||||||
presenceJSONBytes, _ = json.Marshal(v)
|
for _, presenceJSONString := range presenceJSONStrings {
|
||||||
presenceJSONStrings = append(presenceJSONStrings, string(presenceJSONBytes))
|
var p xmpp.Presence
|
||||||
roomPresences.Set(room, presenceJSONStrings)
|
_ = json.Unmarshal([]byte(presenceJSONString), &p)
|
||||||
|
|
||||||
|
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для grouchat)
|
||||||
|
// просто замещаем его.
|
||||||
|
if p.From == v.From {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newPresenceJSONStrings = append(newPresenceJSONStrings, presenceJSONString)
|
||||||
|
}
|
||||||
|
|
||||||
|
presenceJSONBytes, _ = json.Marshal(v) //nolint:errchkjson
|
||||||
|
newPresenceJSONStrings = append(newPresenceJSONStrings, string(presenceJSONBytes))
|
||||||
|
roomPresences.Set(room, newPresenceJSONStrings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате.
|
// Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате.
|
||||||
|
|||||||
123
lib.go
123
lib.go
@@ -2,11 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -68,14 +70,19 @@ func sigHandler() {
|
|||||||
|
|
||||||
// readConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге
|
// readConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге
|
||||||
// нет.
|
// нет.
|
||||||
func readConfig() { //nolint:gocognit,gocyclo
|
func readConfig() error { //nolint:gocognit,gocyclo
|
||||||
configLoaded := false
|
var (
|
||||||
executablePath, err := os.Executable()
|
err error
|
||||||
|
executablePath string
|
||||||
|
configLoaded = false
|
||||||
|
)
|
||||||
|
|
||||||
|
executablePath, err = os.Executable()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to get current executable path: %s", err)
|
err := fmt.Errorf("unable to get current executable path: %w", err)
|
||||||
|
|
||||||
os.Exit(1)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
|
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
|
||||||
@@ -210,8 +217,7 @@ func readConfig() { //nolint:gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Jabber.Nick == "" {
|
if sampleConfig.Jabber.Nick == "" {
|
||||||
log.Errorf("Jabber nick is not defined in config, quitting")
|
return errors.New("jabber nick is not defined in config, quitting") //nolint:goerr113
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Jabber.Resource == "" {
|
if sampleConfig.Jabber.Resource == "" {
|
||||||
@@ -227,13 +233,15 @@ func readConfig() { //nolint:gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Если sampleConfig.Jabber.Password не задан, то авторизации не будет
|
// Если sampleConfig.Jabber.Password не задан, то авторизации не будет
|
||||||
// Если sampleConfig.Jabber.Sasl не задан, то авторизация происходит через NickServ
|
|
||||||
|
|
||||||
// Если sampleConfig.Jabber.BanPhrasesEnable не задан, то он false
|
// Если не задано ни одного мастера, то бот сам себе мастер
|
||||||
|
if len(sampleConfig.Jabber.BotMasters) == 0 {
|
||||||
|
sampleConfig.Jabber.BotMasters[0] = sampleConfig.Jabber.User
|
||||||
|
}
|
||||||
|
|
||||||
// Если список фраз, с которыми банят пустой, то вносим в него одну позицию с пустой строкой
|
// Нам бот нужен в каких-то чат-румах, а не "просто так"
|
||||||
if len(sampleConfig.Jabber.BanPhrases) == 0 {
|
if len(sampleConfig.Jabber.Channels) < 1 {
|
||||||
sampleConfig.Jabber.BanPhrases[0] = ""
|
return errors.New("no jabber channels/rooms defined in config, quitting") //nolint:goerr113
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если список фраз с которыми стартует бот пустой, вносим в него 1 запись с пустой строкой
|
// Если список фраз с которыми стартует бот пустой, вносим в него 1 запись с пустой строкой
|
||||||
@@ -249,10 +257,15 @@ func readConfig() { //nolint:gocognit,gocyclo
|
|||||||
// Если sampleConfig.Jabber.RuntimeStatus.RotationTime не задан, то он равен 0
|
// Если sampleConfig.Jabber.RuntimeStatus.RotationTime не задан, то он равен 0
|
||||||
// Если sampleConfig.Jabber.RuntimeStatus.RotationSplayTime не задан, то он равен 0
|
// Если sampleConfig.Jabber.RuntimeStatus.RotationSplayTime не задан, то он равен 0
|
||||||
|
|
||||||
// Нам бот нужен в каких-то чат-румах, а не "просто так"
|
// Если sampleConfig.Jabber.BanPhrasesEnable не задан, то он false
|
||||||
if len(sampleConfig.Jabber.Channels) < 1 {
|
|
||||||
log.Errorf("No jabber channels/rooms defined in config, quitting")
|
// Если список фраз, с которыми банят пустой, то вносим в него одну позицию с пустой строкой
|
||||||
os.Exit(1)
|
if len(sampleConfig.Jabber.BanPhrases) == 0 {
|
||||||
|
sampleConfig.Jabber.BanPhrases[0] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if sampleConfig.CSign == "" {
|
||||||
|
sampleConfig.CSign = "!"
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Loglevel == "" {
|
if sampleConfig.Loglevel == "" {
|
||||||
@@ -271,19 +284,26 @@ func readConfig() { //nolint:gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !configLoaded {
|
if !configLoaded {
|
||||||
log.Error("Config was not loaded! Refusing to start.")
|
return errors.New("config was not loaded!") //nolint:goerr113
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return err //nolint:wrapcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
// readWhitelist читает и валидирует белые списки пользователей.
|
// readWhitelist читает и валидирует белые списки пользователей.
|
||||||
func readWhitelist() {
|
func readWhitelist() error {
|
||||||
whitelistLoaded := false
|
var (
|
||||||
executablePath, err := os.Executable()
|
whitelistLoaded = false
|
||||||
|
err error
|
||||||
|
executablePath string
|
||||||
|
)
|
||||||
|
|
||||||
|
executablePath, err = os.Executable()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to get current executable path: %s", err)
|
err = fmt.Errorf("unable to get current executable path: %w", err)
|
||||||
os.Exit(1)
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
whitelistJSONPath := fmt.Sprintf("%s/data/whitelist.json", filepath.Dir(executablePath))
|
whitelistJSONPath := fmt.Sprintf("%s/data/whitelist.json", filepath.Dir(executablePath))
|
||||||
@@ -360,21 +380,26 @@ func readWhitelist() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !whitelistLoaded {
|
if !whitelistLoaded {
|
||||||
log.Error("Whitelist was not loaded! Refusing to start.")
|
return errors.New("whitelist was not loaded!") //nolint:goerr113
|
||||||
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// readBlacklist читает и валидирует чёрные списки пользователей.
|
// readBlacklist читает и валидирует чёрные списки пользователей.
|
||||||
func readBlacklist() {
|
func readBlacklist() error {
|
||||||
blacklistLoaded := false
|
var (
|
||||||
executablePath, err := os.Executable()
|
blacklistLoaded = false
|
||||||
|
err error
|
||||||
|
executablePath string
|
||||||
|
)
|
||||||
|
|
||||||
|
executablePath, err = os.Executable()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to get current executable path: %s", err)
|
err = fmt.Errorf("unable to get current executable path: %w", err)
|
||||||
|
|
||||||
os.Exit(1)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
whitelistJSONPath := fmt.Sprintf("%s/data/blacklist.json", filepath.Dir(executablePath))
|
whitelistJSONPath := fmt.Sprintf("%s/data/blacklist.json", filepath.Dir(executablePath))
|
||||||
@@ -451,10 +476,10 @@ func readBlacklist() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !blacklistLoaded {
|
if !blacklistLoaded {
|
||||||
log.Error("Blacklist was not loaded! Refusing to start.")
|
return errors.New("blacklist was not loaded!")
|
||||||
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// establishConnection устанавливает соединение с jabber-сервером.
|
// establishConnection устанавливает соединение с jabber-сервером.
|
||||||
@@ -582,7 +607,7 @@ func joinMuc(room string) {
|
|||||||
|
|
||||||
go RotateStatus(room)
|
go RotateStatus(room)
|
||||||
|
|
||||||
// Время прoверить участников на предмет злобности
|
// Время проверить участников на предмет злобности
|
||||||
namesInterface, present := roomPresences.Get(room)
|
namesInterface, present := roomPresences.Get(room)
|
||||||
|
|
||||||
// Если room есть в списке presence-ов, то фигачим. room там должен быть, просто обязан.
|
// Если room есть в списке presence-ов, то фигачим. room там должен быть, просто обязан.
|
||||||
@@ -855,4 +880,32 @@ func interfaceToStringSlice(iface interface{}) []string {
|
|||||||
return mySlice
|
return mySlice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRealJIDfromNick достаёт из запомненных presence-ов по даденному nick-у real jid с resource-ом. Nick должен
|
||||||
|
// содержать имя конфы, откуда участник.
|
||||||
|
func getRealJIDfromNick(fullNick string) string {
|
||||||
|
var p xmpp.Presence
|
||||||
|
|
||||||
|
room := (strings.SplitN(fullNick, "/", 2))[0]
|
||||||
|
|
||||||
|
// Достанем presence участника
|
||||||
|
presenceJSONInterface, present := roomPresences.Get(room)
|
||||||
|
|
||||||
|
// Никого нет дома
|
||||||
|
if !present {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
presenceJSONStrings := interfaceToStringSlice(presenceJSONInterface)
|
||||||
|
|
||||||
|
for _, presepresenceJSONString := range presenceJSONStrings {
|
||||||
|
_ = json.Unmarshal([]byte(presepresenceJSONString), &p)
|
||||||
|
|
||||||
|
if p.From == fullNick {
|
||||||
|
return p.JID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
19
main.go
19
main.go
@@ -24,7 +24,11 @@ func init() {
|
|||||||
TimestampFormat: "2006-01-02 15:04:05",
|
TimestampFormat: "2006-01-02 15:04:05",
|
||||||
})
|
})
|
||||||
|
|
||||||
readConfig()
|
if err := readConfig(); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// no panic
|
// no panic
|
||||||
switch config.Loglevel {
|
switch config.Loglevel {
|
||||||
@@ -67,8 +71,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Самое время вгрузить белые и чёрные списки, если что - получим ошибку в лог.
|
// Самое время вгрузить белые и чёрные списки, если что - получим ошибку в лог.
|
||||||
readWhitelist()
|
if err := readWhitelist(); err != nil {
|
||||||
readBlacklist()
|
log.Error(err)
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := readBlacklist(); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// github.com/mattn/go-xmpp пишет в stdio, нам этого не надо, ловим выхлоп его в logrus с уровнем trace
|
// github.com/mattn/go-xmpp пишет в stdio, нам этого не надо, ловим выхлоп его в logrus с уровнем trace
|
||||||
xmpp.DebugWriter = log.WithFields(log.Fields{"logger": "stdlib"}).WriterLevel(log.TraceLevel)
|
xmpp.DebugWriter = log.WithFields(log.Fields{"logger": "stdlib"}).WriterLevel(log.TraceLevel)
|
||||||
|
|||||||
2
types.go
2
types.go
@@ -23,6 +23,7 @@ type myConfig struct {
|
|||||||
Resource string `json:"resource,omitempty"`
|
Resource string `json:"resource,omitempty"`
|
||||||
User string `json:"user,omitempty"`
|
User string `json:"user,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
BotMasters []string `json:"bot_masters"`
|
||||||
Channels []string `json:"channels"`
|
Channels []string `json:"channels"`
|
||||||
StartupStatus []string `json:"startup_status,omitempty"`
|
StartupStatus []string `json:"startup_status,omitempty"`
|
||||||
RuntimeStatus struct {
|
RuntimeStatus struct {
|
||||||
@@ -34,6 +35,7 @@ type myConfig struct {
|
|||||||
BanPhrases []string `json:"ban_phrases,omitempty"`
|
BanPhrases []string `json:"ban_phrases,omitempty"`
|
||||||
} `json:"jabber,omitempty"`
|
} `json:"jabber,omitempty"`
|
||||||
|
|
||||||
|
CSign string `json:"csign,omitempty"`
|
||||||
Loglevel string `json:"loglevel,omitempty"`
|
Loglevel string `json:"loglevel,omitempty"`
|
||||||
Log string `json:"log,omitempty"`
|
Log string `json:"log,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user