Add better bridge states
This commit is contained in:
parent
7860fb64c8
commit
bbc4da21b7
9 changed files with 100 additions and 64 deletions
|
@ -26,10 +26,20 @@ const (
|
|||
GMNotConnected status.BridgeStateErrorCode = "gm-not-connected"
|
||||
GMConnecting status.BridgeStateErrorCode = "gm-connecting"
|
||||
GMConnectionFailed status.BridgeStateErrorCode = "gm-connection-failed"
|
||||
|
||||
GMBrowserInactive status.BridgeStateErrorCode = "gm-browser-inactive"
|
||||
GMBrowserInactiveTimeout status.BridgeStateErrorCode = "gm-browser-inactive-timeout"
|
||||
GMBrowserInactiveInactivity status.BridgeStateErrorCode = "gm-browser-inactive-inactivity"
|
||||
)
|
||||
|
||||
func init() {
|
||||
status.BridgeStateHumanErrors.Update(status.BridgeStateErrorMap{})
|
||||
status.BridgeStateHumanErrors.Update(status.BridgeStateErrorMap{
|
||||
GMListenError: "Error polling messages from Google Messages server, the bridge will try to reconnect",
|
||||
GMFatalError: "Google Messages login was invalidated, please re-link the bridge",
|
||||
GMBrowserInactive: "Google Messages opened in another browser",
|
||||
GMBrowserInactiveTimeout: "Google Messages disconnected due to timeout",
|
||||
GMBrowserInactiveInactivity: "Google Messages disconnected due to inactivity",
|
||||
})
|
||||
}
|
||||
|
||||
func (user *User) GetRemoteID() string {
|
||||
|
|
|
@ -148,9 +148,7 @@ func fnDeleteSession(ce *WrappedCommandEvent) {
|
|||
ce.Reply("Nothing to purge: no session information stored and no active connection.")
|
||||
return
|
||||
}
|
||||
ce.User.removeFromPhoneMap(status.BridgeState{StateEvent: status.StateLoggedOut})
|
||||
ce.User.DeleteConnection()
|
||||
ce.User.DeleteSession()
|
||||
ce.User.Logout(status.BridgeState{StateEvent: status.StateLoggedOut})
|
||||
ce.Reply("Session information purged")
|
||||
}
|
||||
|
||||
|
|
2
go.mod
2
go.mod
|
@ -9,7 +9,7 @@ require (
|
|||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
go.mau.fi/mautrix-gmessages/libgm v0.1.0
|
||||
maunium.net/go/maulogger/v2 v2.4.1
|
||||
maunium.net/go/mautrix v0.15.4-0.20230711231757-65db706cd3ce
|
||||
maunium.net/go/mautrix v0.15.4-0.20230714233218-82f817eff669
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
4
go.sum
4
go.sum
|
@ -81,5 +81,5 @@ maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
|||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
|
||||
maunium.net/go/mautrix v0.15.4-0.20230711231757-65db706cd3ce h1:SYYPKkcJLI012g+p3jZ/Qnqm5VQd7kFZSSEHOtI4NZA=
|
||||
maunium.net/go/mautrix v0.15.4-0.20230711231757-65db706cd3ce/go.mod h1:zLrQqdxJlLkurRCozTc9CL6FySkgZlO/kpCYxBILSLE=
|
||||
maunium.net/go/mautrix v0.15.4-0.20230714233218-82f817eff669 h1:/7+suCGeh70hK0E7P3/GZLJ+8sMAC82ZBWYDoKSbpOE=
|
||||
maunium.net/go/mautrix v0.15.4-0.20230714233218-82f817eff669/go.mod h1:zLrQqdxJlLkurRCozTc9CL6FySkgZlO/kpCYxBILSLE=
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"go.mau.fi/mautrix-gmessages/libgm/binary"
|
||||
|
@ -28,10 +29,19 @@ func NewAuthTokenRefreshed(token []byte) *AuthTokenRefreshed {
|
|||
}
|
||||
}
|
||||
|
||||
type ListenFatalError struct {
|
||||
type HTTPError struct {
|
||||
Action string
|
||||
Resp *http.Response
|
||||
}
|
||||
|
||||
func (he HTTPError) Error() string {
|
||||
return fmt.Sprintf("http %d while %s", he.Resp.StatusCode, he.Action)
|
||||
}
|
||||
|
||||
type ListenFatalError struct {
|
||||
Error error
|
||||
}
|
||||
|
||||
type ListenTemporaryError struct {
|
||||
Error error
|
||||
}
|
||||
|
|
|
@ -9,27 +9,3 @@ func NewBrowserActive(sessionId string) *BrowserActive {
|
|||
SessionId: sessionId,
|
||||
}
|
||||
}
|
||||
|
||||
type MOBILE_BATTERY_RESTORED struct{}
|
||||
|
||||
func NewMobileBatteryRestored() *MOBILE_BATTERY_RESTORED {
|
||||
return &MOBILE_BATTERY_RESTORED{}
|
||||
}
|
||||
|
||||
type MOBILE_BATTERY_LOW struct{}
|
||||
|
||||
func NewMobileBatteryLow() *MOBILE_BATTERY_LOW {
|
||||
return &MOBILE_BATTERY_LOW{}
|
||||
}
|
||||
|
||||
type MOBILE_DATA_CONNECTION struct{}
|
||||
|
||||
func NewMobileDataConnection() *MOBILE_DATA_CONNECTION {
|
||||
return &MOBILE_DATA_CONNECTION{}
|
||||
}
|
||||
|
||||
type MOBILE_WIFI_CONNECTION struct{}
|
||||
|
||||
func NewMobileWifiConnection() *MOBILE_WIFI_CONNECTION {
|
||||
return &MOBILE_WIFI_CONNECTION{}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ func (r *RPC) ListenReceiveMessages(payload []byte) {
|
|||
err := r.client.refreshAuthToken()
|
||||
if err != nil {
|
||||
r.client.Logger.Err(err).Msg("Error refreshing auth token")
|
||||
r.client.triggerEvent(&events.ListenFatalError{Error: fmt.Errorf("failed to refresh auth token: %w", err)})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -59,12 +60,12 @@ func (r *RPC) ListenReceiveMessages(payload []byte) {
|
|||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode >= 400 && resp.StatusCode < 501 {
|
||||
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
||||
r.client.Logger.Error().Int("status_code", resp.StatusCode).Msg("Error making listen request")
|
||||
r.client.triggerEvent(&events.ListenFatalError{Resp: resp})
|
||||
r.client.triggerEvent(&events.ListenFatalError{Error: events.HTTPError{Action: "polling", Resp: resp}})
|
||||
return
|
||||
} else if resp.StatusCode >= 500 {
|
||||
r.client.triggerEvent(&events.ListenTemporaryError{Error: fmt.Errorf("http %d while polling", resp.StatusCode)})
|
||||
r.client.triggerEvent(&events.ListenTemporaryError{Error: events.HTTPError{Action: "polling", Resp: resp}})
|
||||
errored = true
|
||||
r.client.Logger.Debug().Int("statusCode", resp.StatusCode).Msg("5xx error in long polling, retrying in 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
|
|
|
@ -34,27 +34,7 @@ func (c *Client) handleUserAlertEvent(res *pblite.Response, data *binary.UserAle
|
|||
} else {
|
||||
go c.handleClientReady(newSessionId)
|
||||
}
|
||||
case binary.AlertType_MOBILE_BATTERY_LOW:
|
||||
c.Logger.Info().Msg("[MOBILE_BATTERY_LOW] Mobile device is on low battery")
|
||||
evt := events.NewMobileBatteryLow()
|
||||
c.triggerEvent(evt)
|
||||
|
||||
case binary.AlertType_MOBILE_BATTERY_RESTORED:
|
||||
c.Logger.Info().Msg("[MOBILE_BATTERY_RESTORED] Mobile device has restored enough battery!")
|
||||
evt := events.NewMobileBatteryRestored()
|
||||
c.triggerEvent(evt)
|
||||
|
||||
case binary.AlertType_MOBILE_DATA_CONNECTION:
|
||||
c.Logger.Info().Msg("[MOBILE_DATA_CONNECTION] Mobile device is now using data connection")
|
||||
evt := events.NewMobileDataConnection()
|
||||
c.triggerEvent(evt)
|
||||
|
||||
case binary.AlertType_MOBILE_WIFI_CONNECTION:
|
||||
c.Logger.Info().Msg("[MOBILE_WIFI_CONNECTION] Mobile device is now using wifi connection")
|
||||
evt := events.NewMobileWifiConnection()
|
||||
c.triggerEvent(evt)
|
||||
|
||||
default:
|
||||
c.Logger.Info().Any("data", data).Any("res", res).Msg("Got unknown alert type")
|
||||
c.triggerEvent(data)
|
||||
}
|
||||
}
|
||||
|
|
77
user.go
77
user.go
|
@ -67,6 +67,11 @@ type User struct {
|
|||
|
||||
spaceMembershipChecked bool
|
||||
|
||||
longPollingError error
|
||||
browserInactiveType status.BridgeStateErrorCode
|
||||
batteryLow bool
|
||||
mobileData bool
|
||||
|
||||
DoublePuppetIntent *appservice.IntentAPI
|
||||
|
||||
hackyLoginCommand *WrappedCommandEvent
|
||||
|
@ -514,21 +519,21 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
user.hackyLoginCommandPrevEvent = user.sendQR(user.hackyLoginCommand, v.URL, user.hackyLoginCommandPrevEvent)
|
||||
}
|
||||
case *events.ListenFatalError:
|
||||
user.BridgeState.Send(status.BridgeState{
|
||||
StateEvent: status.StateUnknownError,
|
||||
user.Logout(status.BridgeState{
|
||||
StateEvent: status.StateBadCredentials,
|
||||
Error: GMFatalError,
|
||||
Message: fmt.Sprintf("HTTP %d in long polling loop", v.Resp.StatusCode),
|
||||
Info: map[string]any{"go_error": v.Error.Error()},
|
||||
})
|
||||
case *events.ListenTemporaryError:
|
||||
user.longPollingError = v.Error
|
||||
user.BridgeState.Send(status.BridgeState{
|
||||
StateEvent: status.StateTransientDisconnect,
|
||||
Error: GMListenError,
|
||||
Message: v.Error.Error(),
|
||||
Info: map[string]any{"go_error": v.Error.Error()},
|
||||
})
|
||||
case *events.ListenRecovered:
|
||||
user.BridgeState.Send(status.BridgeState{
|
||||
StateEvent: status.StateConnected,
|
||||
})
|
||||
user.longPollingError = nil
|
||||
user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
||||
case *events.PairSuccessful:
|
||||
user.hackyLoginCommand = nil
|
||||
user.hackyLoginCommandPrevEvent = ""
|
||||
|
@ -550,7 +555,8 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
portal := user.GetPortalByID(v.GetConversationID())
|
||||
portal.messages <- PortalMessage{evt: v, source: user}
|
||||
case *events.ClientReady:
|
||||
user.zlog.Trace().Any("data", v).Msg("Client is ready!")
|
||||
user.browserInactiveType = ""
|
||||
user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
||||
go func() {
|
||||
for _, conv := range v.Conversations {
|
||||
user.syncConversation(conv)
|
||||
|
@ -558,11 +564,66 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
}()
|
||||
case *events.BrowserActive:
|
||||
user.zlog.Trace().Any("data", v).Msg("Browser active")
|
||||
user.browserInactiveType = ""
|
||||
case *binary.UserAlertEvent:
|
||||
user.handleUserAlert(v)
|
||||
default:
|
||||
user.zlog.Trace().Any("data", v).Type("data_type", v).Msg("Unknown event")
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) handleUserAlert(v *binary.UserAlertEvent) {
|
||||
user.zlog.Debug().Any("data", v).Msg("Got user alert event")
|
||||
switch v.GetAlertType() {
|
||||
case binary.AlertType_BROWSER_INACTIVE:
|
||||
// TODO aggressively reactivate if configured to do so
|
||||
user.browserInactiveType = GMBrowserInactive
|
||||
case binary.AlertType_BROWSER_INACTIVE_FROM_TIMEOUT:
|
||||
user.browserInactiveType = GMBrowserInactiveTimeout
|
||||
case binary.AlertType_BROWSER_INACTIVE_FROM_INACTIVITY:
|
||||
user.browserInactiveType = GMBrowserInactiveInactivity
|
||||
case binary.AlertType_MOBILE_DATA_CONNECTION:
|
||||
user.mobileData = true
|
||||
case binary.AlertType_MOBILE_WIFI_CONNECTION:
|
||||
user.mobileData = false
|
||||
case binary.AlertType_MOBILE_BATTERY_LOW:
|
||||
user.batteryLow = true
|
||||
case binary.AlertType_MOBILE_BATTERY_RESTORED:
|
||||
user.batteryLow = false
|
||||
default:
|
||||
return
|
||||
}
|
||||
user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
||||
}
|
||||
|
||||
func (user *User) FillBridgeState(state status.BridgeState) status.BridgeState {
|
||||
if state.Info == nil {
|
||||
state.Info = make(map[string]any)
|
||||
}
|
||||
state.Info["battery_low"] = user.batteryLow
|
||||
state.Info["mobile_data"] = user.mobileData
|
||||
state.Info["browser_active"] = user.browserInactiveType == ""
|
||||
if state.StateEvent == status.StateConnected {
|
||||
if user.longPollingError != nil {
|
||||
state.StateEvent = status.StateTransientDisconnect
|
||||
state.Error = GMListenError
|
||||
state.Info["go_error"] = user.longPollingError.Error()
|
||||
}
|
||||
if user.browserInactiveType != "" {
|
||||
state.StateEvent = status.StateTransientDisconnect
|
||||
state.Error = user.browserInactiveType
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func (user *User) Logout(state status.BridgeState) {
|
||||
user.removeFromPhoneMap(state)
|
||||
// TODO invalidate token
|
||||
user.DeleteConnection()
|
||||
user.DeleteSession()
|
||||
}
|
||||
|
||||
func (user *User) syncConversation(v *binary.Conversation) {
|
||||
updateType := v.GetStatus()
|
||||
portal := user.GetPortalByID(v.GetConversationID())
|
||||
|
|
Loading…
Reference in a new issue