diff --git a/irc/getters.go b/irc/getters.go index d0e08256..12839710 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -223,6 +223,13 @@ func (session *Session) SetAway(awayMessage string) (wasAway, nowAway string) { return } +func (session *Session) ConnID() string { + if session == nil { + return "*" + } + return session.connID +} + func (client *Client) autoAwayEnabledNoMutex(config *Config) bool { return client.registered && client.alwaysOn && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) diff --git a/irc/handlers.go b/irc/handlers.go index 85354a3b..3dd6ba40 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -137,7 +137,7 @@ func sendSuccessfulAccountAuth(service *ircService, client *Client, rb *Response } } - client.server.logger.Info("accounts", "client", details.nick, "logged into account", details.accountName) + client.server.logger.Info("accounts", rb.session.ConnID(), details.nick, "logged into account", details.accountName) } func (server *Server) sendLoginSnomask(nickMask, accountName string) { @@ -1852,14 +1852,14 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respon if 1 < len(msg.Params) { // parse out real mode changes params := msg.Params[1:] - var unknown map[rune]bool + var unknown []rune changes, unknown = modes.ParseChannelModeChanges(params...) // alert for unknown mode changes - for char := range unknown { + for _, char := range unknown { rb.Add(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), client.t("is an unknown mode character to me")) } - if len(unknown) == 1 && len(changes) == 0 { + if len(unknown) != 0 && len(changes) == 0 { return false } } @@ -1943,10 +1943,10 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respon changes, unknown := modes.ParseUserModeChanges(params...) // alert for unknown mode changes - for char := range unknown { + for _, char := range unknown { rb.Add(nil, server.name, ERR_UNKNOWNMODE, cDetails.nick, string(char), client.t("is an unknown mode character to me")) } - if len(unknown) == 1 && len(changes) == 0 { + if len(unknown) != 0 && len(changes) == 0 { return false } diff --git a/irc/isupport/list.go b/irc/isupport/list.go index 36b30d37..d54df127 100644 --- a/irc/isupport/list.go +++ b/irc/isupport/list.go @@ -5,7 +5,7 @@ package isupport import ( "fmt" - "sort" + "slices" "strings" ) @@ -58,7 +58,7 @@ func getTokenString(name string, value string) string { // GetDifference returns the difference between two token lists. func (il *List) GetDifference(newil *List) [][]string { - var outTokens sort.StringSlice + var outTokens []string // append removed tokens for name := range il.Tokens { @@ -84,7 +84,7 @@ func (il *List) GetDifference(newil *List) [][]string { outTokens = append(outTokens, token) } - sort.Sort(outTokens) + slices.Sort(outTokens) // create output list replies := make([][]string, 0) @@ -117,38 +117,34 @@ func (il *List) GetDifference(newil *List) [][]string { // RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply func (il *List) RegenerateCachedReply() (err error) { - il.CachedReply = make([][]string, 0) - var length int // Length of the current cache - var cache []string // Token list cache - - // make sure we get a sorted list of tokens, needed for tests and looks nice - var tokens sort.StringSlice - for name := range il.Tokens { - tokens = append(tokens, name) - } - sort.Sort(tokens) - - for _, name := range tokens { - token := getTokenString(name, il.Tokens[name]) + var tokens []string + for name, value := range il.Tokens { + token := getTokenString(name, value) if token[0] == ':' || strings.Contains(token, " ") { err = fmt.Errorf("bad isupport token (cannot contain spaces or start with :): %s", token) continue } + tokens = append(tokens, token) + } + // make sure we get a sorted list of tokens, needed for tests and looks nice + slices.Sort(tokens) - if len(token)+length <= maxLastArgLength { - // account for the space separating tokens - if len(cache) > 0 { - length++ - } - cache = append(cache, token) - length += len(token) - } + var cache []string // Tokens in current line + var length int // Length of the current line - if len(cache) == maxParameters || len(token)+length >= maxLastArgLength { + for _, token := range tokens { + // account for the space separating tokens + if len(cache) == maxParameters || (len(token)+1)+length > maxLastArgLength { il.CachedReply = append(il.CachedReply, cache) - cache = make([]string, 0) + cache = nil length = 0 } + + if len(cache) > 0 { + length++ + } + length += len(token) + cache = append(cache, token) } if len(cache) > 0 { diff --git a/irc/isupport/list_test.go b/irc/isupport/list_test.go index 47fb72ef..2041ea17 100644 --- a/irc/isupport/list_test.go +++ b/irc/isupport/list_test.go @@ -37,7 +37,7 @@ func TestISUPPORT(t *testing.T) { } if !reflect.DeepEqual(tListLong.CachedReply, longReplies) { - t.Errorf("Multiple output replies did not match, got [%v]", longReplies) + t.Errorf("Multiple output replies did not match, got [%v]", tListLong.CachedReply) } // create first list diff --git a/irc/modes.go b/irc/modes.go index e6079e24..cd7581ba 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -116,7 +116,7 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool, } // parseDefaultModes uses the provided mode change parser to parse the rawModes. -func parseDefaultModes(rawModes string, parser func(params ...string) (modes.ModeChanges, map[rune]bool)) modes.Modes { +func parseDefaultModes(rawModes string, parser func(params ...string) (modes.ModeChanges, []rune)) modes.Modes { modeChangeStrings := strings.Fields(rawModes) modeChanges, _ := parser(modeChangeStrings...) defaultModes := make(modes.Modes, 0) diff --git a/irc/modes/modes.go b/irc/modes/modes.go index ad160169..5f0eb6b6 100644 --- a/irc/modes/modes.go +++ b/irc/modes/modes.go @@ -7,7 +7,7 @@ package modes import ( "fmt" - "sort" + "slices" "strings" "github.com/ergochat/ergo/irc/utils" @@ -189,10 +189,7 @@ func GetLowestChannelModePrefix(prefixes string) (lowest Mode) { // // ParseUserModeChanges returns the valid changes, and the list of unknown chars. -func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) { - changes := make(ModeChanges, 0) - unknown := make(map[rune]bool) - +func ParseUserModeChanges(params ...string) (changes ModeChanges, unknown []rune) { op := List if 0 < len(params) { @@ -219,19 +216,11 @@ func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) { } } - var isKnown bool - for _, supportedMode := range SupportedUserModes { - if rune(supportedMode) == mode { - isKnown = true - break - } - } - if !isKnown { - unknown[mode] = true - continue + if slices.Contains(SupportedUserModes, Mode(mode)) { + changes = append(changes, change) + } else { + unknown = append(unknown, mode) } - - changes = append(changes, change) } } @@ -239,10 +228,7 @@ func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) { } // ParseChannelModeChanges returns the valid changes, and the list of unknown chars. -func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) { - changes := make(ModeChanges, 0) - unknown := make(map[rune]bool) - +func ParseChannelModeChanges(params ...string) (changes ModeChanges, unknown []rune) { op := List if 0 < len(params) { @@ -304,25 +290,11 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) { } } - var isKnown bool - for _, supportedMode := range SupportedChannelModes { - if rune(supportedMode) == mode { - isKnown = true - break - } - } - for _, supportedMode := range ChannelUserModes { - if rune(supportedMode) == mode { - isKnown = true - break - } + if slices.Contains(SupportedChannelModes, Mode(mode)) || slices.Contains(ChannelUserModes, Mode(mode)) { + changes = append(changes, change) + } else { + unknown = append(unknown, mode) } - if !isKnown { - unknown[mode] = true - continue - } - - changes = append(changes, change) } } @@ -428,33 +400,37 @@ func (set *ModeSet) HighestChannelUserMode() (result Mode) { return } -type ByCodepoint Modes +var ( + rplMyInfo1, rplMyInfo2, rplMyInfo3, chanmodesToken string +) -func (a ByCodepoint) Len() int { return len(a) } -func (a ByCodepoint) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByCodepoint) Less(i, j int) bool { return a[i] < a[j] } +func init() { + initRplMyInfo() + initChanmodesToken() +} -func RplMyInfo() (param1, param2, param3 string) { +func initRplMyInfo() { + // initialize constant strings published in initial numerics userModes := make(Modes, len(SupportedUserModes), len(SupportedUserModes)+1) copy(userModes, SupportedUserModes) // TLS is not in SupportedUserModes because it can't be modified userModes = append(userModes, TLS) - sort.Sort(ByCodepoint(userModes)) + slices.Sort(userModes) channelModes := make(Modes, len(SupportedChannelModes)+len(ChannelUserModes)) copy(channelModes, SupportedChannelModes) copy(channelModes[len(SupportedChannelModes):], ChannelUserModes) - sort.Sort(ByCodepoint(channelModes)) + slices.Sort(channelModes) // XXX enumerate these by hand, i can't see any way to DRY this channelParametrizedModes := Modes{BanMask, ExceptMask, InviteMask, Key, UserLimit, Forward} channelParametrizedModes = append(channelParametrizedModes, ChannelUserModes...) - sort.Sort(ByCodepoint(channelParametrizedModes)) + slices.Sort(channelParametrizedModes) - return userModes.String(), channelModes.String(), channelParametrizedModes.String() + rplMyInfo1, rplMyInfo2, rplMyInfo3 = userModes.String(), channelModes.String(), channelParametrizedModes.String() } -func ChanmodesToken() (result string) { +func initChanmodesToken() { // https://modern.ircdocs.horse#chanmodes-parameter // type A: listable modes with parameters A := Modes{BanMask, ExceptMask, InviteMask} @@ -465,10 +441,18 @@ func ChanmodesToken() (result string) { // type D: modes without parameters D := Modes{InviteOnly, Moderated, NoOutside, OpOnlyTopic, ChanRoleplaying, Secret, NoCTCP, RegisteredOnly, RegisteredOnlySpeak, Auditorium, OpModerated} - sort.Sort(ByCodepoint(A)) - sort.Sort(ByCodepoint(B)) - sort.Sort(ByCodepoint(C)) - sort.Sort(ByCodepoint(D)) + slices.Sort(A) + slices.Sort(B) + slices.Sort(C) + slices.Sort(D) - return fmt.Sprintf("%s,%s,%s,%s", A.String(), B.String(), C.String(), D.String()) + chanmodesToken = fmt.Sprintf("%s,%s,%s,%s", A.String(), B.String(), C.String(), D.String()) +} + +func RplMyInfo() (param1, param2, param3 string) { + return rplMyInfo1, rplMyInfo2, rplMyInfo3 +} + +func ChanmodesToken() (result string) { + return chanmodesToken } diff --git a/irc/modes/modes_test.go b/irc/modes/modes_test.go index 67d28c2f..03f42c33 100644 --- a/irc/modes/modes_test.go +++ b/irc/modes/modes_test.go @@ -5,6 +5,7 @@ package modes import ( "reflect" + "slices" "strings" "testing" ) @@ -16,7 +17,7 @@ func assertEqual(supplied, expected interface{}, t *testing.T) { } func TestParseUserModeChanges(t *testing.T) { - emptyUnknown := make(map[rune]bool) + var emptyUnknown []rune changes, unknown := ParseUserModeChanges("+i") assertEqual(unknown, emptyUnknown, t) assertEqual(changes, ModeChanges{ModeChange{Op: Add, Mode: Invisible}}, t) @@ -48,10 +49,11 @@ func TestParseUserModeChanges(t *testing.T) { } func TestIssue874(t *testing.T) { - emptyUnknown := make(map[rune]bool) + var emptyModeChanges ModeChanges + var emptyUnknown []rune modes, unknown := ParseChannelModeChanges("+k") assertEqual(unknown, emptyUnknown, t) - assertEqual(modes, ModeChanges{}, t) + assertEqual(modes, emptyModeChanges, t) modes, unknown = ParseChannelModeChanges("+k", "beer") assertEqual(unknown, emptyUnknown, t) @@ -151,7 +153,7 @@ func TestParseChannelModeChanges(t *testing.T) { } modes, unknown = ParseChannelModeChanges("+tx") - if len(unknown) != 1 || !unknown['x'] { + if len(unknown) != 1 || !slices.Contains(unknown, 'x') { t.Errorf("expected that x is an unknown mode, instead: %v", unknown) } expected = ModeChange{ diff --git a/irc/services.go b/irc/services.go index 6b85f797..8e243f4b 100644 --- a/irc/services.go +++ b/irc/services.go @@ -7,7 +7,7 @@ import ( "bytes" "fmt" "log" - "sort" + "slices" "strings" "time" @@ -250,7 +250,7 @@ func serviceHelpHandler(service *ircService, server *Server, client *Client, par client.t("Here are the commands you can use:"), }...) // show general help - var shownHelpLines sort.StringSlice + var shownHelpLines []string var disabledCommands bool for _, commandInfo := range service.Commands { // skip commands user can't access @@ -268,13 +268,13 @@ func serviceHelpHandler(service *ircService, server *Server, client *Client, par shownHelpLines = append(shownHelpLines, " "+ircfmt.Unescape(client.t(commandInfo.helpShort))) } + // sort help lines + slices.Sort(shownHelpLines) + if disabledCommands { shownHelpLines = append(shownHelpLines, " "+client.t("... and other commands which have been disabled")) } - // sort help lines - sort.Sort(shownHelpLines) - // push out help text for _, line := range helpBannerLines { sendNotice(line)