Add config option to deliver alerts with PRIVMSG

Add `use_privmsg` config option to deliver alerts with PRIVMSG instead
of the default NOTICE.

This addresses a use case described in
https://github.com/google/alertmanager-irc-relay/pull/1 .

Signed-off-by: Luca Bigliardi <shammash@google.com>
This commit is contained in:
Luca Bigliardi 2020-01-25 18:03:13 +00:00
parent 4e1aeaf931
commit ae6594c606
5 changed files with 75 additions and 1 deletions

View File

@ -48,6 +48,11 @@ irc_channels:
# Send only one message when webhook data is received. # Send only one message when webhook data is received.
# Note: By default a message is sent for each alert in the webhook data. # Note: By default a message is sent for each alert in the webhook data.
msg_once_per_alert_group: no msg_once_per_alert_group: no
#
# Use PRIVMSG instead of NOTICE (default) to send messages.
# Note: Sending PRIVMSG from bots is bad practice, do not enable this unless
# necessary (e.g. unless NOTICEs would weaken your channel moderation policies)
use_privmsg: yes
# Define how IRC messages should be formatted. # Define how IRC messages should be formatted.
# #

View File

@ -41,6 +41,7 @@ type Config struct {
IRCChannels []IRCChannel `yaml:"irc_channels"` IRCChannels []IRCChannel `yaml:"irc_channels"`
MsgTemplate string `yaml:"msg_template"` MsgTemplate string `yaml:"msg_template"`
MsgOnce bool `yaml:"msg_once_per_alert_group"` MsgOnce bool `yaml:"msg_once_per_alert_group"`
UsePrivmsg bool `yaml:"use_privmsg"`
} }
func LoadConfig(configFile string) (*Config, error) { func LoadConfig(configFile string) (*Config, error) {
@ -55,6 +56,7 @@ func LoadConfig(configFile string) (*Config, error) {
IRCUseSSL: true, IRCUseSSL: true,
IRCChannels: []IRCChannel{IRCChannel{Name: "#airtest"}}, IRCChannels: []IRCChannel{IRCChannel{Name: "#airtest"}},
MsgOnce: false, MsgOnce: false,
UsePrivmsg: false,
} }
if configFile != "" { if configFile != "" {

View File

@ -43,6 +43,7 @@ func TestLoadGoodConfig(t *testing.T) {
IRCChannels: []IRCChannel{IRCChannel{Name: "#foobar"}}, IRCChannels: []IRCChannel{IRCChannel{Name: "#foobar"}},
MsgTemplate: defaultMsgTemplate, MsgTemplate: defaultMsgTemplate,
MsgOnce: false, MsgOnce: false,
UsePrivmsg: false,
} }
expectedData, err := yaml.Marshal(expectedConfig) expectedData, err := yaml.Marshal(expectedConfig)
if err != nil { if err != nil {

8
irc.go
View File

@ -62,6 +62,8 @@ type IRCNotifier struct {
PreJoinChannels []IRCChannel PreJoinChannels []IRCChannel
JoinedChannels map[string]ChannelState JoinedChannels map[string]ChannelState
UsePrivmsg bool
NickservDelayWait time.Duration NickservDelayWait time.Duration
BackoffCounter Delayer BackoffCounter Delayer
} }
@ -94,6 +96,7 @@ func NewIRCNotifier(config *Config, alertMsgs chan AlertMsg) (*IRCNotifier, erro
sessionDownSignal: make(chan bool), sessionDownSignal: make(chan bool),
PreJoinChannels: config.IRCChannels, PreJoinChannels: config.IRCChannels,
JoinedChannels: make(map[string]ChannelState), JoinedChannels: make(map[string]ChannelState),
UsePrivmsg: config.UsePrivmsg,
NickservDelayWait: nickservWaitSecs * time.Second, NickservDelayWait: nickservWaitSecs * time.Second,
BackoffCounter: backoffCounter, BackoffCounter: backoffCounter,
} }
@ -196,7 +199,12 @@ func (notifier *IRCNotifier) MaybeSendAlertMsg(alertMsg *AlertMsg) {
return return
} }
notifier.JoinChannel(&IRCChannel{Name: alertMsg.Channel}) notifier.JoinChannel(&IRCChannel{Name: alertMsg.Channel})
if notifier.UsePrivmsg {
notifier.Client.Privmsg(alertMsg.Channel, alertMsg.Alert)
} else {
notifier.Client.Notice(alertMsg.Channel, alertMsg.Alert) notifier.Client.Notice(alertMsg.Channel, alertMsg.Alert)
}
} }
func (notifier *IRCNotifier) Run() { func (notifier *IRCNotifier) Run() {

View File

@ -212,6 +212,7 @@ func makeTestIRCConfig(IRCPort int) *Config {
IRCChannel{Name: "#bar"}, IRCChannel{Name: "#bar"},
IRCChannel{Name: "#baz"}, IRCChannel{Name: "#baz"},
}, },
UsePrivmsg: false,
} }
} }
@ -321,6 +322,63 @@ func TestSendAlertOnPreJoinedChannel(t *testing.T) {
} }
} }
func TestUsePrivmsgToSendAlertOnPreJoinedChannel(t *testing.T) {
server, port := makeTestServer(t)
config := makeTestIRCConfig(port)
config.UsePrivmsg = true
notifier, alertMsgs := makeTestNotifier(t, config)
var testStep sync.WaitGroup
testChannel := "#foo"
testMessage := "test message"
// Send the alert after configured channels have joined, to ensure we
// check for no re-join attempt.
joinedHandler := func(conn *bufio.ReadWriter, line *irc.Line) error {
if line.Args[0] == testChannel {
testStep.Done()
}
return nil
}
server.SetHandler("JOIN", joinedHandler)
testStep.Add(1)
go notifier.Run()
testStep.Wait()
server.SetHandler("JOIN", nil)
privmsgHandler := func(conn *bufio.ReadWriter, line *irc.Line) error {
testStep.Done()
return nil
}
server.SetHandler("PRIVMSG", privmsgHandler)
testStep.Add(1)
alertMsgs <- AlertMsg{Channel: testChannel, Alert: testMessage}
testStep.Wait()
notifier.StopRunning <- true
server.Stop()
expectedCommands := []string{
"NICK foo",
"USER foo 12 * :",
"JOIN #foo",
"JOIN #bar",
"JOIN #baz",
"PRIVMSG #foo :test message",
"QUIT :see ya",
}
if !reflect.DeepEqual(expectedCommands, server.Log) {
t.Error("Alert not sent correctly. Received commands:\n", strings.Join(server.Log, "\n"))
}
}
func TestSendAlertAndJoinChannel(t *testing.T) { func TestSendAlertAndJoinChannel(t *testing.T) {
server, port := makeTestServer(t) server, port := makeTestServer(t)
config := makeTestIRCConfig(port) config := makeTestIRCConfig(port)