diff --git a/config.go b/config.go index ecf0309..80ea1e7 100644 --- a/config.go +++ b/config.go @@ -38,6 +38,24 @@ type HttpCallbackConfig struct { Method string `yaml:"method"` } +type IrcConfig struct { + Server string `yaml:"server"` + Port int `yaml:"port"` + /// Nick which will be used. Users on IRC will see the bot as this nick. + Nick string `yaml:"nick"` + /// Username presented to the server + User string `yaml:"user"` + /// Name presented to the server + Name string `yaml:"name"` + /// Usually the username used for NickServ login + SaslUser string `yaml:"sasl-user"` + /// Usually the password used for NickServ password + SaslPass string `yaml:"sasl-pass"` + SSL bool `yaml:"ssl"` + /// Channels to which this bot will join, send status to and receive commands from + Channels []string `yaml:"channels"` +} + type AppConfig struct { SatelAddr string `yaml:"satel-addr"` ChatIds []int64 `yaml:"tg-chat-ids"` @@ -49,6 +67,7 @@ type AppConfig struct { AlarmCallbacks []HttpCallbackConfig `yaml:"alarm-callbacks"` WriteMemoryProfile bool `yaml:"write-memory-profile"` Matterbridge []MatterbridgeConfig `yaml:"matterbridge"` + Irc []IrcConfig `yaml:"irc"` TelegramApiKey string `yaml:"telegram-api-key"` } @@ -182,5 +201,10 @@ func MakeConfig(logger *log.Logger) AppConfig { } getCmdLineParams(&config, logger) + + if len(config.Irc) > 1 { + logger.Fatal("Configuring of more than one IRC connection is not supported.") + } + return config } diff --git a/config_test.go b/config_test.go index 3ca89f3..2cfeb15 100644 --- a/config_test.go +++ b/config_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "log" "os" "testing" @@ -50,6 +51,21 @@ matterbridge: username: test_username_2 ` +const ircConfig = ` +irc: + - server: irc.libera.chat + port: 6697 + nick: testbot + user: testbot + name: "Test Bot" + sasl-user: testbot + sasl-pass: testbotpassword + ssl: True + channels: + - "#hswro" + - "#testchannelpleaseignore" +` + func TestParseYamlConfig(t *testing.T) { a := assert.New(t) @@ -76,4 +92,18 @@ func TestParseYamlConfig(t *testing.T) { a.Equal(actualConfig.Matterbridge[1].Token, "test_token_2") a.Equal(actualConfig.Matterbridge[1].Gateway, "test_gateway_2") a.Equal(actualConfig.Matterbridge[1].Username, "test_username_2") + + a.Nil(actualConfig.Irc) +} + +func TestParseYamlConfig_IrcConfig(t *testing.T) { + a := assert.New(t) + + actualConfig := parseConfigFromFile( + bytes.Join([][]byte{[]byte(data), []byte(ircConfig)}, []byte{}), + log.New(os.Stderr, "", log.Ltime)) + + a.ElementsMatch([]IrcConfig{{ + "irc.libera.chat", 6697, "testbot", "testbot", "Test Bot", "testbot", "testbotpassword", true, + []string{"#hswro", "#testchannelpleaseignore"}}}, actualConfig.Irc) } diff --git a/go.mod b/go.mod index 5cb33e2..117f432 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,15 @@ go 1.19 require ( git.sr.ht/~michalr/go-satel v0.0.0-20240306182245-7ac13d8e4733 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 + github.com/lrstanley/girc v0.0.0-20250219025855-423afa8a8828 github.com/stretchr/testify v1.8.4 + gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/text v0.14.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/probakowski/go-satel => git.sr.ht/~michalr/go-satel v0.0.0-20211120120346-bed9818777ce diff --git a/go.sum b/go.sum index 3cc4e40..b863fef 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= +github.com/lrstanley/girc v0.0.0-20250219025855-423afa8a8828 h1:tJcJDAvGGM1xy1gt6A/7jzTLOnjJsDTxOkykzEgoh9w= +github.com/lrstanley/girc v0.0.0-20250219025855-423afa8a8828/go.mod h1:lgrnhcF8bg/Bd5HA5DOb4Z+uGqUqGnp4skr+J2GwVgI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/irc.go b/irc.go new file mode 100644 index 0000000..620d6ba --- /dev/null +++ b/irc.go @@ -0,0 +1,65 @@ +package main + +import ( + "html/template" + "log" + "time" + + "github.com/lrstanley/girc" +) + +type IrcBot struct { + client *girc.Client + logger *log.Logger + s SatelNameGetter + config AppConfig +} + +func MakeIrcBot(config AppConfig, logger *log.Logger, s SatelNameGetter) *IrcBot { + if config.Irc == nil { + return nil + } + client := girc.New(girc.Config{ + Server: config.Irc[0].Server, + Port: config.Irc[0].Port, + Nick: config.Irc[0].Nick, + User: config.Irc[0].User, + Name: config.Irc[0].Name, + Out: logger.Writer(), + }) + + return &IrcBot{client, logger, s, config} +} + +func (bot *IrcBot) GetInterestingChannels() []girc.Channel { + return []girc.Channel{} +} + +func (bot *IrcBot) SendMessageToAllChannels(msg string) { + for i, channel := range bot.GetInterestingChannels() { + if i != 0 { + // to avoid sending too many messages at once + // do it only if we have more than one message to send + time.Sleep(time.Millisecond * 500) + } + bot.client.Cmd.Message(channel.Name, msg) + } +} + +type NotifyViaIRCSync struct { + SyncFilterImpl[GenericMessage] + + bot *IrcBot + tpl *template.Template +} + +func (bot *IrcBot) GetNotifyViaIRC(tpl *template.Template) *NotifyViaIRCSync { + return &NotifyViaIRCSync{SyncFilterImpl[GenericMessage]{}, bot, tpl} +} + +func (notifyViaIRC *NotifyViaIRCSync) Call(msg GenericMessage) { + notifyViaIRC.bot.SendMessageToAllChannels( + msg.Format(notifyViaIRC.tpl, notifyViaIRC.bot.s, notifyViaIRC.bot.logger)) + + notifyViaIRC.CallNext(msg) +}