From 6b73c66e12361df8183f986c8856e60dffc951cb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 15 May 2019 23:04:09 +0300 Subject: [PATCH] Improve connection error handling --- commands.go | 55 +++++++++++++++++++++++++---- go.mod | 10 ++++-- go.sum | 86 +++++++++++++++++++++++++++++++++++++++++++++ matrix.go | 10 +++++- portal.go | 2 +- user.go | 38 ++++++++++++++++---- whatsapp-ext/cmd.go | 4 ++- 7 files changed, 188 insertions(+), 17 deletions(-) diff --git a/commands.go b/commands.go index 705972b..444e567 100644 --- a/commands.go +++ b/commands.go @@ -55,8 +55,8 @@ type CommandEvent struct { } // Reply sends a reply to command as notice -func (ce *CommandEvent) Reply(msg string) { - content := format.RenderMarkdown(msg) +func (ce *CommandEvent) Reply(msg string, args ...interface{}) { + content := format.RenderMarkdown(fmt.Sprintf(msg, args...)) content.MsgType = mautrix.MsgNotice _, err := ce.Bot.SendMessageEvent(ce.User.ManagementRoom, mautrix.EventMessage, content) if err != nil { @@ -81,7 +81,7 @@ func (handler *CommandHandler) Handle(roomID types.MatrixRoomID, user *User, mes handler.CommandLogin(ce) case "help": handler.CommandHelp(ce) - case "logout", "sync", "list", "open", "pm": + case "logout", "reconnect", "disconnect", "sync", "list", "open", "pm": if ce.User.Conn == nil { ce.Reply("You are not logged in.") ce.Reply("Please use the login command to log into WhatsApp.") @@ -91,6 +91,10 @@ func (handler *CommandHandler) Handle(roomID types.MatrixRoomID, user *User, mes switch cmd { case "logout": handler.CommandLogout(ce) + case "reconnect": + handler.CommandReconnect(ce) + case "disconnect": + handler.CommandDisconnect(ce) case "sync": handler.CommandSync(ce) case "list": @@ -138,6 +142,43 @@ func (handler *CommandHandler) CommandLogout(ce *CommandEvent) { ce.Reply("Logged out successfully.") } +const cmdReconnectHelp = `reconnect - Reconnect to WhatsApp` + +func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) { + err := ce.User.Conn.Restore() + if err == whatsapp.ErrAlreadyLoggedIn { + if ce.User.Connected { + ce.Reply("You were already connected.") + } else { + ce.User.Connected = true + ce.Reply("You were already connected, but the bridge hadn't noticed. Fixed that now.") + } + } else if err != nil { + ce.User.log.Warnln("Error while reconnecting:", err) + ce.Reply("Error while reconnecting (see logs for details)") + return + } + ce.User.Connected = true + ce.Reply("Reconnected successfully.") +} + +const cmdDisconnectHelp = `disconnect - Disconnect from WhatsApp (without logging out)` + +func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) { + sess, err := ce.User.Conn.Disconnect() + ce.User.Connected = false + if err == whatsapp.ErrNotConnected { + ce.Reply("You were not connected.") + return + } else if err != nil { + ce.User.log.Warnln("Error while disconnecting:", err) + ce.Reply("Error while disconnecting (see logs for details)") + return + } + ce.User.SetSession(&sess) + ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.") +} + const cmdHelpHelp = `help - Prints this help` // CommandHelp handles help command @@ -151,6 +192,8 @@ func (handler *CommandHandler) CommandHelp(ce *CommandEvent) { cmdPrefix + cmdHelpHelp, cmdPrefix + cmdLoginHelp, cmdPrefix + cmdLogoutHelp, + cmdPrefix + cmdReconnectHelp, + cmdPrefix + cmdDisconnectHelp, cmdPrefix + cmdSyncHelp, cmdPrefix + cmdListHelp, cmdPrefix + cmdOpenHelp, @@ -200,7 +243,7 @@ func (handler *CommandHandler) CommandList(ce *CommandEvent) { _, _ = fmt.Fprintf(&groups, "* %s - `%s`\n", contact.Name, contact.Jid) } } - ce.Reply(fmt.Sprintf("### Contacts\n%s\n\n### Groups\n%s", contacts.String(), groups.String())) + ce.Reply("### Contacts\n%s\n\n### Groups\n%s", contacts.String(), groups.String()) } const cmdOpenHelp = `open <_group JID_> - Open a group chat portal.` @@ -215,7 +258,7 @@ func (handler *CommandHandler) CommandOpen(ce *CommandEvent) { jid := ce.Args[0] if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) { - ce.Reply(fmt.Sprintf("That looks like a user JID. Did you mean `pm %s`?", jid[:len(jid)-len(whatsappExt.NewUserSuffix)])) + ce.Reply("That looks like a user JID. Did you mean `pm %s`?", jid[:len(jid)-len(whatsappExt.NewUserSuffix)]) return } @@ -287,7 +330,7 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) { } err := portal.CreateMatrixRoom(user) if err != nil { - ce.Reply(fmt.Sprintf("Failed to create portal room: %v", err)) + ce.Reply("Failed to create portal room: %v", err) return } ce.Reply("Created portal room and invited you to it.") diff --git a/go.mod b/go.mod index 8fe35de..35613bb 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module maunium.net/go/mautrix-whatsapp go 1.12 require ( - github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f // indirect - github.com/Rhymen/go-whatsapp v0.0.1 + github.com/Rhymen/go-whatsapp v0.0.2-0.20190511164245-5d5100902126 github.com/golang/protobuf v1.3.1 // indirect github.com/lib/pq v1.1.1 github.com/mattn/go-sqlite3 v1.10.0 github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 + golang.org/x/sys v0.0.0-20190515190549-87c872767d25 // indirect gopkg.in/yaml.v2 v2.2.2 maunium.net/go/mauflag v1.0.0 maunium.net/go/maulogger/v2 v2.0.0 @@ -18,3 +18,9 @@ require ( ) replace gopkg.in/russross/blackfriday.v2 => github.com/russross/blackfriday/v2 v2.0.1 + +replace maunium.net/go/mautrix-appservice => ../mautrix-appservice-go + +replace maunium.net/go/mautrix => ../mautrix-go + +replace github.com/Rhymen/go-whatsapp => ../../Go/go-whatsapp diff --git a/go.sum b/go.sum index cd07fba..26714f8 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,44 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Rhymen/go-whatsapp v0.0.0-20190208090600-c1173899de99 h1:nP8u82QFq3thWUN6uR0/pMMs5LVBUAIY3i9A9b4npKg= github.com/Rhymen/go-whatsapp v0.0.0-20190208090600-c1173899de99/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= +github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= github.com/Rhymen/go-whatsapp v0.0.1 h1:NP6EthaCa9BK2B4YgntjNDvFWc6PMLw9kRGl1glZO9k= github.com/Rhymen/go-whatsapp v0.0.1/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= +github.com/Rhymen/go-whatsapp v0.0.2-0.20190511164245-5d5100902126 h1:Y1uqyK1zeuQEjop3eV1fwks6dMPbwjWBPjqXLnX3gS4= +github.com/Rhymen/go-whatsapp v0.0.2-0.20190511164245-5d5100902126/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg= +github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME= +github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190511164245-5d5100902126/go.mod h1:DTUiKr1MHRV+eug55J5gB823XKqjeS0CQvJfTBxxseE= +github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU= +github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190511164245-5d5100902126/go.mod h1:+cj/iRC/nROnKIx94xfk8AOKZLwbKKMTHsW2/dBKRWI= +github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw= +github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190511164245-5d5100902126/go.mod h1:KwxNeICDKIt3JocwqZRhZOHja1cjAIyoGP00u87Ey2o= +github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM= +github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190511164245-5d5100902126/go.mod h1:Hi1JJgBfpB2bnQPZgd8XHfOr3oeInryWCcJ5nHYAWyg= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190502144155-8358a9778bd1/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -16,6 +46,9 @@ github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= @@ -30,12 +63,15 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE= github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY= golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -47,8 +83,25 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190509164839-32b2708ab171/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM= golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190326090315-15845e8f865b h1:LlDMQZ0I/u8J45sbt31TecpsFNErRGwDgS4WvT9hKzE= golang.org/x/net v0.0.0-20190326090315-15845e8f865b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -58,12 +111,20 @@ golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR17 golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI= golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o= golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g= golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -72,16 +133,41 @@ golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMe golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438 h1:khxRGsvPk4n2y8I/mLLjp7e5dMTJmH75wvqS6nMwUtY= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515190549-87c872767d25 h1:SSKQq5sjDoW0L0NaDoVl7d7HmtTxM0ezm0Ef9azs4uQ= +golang.org/x/sys v0.0.0-20190515190549-87c872767d25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190511041617-99f201b6807e/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190515174815-473d3dc1d7eb/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190513181449-d00d292a067c/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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.0.0 h1:8PB95zf6e7Ddw8iOWqjrJjAjPcAI50LagA1X0Ur64os= diff --git a/matrix.go b/matrix.go index b4b7673..96ccf92 100644 --- a/matrix.go +++ b/matrix.go @@ -17,11 +17,13 @@ package main import ( + "fmt" "strings" "maunium.net/go/maulogger/v2" "maunium.net/go/mautrix" "maunium.net/go/mautrix-appservice" + "maunium.net/go/mautrix/format" "maunium.net/go/mautrix-whatsapp/types" ) @@ -112,7 +114,7 @@ func (mx *MatrixHandler) HandleMembership(evt *mautrix.Event) { func (mx *MatrixHandler) HandleRoomMetadata(evt *mautrix.Event) { user := mx.bridge.GetUserByMXID(types.MatrixUserID(evt.Sender)) - if user == nil || !user.Whitelisted || !user.IsLoggedIn() { + if user == nil || !user.Whitelisted || !user.IsLoggedIn() || !user.Connected { return } @@ -165,6 +167,12 @@ func (mx *MatrixHandler) HandleMessage(evt *mautrix.Event) { if !user.IsLoggedIn() { return + } else if !user.Connected { + msg := format.RenderMarkdown(fmt.Sprintf("\u26a0 You are not connected to WhatsApp, so your message was not bridged. " + + "Use `%s reconnect` to reconnect.", mx.bridge.Config.Bridge.CommandPrefix)) + msg.MsgType = mautrix.MsgNotice + _, _ = mx.bridge.Bot.SendMessageEvent(roomID, mautrix.EventMessage, msg) + return } portal := mx.bridge.GetPortalByMXID(roomID) diff --git a/portal.go b/portal.go index 8e58f4f..c6dfaae 100644 --- a/portal.go +++ b/portal.go @@ -919,7 +919,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *mautrix.Event) { } portal.markHandled(sender, info, evt.ID) portal.log.Debugln("Sending event", evt.ID, "to WhatsApp") - err = sender.Conn.Send(info) + _, err = sender.Conn.Send(info) if err != nil { portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err) } else { diff --git a/user.go b/user.go index 103bfef..3614e4f 100644 --- a/user.go +++ b/user.go @@ -18,13 +18,17 @@ package main import ( "encoding/json" + "fmt" "strings" "time" - "github.com/Rhymen/go-whatsapp" "github.com/skip2/go-qrcode" log "maunium.net/go/maulogger/v2" + "github.com/Rhymen/go-whatsapp" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/format" + "maunium.net/go/mautrix-whatsapp/database" "maunium.net/go/mautrix-whatsapp/types" "maunium.net/go/mautrix-whatsapp/whatsapp-ext" @@ -39,6 +43,7 @@ type User struct { Admin bool Whitelisted bool + Connected bool } func (bridge *Bridge) GetUserByMXID(userID types.MatrixUserID) *User { @@ -146,7 +151,7 @@ func (user *User) Connect(evenIfNoSession bool) bool { return false } user.Conn = whatsappExt.ExtendConn(conn) - user.Conn.SetClientName("Mautrix-WhatsApp bridge", "mx-wa") + _ = user.Conn.SetClientName("Mautrix-WhatsApp bridge", "mx-wa") user.log.Debugln("WhatsApp connection successful") user.Conn.AddHandler(user) return user.RestoreSession() @@ -154,11 +159,12 @@ func (user *User) Connect(evenIfNoSession bool) bool { func (user *User) RestoreSession() bool { if user.Session != nil { - sess, err := user.Conn.RestoreSession(*user.Session) + sess, err := user.Conn.RestoreWithSession(*user.Session) if err != nil { user.log.Errorln("Failed to restore session:", err) return false } + user.Connected = true user.SetSession(&sess) user.log.Debugln("Session restored successfully") return true @@ -182,14 +188,14 @@ func (user *User) Login(roomID types.MatrixRoomID) { qrCode, err := qrcode.Encode(code, qrcode.Low, 256) if err != nil { user.log.Errorln("Failed to encode QR code:", err) - bot.SendNotice(roomID, "Failed to encode QR code (see logs for details)") + _, _ = bot.SendNotice(roomID, "Failed to encode QR code (see logs for details)") return } resp, err := bot.UploadBytes(qrCode, "image/png") if err != nil { user.log.Errorln("Failed to upload QR code:", err) - bot.SendNotice(roomID, "Failed to upload QR code (see logs for details)") + _, _ = bot.SendNotice(roomID, "Failed to upload QR code (see logs for details)") return } @@ -201,10 +207,11 @@ func (user *User) Login(roomID types.MatrixRoomID) { session, err := user.Conn.Login(qrChan) if err != nil { user.log.Warnln("Failed to log in:", err) - bot.SendNotice(roomID, "Failed to log in: "+err.Error()) + _, _ = bot.SendNotice(roomID, "Failed to log in: "+err.Error()) qrChan <- "error" return } + user.Connected = true user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1) user.Session = &session user.Update() @@ -216,6 +223,15 @@ func (user *User) Login(roomID types.MatrixRoomID) { func (user *User) HandleError(err error) { user.log.Errorln("WhatsApp error:", err) + closed, ok := err.(*whatsapp.ErrConnectionClosed) + if ok { + if closed.Code != 1000 { + msg := fmt.Sprintf("⚠️\u26a0 Your WhatsApp connection failed with websocket status code %d.\n\n" + + "Use the `reconnect` command to reconnect.", closed.Code) + _, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, format.RenderMarkdown(msg)) + } + user.Connected = false + } } func (user *User) HandleJSONParseError(err error) { @@ -313,6 +329,16 @@ func (user *User) HandleCommand(cmd whatsappExt.Command) { portal := user.GetPortalByJID(cmd.JID) portal.UpdateAvatar(user, cmd.ProfilePicInfo) } + case whatsappExt.CommandDisconnect: + var msg string + if cmd.Kind == "replaced" { + msg = "\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" + + "Use the `reconnect` command to disconnect the other client and resume bridging." + } else { + msg = fmt.Sprintf("\u26a0 Your WhatsApp connection was closed by the server (reason code: %s).\n\n" + + "Use the `reconnect` command to reconnect.", cmd.Kind) + } + _, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, format.RenderMarkdown(msg)) } } diff --git a/whatsapp-ext/cmd.go b/whatsapp-ext/cmd.go index ac34d45..15d64ee 100644 --- a/whatsapp-ext/cmd.go +++ b/whatsapp-ext/cmd.go @@ -26,7 +26,8 @@ import ( type CommandType string const ( - CommandPicture CommandType = "picture" + CommandPicture CommandType = "picture" + CommandDisconnect CommandType = "disconnect" ) type Command struct { @@ -34,6 +35,7 @@ type Command struct { JID string `json:"jid"` *ProfilePicInfo + Kind string `json:"kind"` } type CommandHandler interface {