This commit is contained in:
cottongin
2025-11-01 21:00:16 -04:00
parent bd9513b86c
commit dd398c9a8c
31 changed files with 5211 additions and 4 deletions

View File

@@ -215,6 +215,11 @@ 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 {
// Allow invisible characters used for preventing highlights
// U+200B: zero-width space, U+2060: word joiner
if r == '\u200B' || r == '\u2060' || r == '\x0F' {
return r
}
if strings.ContainsRune("!+%@&#$:'\"?*,. ", r) {
return '-'
}
@@ -229,12 +234,36 @@ func (b *Birc) doSend() {
for msg := range b.Local {
<-throttle.C
username := msg.Username
// Insert invisible characters into the actual username to prevent highlights
// The username may already be formatted like "[protocol] <nick> " so we need to find
// the actual nick part and modify that
if len(msg.Username) > 0 {
// Try to find the actual username within angle brackets <username>
if strings.Contains(username, "<") && strings.Contains(username, ">") {
startIdx := strings.Index(username, "<") + 1
endIdx := strings.Index(username, ">")
if startIdx < endIdx && endIdx <= len(username) {
actualNick := username[startIdx:endIdx]
if len(actualNick) > 1 {
// Insert invisible characters after first character of actual nick
modifiedNick := string(actualNick[0]) + "\u200B\u2060\x0F" + actualNick[1:]
username = username[:startIdx] + modifiedNick + username[endIdx:]
b.Log.Infof("Modified username: %q -> %q", msg.Username, username)
}
}
} else if len(username) > 1 {
// Fallback: no angle brackets, just modify the username directly
username = string(username[0]) + "\u200B\u2060\x0F" + username[1:]
b.Log.Infof("Modified username (no brackets): %q -> %q", msg.Username, username)
}
}
// Optional support for the proposed RELAYMSG extension, described at
// https://github.com/jlu5/ircv3-specifications/blob/master/extensions/relaymsg.md
// nolint:nestif
if (b.i.HasCapability("overdrivenetworks.com/relaymsg") || b.i.HasCapability("draft/relaymsg")) &&
b.GetBool("UseRelayMsg") {
username = sanitizeNick(username)
b.Log.Infof("After sanitizeNick: %q (len=%d, bytes=%v)", username, len(username), []byte(username))
text := msg.Text
// Work around girc chomping leading commas on single word messages?
@@ -245,23 +274,24 @@ func (b *Birc) doSend() {
if msg.Event == config.EventUserAction {
b.i.Cmd.SendRawf("RELAYMSG %s %s :\x01ACTION %s\x01", msg.Channel, username, text) //nolint:errcheck
} else {
b.Log.Debugf("Sending RELAYMSG to channel %s: nick=%s", msg.Channel, username)
b.Log.Infof("Sending RELAYMSG to channel %s: nick=%s", msg.Channel, username)
b.i.Cmd.SendRawf("RELAYMSG %s %s :%s", msg.Channel, username, text) //nolint:errcheck
}
} 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)
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, username)
}
b.Log.Infof("Final username before send: %q (len=%d, bytes=%v)", username, len(username), []byte(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.Log.Infof("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.Log.Infof("Sending to channel %s", msg.Channel)
b.i.Cmd.Message(msg.Channel, username+msg.Text)
}
}