diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 83bfc86a..3a40d1cf 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -621,10 +621,6 @@ In this section, we give an overview of the modes Oragono supports. These are the modes which can be set on you when you're connected. -### +a - Away - -If this mode is set, you're marked as being away. This mode is set with the /AWAY command. - ### +i - Invisible If this mode is set, you're marked as 'invisible'. This means that your channels won't be shown when users `/WHOIS` you (except for IRC operators, they can see all the channels you're in). diff --git a/irc/client.go b/irc/client.go index f88d4a83..c7e36b5c 100644 --- a/irc/client.go +++ b/irc/client.go @@ -78,8 +78,6 @@ type Client struct { accountName string // display name of the account: uncasefolded, '*' if not logged in accountRegDate time.Time accountSettings AccountSettings - away bool - autoAway bool awayMessage string brbTimer BrbTimer channels ChannelSet @@ -177,6 +175,9 @@ type Session struct { quitMessage string + awayMessage string + awayAt time.Time + capabilities caps.Set capState caps.State capVersion caps.Version @@ -486,9 +487,7 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, channelToStatus m } if persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) { - client.autoAway = true - client.away = true - client.awayMessage = client.t("User is currently disconnected") + client.setAutoAwayNoMutex(config) } } @@ -675,7 +674,7 @@ func (client *Client) run(session *Session) { session.playResume() session.resumeDetails = nil client.brbTimer.Disable() - client.SetAway(false, "") // clear BRB message if any + session.SetAway("") // clear BRB message if any } else { client.playReattachMessages(session) } @@ -1459,15 +1458,13 @@ func (client *Client) destroy(session *Session) { client.dirtyBits |= IncludeLastSeen } - autoAway := false + becameAutoAway := false var awayMessage string - if alwaysOn && !client.away && remainingSessions == 0 && - persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) { - autoAway = true - client.autoAway = true - client.away = true - awayMessage = config.languageManager.Translate(client.languages, `User is currently disconnected`) - client.awayMessage = awayMessage + if alwaysOn && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) { + wasAway := client.awayMessage != "" + client.setAutoAwayNoMutex(config) + awayMessage = client.awayMessage + becameAutoAway = !wasAway && awayMessage != "" } if client.registrationTimer != nil { @@ -1524,7 +1521,7 @@ func (client *Client) destroy(session *Session) { client.server.stats.Remove(registered, invisible, operator) } - if autoAway { + if becameAutoAway { dispatchAwayNotify(client, true, awayMessage) } diff --git a/irc/getters.go b/irc/getters.go index 5c1a43f5..2580683d 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -107,6 +107,7 @@ func (client *Client) AllSessionData(currentSession *Session, hasPrivs bool) (da } func (client *Client) AddSession(session *Session) (success bool, numSessions int, lastSeen time.Time, back bool) { + config := client.server.Config() client.stateMutex.Lock() defer client.stateMutex.Unlock() @@ -126,11 +127,12 @@ func (client *Client) AddSession(session *Session) (success bool, numSessions in client.setLastSeen(time.Now().UTC(), session.deviceID) } client.sessions = newSessions - if client.autoAway { - back = true - client.autoAway = false - client.away = false + // TODO(#1551) there should be a cap to opt out of this behavior on a session + if persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) { client.awayMessage = "" + if len(client.sessions) == 1 { + back = true + } } return true, len(client.sessions), lastSeen, back } @@ -196,20 +198,54 @@ func (client *Client) Hostname() string { func (client *Client) Away() (result bool, message string) { client.stateMutex.Lock() - result, message = client.away, client.awayMessage + message = client.awayMessage client.stateMutex.Unlock() + result = client.awayMessage != "" return } -func (client *Client) SetAway(away bool, awayMessage string) (changed bool) { +func (session *Session) SetAway(awayMessage string) { + client := session.client + config := client.server.Config() + client.stateMutex.Lock() - changed = away != client.away - client.away = away - client.awayMessage = awayMessage - client.stateMutex.Unlock() + defer client.stateMutex.Unlock() + + session.awayMessage = awayMessage + session.awayAt = time.Now().UTC() + + autoAway := client.registered && client.alwaysOn && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) + if autoAway { + client.setAutoAwayNoMutex(config) + } else { + client.awayMessage = awayMessage + } return } +func (client *Client) setAutoAwayNoMutex(config *Config) { + // aggregate the away statuses of the individual sessions: + var globalAwayState string + var awaySetAt time.Time + for _, cSession := range client.sessions { + if cSession.awayMessage == "" { + // a session is active, we are not auto-away + client.awayMessage = "" + return + } else if cSession.awayAt.After(awaySetAt) { + // choose the latest available away message from any session + globalAwayState = cSession.awayMessage + awaySetAt = cSession.awayAt + } + } + if awaySetAt.IsZero() { + // no sessions, enable auto-away + client.awayMessage = config.languageManager.Translate(client.languages, `User is currently disconnected`) + } else { + client.awayMessage = globalAwayState + } +} + func (client *Client) AlwaysOn() (alwaysOn bool) { client.stateMutex.RLock() alwaysOn = client.registered && client.alwaysOn @@ -269,12 +305,6 @@ func (client *Client) AwayMessage() (result string) { return } -func (client *Client) SetAwayMessage(message string) { - client.stateMutex.Lock() - client.awayMessage = message - client.stateMutex.Unlock() -} - func (client *Client) Account() string { client.stateMutex.RLock() defer client.stateMutex.RUnlock() diff --git a/irc/handlers.go b/irc/handlers.go index ddd4d9c1..b910e1f1 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -352,7 +352,7 @@ func awayHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons } } - client.SetAway(isAway, awayMessage) + rb.session.SetAway(awayMessage) if isAway { rb.Add(nil, server.name, RPL_NOWAWAY, client.nick, client.t("You have been marked as being away")) @@ -440,7 +440,7 @@ func brbHandler(server *Server, client *Client, msg ircmsg.Message, rb *Response if len(client.Sessions()) == 1 { // true BRB - client.SetAway(true, message) + rb.session.SetAway(message) } return true