mirror of
https://github.com/tulir/mautrix-whatsapp
synced 2024-12-14 01:14:29 +01:00
Add automatic connection retries
This commit is contained in:
parent
38540d8efb
commit
23747d4917
4 changed files with 73 additions and 14 deletions
|
@ -182,6 +182,9 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
||||||
ce.Reply("You are not logged in.")
|
ce.Reply("You are not logged in.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if err == whatsapp.ErrLoginInProgress {
|
||||||
|
ce.Reply("A login or reconnection is already in progress.")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ce.User.log.Warnln("Error while reconnecting:", err)
|
ce.User.log.Warnln("Error while reconnecting:", err)
|
||||||
|
@ -190,6 +193,7 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
||||||
ce.Reply("You were already connected.")
|
ce.Reply("You were already connected.")
|
||||||
} else {
|
} else {
|
||||||
ce.User.Connected = true
|
ce.User.Connected = true
|
||||||
|
ce.User.ConnectionErrors = 0
|
||||||
ce.Reply("You were already connected, but the bridge hadn't noticed. Fixed that now.")
|
ce.Reply("You were already connected, but the bridge hadn't noticed. Fixed that now.")
|
||||||
}
|
}
|
||||||
} else if err.Error() == "restore session connection timed out" {
|
} else if err.Error() == "restore session connection timed out" {
|
||||||
|
@ -200,6 +204,7 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ce.User.Connected = true
|
ce.User.Connected = true
|
||||||
|
ce.User.ConnectionErrors = 0
|
||||||
ce.Reply("Reconnected successfully.")
|
ce.Reply("Reconnected successfully.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,9 @@ type BridgeConfig struct {
|
||||||
UsernameTemplate string `yaml:"username_template"`
|
UsernameTemplate string `yaml:"username_template"`
|
||||||
DisplaynameTemplate string `yaml:"displayname_template"`
|
DisplaynameTemplate string `yaml:"displayname_template"`
|
||||||
|
|
||||||
ConnectionTimeout int `yaml:"connection_timeout"`
|
ConnectionTimeout int `yaml:"connection_timeout"`
|
||||||
|
MaxConnectionAttempts int `yaml:"max_connection_attempts"`
|
||||||
|
ReportConnectionRetry bool `yaml:"report_connection_retry"`
|
||||||
|
|
||||||
CommandPrefix string `yaml:"command_prefix"`
|
CommandPrefix string `yaml:"command_prefix"`
|
||||||
|
|
||||||
|
@ -43,6 +45,12 @@ type BridgeConfig struct {
|
||||||
displaynameTemplate *template.Template `yaml:"-"`
|
displaynameTemplate *template.Template `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *BridgeConfig) setDefaults() {
|
||||||
|
bc.ConnectionTimeout = 20
|
||||||
|
bc.MaxConnectionAttempts = 3
|
||||||
|
bc.ReportConnectionRetry = true
|
||||||
|
}
|
||||||
|
|
||||||
type umBridgeConfig BridgeConfig
|
type umBridgeConfig BridgeConfig
|
||||||
|
|
||||||
func (bc *BridgeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (bc *BridgeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
@ -61,7 +69,7 @@ func (bc *BridgeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type UsernameTemplateArgs struct {
|
type UsernameTemplateArgs struct {
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) {
|
func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) {
|
||||||
|
|
|
@ -64,6 +64,7 @@ type Config struct {
|
||||||
func (config *Config) setDefaults() {
|
func (config *Config) setDefaults() {
|
||||||
config.AppService.Database.MaxOpenConns = 20
|
config.AppService.Database.MaxOpenConns = 20
|
||||||
config.AppService.Database.MaxIdleConns = 2
|
config.AppService.Database.MaxIdleConns = 2
|
||||||
|
config.Bridge.setDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load(path string) (*Config, error) {
|
func Load(path string) (*Config, error) {
|
||||||
|
|
69
user.go
69
user.go
|
@ -44,6 +44,8 @@ type User struct {
|
||||||
Admin bool
|
Admin bool
|
||||||
Whitelisted bool
|
Whitelisted bool
|
||||||
Connected bool
|
Connected bool
|
||||||
|
|
||||||
|
ConnectionErrors int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetUserByMXID(userID types.MatrixUserID) *User {
|
func (bridge *Bridge) GetUserByMXID(userID types.MatrixUserID) *User {
|
||||||
|
@ -182,6 +184,7 @@ func (user *User) RestoreSession() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
user.Connected = true
|
user.Connected = true
|
||||||
|
user.ConnectionErrors = 0
|
||||||
user.SetSession(&sess)
|
user.SetSession(&sess)
|
||||||
user.log.Debugln("Session restored successfully")
|
user.log.Debugln("Session restored successfully")
|
||||||
}
|
}
|
||||||
|
@ -236,6 +239,7 @@ func (user *User) Login(ce *CommandEvent) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.Connected = true
|
user.Connected = true
|
||||||
|
user.ConnectionErrors = 0
|
||||||
user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1)
|
user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1)
|
||||||
user.SetSession(&session)
|
user.SetSession(&session)
|
||||||
ce.Reply("Successfully logged in. Now, you may ask for `sync [--create]`.")
|
ce.Reply("Successfully logged in. Now, you may ask for `sync [--create]`.")
|
||||||
|
@ -243,22 +247,63 @@ func (user *User) Login(ce *CommandEvent) {
|
||||||
|
|
||||||
func (user *User) HandleError(err error) {
|
func (user *User) HandleError(err error) {
|
||||||
user.log.Errorln("WhatsApp error:", err)
|
user.log.Errorln("WhatsApp error:", err)
|
||||||
closed, ok := err.(*whatsapp.ErrConnectionClosed)
|
var msg string
|
||||||
if ok {
|
if closed, ok := err.(*whatsapp.ErrConnectionClosed); ok {
|
||||||
if closed.Code != 1000 {
|
user.Connected = false
|
||||||
msg := fmt.Sprintf("\u26a0 Your WhatsApp connection was closed with websocket status code %d.\n\n"+
|
if closed.Code == 1000 {
|
||||||
"Use the `reconnect` command to reconnect.", closed.Code)
|
// Normal closure
|
||||||
_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, format.RenderMarkdown(msg))
|
return
|
||||||
}
|
}
|
||||||
|
user.ConnectionErrors++
|
||||||
|
msg = fmt.Sprintf("Your WhatsApp connection was closed with websocket status code %d", closed.Code)
|
||||||
|
} else if failed, ok := err.(*whatsapp.ErrConnectionFailed); ok {
|
||||||
user.Connected = false
|
user.Connected = false
|
||||||
|
user.ConnectionErrors++
|
||||||
|
msg = fmt.Sprintf("Your WhatsApp connection failed: %v", failed.Err)
|
||||||
|
} else {
|
||||||
|
// Unknown error, probably mostly harmless
|
||||||
|
return
|
||||||
}
|
}
|
||||||
failed, ok := err.(*whatsapp.ErrConnectionFailed)
|
if user.ConnectionErrors > user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||||
if ok {
|
content := format.RenderMarkdown(fmt.Sprintf("%s. Use the `reconnect` command to reconnect.", msg))
|
||||||
msg := fmt.Sprintf("\u26a0 Your WhatsApp connection failed: %v.\n\n"+
|
_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, content)
|
||||||
"Use the `reconnect` command to reconnect.", failed.Err)
|
return
|
||||||
_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, format.RenderMarkdown(msg))
|
|
||||||
user.Connected = false
|
|
||||||
}
|
}
|
||||||
|
if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||||
|
_, _ = user.bridge.Bot.SendNotice(user.ManagementRoom, fmt.Sprintf("%s. Reconnecting...", msg))
|
||||||
|
// Don't want the same error to be repeated
|
||||||
|
msg = ""
|
||||||
|
}
|
||||||
|
tries := 0
|
||||||
|
for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||||
|
err = user.Conn.Restore()
|
||||||
|
if err == nil {
|
||||||
|
user.ConnectionErrors = 0
|
||||||
|
user.Connected = true
|
||||||
|
_, _ = user.bridge.Bot.SendNotice(user.ManagementRoom, "Reconnected successfully")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.log.Errorln("Error while trying to reconnect after disconnection:", err)
|
||||||
|
tries++
|
||||||
|
user.ConnectionErrors++
|
||||||
|
if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||||
|
if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||||
|
_, _ = user.bridge.Bot.SendNotice(user.ManagementRoom,
|
||||||
|
fmt.Sprintf("Reconnection attempt failed: %v. Retrying in 10 seconds...", err))
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.bridge.Config.Bridge.ReportConnectionRetry {
|
||||||
|
msg = fmt.Sprintf("%d reconnection attempts failed. Use the `reconnect` command to try to reconnect manually.", tries)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("\u26a0 %sAdditionally, %d reconnection attempts failed. "+
|
||||||
|
"Use the `reconnect` command to try to reconnect.", msg, tries)
|
||||||
|
}
|
||||||
|
|
||||||
|
content := format.RenderMarkdown(msg)
|
||||||
|
_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleJSONParseError(err error) {
|
func (user *User) HandleJSONParseError(err error) {
|
||||||
|
|
Loading…
Reference in a new issue