Update go-whatsapp to break everything and maybe improve things

This commit is contained in:
Tulir Asokan 2021-02-18 23:36:14 +02:00
parent ca118e8678
commit 7bd47fabb2
10 changed files with 173 additions and 153 deletions

View file

@ -17,6 +17,7 @@
package main package main
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"math" "math"
@ -254,7 +255,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid) handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid)
portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid)) portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid))
if len(portal.MXID) > 0 { if len(portal.MXID) > 0 {
portal.Sync(ce.User, whatsapp.Contact{Jid: portal.Key.JID}) portal.Sync(ce.User, whatsapp.Contact{JID: portal.Key.JID})
ce.Reply("Successfully joined group \"%s\" and synced portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID) ce.Reply("Successfully joined group \"%s\" and synced portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
} else { } else {
err = portal.CreateMatrixRoom(ce.User) err = portal.CreateMatrixRoom(ce.User)
@ -411,11 +412,11 @@ func (handler *CommandHandler) CommandLogout(ce *CommandEvent) {
ce.Reply("Unknown error while logging out: %v", err) ce.Reply("Unknown error while logging out: %v", err)
return return
} }
ce.User.Disconnect()
ce.User.removeFromJIDMap() ce.User.removeFromJIDMap()
// TODO this causes a foreign key violation, which should be fixed // TODO this causes a foreign key violation, which should be fixed
//ce.User.JID = "" //ce.User.JID = ""
ce.User.SetSession(nil) ce.User.SetSession(nil)
ce.User.DeleteConnection()
ce.Reply("Logged out successfully.") ce.Reply("Logged out successfully.")
} }
@ -469,9 +470,10 @@ func (handler *CommandHandler) CommandDeleteSession(ce *CommandEvent) {
ce.Reply("Nothing to purge: no session information stored and no active connection.") ce.Reply("Nothing to purge: no session information stored and no active connection.")
return return
} }
ce.User.Disconnect() //ce.User.JID = ""
ce.User.removeFromJIDMap() ce.User.removeFromJIDMap()
ce.User.SetSession(nil) ce.User.SetSession(nil)
ce.User.DeleteConnection()
ce.Reply("Session information purged") ce.Reply("Session information purged")
} }
@ -489,24 +491,21 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
} }
wasConnected := true wasConnected := true
sess, err := ce.User.Conn.Disconnect() err := ce.User.Conn.Disconnect()
if err == whatsapp.ErrNotConnected { if err == whatsapp.ErrNotConnected {
wasConnected = false wasConnected = false
} else if err != nil { } else if err != nil {
ce.User.log.Warnln("Error while disconnecting:", err) ce.User.log.Warnln("Error while disconnecting:", err)
} else {
ce.User.SetSession(&sess)
} }
err = ce.User.Conn.Restore(true) ctx := context.Background()
err = ce.User.Conn.Restore(true, ctx)
if err == whatsapp.ErrInvalidSession { if err == whatsapp.ErrInvalidSession {
if ce.User.Session != nil { if ce.User.Session != nil {
ce.User.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...") ce.User.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
var sess whatsapp.Session ce.User.Conn.SetSession(*ce.User.Session)
sess, err = ce.User.Conn.RestoreWithSession(*ce.User.Session) err = ce.User.Conn.Restore(true, ctx)
if err == nil {
ce.User.SetSession(&sess)
}
} else { } else {
ce.Reply("You are not logged in.") ce.Reply("You are not logged in.")
return return
@ -520,17 +519,11 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
} }
if err != nil { if err != nil {
ce.User.log.Warnln("Error while reconnecting:", err) ce.User.log.Warnln("Error while reconnecting:", err)
if errors.Is(err, whatsapp.ErrRestoreSessionTimeout) {
ce.Reply("Reconnection timed out. Is WhatsApp on your phone reachable?")
} else {
ce.Reply("Unknown error while reconnecting: %v", err) ce.Reply("Unknown error while reconnecting: %v", err)
}
ce.User.log.Debugln("Disconnecting due to failed session restore in reconnect command...") ce.User.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
sess, err = ce.User.Conn.Disconnect() err = ce.User.Conn.Disconnect()
if err != nil { if err != nil {
ce.User.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err) ce.User.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
} else {
ce.User.SetSession(&sess)
} }
return return
} }
@ -553,7 +546,7 @@ func (handler *CommandHandler) CommandDeleteConnection(ce *CommandEvent) {
ce.Reply("You don't have a WhatsApp connection.") ce.Reply("You don't have a WhatsApp connection.")
return return
} }
ce.User.Disconnect() ce.User.DeleteConnection()
ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.") ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.")
} }
@ -564,7 +557,7 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) {
ce.Reply("You don't have a WhatsApp connection.") ce.Reply("You don't have a WhatsApp connection.")
return return
} }
sess, err := ce.User.Conn.Disconnect() err := ce.User.Conn.Disconnect()
if err == whatsapp.ErrNotConnected { if err == whatsapp.ErrNotConnected {
ce.Reply("You were not connected.") ce.Reply("You were not connected.")
return return
@ -572,8 +565,6 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) {
ce.User.log.Warnln("Error while disconnecting:", err) ce.User.log.Warnln("Error while disconnecting:", err)
ce.Reply("Unknown error while disconnecting: %v", err) ce.Reply("Unknown error while disconnecting: %v", err)
return return
} else {
ce.User.SetSession(&sess)
} }
ce.User.bridge.Metrics.TrackConnectionState(ce.User.JID, false) ce.User.bridge.Metrics.TrackConnectionState(ce.User.JID, false)
ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.") ce.Reply("Successfully disconnected. Use the `reconnect` command to reconnect.")
@ -741,9 +732,9 @@ func formatContacts(contacts bool, input map[string]whatsapp.Contact) (result []
} }
if contacts { if contacts {
result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact.Name, contact.Notify, contact.Jid[:len(contact.Jid)-len(whatsapp.NewUserSuffix)])) result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact.Name, contact.Notify, contact.JID[:len(contact.JID)-len(whatsapp.NewUserSuffix)]))
} else { } else {
result = append(result, fmt.Sprintf("* %s - `%s`", contact.Name, contact.Jid)) result = append(result, fmt.Sprintf("* %s - `%s`", contact.Name, contact.JID))
} }
} }
sort.Sort(sort.StringSlice(result)) sort.Sort(sort.StringSlice(result))
@ -875,11 +866,11 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) {
"To create a portal anyway, use `pm --force <number>`.") "To create a portal anyway, use `pm --force <number>`.")
return return
} }
contact = whatsapp.Contact{Jid: jid} contact = whatsapp.Contact{JID: jid}
} }
puppet := user.bridge.GetPuppetByJID(contact.Jid) puppet := user.bridge.GetPuppetByJID(contact.JID)
puppet.Sync(user, contact) puppet.Sync(user, contact)
portal := user.bridge.GetPortalByJID(database.NewPortalKey(contact.Jid, user.JID)) portal := user.bridge.GetPortalByJID(database.NewPortalKey(contact.JID, user.JID))
if len(portal.MXID) > 0 { if len(portal.MXID) > 0 {
err := portal.MainIntent().EnsureInvited(portal.MXID, user.MXID) err := portal.MainIntent().EnsureInvited(portal.MXID, user.MXID)
if err != nil { if err != nil {

View file

@ -165,8 +165,8 @@ type UsernameTemplateArgs struct {
func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) { func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) {
var buf bytes.Buffer var buf bytes.Buffer
if index := strings.IndexRune(contact.Jid, '@'); index > 0 { if index := strings.IndexRune(contact.JID, '@'); index > 0 {
contact.Jid = "+" + contact.Jid[:index] contact.JID = "+" + contact.JID[:index]
} }
bc.displaynameTemplate.Execute(&buf, contact) bc.displaynameTemplate.Execute(&buf, contact)
var quality int8 var quality int8
@ -175,7 +175,7 @@ func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8
quality = 3 quality = 3
case len(contact.Name) > 0 || len(contact.Short) > 0: case len(contact.Name) > 0 || len(contact.Short) > 0:
quality = 2 quality = 2
case len(contact.Jid) > 0: case len(contact.JID) > 0:
quality = 1 quality = 1
default: default:
quality = 0 quality = 0

View file

@ -93,7 +93,7 @@ func (user *User) Scan(row Scannable) *User {
if len(jid.String) > 0 && len(clientID.String) > 0 { if len(jid.String) > 0 && len(clientID.String) > 0 {
user.JID = jid.String + whatsapp.NewUserSuffix user.JID = jid.String + whatsapp.NewUserSuffix
user.Session = &whatsapp.Session{ user.Session = &whatsapp.Session{
ClientId: clientID.String, ClientID: clientID.String,
ClientToken: clientToken.String, ClientToken: clientToken.String,
ServerToken: serverToken.String, ServerToken: serverToken.String,
EncKey: encKey, EncKey: encKey,
@ -139,7 +139,7 @@ func (user *User) Insert() {
_, err := user.db.Exec(`INSERT INTO "user" (mxid, jid, management_room, last_connection, client_id, client_token, server_token, enc_key, mac_key) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, _, err := user.db.Exec(`INSERT INTO "user" (mxid, jid, management_room, last_connection, client_id, client_token, server_token, enc_key, mac_key) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
user.MXID, user.jidPtr(), user.MXID, user.jidPtr(),
user.ManagementRoom, user.LastConnection, user.ManagementRoom, user.LastConnection,
sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey) sess.ClientID, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey)
if err != nil { if err != nil {
user.log.Warnfln("Failed to insert %s: %v", user.MXID, err) user.log.Warnfln("Failed to insert %s: %v", user.MXID, err)
} }
@ -158,7 +158,7 @@ func (user *User) Update() {
sess := user.sessionUnptr() sess := user.sessionUnptr()
_, err := user.db.Exec(`UPDATE "user" SET jid=$1, management_room=$2, last_connection=$3, client_id=$4, client_token=$5, server_token=$6, enc_key=$7, mac_key=$8 WHERE mxid=$9`, _, err := user.db.Exec(`UPDATE "user" SET jid=$1, management_room=$2, last_connection=$3, client_id=$4, client_token=$5, server_token=$6, enc_key=$7, mac_key=$8 WHERE mxid=$9`,
user.jidPtr(), user.ManagementRoom, user.LastConnection, user.jidPtr(), user.ManagementRoom, user.LastConnection,
sess.ClientId, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey, sess.ClientID, sess.ClientToken, sess.ServerToken, sess.EncKey, sess.MacKey,
user.MXID) user.MXID)
if err != nil { if err != nil {
user.log.Warnfln("Failed to update %s: %v", user.MXID, err) user.log.Warnfln("Failed to update %s: %v", user.MXID, err)

4
go.mod
View file

@ -12,8 +12,8 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
maunium.net/go/mauflag v1.0.0 maunium.net/go/mauflag v1.0.0
maunium.net/go/maulogger/v2 v2.1.1 maunium.net/go/maulogger/v2 v2.2.2
maunium.net/go/mautrix v0.8.2 maunium.net/go/mautrix v0.8.2
) )
replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.21 replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a

6
go.sum
View file

@ -145,6 +145,8 @@ github.com/tulir/go-whatsapp v0.3.20 h1:nK92MgruqXwk+QlaAS39xhzHNbFvJIEgUIOUrN3i
github.com/tulir/go-whatsapp v0.3.20/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o= github.com/tulir/go-whatsapp v0.3.20/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
github.com/tulir/go-whatsapp v0.3.21 h1:2m7gUw4oHX4kIpMmP9VwCR7KEUK/PHhXLygPFGF9XfI= github.com/tulir/go-whatsapp v0.3.21 h1:2m7gUw4oHX4kIpMmP9VwCR7KEUK/PHhXLygPFGF9XfI=
github.com/tulir/go-whatsapp v0.3.21/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o= github.com/tulir/go-whatsapp v0.3.21/go.mod h1:U5+sm33vrv3wz62YyRM/VS7q2ObXkxU4Xqj/3KOmN9o=
github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a h1:8JSW6oIAgI1TtR7wkvhNpTYhjKWBxk/eFyB8qXeOfyg=
github.com/tulir/go-whatsapp v0.3.22-0.20210218211744-b9f35ff6257a/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -232,6 +234,10 @@ 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/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/maulogger/v2 v2.1.1 h1:NAZNc6XUFJzgzfewCzVoGkxNAsblLCSSEdtDuIjP0XA= maunium.net/go/maulogger/v2 v2.1.1 h1:NAZNc6XUFJzgzfewCzVoGkxNAsblLCSSEdtDuIjP0XA=
maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
maunium.net/go/maulogger/v2 v2.2.1 h1:qwEDOdT7OhwqvFBXhSD0lqW2O2Oc/DbP/uv3zaai0W8=
maunium.net/go/maulogger/v2 v2.2.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
maunium.net/go/maulogger/v2 v2.2.2 h1:NCw+7Be1GQFm8xXJ4M2C0Q8yLBTx3c5s7UZ4y1anZMU=
maunium.net/go/maulogger/v2 v2.2.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
maunium.net/go/mautrix v0.8.0-rc.3 h1:bb18oNxHUmeiJ0V63YTRVGMjgoeLwu+G40l4n42Z5GI= maunium.net/go/mautrix v0.8.0-rc.3 h1:bb18oNxHUmeiJ0V63YTRVGMjgoeLwu+G40l4n42Z5GI=
maunium.net/go/mautrix v0.8.0-rc.3/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY= maunium.net/go/mautrix v0.8.0-rc.3/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY=
maunium.net/go/mautrix v0.8.0-rc.4 h1:3JXoL2JJPE5nh/YSw9sv9dQA9ulma9yHTMOBMBY1xdo= maunium.net/go/mautrix v0.8.0-rc.4 h1:3JXoL2JJPE5nh/YSw9sv9dQA9ulma9yHTMOBMBY1xdo=

View file

@ -384,11 +384,9 @@ func (bridge *Bridge) Stop() {
continue continue
} }
bridge.Log.Debugln("Disconnecting", user.MXID) bridge.Log.Debugln("Disconnecting", user.MXID)
sess, err := user.Conn.Disconnect() err := user.Conn.Disconnect()
if err != nil { if err != nil {
bridge.Log.Errorfln("Error while disconnecting %s: %v", user.MXID, err) bridge.Log.Errorfln("Error while disconnecting %s: %v", user.MXID, err)
} else {
user.SetSession(&sess)
} }
} }
} }

View file

@ -1771,7 +1771,7 @@ func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) {
"-pix_fmt", "yuv420p", "-c:v", "libx264", "-movflags", "+faststart", "-pix_fmt", "yuv420p", "-c:v", "libx264", "-movflags", "+faststart",
"-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'", "-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'",
outputFileName) outputFileName)
vcLog := portal.log.Sub("VideoConverter").WithDefaultLevel(log.LevelWarn) vcLog := portal.log.Sub("VideoConverter").Writer(log.LevelWarn)
cmd.Stdout = vcLog cmd.Stdout = vcLog
cmd.Stderr = vcLog cmd.Stderr = vcLog
@ -2319,7 +2319,7 @@ func (portal *Portal) HandleMatrixMeta(sender *User, evt *event.Event) {
return return
} }
portal.Topic = content.Topic portal.Topic = content.Topic
resp, err = sender.Conn.UpdateGroupDescription(portal.Key.JID, content.Topic) resp, err = sender.Conn.UpdateGroupDescription(sender.JID, portal.Key.JID, content.Topic)
case *event.RoomAvatarEventContent: case *event.RoomAvatarEventContent:
return return
} }

View file

@ -125,7 +125,7 @@ func (prov *ProvisioningAPI) DeleteSession(w http.ResponseWriter, r *http.Reques
}) })
return return
} }
user.Disconnect() user.DeleteConnection()
user.SetSession(nil) user.SetSession(nil)
jsonResponse(w, http.StatusOK, Response{true, "Session information purged"}) jsonResponse(w, http.StatusOK, Response{true, "Session information purged"})
} }
@ -139,7 +139,7 @@ func (prov *ProvisioningAPI) DeleteConnection(w http.ResponseWriter, r *http.Req
}) })
return return
} }
user.Disconnect() user.DeleteConnection()
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp and connection deleted"}) jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp and connection deleted"})
} }
@ -152,7 +152,7 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request)
}) })
return return
} }
sess, err := user.Conn.Disconnect() err := user.Conn.Disconnect()
if err == whatsapp.ErrNotConnected { if err == whatsapp.ErrNotConnected {
jsonResponse(w, http.StatusNotFound, Error{ jsonResponse(w, http.StatusNotFound, Error{
Error: "You were not connected", Error: "You were not connected",
@ -166,8 +166,6 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request)
ErrCode: err.Error(), ErrCode: err.Error(),
}) })
return return
} else {
user.SetSession(&sess)
} }
user.bridge.Metrics.TrackConnectionState(user.JID, false) user.bridge.Metrics.TrackConnectionState(user.JID, false)
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"}) jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"})
@ -190,25 +188,21 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
user.log.Debugln("Received /reconnect request, disconnecting") user.log.Debugln("Received /reconnect request, disconnecting")
wasConnected := true wasConnected := true
sess, err := user.Conn.Disconnect() err := user.Conn.Disconnect()
if err == whatsapp.ErrNotConnected { if err == whatsapp.ErrNotConnected {
wasConnected = false wasConnected = false
} else if err != nil { } else if err != nil {
user.log.Warnln("Error while disconnecting:", err) user.log.Warnln("Error while disconnecting:", err)
} else {
user.SetSession(&sess)
} }
user.log.Debugln("Restoring session for /reconnect") user.log.Debugln("Restoring session for /reconnect")
err = user.Conn.Restore(true) err = user.Conn.Restore(true, r.Context())
user.log.Debugfln("Restore session for /reconnect responded with %v", err) user.log.Debugfln("Restore session for /reconnect responded with %v", err)
if err == whatsapp.ErrInvalidSession { if err == whatsapp.ErrInvalidSession {
if user.Session != nil { if user.Session != nil {
user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...") user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
sess, err = user.Conn.RestoreWithSession(*user.Session) user.Conn.SetSession(*user.Session)
if err == nil { err = user.Conn.Restore(true, r.Context())
user.SetSession(&sess)
}
} else { } else {
jsonResponse(w, http.StatusForbidden, Error{ jsonResponse(w, http.StatusForbidden, Error{
Error: "You're not logged in", Error: "You're not logged in",
@ -216,7 +210,8 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
}) })
return return
} }
} else if err == whatsapp.ErrLoginInProgress { }
if err == whatsapp.ErrLoginInProgress {
jsonResponse(w, http.StatusConflict, Error{ jsonResponse(w, http.StatusConflict, Error{
Error: "A login or reconnection is already in progress.", Error: "A login or reconnection is already in progress.",
ErrCode: "login in progress", ErrCode: "login in progress",
@ -231,23 +226,14 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
} }
if err != nil { if err != nil {
user.log.Warnln("Error while reconnecting:", err) user.log.Warnln("Error while reconnecting:", err)
if errors.Is(err, whatsapp.ErrRestoreSessionTimeout) { jsonResponse(w, http.StatusInternalServerError, Error{
jsonResponse(w, http.StatusForbidden, Error{
Error: "Reconnection timed out. Is WhatsApp on your phone reachable?",
ErrCode: err.Error(),
})
} else {
jsonResponse(w, http.StatusForbidden, Error{
Error: fmt.Sprintf("Unknown error while reconnecting: %v", err), Error: fmt.Sprintf("Unknown error while reconnecting: %v", err),
ErrCode: err.Error(), ErrCode: err.Error(),
}) })
}
user.log.Debugln("Disconnecting due to failed session restore in reconnect command...") user.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
sess, err := user.Conn.Disconnect() err = user.Conn.Disconnect()
if err != nil { if err != nil {
user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err) user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
} else {
user.SetSession(&sess)
} }
return return
} }
@ -339,7 +325,7 @@ func (prov *ProvisioningAPI) Logout(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
user.Disconnect() user.DeleteConnection()
} }
user.bridge.Metrics.TrackConnectionState(user.JID, false) user.bridge.Metrics.TrackConnectionState(user.JID, false)
@ -407,7 +393,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
}) })
user.log.Debugln("Starting login via provisioning API") user.log.Debugln("Starting login via provisioning API")
session, err := user.Conn.LoginWithRetry(qrChan, ctx, user.bridge.Config.Bridge.LoginQRRegenCount) session, jid, err := user.Conn.Login(qrChan, ctx, user.bridge.Config.Bridge.LoginQRRegenCount)
qrChan <- "stop" qrChan <- "stop"
if err != nil { if err != nil {
var msg string var msg string
@ -419,7 +405,7 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
msg = "QR code scan timed out. Please try again." msg = "QR code scan timed out. Please try again."
} else if errors.Is(err, whatsapp.ErrInvalidWebsocket) { } else if errors.Is(err, whatsapp.ErrInvalidWebsocket) {
msg = "WhatsApp connection error. Please try again." msg = "WhatsApp connection error. Please try again."
user.Disconnect() // TODO might need to make sure it reconnects?
} else { } else {
msg = fmt.Sprintf("Unknown error while logging in: %v", err) msg = fmt.Sprintf("Unknown error while logging in: %v", err)
} }
@ -430,9 +416,9 @@ func (prov *ProvisioningAPI) Login(w http.ResponseWriter, r *http.Request) {
}) })
return return
} }
user.log.Debugln("Successful login via provisioning API") user.log.Debugln("Successful login as", jid, "via provisioning API")
user.ConnectionErrors = 0 user.ConnectionErrors = 0
user.JID = strings.Replace(user.Conn.Info.Wid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) user.JID = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1)
user.addToJIDMap() user.addToJIDMap()
user.SetSession(&session) user.SetSession(&session)
_ = c.WriteJSON(map[string]interface{}{ _ = c.WriteJSON(map[string]interface{}{

View file

@ -291,8 +291,8 @@ func (puppet *Puppet) Sync(source *User, contact whatsapp.Contact) {
puppet.log.Errorln("Failed to ensure registered:", err) puppet.log.Errorln("Failed to ensure registered:", err)
} }
if contact.Jid == source.JID { if contact.JID == source.JID {
contact.Notify = source.Conn.Info.Pushname contact.Notify = source.pushName
} }
update := false update := false

171
user.go
View file

@ -17,6 +17,7 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -61,6 +62,7 @@ type User struct {
cleanDisconnection bool cleanDisconnection bool
batteryWarningsSent int batteryWarningsSent int
lastReconnection int64 lastReconnection int64
pushName string
chatListReceived chan struct{} chatListReceived chan struct{}
syncPortalsDone chan struct{} syncPortalsDone chan struct{}
@ -73,6 +75,7 @@ type User struct {
mgmtCreateLock sync.Mutex mgmtCreateLock sync.Mutex
connLock sync.Mutex connLock sync.Mutex
cancelReconnect func()
} }
func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User { func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User {
@ -234,55 +237,56 @@ func (user *User) SetSession(session *whatsapp.Session) {
func (user *User) Connect(evenIfNoSession bool) bool { func (user *User) Connect(evenIfNoSession bool) bool {
user.connLock.Lock() user.connLock.Lock()
if user.Conn != nil && user.Conn.IsConnected() { if user.Conn != nil {
user.connLock.Unlock() user.connLock.Unlock()
if user.Conn.IsConnected() {
return true return true
} else {
return user.RestoreSession()
}
} else if !evenIfNoSession && user.Session == nil { } else if !evenIfNoSession && user.Session == nil {
user.connLock.Unlock() user.connLock.Unlock()
return false return false
} }
if user.Conn != nil {
user.Disconnect()
}
user.log.Debugln("Connecting to WhatsApp") user.log.Debugln("Connecting to WhatsApp")
timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout) timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout)
if timeout == 0 { if timeout == 0 {
timeout = 20 timeout = 20
} }
conn, err := whatsapp.NewConnWithOptions(&whatsapp.Options{ user.Conn = whatsapp.NewConn(&whatsapp.Options{
Timeout: timeout * time.Second, Timeout: timeout * time.Second,
LongClientName: user.bridge.Config.WhatsApp.OSName, LongClientName: user.bridge.Config.WhatsApp.OSName,
ShortClientName: user.bridge.Config.WhatsApp.BrowserName, ShortClientName: user.bridge.Config.WhatsApp.BrowserName,
ClientVersion: WAVersion, ClientVersion: WAVersion,
Log: user.log.Sub("Conn"),
Handler: []whatsapp.Handler{user},
}) })
if err != nil {
user.log.Errorln("Failed to connect to WhatsApp:", err)
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp server. " +
"This indicates a network problem on the bridge server. See bridge logs for more info.")
user.connLock.Unlock()
return false
}
user.Conn = conn
user.log.Debugln("WhatsApp connection successful")
user.Conn.AddHandler(user)
user.connLock.Unlock() user.connLock.Unlock()
return user.RestoreSession() return user.RestoreSession()
} }
func (user *User) Disconnect() { func (user *User) DeleteConnection() {
sess, err := user.Conn.Disconnect() user.connLock.Lock()
if user.Conn == nil {
user.connLock.Unlock()
return
}
err := user.Conn.Disconnect()
if err != nil && err != whatsapp.ErrNotConnected { if err != nil && err != whatsapp.ErrNotConnected {
user.log.Warnln("Error disconnecting: %v", err) user.log.Warnln("Error disconnecting: %v", err)
} }
user.SetSession(&sess)
user.Conn.RemoveHandlers() user.Conn.RemoveHandlers()
user.Conn = nil user.Conn = nil
user.bridge.Metrics.TrackConnectionState(user.JID, false) user.bridge.Metrics.TrackConnectionState(user.JID, false)
user.connLock.Unlock()
} }
func (user *User) RestoreSession() bool { func (user *User) RestoreSession() bool {
if user.Session != nil { if user.Session != nil {
sess, err := user.Conn.RestoreWithSession(*user.Session) user.Conn.SetSession(*user.Session)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
err := user.Conn.Restore(true, ctx)
if err == whatsapp.ErrAlreadyLoggedIn { if err == whatsapp.ErrAlreadyLoggedIn {
return true return true
} else if err != nil { } else if err != nil {
@ -290,24 +294,23 @@ func (user *User) RestoreSession() bool {
if errors.Is(err, whatsapp.ErrUnpaired) { if errors.Is(err, whatsapp.ErrUnpaired) {
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " + user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " +
"To re-pair your phone, log in again.") "To re-pair your phone, log in again.")
user.Disconnect()
user.removeFromJIDMap() user.removeFromJIDMap()
//user.JID = "" //user.JID = ""
user.SetSession(nil) user.SetSession(nil)
user.DeleteConnection()
return false return false
} else { } else {
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " + user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " +
"on your phone is reachable and use `reconnect` to try connecting again.") "on your phone is reachable and use `reconnect` to try connecting again.")
} }
user.log.Debugln("Disconnecting due to failed session restore...") user.log.Debugln("Disconnecting due to failed session restore...")
_, err := user.Conn.Disconnect() err = user.Conn.Disconnect()
if err != nil { if err != nil {
user.log.Errorln("Failed to disconnect after failed session restore:", err) user.log.Errorln("Failed to disconnect after failed session restore:", err)
} }
return false return false
} }
user.ConnectionErrors = 0 user.ConnectionErrors = 0
user.SetSession(&sess)
user.log.Debugln("Session restored successfully") user.log.Debugln("Session restored successfully")
user.PostLogin() user.PostLogin()
} }
@ -382,7 +385,7 @@ func (user *User) Login(ce *CommandEvent) {
qrChan := make(chan string, 3) qrChan := make(chan string, 3)
eventIDChan := make(chan id.EventID, 1) eventIDChan := make(chan id.EventID, 1)
go user.loginQrChannel(ce, qrChan, eventIDChan) go user.loginQrChannel(ce, qrChan, eventIDChan)
session, err := user.Conn.LoginWithRetry(qrChan, nil, user.bridge.Config.Bridge.LoginQRRegenCount) session, jid, err := user.Conn.Login(qrChan, nil, user.bridge.Config.Bridge.LoginQRRegenCount)
qrChan <- "stop" qrChan <- "stop"
if err != nil { if err != nil {
var eventID id.EventID var eventID id.EventID
@ -416,8 +419,9 @@ func (user *User) Login(ce *CommandEvent) {
} }
// TODO there's a bit of duplication between this and the provisioning API login method // TODO there's a bit of duplication between this and the provisioning API login method
// Also between the two logout methods (commands.go and provisioning.go) // Also between the two logout methods (commands.go and provisioning.go)
user.log.Debugln("Successful login as", jid, "via command")
user.ConnectionErrors = 0 user.ConnectionErrors = 0
user.JID = strings.Replace(user.Conn.Info.Wid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1) user.JID = strings.Replace(jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, 1)
user.addToJIDMap() user.addToJIDMap()
user.SetSession(&session) user.SetSession(&session)
ce.Reply("Successfully logged in, synchronizing chats...") ce.Reply("Successfully logged in, synchronizing chats...")
@ -499,20 +503,24 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface
} }
} }
func (user *User) postConnPing(conn *whatsapp.Conn) bool { func (user *User) postConnPing() bool {
if user.Conn != conn {
user.log.Warnln("Connection changed before scheduled post-connection ping, canceling ping")
return false
}
user.log.Debugln("Making post-connection ping") user.log.Debugln("Making post-connection ping")
err := conn.AdminTest() var err error
if err != nil { for i := 0; ; i++ {
err = user.Conn.AdminTest()
if err == nil {
user.log.Debugln("Post-connection ping OK")
return true
} else if errors.Is(err, whatsapp.ErrConnectionTimeout) && i < 5 {
user.log.Warnfln("Post-connection ping timed out, sending new one")
} else {
break
}
}
user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err) user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err)
sess, disconnectErr := conn.Disconnect() disconnectErr := user.Conn.Disconnect()
if disconnectErr != nil { if disconnectErr != nil {
user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr) user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr)
} else {
user.Session = &sess
} }
user.bridge.Metrics.TrackDisconnection(user.MXID) user.bridge.Metrics.TrackDisconnection(user.MXID)
go func() { go func() {
@ -520,10 +528,6 @@ func (user *User) postConnPing(conn *whatsapp.Conn) bool {
user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err)) user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err))
}() }()
return false return false
} else {
user.log.Debugln("Post-connection ping OK")
return true
}
} }
func (user *User) intPostLogin(conn *whatsapp.Conn) { func (user *User) intPostLogin(conn *whatsapp.Conn) {
@ -538,11 +542,11 @@ func (user *User) intPostLogin(conn *whatsapp.Conn) {
user.log.Debugln("Chat list receive confirmation received in PostLogin") user.log.Debugln("Chat list receive confirmation received in PostLogin")
case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second): case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
user.log.Warnln("Timed out waiting for chat list to arrive!") user.log.Warnln("Timed out waiting for chat list to arrive!")
user.postConnPing(conn) user.postConnPing()
return return
} }
if !user.postConnPing(conn) { if !user.postConnPing() {
user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.") user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.")
return return
} }
@ -557,16 +561,14 @@ func (user *User) intPostLogin(conn *whatsapp.Conn) {
} }
} }
type InfoGetter interface { type NormalMessage interface {
GetInfo() whatsapp.MessageInfo GetInfo() whatsapp.MessageInfo
} }
func (user *User) HandleEvent(event interface{}) { func (user *User) HandleEvent(event interface{}) {
switch v := event.(type) { switch v := event.(type) {
case whatsapp.TextMessage, whatsapp.ImageMessage, whatsapp.StickerMessage, whatsapp.VideoMessage, case NormalMessage:
whatsapp.AudioMessage, whatsapp.DocumentMessage, whatsapp.ContactMessage, whatsapp.StubMessage, info := v.GetInfo()
whatsapp.LocationMessage:
info := v.(InfoGetter).GetInfo()
user.messageInput <- PortalMessage{info.RemoteJid, user, v, info.Timestamp} user.messageInput <- PortalMessage{info.RemoteJid, user, v, info.Timestamp}
case whatsapp.MessageRevocation: case whatsapp.MessageRevocation:
user.messageInput <- PortalMessage{v.RemoteJid, user, v, 0} user.messageInput <- PortalMessage{v.RemoteJid, user, v, 0}
@ -596,6 +598,8 @@ func (user *User) HandleEvent(event interface{}) {
user.HandleCommand(v) user.HandleCommand(v)
case whatsapp.ChatUpdate: case whatsapp.ChatUpdate:
user.HandleChatUpdate(v) user.HandleChatUpdate(v)
case whatsapp.ConnInfo:
user.HandleConnInfo(v)
case json.RawMessage: case json.RawMessage:
user.HandleJSONMessage(v) user.HandleJSONMessage(v)
case *waProto.WebMessageInfo: case *waProto.WebMessageInfo:
@ -614,11 +618,11 @@ func (user *User) HandleStreamEvent(evt whatsapp.StreamEvent) {
if user.lastReconnection+60 > time.Now().Unix() { if user.lastReconnection+60 > time.Now().Unix() {
user.lastReconnection = 0 user.lastReconnection = 0
user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds") user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds")
conn := user.Conn
go func() { go func() {
time.Sleep(20 * time.Second) time.Sleep(20 * time.Second)
// TODO if this happens during the post-login sync, it can get stuck forever // TODO if this happens during the post-login sync, it can get stuck forever
user.postConnPing(conn) // TODO check if the above is still true
user.postConnPing()
}() }()
} }
} else { } else {
@ -630,10 +634,10 @@ func (user *User) HandleChatList(chats []whatsapp.Chat) {
user.log.Infoln("Chat list received") user.log.Infoln("Chat list received")
chatMap := make(map[string]whatsapp.Chat) chatMap := make(map[string]whatsapp.Chat)
for _, chat := range user.Conn.Store.Chats { for _, chat := range user.Conn.Store.Chats {
chatMap[chat.Jid] = chat chatMap[chat.JID] = chat
} }
for _, chat := range chats { for _, chat := range chats {
chatMap[chat.Jid] = chat chatMap[chat.JID] = chat
} }
select { select {
case user.chatListReceived <- struct{}{}: case user.chatListReceived <- struct{}{}:
@ -655,14 +659,14 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
for _, chat := range chatMap { for _, chat := range chatMap {
ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64) ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
if err != nil { if err != nil {
user.log.Warnfln("Non-integer last message time in %s: %s", chat.Jid, chat.LastMessageTime) user.log.Warnfln("Non-integer last message time in %s: %s", chat.JID, chat.LastMessageTime)
continue continue
} }
portal := user.GetPortalByJID(chat.Jid) portal := user.GetPortalByJID(chat.JID)
chats = append(chats, Chat{ chats = append(chats, Chat{
Portal: portal, Portal: portal,
Contact: user.Conn.Store.Contacts[chat.Jid], Contact: user.Conn.Store.Contacts[chat.JID],
LastMessageTime: ts, LastMessageTime: ts,
}) })
var inCommunity, ok bool var inCommunity, ok bool
@ -777,7 +781,7 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
func (user *User) HandleContactList(contacts []whatsapp.Contact) { func (user *User) HandleContactList(contacts []whatsapp.Contact) {
contactMap := make(map[string]whatsapp.Contact) contactMap := make(map[string]whatsapp.Contact)
for _, contact := range contacts { for _, contact := range contacts {
contactMap[contact.Jid] = contact contactMap[contact.JID] = contact
} }
go user.syncPuppets(contactMap) go user.syncPuppets(contactMap)
} }
@ -786,10 +790,20 @@ func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
if contacts == nil { if contacts == nil {
contacts = user.Conn.Store.Contacts contacts = user.Conn.Store.Contacts
} }
_, hasSelf := contacts[user.JID]
if !hasSelf {
contacts[user.JID] = whatsapp.Contact{
Name: user.pushName,
Notify: user.pushName,
JID: user.JID,
}
}
user.log.Infoln("Syncing puppet info from contacts") user.log.Infoln("Syncing puppet info from contacts")
for jid, contact := range contacts { for jid, contact := range contacts {
if strings.HasSuffix(jid, whatsapp.NewUserSuffix) { if strings.HasSuffix(jid, whatsapp.NewUserSuffix) {
puppet := user.bridge.GetPuppetByJID(contact.Jid) puppet := user.bridge.GetPuppetByJID(contact.JID)
puppet.Sync(user, contact) puppet.Sync(user, contact)
} }
} }
@ -846,14 +860,18 @@ func (user *User) tryReconnect(msg string) {
baseDelay = -baseDelay + 1 baseDelay = -baseDelay + 1
} }
delay := baseDelay delay := baseDelay
conn := user.Conn
takeover := false takeover := false
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
user.cancelReconnect = cancel
for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts { for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
if user.Conn != conn { select {
user.log.Debugln("Connection was recreated, aborting reconnection attempts") case <-ctx.Done():
user.log.Debugln("tryReconnect context cancelled, aborting reconnection attempts")
return return
default:
} }
err := conn.Restore(takeover) err := user.Conn.Restore(takeover, ctx)
takeover = true takeover = true
if err == nil { if err == nil {
user.ConnectionErrors = 0 user.ConnectionErrors = 0
@ -863,14 +881,23 @@ func (user *User) tryReconnect(msg string) {
user.PostLogin() user.PostLogin()
return return
} else if errors.Is(err, whatsapp.ErrBadRequest) { } else if errors.Is(err, whatsapp.ErrBadRequest) {
user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...") user.log.Warnln("Got init 400 error when trying to reconnect, resetting connection...")
sess, err := conn.Disconnect() err = user.Conn.Disconnect()
if err != nil { if err != nil {
user.log.Debugln("Error while disconnecting for connection reset:", err) user.log.Debugln("Error while disconnecting for connection reset:", err)
} }
user.SetSession(&sess) } else if errors.Is(err, whatsapp.ErrUnpaired) {
} user.log.Errorln("Got init 401 (unpaired) error when trying to reconnect, not retrying")
user.removeFromJIDMap()
//user.JID = ""
user.SetSession(nil)
user.DeleteConnection()
user.sendMarkdownBridgeAlert("\u26a0 Failed to reconnect to WhatsApp: unpaired from phone. " +
"To re-pair your phone, log in again.")
return
} else {
user.log.Errorln("Error while trying to reconnect after disconnection:", err) user.log.Errorln("Error while trying to reconnect after disconnection:", err)
}
tries++ tries++
user.ConnectionErrors++ user.ConnectionErrors++
if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts { if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
@ -930,10 +957,10 @@ func (user *User) handleMessageLoop() {
func (user *User) HandleNewContact(contact whatsapp.Contact) { func (user *User) HandleNewContact(contact whatsapp.Contact) {
user.log.Debugfln("Contact message: %+v", contact) user.log.Debugfln("Contact message: %+v", contact)
if strings.HasSuffix(contact.Jid, whatsapp.OldUserSuffix) { if strings.HasSuffix(contact.JID, whatsapp.OldUserSuffix) {
contact.Jid = strings.Replace(contact.Jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1) contact.JID = strings.Replace(contact.JID, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1)
} }
puppet := user.bridge.GetPuppetByJID(contact.Jid) puppet := user.bridge.GetPuppetByJID(contact.JID)
puppet.UpdateName(user, contact) puppet.UpdateName(user, contact)
} }
@ -1176,11 +1203,23 @@ func (user *User) HandleChatUpdate(cmd whatsapp.ChatUpdate) {
go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, nil, cmd.Data.UserChange.JIDs) go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, nil, cmd.Data.UserChange.JIDs)
case whatsapp.ChatActionIntroduce: case whatsapp.ChatActionIntroduce:
if cmd.Data.SenderJID != "unknown" { if cmd.Data.SenderJID != "unknown" {
go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID}) go portal.Sync(user, whatsapp.Contact{JID: portal.Key.JID})
} }
} }
} }
func (user *User) HandleConnInfo(info whatsapp.ConnInfo) {
if user.Session != nil && info.Connected && len(info.ClientToken) > 0 {
user.log.Debugln("Received new tokens")
user.Session.ClientToken = info.ClientToken
user.Session.ServerToken = info.ServerToken
user.Session.Wid = info.WID
}
if len(info.PushName) > 0 {
user.pushName = info.PushName
}
}
func (user *User) HandleJSONMessage(message json.RawMessage) { func (user *User) HandleJSONMessage(message json.RawMessage) {
if !json.Valid(message) { if !json.Valid(message) {
return return