diff --git a/config/bridge.go b/config/bridge.go index d263333..8056a24 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -34,6 +34,7 @@ type BridgeConfig struct { DisplaynameTemplate string `yaml:"displayname_template"` ConnectionTimeout int `yaml:"connection_timeout"` + LoginQRRegenCount int `yaml:"login_qr_regen_count"` MaxConnectionAttempts int `yaml:"max_connection_attempts"` ConnectionRetryDelay int `yaml:"connection_retry_delay"` ReportConnectionRetry bool `yaml:"report_connection_retry"` @@ -62,6 +63,7 @@ type BridgeConfig struct { func (bc *BridgeConfig) setDefaults() { bc.ConnectionTimeout = 20 + bc.LoginQRRegenCount = 2 bc.MaxConnectionAttempts = 3 bc.ConnectionRetryDelay = -1 bc.ReportConnectionRetry = true diff --git a/example-config.yaml b/example-config.yaml index 3947b00..4912846 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -60,6 +60,9 @@ bridge: # WhatsApp connection timeout in seconds. connection_timeout: 20 + # Number of times to regenerate QR code when logging in. + # The regenerated QR code is sent as an edit and essentially multiplies the login timeout (20 seconds) + login_qr_regen_count: 2 # Maximum number of times to retry connecting on connection error. max_connection_attempts: 3 # Number of seconds to wait between connection attempts. diff --git a/user.go b/user.go index c7ee645..8a31835 100644 --- a/user.go +++ b/user.go @@ -221,11 +221,11 @@ func (user *User) IsLoggedIn() bool { return user.Session != nil || user.Conn != nil } -func (user *User) Login(ce *CommandEvent) { - qrChan := make(chan string, 2) - go func() { - code := <-qrChan - if code == "error" { +func (user *User) loginQrChannel(ce *CommandEvent, qrChan <-chan string, eventIDChan chan<- string) { + var qrEventID string + for code := range qrChan { + fmt.Println("qrChan:", code) + if code == "stop" { return } qrCode, err := qrcode.Encode(code, qrcode.Low, 256) @@ -244,24 +244,70 @@ func (user *User) Login(ce *CommandEvent) { return } - _, err = bot.SendImage(ce.RoomID, string(code), resp.ContentURI) - if err != nil { - user.log.Errorln("Failed to send QR code to user:", err) + if qrEventID == "" { + sendResp, err := bot.SendImage(ce.RoomID, code, resp.ContentURI) + if err != nil { + user.log.Errorln("Failed to send QR code to user:", err) + return + } + qrEventID = sendResp.EventID + eventIDChan <- qrEventID + } else { + _, err = bot.SendMessageEvent(ce.RoomID, mautrix.EventMessage, &mautrix.Content{ + MsgType: mautrix.MsgImage, + Body: code, + URL: resp.ContentURI, + NewContent: &mautrix.Content{ + MsgType: mautrix.MsgImage, + Body: code, + URL: resp.ContentURI, + }, + RelatesTo: &mautrix.RelatesTo{ + Type: mautrix.RelReplace, + EventID: qrEventID, + }, + }) + if err != nil { + user.log.Errorln("Failed to send edited QR code to user:", err) + } } - }() - session, err := user.Conn.Login(qrChan) + } +} + +func (user *User) Login(ce *CommandEvent) { + qrChan := make(chan string, 3) + eventIDChan := make(chan string, 1) + go user.loginQrChannel(ce, qrChan, eventIDChan) + session, err := user.Conn.LoginWithRetry(qrChan, user.bridge.Config.Bridge.LoginQRRegenCount) + qrChan <- "stop" if err != nil { - qrChan <- "error" + var eventID string + select { + case eventID = <-eventIDChan: + default: + } + reply := mautrix.Content{ + MsgType: mautrix.MsgText, + } if err == whatsapp.ErrAlreadyLoggedIn { - ce.Reply("You're already logged in.") + reply.Body = "You're already logged in" } else if err == whatsapp.ErrLoginInProgress { - ce.Reply("You have a login in progress already.") - } else if err.Error() == "qr code scan timed out" { - ce.Reply("QR code scan timed out. Please try again.") + reply.Body = "You have a login in progress already." + } else if err == whatsapp.ErrLoginTimedOut { + reply.Body = "QR code scan timed out. Please try again." } else { user.log.Warnln("Failed to log in:", err) - ce.Reply("Unknown error while logging in: %v", err) + reply.Body = fmt.Sprintf("Unknown error while logging in: %v", err) } + msg := reply + if eventID != "" { + msg.NewContent = &reply + msg.RelatesTo = &mautrix.RelatesTo{ + Type: mautrix.RelReplace, + EventID: eventID, + } + } + _, _ = ce.Bot.SendMessageEvent(ce.RoomID, mautrix.EventMessage, &msg) return } user.Connected = true