Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
484bfa9ba0 | ||
|
|
8f8073ca3e | ||
|
|
7bcc407857 | ||
|
|
8c24a1d8c6 |
83
.golangci.yml
Normal file
83
.golangci.yml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
version: "2"
|
||||||
|
run:
|
||||||
|
go: "1.21"
|
||||||
|
modules-download-mode: readonly
|
||||||
|
tests: false
|
||||||
|
allow-parallel-runners: true
|
||||||
|
linters:
|
||||||
|
default: none
|
||||||
|
enable:
|
||||||
|
- asasalint
|
||||||
|
- asciicheck
|
||||||
|
- bodyclose
|
||||||
|
- containedctx
|
||||||
|
- contextcheck
|
||||||
|
- decorder
|
||||||
|
- durationcheck
|
||||||
|
- errcheck
|
||||||
|
- errchkjson
|
||||||
|
- errorlint
|
||||||
|
- exhaustive
|
||||||
|
- ginkgolinter
|
||||||
|
- gocheckcompilerdirectives
|
||||||
|
- gocritic
|
||||||
|
- godot
|
||||||
|
- gosec
|
||||||
|
- gosmopolitan
|
||||||
|
- govet
|
||||||
|
- grouper
|
||||||
|
- importas
|
||||||
|
- ineffassign
|
||||||
|
- ireturn
|
||||||
|
- makezero
|
||||||
|
- mirror
|
||||||
|
- nakedret
|
||||||
|
- nilerr
|
||||||
|
- nilnil
|
||||||
|
- nlreturn
|
||||||
|
- noctx
|
||||||
|
- nolintlint
|
||||||
|
- nonamedreturns
|
||||||
|
- predeclared
|
||||||
|
- promlinter
|
||||||
|
- reassign
|
||||||
|
- revive
|
||||||
|
- rowserrcheck
|
||||||
|
- staticcheck
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- wastedassign
|
||||||
|
- whitespace
|
||||||
|
- wsl
|
||||||
|
settings:
|
||||||
|
gosec:
|
||||||
|
excludes:
|
||||||
|
# Оно не используется для повышения безопасности или каких-то таких секъюрных вещей, оно используется для выборки
|
||||||
|
# произвольного элемента из массива, так что ИДИ НАХУЙ с этой проверкой и не еби мозги липовой безопасностью.
|
||||||
|
- G404
|
||||||
|
# Не еби мозги. Сертификаты для локалхоста или для локальной сети могут быть невалидными, если ты сможешь
|
||||||
|
# выписать валидный серт для локалхоста, тебе миллионы разработчиков поставят золотой памятник. Просто потому
|
||||||
|
# что эти пидоры-безпасники всрали всё что только можно всрать своими дебильными политиками.
|
||||||
|
- G402
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
- vendor$
|
||||||
3
Makefile
3
Makefile
@@ -6,8 +6,7 @@ BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
|
|||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
${GOOPTS} go build ${BUILDOPTS} -o aleesa-irc-go types.go globals.go collection.go irc-user-mode.go \
|
${GOOPTS} go build ${BUILDOPTS} -o aleesa-irc-go .
|
||||||
settings-db-util.go util.go msg-parsers.go aleesa-irc-go-lib.go main.go
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean
|
go clean
|
||||||
|
|||||||
@@ -4,22 +4,22 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"aleesa-irc-go/internal/boolcollection"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
irc "github.com/thoj/go-ircevent"
|
irc "github.com/thoj/go-ircevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ircClientInit горутинка для работы с протоколом irc
|
// ircClientInit горутинка для работы с протоколом irc.
|
||||||
func ircClientRun() {
|
func ircClientRun() {
|
||||||
for {
|
for !shutdown {
|
||||||
if shutdown {
|
// Иницализируем irc-клиента.
|
||||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
// TODO: make use of capabilities: https://defs.ircdocs.horse/defs/clientcaps .
|
||||||
break
|
// TODO: make use of tags caps https://defs.ircdocs.horse/defs/tags .
|
||||||
}
|
|
||||||
|
|
||||||
// Иницализируем irc-клиента
|
|
||||||
serverString := fmt.Sprintf("%s:%d", config.Irc.Server, config.Irc.Port)
|
serverString := fmt.Sprintf("%s:%d", config.Irc.Server, config.Irc.Port)
|
||||||
log.Debugf("Preparing to connect to %s", serverString)
|
log.Debugf("Preparing to connect to %s", serverString)
|
||||||
log.Debugf("Using nick %s and username %s", config.Irc.Nick, config.Irc.User)
|
log.Debugf("Using nick %s and username %s", config.Irc.Nick, config.Irc.User)
|
||||||
@@ -29,10 +29,12 @@ func ircClientRun() {
|
|||||||
|
|
||||||
if config.Irc.Ssl {
|
if config.Irc.Ssl {
|
||||||
log.Debug("Force use ssl for connection")
|
log.Debug("Force use ssl for connection")
|
||||||
|
|
||||||
ircClient.UseTLS = true
|
ircClient.UseTLS = true
|
||||||
|
|
||||||
if !config.Irc.SslVerify {
|
if !config.Irc.SslVerify {
|
||||||
log.Debug("Skip server certificate validation")
|
log.Debug("Skip server certificate validation")
|
||||||
|
|
||||||
ircClient.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
ircClient.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
} else {
|
} else {
|
||||||
log.Debug("Force server certificate validation")
|
log.Debug("Force server certificate validation")
|
||||||
@@ -47,13 +49,48 @@ func ircClientRun() {
|
|||||||
ircClient.UseSASL = true
|
ircClient.UseSASL = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Навесим коллбэков на некоторые ответы сервера на наши запросы
|
// Навесим коллбэков на некоторые ответы сервера на наши запросы.
|
||||||
|
|
||||||
|
// 001 RPL_WELCOME уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||||
|
|
||||||
|
// Сделаем уже что-то полезное! Motd нам уже прислали и теперь можно авторизоваться и джойниться
|
||||||
|
ircClient.AddCallback("004", func(e *irc.Event) {
|
||||||
|
// Из это строки мы можем узнать, какие флаги для user MODE и channem MODE можно навешивать.
|
||||||
|
// Как минимум, эта строка нам нужна, чтобы выяснить, можем ли мы взять себе +B, мы же бот :).
|
||||||
|
e.Connection.Lock()
|
||||||
|
|
||||||
|
// Формат строки с MODE-ами https://datatracker.ietf.org/doc/html/rfc2812#section-5.1 .
|
||||||
|
availableUserModes = boolcollection.NewCollection()
|
||||||
|
availableChanModes = boolcollection.NewCollection()
|
||||||
|
|
||||||
|
log.Debugf("Add %s to list of available user modes", e.Arguments[3])
|
||||||
|
|
||||||
|
for _, mode := range strings.Split(e.Arguments[3], "") {
|
||||||
|
availableUserModes.Set(mode, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
availableUserModes.Set("announced", true)
|
||||||
|
|
||||||
|
log.Debugf("Add %s to list available channel modes", e.Arguments[4])
|
||||||
|
|
||||||
|
for _, mode := range strings.Split(e.Arguments[4], "") {
|
||||||
|
availableChanModes.Set(mode, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
availableChanModes.Set("announced", true)
|
||||||
|
|
||||||
|
e.Connection.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("005", func(e *irc.Event) {
|
||||||
|
// TODO: Make use of it. https://defs.ircdocs.horse/defs/isupport
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("319", func(e *irc.Event) {
|
ircClient.AddCallback("319", func(e *irc.Event) {
|
||||||
/* Это одна из строк с данными, прилетающая в ответ на запрос whois на определённого юзера
|
/* Это одна из строк с данными, прилетающая в ответ на запрос whois на определённого юзера
|
||||||
* Из этой строки нас интересует, на каких каналах пользователь op (то есть с префиксом @) или имеет voice
|
* Из этой строки нас интересует, на каких каналах пользователь op (то есть с префиксом @) или имеет voice
|
||||||
* (то есть с префиксом +) чтобы внести его в свою базу mode-ов.
|
* (то есть с префиксом +) чтобы внести его в свою базу mode-ов.
|
||||||
*/
|
*/
|
||||||
log.Debugf("319 RPL_WHOISCHANNELS, %s", e.Raw)
|
|
||||||
channelsWithModes := strings.Split(e.Arguments[2], " ")
|
channelsWithModes := strings.Split(e.Arguments[2], " ")
|
||||||
dstNick := e.Arguments[1]
|
dstNick := e.Arguments[1]
|
||||||
|
|
||||||
@@ -65,27 +102,30 @@ func ircClientRun() {
|
|||||||
|
|
||||||
if channel == mychannel {
|
if channel == mychannel {
|
||||||
// Для каждого канала формат строго 1 из 4-х: #channel | @#channel | +#channel | @+#channel
|
// Для каждого канала формат строго 1 из 4-х: #channel | @#channel | +#channel | @+#channel
|
||||||
switch {
|
switch modes {
|
||||||
case modes == "@+":
|
case "@+":
|
||||||
userModeUpdateUser(channel, dstNick, "+o")
|
userModeUpdateUser(channel, dstNick, "+o")
|
||||||
userModeUpdateUser(channel, dstNick, "+v")
|
userModeUpdateUser(channel, dstNick, "+v")
|
||||||
case modes == "@":
|
case "@":
|
||||||
userModeUpdateUser(channel, dstNick, "+o")
|
userModeUpdateUser(channel, dstNick, "+o")
|
||||||
case modes == "+":
|
case "+":
|
||||||
userModeUpdateUser(channel, dstNick, "+v")
|
userModeUpdateUser(channel, dstNick, "+v")
|
||||||
default:
|
default:
|
||||||
userModeUpdateUser(channel, dstNick, "-o")
|
userModeUpdateUser(channel, dstNick, "-o")
|
||||||
userModeUpdateUser(channel, dstNick, "-v")
|
userModeUpdateUser(channel, dstNick, "-v")
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 433 ERR_NICKNAMEINUSE уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||||
|
|
||||||
ircClient.AddCallback("353", func(e *irc.Event) {
|
ircClient.AddCallback("353", func(e *irc.Event) {
|
||||||
// Это одна из строк данных, прилетающая в ответ на запрос names, на канале
|
// Это одна из строк данных, прилетающая в ответ на запрос names, на канале
|
||||||
log.Debugf("353 RPL_NAMREPLY, %s", e.Raw)
|
e.Connection.Lock()
|
||||||
namesString := e.Arguments[3]
|
namesString := e.Arguments[3]
|
||||||
channel := e.Arguments[2]
|
channel := e.Arguments[2]
|
||||||
|
|
||||||
@@ -93,6 +133,7 @@ func ircClientRun() {
|
|||||||
if channel == mychannel {
|
if channel == mychannel {
|
||||||
for _, name := range strings.Split(namesString, " ") {
|
for _, name := range strings.Split(namesString, " ") {
|
||||||
mode := name[:1]
|
mode := name[:1]
|
||||||
|
|
||||||
var nick string
|
var nick string
|
||||||
|
|
||||||
if mode == "@" || mode == "+" {
|
if mode == "@" || mode == "+" {
|
||||||
@@ -101,25 +142,58 @@ func ircClientRun() {
|
|||||||
nick = name
|
nick = name
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch mode {
|
||||||
case mode == "@":
|
case "@":
|
||||||
userModeUpdateUser(channel, nick, "+o")
|
userModeUpdateUser(channel, nick, "+o")
|
||||||
case mode == "+":
|
case "+":
|
||||||
userModeUpdateUser(channel, nick, "+v")
|
userModeUpdateUser(channel, nick, "+v")
|
||||||
default:
|
default:
|
||||||
userModeUpdateUser(channel, nick, "-o")
|
userModeUpdateUser(channel, nick, "-o")
|
||||||
userModeUpdateUser(channel, nick, "-v")
|
userModeUpdateUser(channel, nick, "-v")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e.Connection.Unlock()
|
||||||
})
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("352", func(e *irc.Event) {
|
// Если сервер не может прочитать MOTD, то он может вернуть 422 ERR_NOMOTD, тоде самое навесим и туда тоже.
|
||||||
// Относительно бесполезная для нас команда. Не возвращает канала (в случае с ngircd), так что узнать mode
|
ircClient.AddCallback("376", func(e *irc.Event) {
|
||||||
// юзера на конкретном канале из этого невозможно. А остальные данные пока непонятно куда применять.
|
// TODO: make use of modes https://defs.ircdocs.horse/defs/usermodes
|
||||||
log.Warnf("352 RPL_WHOREPLY, %s", e.Raw)
|
// Если у нас есть доступный +B возьмём его себе, мы же бот.
|
||||||
|
announced, _ := availableUserModes.Get("announced")
|
||||||
|
botFlag, _ := availableUserModes.Get("B")
|
||||||
|
|
||||||
|
if announced {
|
||||||
|
if botFlag {
|
||||||
|
log.Info("Grabbing +B flag to mark me as bot")
|
||||||
|
ircClient.Mode(ircClient.GetNick(), "+B")
|
||||||
|
// TODO: Wait for it.
|
||||||
|
} else {
|
||||||
|
log.Info("Skip +B flag, server does not support it, noone will know that I am bot")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Этого по идее не должно быть.
|
||||||
|
log.Info("Skip +B flag, server did not announce modes (yet?), noone will know that I am bot")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Irc.Sasl && config.Irc.Password != "" && !nickIsUsed {
|
||||||
|
log.Info("Identifying via NickServ")
|
||||||
|
|
||||||
|
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
||||||
|
imChan <- iMsg{ChatID: "NickServ", Text: message}
|
||||||
|
}
|
||||||
|
// TODO: wait until +R flag been set? could be implemented with waiting for channel message.
|
||||||
|
|
||||||
|
log.Debug("Trying to join to preconfigured channels")
|
||||||
|
|
||||||
|
for _, channel := range config.Irc.Channels {
|
||||||
|
log.Infof("Joining to %s channel", channel)
|
||||||
|
ircClient.Join(channel)
|
||||||
|
}
|
||||||
|
// TODO: wait for join or join error.
|
||||||
})
|
})
|
||||||
|
|
||||||
// Навесим коллбэков на все возможные и невозможные error status code, которые мы можем получить и сдампим это
|
// Навесим коллбэков на все возможные и невозможные error status code, которые мы можем получить и сдампим это
|
||||||
@@ -157,6 +231,39 @@ func ircClientRun() {
|
|||||||
log.Errorf("421 ERR_UNKNOWNCOMMAND, %s", e.Raw)
|
log.Errorf("421 ERR_UNKNOWNCOMMAND, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Аналогичный коллбэк висит на 376 RPL_ENDOFMOTD.
|
||||||
|
ircClient.AddCallback("422", func(e *irc.Event) {
|
||||||
|
// TODO: make use of modes https://defs.ircdocs.horse/defs/usermodes
|
||||||
|
// Если у нас есть доступный +B возьмём его себе, мы же бот.
|
||||||
|
botFlag, ok := availableUserModes.Get("B")
|
||||||
|
|
||||||
|
if ok && botFlag {
|
||||||
|
log.Info("Grabbing +B flag to mark me as bot")
|
||||||
|
ircClient.Mode(ircClient.GetNick(), "+B")
|
||||||
|
} else {
|
||||||
|
if ok {
|
||||||
|
log.Info("Skip +B flag, server does not support it, noone will know that I am bot")
|
||||||
|
} else {
|
||||||
|
log.Info("Skip +B flag, server did not announce modes (yet?), noone will know that I am bot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Irc.Sasl && config.Irc.Password != "" && !nickIsUsed {
|
||||||
|
log.Info("Identifying via NickServ")
|
||||||
|
|
||||||
|
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
||||||
|
imChan <- iMsg{ChatID: "NickServ", Text: message}
|
||||||
|
}
|
||||||
|
// TODO: wait until +R flag been set? could be implemented with waiting for channel message.
|
||||||
|
|
||||||
|
log.Debug("Trying to join to preconfigured channels")
|
||||||
|
|
||||||
|
for _, channel := range config.Irc.Channels {
|
||||||
|
log.Infof("Joining to %s channel", channel)
|
||||||
|
ircClient.Join(channel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("431", func(e *irc.Event) {
|
ircClient.AddCallback("431", func(e *irc.Event) {
|
||||||
log.Errorf("431 ERR_NONICKNAMEGIVEN, %s", e.Raw)
|
log.Errorf("431 ERR_NONICKNAMEGIVEN, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -167,8 +274,11 @@ func ircClientRun() {
|
|||||||
|
|
||||||
ircClient.AddCallback("433", func(e *irc.Event) {
|
ircClient.AddCallback("433", func(e *irc.Event) {
|
||||||
log.Errorf("433 ERR_NICKNAMEINUSE, %s", e.Raw)
|
log.Errorf("433 ERR_NICKNAMEINUSE, %s", e.Raw)
|
||||||
|
|
||||||
nickIsUsed = true
|
nickIsUsed = true
|
||||||
time.Sleep(60 * time.Second)
|
|
||||||
|
<-time.NewTimer(60 * time.Second).C // 1 minute sleep.
|
||||||
|
|
||||||
ircClient.Nick(config.Irc.Nick)
|
ircClient.Nick(config.Irc.Nick)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -179,6 +289,7 @@ func ircClientRun() {
|
|||||||
log.Errorf("436 ERR_NICKCOLLISION, %s", e.Raw)
|
log.Errorf("436 ERR_NICKCOLLISION, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 437: ERR_UNAVAILRESOURCE уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||||
ircClient.AddCallback("437", func(e *irc.Event) {
|
ircClient.AddCallback("437", func(e *irc.Event) {
|
||||||
log.Errorf("437 ERR_UNAVAILRESOURCE, %s", e.Raw)
|
log.Errorf("437 ERR_UNAVAILRESOURCE, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -191,6 +302,16 @@ func ircClientRun() {
|
|||||||
log.Warnf("442 ERR_NOTONCHANNEL, %s", e.Raw)
|
log.Warnf("442 ERR_NOTONCHANNEL, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("443", func(e *irc.Event) {
|
||||||
|
// Returned when a client tries to invite a user to a channel they're already on.
|
||||||
|
log.Errorf("443 ERR_USERONCHANNEL, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("446", func(e *irc.Event) {
|
||||||
|
// Returned by USERS when it has been disabled or not implemented.
|
||||||
|
log.Errorf("446 ERR_USERSDISABLED, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("451", func(e *irc.Event) {
|
ircClient.AddCallback("451", func(e *irc.Event) {
|
||||||
// Предполагается, что надо авторизоваться, перед тем как что-то делать на сервере
|
// Предполагается, что надо авторизоваться, перед тем как что-то делать на сервере
|
||||||
log.Errorf("451 ERR_NOTREGISTERED, %s", e.Raw)
|
log.Errorf("451 ERR_NOTREGISTERED, %s", e.Raw)
|
||||||
@@ -213,18 +334,19 @@ func ircClientRun() {
|
|||||||
log.Errorf("465 ERR_YOUREBANNEDCREEP, %s", e.Raw)
|
log.Errorf("465 ERR_YOUREBANNEDCREEP, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("467", func(e *irc.Event) {
|
||||||
|
log.Errorf("467 ERR_KEYSET, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("471", func(e *irc.Event) {
|
ircClient.AddCallback("471", func(e *irc.Event) {
|
||||||
// Это значит, что народу на канале максимальное количество. Будем пробовать присунуться раз в 30 сек.
|
// Это значит, что народу на канале максимальное количество. Будем пробовать присунуться раз в 30 сек.
|
||||||
channel := e.Arguments[1]
|
channel := e.Arguments[1]
|
||||||
log.Warnf("471 ERR_CHANNELISFULL, %s", e.Raw)
|
log.Warnf("471 ERR_CHANNELISFULL, %s", e.Raw)
|
||||||
time.Sleep(30 * time.Second)
|
<-time.NewTimer(30 * time.Second).C
|
||||||
|
|
||||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
||||||
for _, ircChan := range config.Irc.Channels {
|
if slices.Contains(config.Irc.Channels, channel) {
|
||||||
if ircChan == channel {
|
ircClient.Join(channel)
|
||||||
ircClient.Join(channel)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -237,14 +359,11 @@ func ircClientRun() {
|
|||||||
// По завершении работ +i снимают.
|
// По завершении работ +i снимают.
|
||||||
channel := e.Arguments[1]
|
channel := e.Arguments[1]
|
||||||
log.Errorf("473 ERR_INVITEONLYCHAN, %s", e.Raw)
|
log.Errorf("473 ERR_INVITEONLYCHAN, %s", e.Raw)
|
||||||
time.Sleep(30 * time.Second)
|
<-time.NewTimer(30 * time.Second).C
|
||||||
|
|
||||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
// Проверяем, а должны ли мы быть заджоенными к указанному, каналу, а то вдруг нет?
|
||||||
for _, ircChan := range config.Irc.Channels {
|
if slices.Contains(config.Irc.Channels, channel) {
|
||||||
if ircChan == channel {
|
ircClient.Join(channel)
|
||||||
ircClient.Join(channel)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -254,18 +373,21 @@ func ircClientRun() {
|
|||||||
|
|
||||||
log.Errorf("474 ERR_BANNEDFROMCHAN, %s", e.Raw)
|
log.Errorf("474 ERR_BANNEDFROMCHAN, %s", e.Raw)
|
||||||
// Если нас забанили, то информацию о MODE-ах пользователей мы теряем
|
// Если нас забанили, то информацию о MODE-ах пользователей мы теряем
|
||||||
time.Sleep(30 * time.Second)
|
<-time.NewTimer(30 * time.Second).C
|
||||||
|
|
||||||
// Вдруг, нас забанили, но какбэ не навсегда?
|
// Вдруг, нас забанили, но какбэ не навсегда?
|
||||||
|
// TODO: вынести в настройки?
|
||||||
|
|
||||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
||||||
for _, ircChan := range config.Irc.Channels {
|
if slices.Contains(config.Irc.Channels, channel) {
|
||||||
if ircChan == channel {
|
ircClient.Join(channel)
|
||||||
ircClient.Join(channel)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("475", func(e *irc.Event) {
|
||||||
|
log.Errorf("475 ERR_BADCHANNELKEY, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("477", func(e *irc.Event) {
|
ircClient.AddCallback("477", func(e *irc.Event) {
|
||||||
log.Errorf("477 ERR_NOCHANMODES, %s", e.Raw)
|
log.Errorf("477 ERR_NOCHANMODES, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -274,6 +396,10 @@ func ircClientRun() {
|
|||||||
log.Errorf("478 ERR_BANLISTFULL, %s", e.Raw)
|
log.Errorf("478 ERR_BANLISTFULL, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("481", func(e *irc.Event) {
|
||||||
|
log.Errorf("481 ERR_NOPRIVILEGES, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("482", func(e *irc.Event) {
|
ircClient.AddCallback("482", func(e *irc.Event) {
|
||||||
log.Errorf("482 ERR_CHANOPRIVSNEEDED, %s", e.Raw)
|
log.Errorf("482 ERR_CHANOPRIVSNEEDED, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -286,6 +412,10 @@ func ircClientRun() {
|
|||||||
log.Errorf("485 ERR_UNIQOPPRIVSNEEDED, %s", e.Raw)
|
log.Errorf("485 ERR_UNIQOPPRIVSNEEDED, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("491", func(e *irc.Event) {
|
||||||
|
log.Errorf("491 ERR_NOOPERHOST, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("501", func(e *irc.Event) {
|
ircClient.AddCallback("501", func(e *irc.Event) {
|
||||||
log.Errorf("501 ERR_UMODEUNKNOWNFLAG, %s", e.Raw)
|
log.Errorf("501 ERR_UMODEUNKNOWNFLAG, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -294,22 +424,6 @@ func ircClientRun() {
|
|||||||
log.Errorf("502 ERR_USERSDONTMATCH, %s", e.Raw)
|
log.Errorf("502 ERR_USERSDONTMATCH, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Сделаем уже что-то полезное! Motd нам уже прислали и теперь можно авторизоваться и джойниться
|
|
||||||
ircClient.AddCallback("001", func(e *irc.Event) {
|
|
||||||
if !config.Irc.Sasl && config.Irc.Password != "" && !nickIsUsed {
|
|
||||||
log.Info("Identifying via NickServ")
|
|
||||||
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
|
||||||
imChan <- iMsg{ChatId: "NickServ", Text: message}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("Trying to join to preconfigured channels")
|
|
||||||
|
|
||||||
for _, channel := range config.Irc.Channels {
|
|
||||||
log.Infof("Joining to %s channel", channel)
|
|
||||||
ircClient.Join(channel)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Навесим коллбэков на другие, интересные нам события
|
// Навесим коллбэков на другие, интересные нам события
|
||||||
ircClient.AddCallback("KICK", func(e *irc.Event) {
|
ircClient.AddCallback("KICK", func(e *irc.Event) {
|
||||||
dstNick := e.Arguments[1]
|
dstNick := e.Arguments[1]
|
||||||
@@ -321,7 +435,8 @@ func ircClientRun() {
|
|||||||
userMode.Delete(channel)
|
userMode.Delete(channel)
|
||||||
// TODO: reason?
|
// TODO: reason?
|
||||||
log.Warnf("%s kicks us from %s", srcFullNick, channel)
|
log.Warnf("%s kicks us from %s", srcFullNick, channel)
|
||||||
time.Sleep(10 * time.Second)
|
<-time.NewTimer(10 * time.Second).C
|
||||||
|
|
||||||
ircClient.Join(channel)
|
ircClient.Join(channel)
|
||||||
} else {
|
} else {
|
||||||
// Кого-то другого кикнули с канала
|
// Кого-то другого кикнули с канала
|
||||||
@@ -341,14 +456,15 @@ func ircClientRun() {
|
|||||||
log.Warn("I regain my nick")
|
log.Warn("I regain my nick")
|
||||||
} else {
|
} else {
|
||||||
log.Warn("I regain my nick, trying to identify myself via NickServ")
|
log.Warn("I regain my nick, trying to identify myself via NickServ")
|
||||||
|
|
||||||
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
||||||
imChan <- iMsg{ChatId: "NickServ", Text: message}
|
imChan <- iMsg{ChatID: "NickServ", Text: message}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%s renames themself to %s", srcNick, dstNick)
|
log.Infof("%s renames themself to %s", srcNick, dstNick)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Неважно чей ник сменился, надо забыть, что было и снова узнать mode-ы сменишего nick джентельмена
|
// Неважно чей ник сменился, надо забыть, что было и снова узнать mode-ы сменишего nick джентельмена.
|
||||||
// TODO: реализовать userModeRenameUser()
|
// TODO: реализовать userModeRenameUser()
|
||||||
userModePurgeUser(srcNick)
|
userModePurgeUser(srcNick)
|
||||||
ircClient.Whois(dstNick)
|
ircClient.Whois(dstNick)
|
||||||
@@ -360,12 +476,12 @@ func ircClientRun() {
|
|||||||
channel := e.Arguments[0]
|
channel := e.Arguments[0]
|
||||||
|
|
||||||
if nick == ircClient.GetNick() {
|
if nick == ircClient.GetNick() {
|
||||||
|
// Команда names отправляется автоматом.
|
||||||
log.Infof("I joined to %s", channel)
|
log.Infof("I joined to %s", channel)
|
||||||
// Команда names отправляется автоматом
|
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%s joined to %s", fullNick, channel)
|
log.Infof("%s joined to %s", fullNick, channel)
|
||||||
// Технически, тут не надо спрашивать whois на пользователя, но мы спрашиваем, чтобы уточнить mode,
|
// Технически, тут не надо спрашивать whois на пользователя, но мы спрашиваем, чтобы уточнить mode,
|
||||||
// вдруг сервер проставляет mode заранее (хотя не должен)
|
// вдруг сервер проставляет mode заранее (хотя не должен).
|
||||||
ircClient.Whois(nick)
|
ircClient.Whois(nick)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -403,6 +519,7 @@ func ircClientRun() {
|
|||||||
channel := e.Arguments[0]
|
channel := e.Arguments[0]
|
||||||
fullSrcNick := e.Source
|
fullSrcNick := e.Source
|
||||||
srcNick := e.Nick
|
srcNick := e.Nick
|
||||||
|
|
||||||
var dstNick string
|
var dstNick string
|
||||||
|
|
||||||
if len(e.Arguments) >= 3 { // кого-то по-MODE-или на канале
|
if len(e.Arguments) >= 3 { // кого-то по-MODE-или на канале
|
||||||
@@ -415,6 +532,7 @@ func ircClientRun() {
|
|||||||
}
|
}
|
||||||
} else { // Установка mode-а при заходе на сервер
|
} else { // Установка mode-а при заходе на сервер
|
||||||
dstNick = e.Arguments[0]
|
dstNick = e.Arguments[0]
|
||||||
|
|
||||||
log.Infof("Server set my mode to %s", mode)
|
log.Infof("Server set my mode to %s", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,17 +572,16 @@ func ircClientRun() {
|
|||||||
ircMsgParser(e.Arguments[0], e.Nick, e.User, e.Source, e.Arguments[1])
|
ircMsgParser(e.Arguments[0], e.Nick, e.User, e.Source, e.Arguments[1])
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
ircClient.AddCallback("*", func(e *irc.Event) {
|
||||||
ircClient.AddCallback("*", func(e *irc.Event) {
|
log.Debugf("Incoming EVENT (Raw): %s", e.Raw)
|
||||||
log.Debugf("Incoming EVENT (Raw): %s", e.Raw)
|
})
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
log.Debugf("Make an actual connection to irc")
|
log.Debugf("Make an actual connection to irc")
|
||||||
|
|
||||||
if err := ircClient.Connect(serverString); err != nil {
|
if err := ircClient.Connect(serverString); err != nil {
|
||||||
log.Fatalf("Unable to prepare irc connection: %s", err)
|
log.Errorf("Unable to prepare irc connection: %s", err)
|
||||||
time.Sleep(3 * time.Second)
|
<-time.NewTimer(3 * time.Second).C // Sleep for 3 seconds.
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,21 +595,22 @@ func ircSend() {
|
|||||||
for {
|
for {
|
||||||
m := <-imChan
|
m := <-imChan
|
||||||
|
|
||||||
switch {
|
switch config.Irc.RateLimit.Type {
|
||||||
case config.Irc.RateLimit.Type == "simple_delay":
|
case "simple_delay":
|
||||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if m.Text[0:4] == "/me " {
|
if m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text)
|
ircClient.Action(m.ChatID, m.Text)
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
sleepDelay := time.Duration(config.Irc.RateLimit.SimpleDelay) * time.Millisecond
|
sleepDelay := time.Duration(config.Irc.RateLimit.SimpleDelay) * time.Millisecond
|
||||||
log.Debugf("Due to delay type simple_delay waiting for %d milliseconds", int(sleepDelay))
|
log.Debugf("Due to delay type simple_delay waiting for %d milliseconds", int(sleepDelay))
|
||||||
time.Sleep(sleepDelay)
|
<-time.NewTimer(sleepDelay).C
|
||||||
case config.Irc.RateLimit.Type == "token_bucket":
|
case "token_bucket":
|
||||||
var currentTimeMs = time.Now().UnixMilli()
|
var currentTimeMs = time.Now().UnixMilli()
|
||||||
|
|
||||||
var expirationTimeMs = config.Irc.RateLimit.TokenBucket.ExpirationTime * 1000
|
var expirationTimeMs = config.Irc.RateLimit.TokenBucket.ExpirationTime * 1000
|
||||||
|
|
||||||
if len(msgBucket.Timestamps) > 0 {
|
if len(msgBucket.Timestamps) > 0 {
|
||||||
@@ -506,6 +624,7 @@ func ircSend() {
|
|||||||
|
|
||||||
bucketFill := len(newBucket.Timestamps)
|
bucketFill := len(newBucket.Timestamps)
|
||||||
log.Debugf("Message bucket filled with %d/%d messages", bucketFill, config.Irc.RateLimit.TokenBucket.Size)
|
log.Debugf("Message bucket filled with %d/%d messages", bucketFill, config.Irc.RateLimit.TokenBucket.Size)
|
||||||
|
|
||||||
newBucket.Timestamps = append(newBucket.Timestamps, currentTimeMs)
|
newBucket.Timestamps = append(newBucket.Timestamps, currentTimeMs)
|
||||||
|
|
||||||
if bucketFill >= config.Irc.RateLimit.TokenBucket.Size {
|
if bucketFill >= config.Irc.RateLimit.TokenBucket.Size {
|
||||||
@@ -516,38 +635,41 @@ func ircSend() {
|
|||||||
} else {
|
} else {
|
||||||
// Проставим время отправки сообщения
|
// Проставим время отправки сообщения
|
||||||
log.Debug("Message bucket is empty")
|
log.Debug("Message bucket is empty")
|
||||||
|
|
||||||
msgBucket.Timestamps = append(msgBucket.Timestamps, currentTimeMs)
|
msgBucket.Timestamps = append(msgBucket.Timestamps, currentTimeMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msgBucket.IsFull {
|
if msgBucket.IsFull {
|
||||||
log.Debug("Message bucket overflowed, hitting ratelimit")
|
log.Debug("Message bucket overflowed, hitting ratelimit")
|
||||||
|
|
||||||
sleepPeriod := expirationTimeMs - (currentTimeMs - msgBucket.Timestamps[len(msgBucket.Timestamps)-1])
|
sleepPeriod := expirationTimeMs - (currentTimeMs - msgBucket.Timestamps[len(msgBucket.Timestamps)-1])
|
||||||
|
|
||||||
if config.Irc.RateLimit.TokenBucket.Limit > 0 {
|
if config.Irc.RateLimit.TokenBucket.Limit > 0 {
|
||||||
sleepPeriod = sleepPeriod / int64(config.Irc.RateLimit.TokenBucket.Limit)
|
sleepPeriod /= int64(config.Irc.RateLimit.TokenBucket.Limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Sleeping for %d milliseconds", sleepPeriod)
|
log.Debugf("Sleeping for %d milliseconds", sleepPeriod)
|
||||||
time.Sleep(time.Duration(sleepPeriod) * time.Millisecond)
|
<-time.NewTimer(time.Duration(sleepPeriod) * time.Millisecond).C
|
||||||
|
|
||||||
currentTimeMs = time.Now().UnixMilli()
|
currentTimeMs = time.Now().UnixMilli()
|
||||||
// Обновим время отправки сообщения
|
// Обновим время отправки сообщения
|
||||||
msgBucket.Timestamps[len(msgBucket.Timestamps)-1] = currentTimeMs
|
msgBucket.Timestamps[len(msgBucket.Timestamps)-1] = currentTimeMs
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text[4:])
|
ircClient.Action(m.ChatID, m.Text[4:])
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text[4:])
|
ircClient.Action(m.ChatID, m.Text[4:])
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,12 +678,12 @@ func ircSend() {
|
|||||||
func ircSendUnrestricted() {
|
func ircSendUnrestricted() {
|
||||||
for {
|
for {
|
||||||
m := <-imChanUnrestricted
|
m := <-imChanUnrestricted
|
||||||
log.Debugf("Skipping ratelimit and sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Skipping ratelimit and sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text[4:])
|
ircClient.Action(m.ChatID, m.Text[4:])
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
globals.go
35
globals.go
@@ -4,47 +4,56 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"aleesa-irc-go/internal/anycollection"
|
||||||
|
"aleesa-irc-go/internal/boolcollection"
|
||||||
|
|
||||||
"github.com/cockroachdb/pebble"
|
"github.com/cockroachdb/pebble"
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
irc "github.com/thoj/go-ircevent"
|
irc "github.com/thoj/go-ircevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config - это у нас глобальная штука :)
|
// Config - это у нас глобальная штука :).
|
||||||
var config myConfig
|
var config myConfig
|
||||||
|
|
||||||
// To break circular message forwarding we must set some sane default, it can be overridden via config
|
// To break circular message forwarding we must set some sane default, it can be overridden via config.
|
||||||
var forwardMax int64 = 5
|
var forwardMax int64 = 5
|
||||||
|
|
||||||
// Объектики irc-клиента
|
// Объектики irc-клиента.
|
||||||
var ircClient *irc.Connection
|
var ircClient *irc.Connection
|
||||||
var nickIsUsed = false
|
var nickIsUsed = false
|
||||||
|
|
||||||
// Объектики клиента-редиски
|
// Объектики клиента-редиски.
|
||||||
var redisClient *redis.Client
|
var redisClient *redis.Client
|
||||||
var subscriber *redis.PubSub
|
var subscriber *redis.PubSub
|
||||||
|
|
||||||
// Main context
|
// Main context.
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
|
|
||||||
// Ставится в true, если мы получили сигнал на выключение
|
// Ставится в true, если мы получили сигнал на выключение.
|
||||||
var shutdown = false
|
var shutdown = false
|
||||||
|
|
||||||
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов
|
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
||||||
var sigChan = make(chan os.Signal, 1)
|
var sigChan = make(chan os.Signal, 1)
|
||||||
|
|
||||||
// Канал, в который пишутся сообщения для отправки в IRC в обычном порядке
|
// Канал, в который пишутся сообщения для отправки в IRC в обычном порядке.
|
||||||
var imChan = make(chan iMsg, 10000)
|
var imChan = make(chan iMsg, 10000)
|
||||||
|
|
||||||
// Канал, в который пишутся сообщения для отправки в IRC, если ограничений нет (у бота +o или +v на канале)
|
// Канал, в который пишутся сообщения для отправки в IRC, если ограничений нет (у бота +o или +v на канале).
|
||||||
var imChanUnrestricted = make(chan iMsg, 100)
|
var imChanUnrestricted = make(chan iMsg, 100)
|
||||||
|
|
||||||
// "Ведёрко" с timestamp-ами последних отправленных сообщений
|
// "Ведёрко" с timestamp-ами последних отправленных сообщений.
|
||||||
var msgBucket bucket
|
var msgBucket bucket
|
||||||
|
|
||||||
// Мапка с открытыми дескрипторами баз с настройками
|
// Мапка с открытыми дескрипторами баз с настройками.
|
||||||
var settingsDB = make(map[string]*pebble.DB)
|
var settingsDB = make(map[string]*pebble.DB)
|
||||||
|
|
||||||
// "Базюлька" с MODE-ами пользователей на каналах
|
// "Базюлька" с MODE-ами пользователей на каналах.
|
||||||
var userMode = NewCollection()
|
var userMode = anycollection.NewCollection()
|
||||||
|
|
||||||
|
// "Базюлька" с доступными MODE-ами пользователя.
|
||||||
|
var availableUserModes = boolcollection.NewCollection()
|
||||||
|
|
||||||
|
// "Базюлька" c с доступными MODE-ами каналов.
|
||||||
|
var availableChanModes = boolcollection.NewCollection()
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package main
|
package anycollection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collection это sync.Map-ка, хранящая в себе произвольные итемы
|
// Collection это sync.Map-ка, хранящая в себе произвольные итемы.
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
items sync.Map
|
items sync.Map
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item представляет собой интерфейс для хранения произвольных данных
|
// Item представляет собой интерфейс для хранения произвольных данных.
|
||||||
type item struct {
|
type item struct {
|
||||||
data interface{}
|
data any
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollection создаёт инстанс коллекции
|
// NewCollection создаёт инстанс коллекции.
|
||||||
func NewCollection() *Collection {
|
func NewCollection() *Collection {
|
||||||
c := &Collection{
|
c := &Collection{
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
@@ -24,8 +24,8 @@ func NewCollection() *Collection {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get достаёт данные по заданному ключу из коллекции
|
// Get достаёт данные по заданному ключу из коллекции.
|
||||||
func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
func (collection *Collection) Get(key any) (any, bool) {
|
||||||
obj, exists := collection.items.Load(key)
|
obj, exists := collection.items.Load(key)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -37,16 +37,16 @@ func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
|||||||
return item.data, true
|
return item.data, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set сохраняет данные с заданным ключом в коллекцию
|
// Set сохраняет данные с заданным ключом в коллекцию.
|
||||||
func (collection *Collection) Set(key interface{}, value interface{}) {
|
func (collection *Collection) Set(key any, value any) {
|
||||||
collection.items.Store(key, item{
|
collection.items.Store(key, item{
|
||||||
data: value,
|
data: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range применяет функцию f ко всем ключам в коллекции
|
// Range применяет функцию f ко всем ключам в коллекции.
|
||||||
func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
func (collection *Collection) Range(f func(key, value any) bool) {
|
||||||
fn := func(key, value interface{}) bool {
|
fn := func(key, value any) bool {
|
||||||
item := value.(item)
|
item := value.(item)
|
||||||
|
|
||||||
return f(key, item.data)
|
return f(key, item.data)
|
||||||
@@ -55,12 +55,12 @@ func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
|||||||
collection.items.Range(fn)
|
collection.items.Range(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete удаляет ключ и значение из коллекции данных
|
// Delete удаляет ключ и значение из коллекции данных.
|
||||||
func (collection *Collection) Delete(key interface{}) {
|
func (collection *Collection) Delete(key any) {
|
||||||
collection.items.Delete(key)
|
collection.items.Delete(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close очищает и высвобождает ресурсы, занятые коллекцией
|
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
||||||
func (collection *Collection) Close() {
|
func (collection *Collection) Close() {
|
||||||
collection.close <- struct{}{}
|
collection.close <- struct{}{}
|
||||||
collection.items = sync.Map{}
|
collection.items = sync.Map{}
|
||||||
58
internal/boolcollection/boolcollection.go
Normal file
58
internal/boolcollection/boolcollection.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package boolcollection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collection это sync.Map-ка, хранящая в себе итемы типа bool.
|
||||||
|
type Collection struct {
|
||||||
|
items sync.Map
|
||||||
|
close chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item представляет собой интерфейс для хранения данных типа bool.
|
||||||
|
type item struct {
|
||||||
|
data bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollection создаёт инстанс коллекции.
|
||||||
|
func NewCollection() *Collection {
|
||||||
|
c := &Collection{
|
||||||
|
close: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get достаёт данные по заданному ключу из коллекции.
|
||||||
|
func (collection *Collection) Get(key string) (bool, bool) {
|
||||||
|
obj, exists := collection.items.Load(key)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
item := obj.(item)
|
||||||
|
|
||||||
|
return item.data, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set сохраняет данные с заданным ключом в коллекцию.
|
||||||
|
func (collection *Collection) Set(key string, value bool) {
|
||||||
|
collection.items.Store(key, item{
|
||||||
|
data: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete удаляет ключ и значение из коллекции данных.
|
||||||
|
func (collection *Collection) Delete(key string) {
|
||||||
|
collection.items.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
||||||
|
func (collection *Collection) Close() {
|
||||||
|
collection.close <- struct{}{}
|
||||||
|
collection.items = sync.Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
@@ -8,7 +8,7 @@ import "strings"
|
|||||||
активируют мягкий ratelimit на стороне сервера, без них, сервер просто рвёт соединение по превышению лимитов
|
активируют мягкий ratelimit на стороне сервера, без них, сервер просто рвёт соединение по превышению лимитов
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Удаляет сведения о MODE-ах пользователя для заданного канала
|
// Удаляет сведения о MODE-ах пользователя для заданного канала.
|
||||||
func userModeDeleteUser(ircChan string, nick string) {
|
func userModeDeleteUser(ircChan string, nick string) {
|
||||||
channel, ok := userMode.Get(ircChan)
|
channel, ok := userMode.Get(ircChan)
|
||||||
|
|
||||||
@@ -24,16 +24,17 @@ func userModeDeleteUser(ircChan string, nick string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Удаляет сведения о MODE-ах пользователя для всех каналов, на которых есть бот
|
// Удаляет сведения о MODE-ах пользователя для всех каналов, на которых есть бот.
|
||||||
func userModePurgeUser(nick string) {
|
func userModePurgeUser(nick string) {
|
||||||
for _, channel := range config.Irc.Channels {
|
for _, channel := range config.Irc.Channels {
|
||||||
userModeDeleteUser(channel, nick)
|
userModeDeleteUser(channel, nick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляет или создаёт запись о mode-ах пользователей
|
// Обновляет или создаёт запись о mode-ах пользователей.
|
||||||
func userModeUpdateUser(ircChan string, nick string, modes string) {
|
func userModeUpdateUser(ircChan string, nick string, modes string) {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
|
|
||||||
channel, ok := userMode.Get(ircChan)
|
channel, ok := userMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
@@ -48,12 +49,12 @@ func userModeUpdateUser(ircChan string, nick string, modes string) {
|
|||||||
channelData[nick] = make(map[string]bool)
|
channelData[nick] = make(map[string]bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch modes[:1] {
|
||||||
case modes[:1] == "+":
|
case "+":
|
||||||
for _, mode := range strings.Split(modes[1:], "") {
|
for _, mode := range strings.Split(modes[1:], "") {
|
||||||
channelData[nick][mode] = true
|
channelData[nick][mode] = true
|
||||||
}
|
}
|
||||||
case modes[:1] == "-":
|
case "-":
|
||||||
for _, mode := range strings.Split(modes[1:], "") {
|
for _, mode := range strings.Split(modes[1:], "") {
|
||||||
channelData[nick][mode] = false
|
channelData[nick][mode] = false
|
||||||
}
|
}
|
||||||
@@ -62,9 +63,10 @@ func userModeUpdateUser(ircChan string, nick string, modes string) {
|
|||||||
userMode.Set(ircChan, channelData)
|
userMode.Set(ircChan, channelData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращает значение MODE-а v для запрошенного ника
|
// Возвращает значение MODE-а v для запрошенного ника.
|
||||||
func userModeIsVoiced(ircChan string, nick string) bool {
|
func userModeIsVoiced(ircChan string, nick string) bool {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
|
|
||||||
channel, ok := userMode.Get(ircChan)
|
channel, ok := userMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
@@ -88,9 +90,10 @@ func userModeIsVoiced(ircChan string, nick string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращает значение MODE-а o для запрошенного ника
|
// Возвращает значение MODE-а o для запрошенного ника.
|
||||||
func userModeIsOped(ircChan string, nick string) bool {
|
func userModeIsOped(ircChan string, nick string) bool {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
|
|
||||||
channel, ok := userMode.Get(ircChan)
|
channel, ok := userMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
@@ -116,6 +119,7 @@ func userModeIsOped(ircChan string, nick string) bool {
|
|||||||
|
|
||||||
func userModeIsHere(ircChan string, nick string) bool {
|
func userModeIsHere(ircChan string, nick string) bool {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
|
|
||||||
channel, ok := userMode.Get(ircChan)
|
channel, ok := userMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
4
main.go
4
main.go
@@ -11,7 +11,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Производит некоторую инициализацию перед запуском main()
|
// Производит некоторую инициализацию перед запуском main().
|
||||||
func init() {
|
func init() {
|
||||||
log.SetFormatter(&log.TextFormatter{
|
log.SetFormatter(&log.TextFormatter{
|
||||||
DisableQuote: true,
|
DisableQuote: true,
|
||||||
@@ -52,7 +52,7 @@ func init() {
|
|||||||
userMode.Delete("channel")
|
userMode.Delete("channel")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Собственно, какбэ "точка входа" - основная процедура в нашем боте
|
// Собственно, какбэ "точка входа" - основная процедура в нашем боте.
|
||||||
func main() {
|
func main() {
|
||||||
// Main context
|
// Main context
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
|
|||||||
149
msg-parsers.go
149
msg-parsers.go
@@ -9,8 +9,8 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки
|
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки.
|
||||||
func ircMsgParser(channel string, nick string, user string, source string, msg string) {
|
func ircMsgParser(channel string, nick string, user string, source string, msg string) { //nolint: revive
|
||||||
// nick - это выбранный пользователем nick (если он занят, то его "нарисует" сервер)
|
// nick - это выбранный пользователем nick (если он занят, то его "нарисует" сервер)
|
||||||
// user - это короткое имя пользователя, под которым его видит сервер
|
// user - это короткое имя пользователя, под которым его видит сервер
|
||||||
// source - это длинное имя пользователя, оно содержит в себе помимо user, ещё и ip с которого пришёл пользователь
|
// source - это длинное имя пользователя, оно содержит в себе помимо user, ещё и ip с которого пришёл пользователь
|
||||||
@@ -48,50 +48,50 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case cmd == "help" || msg == "помощь":
|
case cmd == "help" || msg == "помощь":
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%shelp | %sпомощь - это сообщение", config.Csign, config.Csign)}
|
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("%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("%sbuni - комикс-стрип hapi buni", config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sbunny - кролик", 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("%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("%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("%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("%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("%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("%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("%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("%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("%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("%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("%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("%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("%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("%skarma фраза - посмотреть карму фразы", config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sкарма фраза - посмотреть карму фразы", 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.Sprintln("фраза++ | фраза-- - повысить или понизить карму фразы")}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%slat | %sлат - сгенерировать фразу из крылатого латинского выражения", config.Csign, config.Csign)}
|
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("%smonkeyuser - комикс-стрип MonkeyUser", config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sowl | %sсова - сова", config.Csign, 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("%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("%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("%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("%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("%sver | %sversion - написать что-то про версию ПО", config.Csign, config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sверсия - написать что-то про версию ПО", 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("%sw <город> | %sп <город> - погода в городе", config.Csign, config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sxkcd - комикс-стрип с xkcb.ru", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sxkcd - комикс-стрип с xkcb.ru", config.Csign)}
|
||||||
|
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sadmin - настройки некоторых плагинов бота для канала", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin - настройки некоторых плагинов бота для канала", config.Csign)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case cmd == "admin":
|
case cmd == "admin":
|
||||||
if userModeIsOped(channel, nick) {
|
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 # - где 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 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 # - где 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)}
|
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
|
return
|
||||||
@@ -100,16 +100,16 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
value := getSetting(channel, "oboobs")
|
value := getSetting(channel, "oboobs")
|
||||||
|
|
||||||
switch {
|
switch value {
|
||||||
case value == "":
|
case "":
|
||||||
_ = saveSetting(channel, "oboobs", "0")
|
_ = saveSetting(channel, "oboobs", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
case value == "0":
|
case "0":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
case value == "1":
|
case "1":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
||||||
default:
|
default:
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +120,9 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
err := saveSetting(channel, "oboobs", "1")
|
err := saveSetting(channel, "oboobs", "1")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs всё ещё выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs всё ещё выключен"}
|
||||||
} else {
|
} else {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
case cmd == "admin oboobs 0":
|
case cmd == "admin oboobs 0":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
_ = saveSetting(channel, "oboobs", "0")
|
_ = saveSetting(channel, "oboobs", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -140,16 +140,16 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
value := getSetting(channel, "obutts")
|
value := getSetting(channel, "obutts")
|
||||||
|
|
||||||
switch {
|
switch value {
|
||||||
case value == "":
|
case "":
|
||||||
_ = saveSetting(channel, "obutts", "0")
|
_ = saveSetting(channel, "obutts", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
case value == "0":
|
case "0":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
case value == "1":
|
case "1":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
||||||
default:
|
default:
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,9 +160,9 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
err := saveSetting(channel, "obutts", "1")
|
err := saveSetting(channel, "obutts", "1")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts всё ещё выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts всё ещё выключен"}
|
||||||
} else {
|
} else {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
case cmd == "admin obutts 0":
|
case cmd == "admin obutts 0":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
_ = saveSetting(channel, "obutts", "0")
|
_ = saveSetting(channel, "obutts", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -192,6 +192,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd == command {
|
if cmd == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,6 +207,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd == command {
|
if cmd == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +228,8 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
message.Misc.Username = strings.TrimSpace(pile[1])
|
message.Misc.Username = strings.TrimSpace(pile[1])
|
||||||
} else {
|
} else {
|
||||||
msg = fmt.Sprintf("Я тут не вижу участника с ником %s", userNick)
|
msg = fmt.Sprintf("Я тут не вижу участника с ником %s", userNick)
|
||||||
imChan <- iMsg{ChatId: channel, Text: msg}
|
imChan <- iMsg{ChatID: channel, Text: msg}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} // в противном случае юзер просто поставил пробел на конце команды, интерпретируем это как
|
} // в противном случае юзер просто поставил пробел на конце команды, интерпретируем это как
|
||||||
@@ -234,6 +237,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
done = true
|
done = true
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,6 +254,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmdLen > len(command) && cmd[0:len(command)] == command {
|
if cmdLen > len(command) && cmd[0:len(command)] == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,6 +272,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd[:cmdLen] == command {
|
if cmd[:cmdLen] == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,6 +289,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd[:cmdLen] == command {
|
if cmd[:cmdLen] == command {
|
||||||
// done = true
|
// done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,6 +304,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Unable to to serialize message for redis: %s", err)
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,6 +359,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Unable to to serialize message for redis: %s", err)
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,11 +370,9 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// redisMsgParser парсит json-чики прилетевшие из REDIS-ки, причём, json-чики должны быть относительно валидными
|
// redisMsgParser парсит json-чики прилетевшие из REDIS-ки, причём, json-чики должны быть относительно валидными.
|
||||||
func redisMsgParser(msg string) {
|
func redisMsgParser(msg string) {
|
||||||
if shutdown {
|
if shutdown {
|
||||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
||||||
@@ -378,43 +385,51 @@ func redisMsgParser(msg string) {
|
|||||||
|
|
||||||
if err := json.Unmarshal([]byte(msg), &j); err != nil {
|
if err := json.Unmarshal([]byte(msg), &j); err != nil {
|
||||||
log.Warnf("Unable to to parse message from redis channel: %s", err)
|
log.Warnf("Unable to to parse message from redis channel: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate our j
|
// Validate our j
|
||||||
if exist := j.From; exist == "" {
|
if exist := j.From; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no from field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no from field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Chatid; exist == "" {
|
if exist := j.Chatid; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no chatid field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no chatid field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Userid; exist == "" {
|
if exist := j.Userid; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no userid field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no userid field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Message; exist == "" {
|
if exist := j.Message; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no message field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no message field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Plugin; exist == "" {
|
if exist := j.Plugin; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no plugin field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no plugin field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Mode; exist == "" {
|
if exist := j.Mode; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no mode field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no mode field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// j.Misc.Answer может и не быть, тогда ответа на такое сообщение не будет
|
// j.Misc.Answer может и не быть, тогда ответа на такое сообщение не будет
|
||||||
if j.Misc.Answer == 0 {
|
if j.Misc.Answer == 0 {
|
||||||
log.Debug("Field Misc->Answer = 0, skipping message")
|
log.Debug("Field Misc->Answer = 0, skipping message")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,13 +453,11 @@ func redisMsgParser(msg string) {
|
|||||||
|
|
||||||
for _, message := range lines {
|
for _, message := range lines {
|
||||||
if userModeIsOped(j.Chatid, ircClient.GetNick()) || userModeIsVoiced(j.Chatid, ircClient.GetNick()) {
|
if userModeIsOped(j.Chatid, ircClient.GetNick()) || userModeIsVoiced(j.Chatid, ircClient.GetNick()) {
|
||||||
imChanUnrestricted <- iMsg{ChatId: j.Chatid, Text: message}
|
imChanUnrestricted <- iMsg{ChatID: j.Chatid, Text: message}
|
||||||
} else {
|
} else {
|
||||||
imChan <- iMsg{ChatId: j.Chatid, Text: message}
|
imChan <- iMsg{ChatID: j.Chatid, Text: message}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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: */
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StoreKV сохраняет в указанной бд ключ и значение
|
// StoreKV сохраняет в указанной бд ключ и значение.
|
||||||
func StoreKV(db *pebble.DB, key string, value string) error {
|
func StoreKV(db *pebble.DB, key string, value string) error {
|
||||||
var kArray = []byte(key)
|
var kArray = []byte(key)
|
||||||
|
|
||||||
var vArray = []byte(value)
|
var vArray = []byte(value)
|
||||||
|
|
||||||
err := db.Set(kArray, vArray, pebble.Sync)
|
err := db.Set(kArray, vArray, pebble.Sync)
|
||||||
@@ -21,11 +22,12 @@ func StoreKV(db *pebble.DB, key string, value string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchV достаёт значение по ключу
|
// FetchV достаёт значение по ключу.
|
||||||
func FetchV(db *pebble.DB, key string) (string, error) {
|
func FetchV(db *pebble.DB, key string) (string, error) {
|
||||||
var kArray = []byte(key)
|
var kArray = []byte(key)
|
||||||
|
|
||||||
var vArray []byte
|
var vArray []byte
|
||||||
|
|
||||||
var valueString = ""
|
var valueString = ""
|
||||||
|
|
||||||
vArray, closer, err := db.Get(kArray)
|
vArray, closer, err := db.Get(kArray)
|
||||||
@@ -40,7 +42,7 @@ func FetchV(db *pebble.DB, key string) (string, error) {
|
|||||||
return valueString, err
|
return valueString, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Достанем настройку из БД с настройками
|
// Достанем настройку из БД с настройками.
|
||||||
func getSetting(chatID string, setting string) string {
|
func getSetting(chatID string, setting string) string {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -58,6 +60,7 @@ func getSetting(chatID string, setting string) string {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,13 +69,14 @@ func getSetting(chatID string, setting string) string {
|
|||||||
|
|
||||||
// Если из базы ничего не вынулось, по каким-то причинам, то просто вернём пустую строку
|
// Если из базы ничего не вынулось, по каким-то причинам, то просто вернём пустую строку
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pebble.ErrNotFound) {
|
switch {
|
||||||
|
case errors.Is(err, pebble.ErrNotFound):
|
||||||
log.Debugf("Unable to get value for %s: no record found in db %s", setting, database)
|
log.Debugf("Unable to get value for %s: no record found in db %s", setting, database)
|
||||||
} else if errors.Is(err, fs.ErrNotExist) {
|
case errors.Is(err, fs.ErrNotExist):
|
||||||
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
||||||
} else if errors.Is(err, oserror.ErrNotExist) {
|
case errors.Is(err, oserror.ErrNotExist):
|
||||||
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
||||||
} else {
|
default:
|
||||||
log.Errorf("Unable to get value for %s in db dir %s: %s", setting, database, err)
|
log.Errorf("Unable to get value for %s in db dir %s: %s", setting, database, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +86,12 @@ func getSetting(chatID string, setting string) string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сохраним настройку в БД с настройками
|
// Сохраним настройку в БД с настройками.
|
||||||
func saveSetting(chatID string, setting string, value string) error {
|
func saveSetting(chatID string, setting string, value string) error {
|
||||||
var chatHash = sha256.Sum256([]byte(chatID))
|
var chatHash = sha256.Sum256([]byte(chatID))
|
||||||
|
|
||||||
var database = fmt.Sprintf("settings_db/%x", chatHash)
|
var database = fmt.Sprintf("settings_db/%x", chatHash)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Если БД не открыта, откроем её
|
// Если БД не открыта, откроем её
|
||||||
@@ -100,6 +106,7 @@ func saveSetting(chatID string, setting string, value string) error {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
types.go
13
types.go
@@ -1,6 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// Конфиг
|
// Конфиг.
|
||||||
type myConfig struct {
|
type myConfig struct {
|
||||||
Redis struct {
|
Redis struct {
|
||||||
Server string `json:"server,omitempty"`
|
Server string `json:"server,omitempty"`
|
||||||
@@ -35,7 +35,7 @@ type myConfig struct {
|
|||||||
DataDir string `json:"data_dir,omitempty"`
|
DataDir string `json:"data_dir,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Входящее сообщение из pubsub-канала redis-ки
|
// Входящее сообщение из pubsub-канала redis-ки.
|
||||||
type rMsg struct {
|
type rMsg struct {
|
||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
Chatid string `json:"chatid,omitempty"`
|
Chatid string `json:"chatid,omitempty"`
|
||||||
@@ -55,7 +55,7 @@ type rMsg struct {
|
|||||||
} `json:"Misc"`
|
} `json:"Misc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Исходящее сообщение в pubsub-канал redis-ки
|
// Исходящее сообщение в pubsub-канал redis-ки.
|
||||||
type sMsg struct {
|
type sMsg struct {
|
||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
Chatid string `json:"chatid"`
|
Chatid string `json:"chatid"`
|
||||||
@@ -75,14 +75,13 @@ type sMsg struct {
|
|||||||
} `json:"misc"`
|
} `json:"misc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сообщение в irc
|
// TODO: Имплементировать тип сообщения - privmsg или action.
|
||||||
// TODO: Имплементировать тип сообщения - privmsg или action
|
|
||||||
type iMsg struct {
|
type iMsg struct {
|
||||||
ChatId string
|
ChatID string
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Ведёрко" для реализации ограничителя скорости отправки исходящих сообщений - Token Bucket
|
// "Ведёрко" для реализации ограничителя скорости отправки исходящих сообщений - Token Bucket.
|
||||||
type bucket struct {
|
type bucket struct {
|
||||||
Timestamps []int64
|
Timestamps []int64
|
||||||
IsFull bool
|
IsFull bool
|
||||||
|
|||||||
17
util.go
17
util.go
@@ -11,7 +11,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет
|
// Читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет.
|
||||||
func readConfig() {
|
func readConfig() {
|
||||||
configLoaded := false
|
configLoaded := false
|
||||||
executablePath, err := os.Executable()
|
executablePath, err := os.Executable()
|
||||||
@@ -40,6 +40,7 @@ func readConfig() {
|
|||||||
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата
|
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата
|
||||||
if fileInfo.Size() > 65535 {
|
if fileInfo.Size() > 65535 {
|
||||||
log.Warnf("Config file %s is too long for config, skipping", location)
|
log.Warnf("Config file %s is too long for config, skipping", location)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ func readConfig() {
|
|||||||
// Не удалось прочитать, попробуем следующего кандидата
|
// Не удалось прочитать, попробуем следующего кандидата
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip reading config file %s: %s", location, err)
|
log.Warnf("Skip reading config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,12 +57,14 @@ func readConfig() {
|
|||||||
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
|
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
|
||||||
// сериализуем в json, а потом json преврщааем в стркутурку. Не очень эффективно, но он и не часто требуется.
|
// сериализуем в json, а потом json преврщааем в стркутурку. Не очень эффективно, но он и не часто требуется.
|
||||||
var sampleConfig myConfig
|
var sampleConfig myConfig
|
||||||
|
|
||||||
var tmp map[string]interface{}
|
var tmp map[string]interface{}
|
||||||
err = hjson.Unmarshal(buf, &tmp)
|
err = hjson.Unmarshal(buf, &tmp)
|
||||||
|
|
||||||
// Не удалось распарсить - попробуем следующего кандидата
|
// Не удалось распарсить - попробуем следующего кандидата
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,17 +73,20 @@ func readConfig() {
|
|||||||
// Не удалось преобразовать map-ку в json
|
// Не удалось преобразовать map-ку в json
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
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)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Валидируем значения из конфига
|
// Валидируем значения из конфига
|
||||||
if sampleConfig.Redis.Server == "" {
|
if sampleConfig.Redis.Server == "" {
|
||||||
sampleConfig.Redis.Server = "localhost"
|
sampleConfig.Redis.Server = "localhost"
|
||||||
|
|
||||||
log.Infof("Redis server is not defined in config, using localhost")
|
log.Infof("Redis server is not defined in config, using localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,15 +107,17 @@ func readConfig() {
|
|||||||
// Значения для IRC-клиента
|
// Значения для IRC-клиента
|
||||||
if sampleConfig.Irc.Server == "" {
|
if sampleConfig.Irc.Server == "" {
|
||||||
sampleConfig.Irc.Server = "localhost"
|
sampleConfig.Irc.Server = "localhost"
|
||||||
|
|
||||||
log.Errorf("Irc server is not defined in config, using localhost")
|
log.Errorf("Irc server is not defined in config, using localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Irc.Port == 0 {
|
if sampleConfig.Irc.Port == 0 {
|
||||||
sampleConfig.Irc.Port = 6667
|
sampleConfig.Irc.Port = 6667
|
||||||
|
|
||||||
log.Infof("Irc port is not defined in config, using 6667")
|
log.Infof("Irc port is not defined in config, using 6667")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Irc.Ssl != true {
|
if !sampleConfig.Irc.Ssl {
|
||||||
sampleConfig.Irc.SslVerify = false
|
sampleConfig.Irc.SslVerify = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +190,9 @@ func readConfig() {
|
|||||||
|
|
||||||
config = sampleConfig
|
config = sampleConfig
|
||||||
configLoaded = true
|
configLoaded = true
|
||||||
|
|
||||||
log.Infof("Using %s as config file", location)
|
log.Infof("Using %s as config file", location)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +202,7 @@ func readConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения
|
// Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения.
|
||||||
func sigHandler() {
|
func sigHandler() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user