irc: add support for stateless bridging via draft/relaymsg

As discussed at https://github.com/42wim/matterbridge/issues/667#issuecomment-634214165
This commit is contained in:
James Lu
2020-12-20 19:27:02 +00:00
parent 6de4c7e971
commit 436eb578aa

View File

@@ -205,27 +205,57 @@ func (b *Birc) doConnect() {
} }
} }
// Sanitize nicks for RELAYMSG: replace IRC characters with special meanings with "-"
func sanitizeNick(nick string) string {
sanitize := func(r rune) rune {
if strings.ContainsRune("!+%@&#$:'\"?*,. ", r) {
return '-';
}
return r
}
return strings.Map(sanitize, nick)
}
func (b *Birc) doSend() { func (b *Birc) doSend() {
rate := time.Millisecond * time.Duration(b.MessageDelay) rate := time.Millisecond * time.Duration(b.MessageDelay)
throttle := time.NewTicker(rate) throttle := time.NewTicker(rate)
for msg := range b.Local { for msg := range b.Local {
<-throttle.C <-throttle.C
username := msg.Username username := msg.Username
if b.GetBool("Colornicks") && len(username) > 1 { // Optional support for the proposed RELAYMSG extension, described at
checksum := crc32.ChecksumIEEE([]byte(msg.Username)) // https://github.com/jlu5/ircv3-specifications/blob/master/extensions/relaymsg.md
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes if (b.i.HasCapability("overdrivenetworks.com/relaymsg") || b.i.HasCapability("draft/relaymsg")) &&
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username) b.GetBool("UseRelayMsg") {
} username = sanitizeNick(username)
text := msg.Text
switch msg.Event { // Work around girc chomping leading commas on single word messages?
case config.EventUserAction: if strings.HasPrefix(text, ":") && !strings.ContainsRune(text, ' ') {
b.i.Cmd.Action(msg.Channel, username+msg.Text) text = ":" + text
case config.EventNoticeIRC: }
b.Log.Debugf("Sending notice to channel %s", msg.Channel)
b.i.Cmd.Notice(msg.Channel, username+msg.Text) if msg.Event == config.EventUserAction {
default: b.i.Cmd.SendRawf("RELAYMSG %s %s :\x01ACTION %s\x01", msg.Channel, username, text)
b.Log.Debugf("Sending to channel %s", msg.Channel) } else {
b.i.Cmd.Message(msg.Channel, username+msg.Text) b.Log.Debugf("Sending RELAYMSG to channel %s: nick=%s", msg.Channel, username)
b.i.Cmd.SendRawf("RELAYMSG %s %s :%s", msg.Channel, username, text)
}
} else {
if b.GetBool("Colornicks") {
checksum := crc32.ChecksumIEEE([]byte(msg.Username))
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
}
switch msg.Event {
case config.EventUserAction:
b.i.Cmd.Action(msg.Channel, username+msg.Text)
case config.EventNoticeIRC:
b.Log.Debugf("Sending notice to channel %s", msg.Channel)
b.i.Cmd.Notice(msg.Channel, username+msg.Text)
default:
b.Log.Debugf("Sending to channel %s", msg.Channel)
b.i.Cmd.Message(msg.Channel, username+msg.Text)
}
} }
} }
} }
@@ -263,18 +293,19 @@ func (b *Birc) getClient() (*girc.Client, error) {
b.Log.Debugf("setting pingdelay to %s", pingDelay) b.Log.Debugf("setting pingdelay to %s", pingDelay)
i := girc.New(girc.Config{ i := girc.New(girc.Config{
Server: server, Server: server,
ServerPass: b.GetString("Password"), ServerPass: b.GetString("Password"),
Port: port, Port: port,
Nick: b.GetString("Nick"), Nick: b.GetString("Nick"),
User: user, User: user,
Name: b.GetString("Nick"), Name: b.GetString("Nick"),
SSL: b.GetBool("UseTLS"), SSL: b.GetBool("UseTLS"),
TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
PingDelay: pingDelay, PingDelay: pingDelay,
// skip gIRC internal rate limiting, since we have our own throttling // skip gIRC internal rate limiting, since we have our own throttling
AllowFlood: true, AllowFlood: true,
Debug: debug, Debug: debug,
SupportedCaps: map[string][]string{ "overdrivenetworks.com/relaymsg": nil, "draft/relaymsg": nil },
}) })
return i, nil return i, nil
} }
@@ -311,6 +342,10 @@ func (b *Birc) skipPrivMsg(event girc.Event) bool {
if event.Source.Name == b.Nick { if event.Source.Name == b.Nick {
return true return true
} }
// don't forward messages we sent via RELAYMSG
if relayedNick, ok := event.Tags.Get("relaymsg"); ok && relayedNick == b.Nick {
return true
}
return false return false
} }