Fallback to alt nick

If the nickname we want is taken, fallback to another one by
appending underscores. Use MONITOR to figure out when we can request
our desired nick again.

Closes: https://todo.sr.ht/~emersion/soju/35
This commit is contained in:
Simon Ser 2021-12-04 20:07:23 +01:00
parent f2a28f6e22
commit 1c285a1b72

View file

@ -774,6 +774,8 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
} }
} }
uc.updateMonitor()
uc.forEachDownstream(func(dc *downstreamConn) { uc.forEachDownstream(func(dc *downstreamConn) {
if dc.network == nil { if dc.network == nil {
return return
@ -876,6 +878,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
uc.forEachDownstream(func(dc *downstreamConn) { uc.forEachDownstream(func(dc *downstreamConn) {
dc.updateNick() dc.updateNick()
}) })
uc.updateMonitor()
} }
case "SETNAME": case "SETNAME":
if msg.Prefix == nil { if msg.Prefix == nil {
@ -1522,6 +1525,27 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
uc.monitored.SetValue(prefix.Name, online) uc.monitored.SetValue(prefix.Name, online)
} }
// Check if the nick we want is now free
wantNick := GetNick(&uc.user.User, &uc.network.Network)
wantNickCM := uc.network.casemap(wantNick)
if !online && uc.nickCM != wantNickCM {
found := false
for _, target := range targets {
prefix := irc.ParsePrefix(target)
if uc.network.casemap(prefix.Name) == wantNickCM {
found = true
break
}
}
if found {
uc.logger.Printf("desired nick %q is now available", wantNick)
uc.SendMessage(&irc.Message{
Command: "NICK",
Params: []string{wantNick},
})
}
}
uc.forEachDownstream(func(dc *downstreamConn) { uc.forEachDownstream(func(dc *downstreamConn) {
for _, target := range targets { for _, target := range targets {
prefix := irc.ParsePrefix(target) prefix := irc.ParsePrefix(target)
@ -1687,7 +1711,22 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
return err return err
} }
return fmt.Errorf("fatal server error: %v", text) return fmt.Errorf("fatal server error: %v", text)
case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP: case irc.ERR_NICKNAMEINUSE:
// At this point, we haven't received ISUPPORT so we don't know the
// maximum nickname length or whether the server supports MONITOR. Many
// servers have NICKLEN=30 so let's just use that.
if !uc.registered && len(uc.nick)+1 < 30 {
uc.nick = uc.nick + "_"
uc.nickCM = uc.network.casemap(uc.nick)
uc.logger.Printf("desired nick is not available, falling back to %q", uc.nick)
uc.SendMessage(&irc.Message{
Command: "NICK",
Params: []string{uc.nick},
})
return nil
}
fallthrough
case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
if !uc.registered { if !uc.registered {
return registrationError{msg} return registrationError{msg}
} }
@ -2087,14 +2126,21 @@ func (uc *upstreamConn) updateMonitor() {
if !uc.monitored.Has(targetCM) { if !uc.monitored.Has(targetCM) {
if _, ok := add[targetCM]; !ok { if _, ok := add[targetCM]; !ok {
addList = append(addList, targetCM) addList = append(addList, targetCM)
add[targetCM] = struct{}{}
} }
add[targetCM] = struct{}{}
} else { } else {
seen[targetCM] = struct{}{} seen[targetCM] = struct{}{}
} }
} }
}) })
wantNick := GetNick(&uc.user.User, &uc.network.Network)
wantNickCM := uc.network.casemap(wantNick)
if _, ok := add[wantNickCM]; !ok && !uc.monitored.Has(wantNick) && !uc.isOurNick(wantNick) {
addList = append(addList, wantNickCM)
add[wantNickCM] = struct{}{}
}
removeAll := true removeAll := true
var removeList []string var removeList []string
for targetCM, entry := range uc.monitored.innerMap { for targetCM, entry := range uc.monitored.innerMap {