Massive cleanup - coding style, tiny bugs, typos and other errors

This commit is contained in:
Sergei
2023-07-29 22:23:11 -07:00
committed by eleksir
parent 0f1177bd91
commit e8f8b3cb49
18 changed files with 767 additions and 392 deletions

235
lib.go
View File

@@ -15,7 +15,7 @@ import (
"golang.org/x/exp/slices"
)
// Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения
// sigHandler() Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения.
func sigHandler() {
log.Debug("Installing signal handler")
@@ -28,19 +28,19 @@ func sigHandler() {
case syscall.SIGQUIT:
log.Infoln("Got SIGQUIT, quitting")
// Заходим на новую итерацию, если у нас "неинтересный" сигнал
// Заходим на новую итерацию, если у нас "неинтересный" сигнал.
default:
continue
}
// Чтобы не срать в логи ошибками, проставим shutdown state приложения в true
// Чтобы не срать в логи ошибками, проставим shutdown state приложения в true.
shutdown = true
if isConnected && !shutdown {
log.Debug("Try to set our presence to Unavailable and status to Offline")
// Вот тут понадобится коллекция известных пользователей, чтобы им разослать presence, что бот свалил в offline
// Пока за неимением лучшего сообщим об этом самим себе
// Пока за неимением лучшего сообщим об этом самим себе.
for _, room := range roomsConnected {
if _, err := talk.SendPresence(
xmpp.Presence{ //nolint:exhaustruct
@@ -53,7 +53,7 @@ func sigHandler() {
}
}
// И закрываем соединение
// И закрываем соединение.
log.Debugf("Closing connection to jabber server")
if err := talk.Close(); err != nil {
@@ -65,13 +65,16 @@ func sigHandler() {
}
}
// Читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет
func readConfig() {
// readConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге
// нет.
func readConfig() { //nolint:gocognit
configLoaded := false
executablePath, err := os.Executable()
if err != nil {
log.Errorf("Unable to get current executable path: %s", err)
os.Exit(1)
}
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
@@ -86,12 +89,12 @@ func readConfig() {
for _, location := range locations {
fileInfo, err := os.Stat(location)
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём.
if err != nil {
continue
}
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата.
if fileInfo.Size() > 65535 {
log.Warnf("Config file %s is too long for config, skipping", location)
@@ -100,7 +103,7 @@ func readConfig() {
buf, err := os.ReadFile(location)
// Не удалось прочитать, попробуем следующего кандидата
// Не удалось прочитать, попробуем следующего кандидата.
if err != nil {
log.Warnf("Skip reading config file %s: %s", location, err)
@@ -110,18 +113,21 @@ func readConfig() {
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var sampleConfig myConfig
var tmp map[string]interface{}
var (
sampleConfig myConfig
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
// Не удалось распарсить - попробуем следующего кандидата.
if err != nil {
log.Warnf("Skip parsing config file %s: %s", location, err)
continue
}
tmpjson, err := json.Marshal(tmp)
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
@@ -130,7 +136,7 @@ func readConfig() {
continue
}
if err := json.Unmarshal(tmpjson, &sampleConfig); err != nil {
if err := json.Unmarshal(tmpJSON, &sampleConfig); err != nil {
log.Warnf("Skip parsing config file %s: %s", location, err)
continue
@@ -138,20 +144,22 @@ func readConfig() {
// Значения для Jabber-клиента
if sampleConfig.Jabber.Server == "" {
sampleConfig.Jabber.Server = "localhost"
log.Errorf("Jabber server is not defined in config, using localhost")
log.Error("Jabber server is not defined in config, using localhost")
sampleConfig.Jabber.Server = "localhost" //nolint:wsl
}
if sampleConfig.Jabber.Port == 0 {
sampleConfig.Jabber.Port = 5552
sampleConfig.Jabber.Port = 5222
if sampleConfig.Jabber.Ssl {
if !sampleConfig.Jabber.StartTLS {
sampleConfig.Jabber.Port = 5553
sampleConfig.Jabber.Port = 5223
log.Info("Jabber port is not defined in config, using 5223")
} else {
log.Info("Jabber port is not defined in config, using 5222")
}
}
log.Infof("Jabber port is not defined in config, using 6667")
}
if !sampleConfig.Jabber.Ssl {
@@ -166,26 +174,38 @@ func readConfig() {
if sampleConfig.Jabber.ConnectionTimeout == 0 {
sampleConfig.Jabber.ConnectionTimeout = 10
log.Info("Jabber server connection_timeout not defined in config, using 10 seconds")
}
if sampleConfig.Jabber.ReconnectDelay == 0 {
sampleConfig.Jabber.ReconnectDelay = 3
log.Info("Jabber server reconnect_delay not defined in config, using 3 seconds")
}
if sampleConfig.Jabber.ServerPingDelay == 0 {
sampleConfig.Jabber.ServerPingDelay = 60
log.Info("Jabber server_ping_delay not defined in config, using 60 seconds")
}
if sampleConfig.Jabber.MucPingDelay == 0 {
sampleConfig.Jabber.MucPingDelay = 900
log.Info("Jabber muc_ping_delay not defined in config, using 900 seconds")
}
if sampleConfig.Jabber.MucRejoinDelay == 0 {
sampleConfig.Jabber.MucRejoinDelay = 3
log.Info("Jabber muc_rejoin_delay not defined in config, using 3 seconds")
}
if sampleConfig.Jabber.PingSplayDelay == 0 {
sampleConfig.Jabber.PingSplayDelay = 3
log.Info("Jabber ping_splay_delay not defined in config, using 3 seconds")
}
if sampleConfig.Jabber.Nick == "" {
@@ -195,10 +215,14 @@ func readConfig() {
if sampleConfig.Jabber.Resource == "" {
sampleConfig.Jabber.Resource = "buny bot"
log.Info("Jabber resource not defined in config, using buny bot")
}
if sampleConfig.Jabber.User == "" {
sampleConfig.Jabber.User = fmt.Sprintf("%s@%s", sampleConfig.Jabber.Nick, sampleConfig.Jabber.Server)
log.Infof("Jabber user not defined in config, guessing, it can be %s", sampleConfig.Jabber.User)
}
// Если sampleConfig.Jabber.Password не задан, то авторизации не будет
@@ -212,13 +236,15 @@ func readConfig() {
if sampleConfig.Loglevel == "" {
sampleConfig.Loglevel = "info"
log.Info("loglevel not defined in config, using info")
}
// sampleConfig.Log = "" if not set
config = sampleConfig
configLoaded = true
log.Infof("Using %s as config file", location)
log.Infof("Using %s as config file", location) //nolint:wsl
break
}
@@ -229,13 +255,14 @@ func readConfig() {
}
}
// Читает и валидирует белые списки пользователей
// readWhitelist() читает и валидирует белые списки пользователей.
func readWhitelist() {
whitelistLoaded := false
executablePath, err := os.Executable()
if err != nil {
log.Errorf("Unable to get current executable path: %s", err)
os.Exit(1)
}
whitelistJSONPath := fmt.Sprintf("%s/data/whitelist.json", filepath.Dir(executablePath))
@@ -274,8 +301,11 @@ func readWhitelist() {
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var sampleWhitelist myWhiteList
var tmp map[string]interface{}
var (
sampleWhitelist myWhiteList
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
@@ -285,7 +315,7 @@ func readWhitelist() {
continue
}
tmpjson, err := json.Marshal(tmp)
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
@@ -294,7 +324,7 @@ func readWhitelist() {
continue
}
if err := json.Unmarshal(tmpjson, &sampleWhitelist); err != nil {
if err := json.Unmarshal(tmpJSON, &sampleWhitelist); err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
@@ -302,6 +332,7 @@ func readWhitelist() {
whiteList = sampleWhitelist
whitelistLoaded = true
log.Infof("Using %s as whiteList file", location)
break
@@ -309,17 +340,20 @@ func readWhitelist() {
if !whitelistLoaded {
log.Error("Whitelist was not loaded! Refusing to start.")
os.Exit(1)
}
}
// Читает и валидирует чёрные списки пользователей
// readBlacklist() читает и валидирует чёрные списки пользователей.
func readBlacklist() {
blacklistLoaded := false
executablePath, err := os.Executable()
if err != nil {
log.Errorf("Unable to get current executable path: %s", err)
os.Exit(1)
}
whitelistJSONPath := fmt.Sprintf("%s/data/blacklist.json", filepath.Dir(executablePath))
@@ -358,8 +392,11 @@ func readBlacklist() {
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var sampleBlacklist myBlackList
var tmp map[string]interface{}
var (
sampleBlacklist myBlackList
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
@@ -369,7 +406,7 @@ func readBlacklist() {
continue
}
tmpjson, err := json.Marshal(tmp)
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
@@ -378,7 +415,7 @@ func readBlacklist() {
continue
}
if err := json.Unmarshal(tmpjson, &sampleBlacklist); err != nil {
if err := json.Unmarshal(tmpJSON, &sampleBlacklist); err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
@@ -386,6 +423,7 @@ func readBlacklist() {
blackList = sampleBlacklist
blacklistLoaded = true
log.Infof("Using %s as blacklist file", location)
break
@@ -393,10 +431,12 @@ func readBlacklist() {
if !blacklistLoaded {
log.Error("Blacklist was not loaded! Refusing to start.")
os.Exit(1)
}
}
// establishConnection() устанавливает соединение с jabber-сервером.
func establishConnection() {
var err error
@@ -404,39 +444,32 @@ func establishConnection() {
return
}
// проставляем глобальные переменные
// Проставляем глобальные переменные.
connecting = true
isConnected = false
roomsConnected = make([]string, 0)
for {
talk, err = options.NewClient()
talk, err = options.NewClient()
if err != nil {
log.Error(err)
if err != nil {
log.Error(err)
os.Exit(1)
} else {
break
}
os.Exit(1)
}
// По идее keepalive должен же проходить только, если мы уже на сервере, так?
for {
if _, err := talk.SendKeepAlive(); err != nil {
log.Errorf("Try to send initial KeepAlive, got error: %s", err)
time.Sleep(time.Duration(config.Jabber.ReconnectDelay) * time.Second)
if _, err := talk.SendKeepAlive(); err != nil {
log.Errorf("Try to send initial KeepAlive, got error: %s", err)
time.Sleep(time.Duration(config.Jabber.ReconnectDelay) * time.Second)
// Понятно, что мы уже не в процессе установления соединения, а произошла ошибка
connecting = false
os.Exit(1)
} else {
log.Info("Connected")
// Понятно, что мы уже не в процессе установления соединения, а произошла ошибка
connecting = false
break
}
os.Exit(1)
}
log.Info("Connected")
// Джойнимся к чятикам, но делаем это в фоне, чтобы не блочиться на ошибках, например, если бота забанили
for _, room := range config.Jabber.Channels {
go joinMuc(room)
@@ -467,22 +500,7 @@ func establishConnection() {
}
}
func reEstablishConnection() {
// Серверные capabilities нам *уже* неизвестны
serverCapsQueried = false
serverCapsList = NewCollection()
mucCapsList = NewCollection()
lastMucActivity = NewCollection()
// Пока соединение не установлено, мы про пинги ничего не знаем
serverPingTimestampRx = time.Now().Unix() // Считаем, что если коннект запустился, то первый пинг успешен
serverPingTimestampTx = 0
time.Sleep(time.Duration(config.Jabber.ReconnectDelay) * time.Second)
connecting = false
establishConnection()
}
// Джойнится к конференциям/каналам/комнатам в джаббере
// joinMuc(room string) джойнится к конференциям/каналам/комнатам в джаббере.
func joinMuc(room string) {
log.Debugf("Sending disco#info from %s to %s", talk.JID(), room)
@@ -492,11 +510,13 @@ func joinMuc(room string) {
os.Exit(1)
}
// Ждём, пока muc нам вернёт список фичей
// Ждём, пока muc нам вернёт список фичей.
for i := 0; i < (20 * int(config.Jabber.ConnectionTimeout)); i++ {
var myRoom interface{}
var supported bool
var exist bool
var (
myRoom interface{}
supported bool
exist bool
)
time.Sleep(50 * time.Millisecond)
@@ -508,11 +528,11 @@ func joinMuc(room string) {
if supported, exist = myRoom.(map[string]bool)["muc_unsecured"]; exist {
if supported {
break
} else {
log.Infof("Unable to join to password-protected room. Don't know how to enter passwords :)")
return
}
log.Infof("Unable to join to password-protected room. Don't know how to enter passwords :)")
return
}
}
@@ -520,20 +540,34 @@ func joinMuc(room string) {
log.Errorf("Unable to join to MUC: %s", room)
os.Exit(1)
} else {
log.Infof("Joining to MUC: %s", room)
}
// Ждём, когда прилетит presence из комнаты, тогда мы точно знаем, что мы вошли
log.Infof("Joining to MUC: %s", room)
// Ждём, когда прилетит presence из комнаты, тогда мы точно знаем, что мы вошли.
entered := false
for i := 0; i < (20 * int(config.Jabber.ConnectionTimeout)); i++ {
time.Sleep(50 * time.Millisecond)
if slices.Contains(roomsConnected, room) {
entered = true
break
}
}
// Вот теперь точно можно слать статус
if !entered {
log.Errorf(
"Unable to enter to MUC %s, join timeout after %d seconds (server does not return my presence for this room)",
room,
20*int(config.Jabber.ConnectionTimeout)+1,
)
return
}
// Вот теперь точно можно слать статус.
log.Infof("Joined to MUC: %s", room)
if _, err := talk.SendPresence(
@@ -547,8 +581,9 @@ func joinMuc(room string) {
}
}
// Проверяет живость соединения с сервером
func probeServerLiveness() {
// probeServerLiveness() проверяет живость соединения с сервером. Для многих серверов обязательная штука, без которой
// они выкидывают клиента через некоторое время неактивности.
func probeServerLiveness() { //nolint:gocognit
for {
if shutdown {
return
@@ -569,8 +604,10 @@ func probeServerLiveness() {
// }
if serverCapsQueried { // Сервер ответил на disco#info
var value interface{}
var exist bool
var (
value interface{}
exist bool
)
value, exist = serverCapsList.Get("urn:xmpp:ping")
@@ -602,18 +639,18 @@ func probeServerLiveness() {
if err := talk.PingC2S(talk.JID(), config.Jabber.Server); err != nil {
os.Exit(1)
} else {
serverPingTimestampTx = time.Now().Unix()
}
serverPingTimestampTx = time.Now().Unix()
}
} else { // Первая пуля пока не вылетела, отправляем
log.Debugf("Sending first c2s ping from %s to %s", talk.JID(), config.Jabber.Server)
if err := talk.PingC2S(talk.JID(), config.Jabber.Server); err != nil {
os.Exit(1)
} else {
serverPingTimestampTx = time.Now().Unix()
}
serverPingTimestampTx = time.Now().Unix()
}
// Сервер не анонсировал, что умеет в c2s пинги
@@ -622,8 +659,6 @@ func probeServerLiveness() {
if _, err := talk.SendKeepAlive(); err != nil {
os.Exit(1)
continue
}
}
} else { // Сервер не ответил на disco#info
@@ -631,28 +666,28 @@ func probeServerLiveness() {
if _, err := talk.SendKeepAlive(); err != nil {
os.Exit(1)
continue
}
}
}
}
// Пингует MUC-и, нужно для проверки, что клиент ещё находится в MUC-е
// probeMUCLiveness() Пингует MUC-и, нужно для проверки, что клиент ещё находится в MUC-е.
func probeMUCLiveness() {
for {
for _, room := range roomsConnected {
var exist bool
var lastActivityTs interface{}
var (
exist bool
lastActivityTS interface{}
)
// Если записи про комнату нету, то пинговать её бессмысленно.
if lastActivityTs, exist = lastMucActivity.Get(room); !exist {
if lastActivityTS, exist = lastMucActivity.Get(room); !exist {
continue
}
// Если время последней активности в чятике не превысило
// config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay, ничего не пингуем
if (time.Now().Unix() - lastActivityTs.(int64)) < (config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay) {
// config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay, ничего не пингуем.
if (time.Now().Unix() - lastActivityTS.(int64)) < (config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay) {
continue
}
@@ -673,10 +708,10 @@ func probeMUCLiveness() {
roomMap, exist = mucCapsList.Get(room)
// Пинги комнаты проводим, только если она записана, как прошедшая disco#info и поддерживающая
// Server Optimization
// Server Optimization.
if exist && roomMap.(map[string]bool)["http://jabber.org/protocol/muc#self-ping-optimization"] {
go func(room string) {
// Небольшая рандомная задержка перед пингом комнаты
// Небольшая рандомная задержка перед пингом комнаты.
sleepTime := time.Duration(rand.Int63n(1000*config.Jabber.PingSplayDelay)) * time.Millisecond //nolint:gosec
time.Sleep(sleepTime)