mirror of
https://github.com/tulir/mautrix-whatsapp
synced 2024-12-23 22:14:06 +01:00
Update go-whatsapp to break everything and maybe improve things
This commit is contained in:
parent
ca118e8678
commit
7bd47fabb2
10 changed files with 173 additions and 153 deletions
49
commands.go
49
commands.go
|
@ -17,6 +17,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
@ -254,7 +255,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
|
|||
handler.log.Debugln("%s successfully joined group %s", ce.User.MXID, jid)
|
||||
portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid))
|
||||
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)
|
||||
} else {
|
||||
err = portal.CreateMatrixRoom(ce.User)
|
||||
|
@ -411,11 +412,11 @@ func (handler *CommandHandler) CommandLogout(ce *CommandEvent) {
|
|||
ce.Reply("Unknown error while logging out: %v", err)
|
||||
return
|
||||
}
|
||||
ce.User.Disconnect()
|
||||
ce.User.removeFromJIDMap()
|
||||
// TODO this causes a foreign key violation, which should be fixed
|
||||
//ce.User.JID = ""
|
||||
ce.User.SetSession(nil)
|
||||
ce.User.DeleteConnection()
|
||||
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.")
|
||||
return
|
||||
}
|
||||
ce.User.Disconnect()
|
||||
//ce.User.JID = ""
|
||||
ce.User.removeFromJIDMap()
|
||||
ce.User.SetSession(nil)
|
||||
ce.User.DeleteConnection()
|
||||
ce.Reply("Session information purged")
|
||||
}
|
||||
|
||||
|
@ -489,24 +491,21 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
|||
}
|
||||
|
||||
wasConnected := true
|
||||
sess, err := ce.User.Conn.Disconnect()
|
||||
err := ce.User.Conn.Disconnect()
|
||||
if err == whatsapp.ErrNotConnected {
|
||||
wasConnected = false
|
||||
} else if err != nil {
|
||||
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 ce.User.Session != nil {
|
||||
ce.User.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
|
||||
var sess whatsapp.Session
|
||||
sess, err = ce.User.Conn.RestoreWithSession(*ce.User.Session)
|
||||
if err == nil {
|
||||
ce.User.SetSession(&sess)
|
||||
}
|
||||
ce.User.Conn.SetSession(*ce.User.Session)
|
||||
err = ce.User.Conn.Restore(true, ctx)
|
||||
} else {
|
||||
ce.Reply("You are not logged in.")
|
||||
return
|
||||
|
@ -520,17 +519,11 @@ func (handler *CommandHandler) CommandReconnect(ce *CommandEvent) {
|
|||
}
|
||||
if err != nil {
|
||||
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...")
|
||||
sess, err = ce.User.Conn.Disconnect()
|
||||
err = ce.User.Conn.Disconnect()
|
||||
if err != nil {
|
||||
ce.User.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
|
||||
} else {
|
||||
ce.User.SetSession(&sess)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -553,7 +546,7 @@ func (handler *CommandHandler) CommandDeleteConnection(ce *CommandEvent) {
|
|||
ce.Reply("You don't have a WhatsApp connection.")
|
||||
return
|
||||
}
|
||||
ce.User.Disconnect()
|
||||
ce.User.DeleteConnection()
|
||||
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.")
|
||||
return
|
||||
}
|
||||
sess, err := ce.User.Conn.Disconnect()
|
||||
err := ce.User.Conn.Disconnect()
|
||||
if err == whatsapp.ErrNotConnected {
|
||||
ce.Reply("You were not connected.")
|
||||
return
|
||||
|
@ -572,8 +565,6 @@ func (handler *CommandHandler) CommandDisconnect(ce *CommandEvent) {
|
|||
ce.User.log.Warnln("Error while disconnecting:", err)
|
||||
ce.Reply("Unknown error while disconnecting: %v", err)
|
||||
return
|
||||
} else {
|
||||
ce.User.SetSession(&sess)
|
||||
}
|
||||
ce.User.bridge.Metrics.TrackConnectionState(ce.User.JID, false)
|
||||
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 {
|
||||
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 {
|
||||
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))
|
||||
|
@ -875,11 +866,11 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) {
|
|||
"To create a portal anyway, use `pm --force <number>`.")
|
||||
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)
|
||||
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 {
|
||||
err := portal.MainIntent().EnsureInvited(portal.MXID, user.MXID)
|
||||
if err != nil {
|
||||
|
|
|
@ -165,8 +165,8 @@ type UsernameTemplateArgs struct {
|
|||
|
||||
func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8) {
|
||||
var buf bytes.Buffer
|
||||
if index := strings.IndexRune(contact.Jid, '@'); index > 0 {
|
||||
contact.Jid = "+" + contact.Jid[:index]
|
||||
if index := strings.IndexRune(contact.JID, '@'); index > 0 {
|
||||
contact.JID = "+" + contact.JID[:index]
|
||||
}
|
||||
bc.displaynameTemplate.Execute(&buf, contact)
|
||||
var quality int8
|
||||
|
@ -175,7 +175,7 @@ func (bc BridgeConfig) FormatDisplayname(contact whatsapp.Contact) (string, int8
|
|||
quality = 3
|
||||
case len(contact.Name) > 0 || len(contact.Short) > 0:
|
||||
quality = 2
|
||||
case len(contact.Jid) > 0:
|
||||
case len(contact.JID) > 0:
|
||||
quality = 1
|
||||
default:
|
||||
quality = 0
|
||||
|
|
|
@ -93,7 +93,7 @@ func (user *User) Scan(row Scannable) *User {
|
|||
if len(jid.String) > 0 && len(clientID.String) > 0 {
|
||||
user.JID = jid.String + whatsapp.NewUserSuffix
|
||||
user.Session = &whatsapp.Session{
|
||||
ClientId: clientID.String,
|
||||
ClientID: clientID.String,
|
||||
ClientToken: clientToken.String,
|
||||
ServerToken: serverToken.String,
|
||||
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)`,
|
||||
user.MXID, 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)
|
||||
if err != nil {
|
||||
user.log.Warnfln("Failed to insert %s: %v", user.MXID, err)
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ func (user *User) Update() {
|
|||
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`,
|
||||
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)
|
||||
if err != nil {
|
||||
user.log.Warnfln("Failed to update %s: %v", user.MXID, err)
|
||||
|
|
4
go.mod
4
go.mod
|
@ -12,8 +12,8 @@ require (
|
|||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
gopkg.in/yaml.v2 v2.3.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
|
||||
)
|
||||
|
||||
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
6
go.sum
|
@ -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.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.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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
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/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.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/go.mod h1:TtVePxoEaw6+RZDKVajw66Yaj1lqLjH8l4FF3krsqWY=
|
||||
maunium.net/go/mautrix v0.8.0-rc.4 h1:3JXoL2JJPE5nh/YSw9sv9dQA9ulma9yHTMOBMBY1xdo=
|
||||
|
|
4
main.go
4
main.go
|
@ -384,11 +384,9 @@ func (bridge *Bridge) Stop() {
|
|||
continue
|
||||
}
|
||||
bridge.Log.Debugln("Disconnecting", user.MXID)
|
||||
sess, err := user.Conn.Disconnect()
|
||||
err := user.Conn.Disconnect()
|
||||
if err != nil {
|
||||
bridge.Log.Errorfln("Error while disconnecting %s: %v", user.MXID, err)
|
||||
} else {
|
||||
user.SetSession(&sess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1771,7 +1771,7 @@ func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) {
|
|||
"-pix_fmt", "yuv420p", "-c:v", "libx264", "-movflags", "+faststart",
|
||||
"-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'",
|
||||
outputFileName)
|
||||
vcLog := portal.log.Sub("VideoConverter").WithDefaultLevel(log.LevelWarn)
|
||||
vcLog := portal.log.Sub("VideoConverter").Writer(log.LevelWarn)
|
||||
cmd.Stdout = vcLog
|
||||
cmd.Stderr = vcLog
|
||||
|
||||
|
@ -2319,7 +2319,7 @@ func (portal *Portal) HandleMatrixMeta(sender *User, evt *event.Event) {
|
|||
return
|
||||
}
|
||||
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:
|
||||
return
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ func (prov *ProvisioningAPI) DeleteSession(w http.ResponseWriter, r *http.Reques
|
|||
})
|
||||
return
|
||||
}
|
||||
user.Disconnect()
|
||||
user.DeleteConnection()
|
||||
user.SetSession(nil)
|
||||
jsonResponse(w, http.StatusOK, Response{true, "Session information purged"})
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ func (prov *ProvisioningAPI) DeleteConnection(w http.ResponseWriter, r *http.Req
|
|||
})
|
||||
return
|
||||
}
|
||||
user.Disconnect()
|
||||
user.DeleteConnection()
|
||||
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
|
||||
}
|
||||
sess, err := user.Conn.Disconnect()
|
||||
err := user.Conn.Disconnect()
|
||||
if err == whatsapp.ErrNotConnected {
|
||||
jsonResponse(w, http.StatusNotFound, Error{
|
||||
Error: "You were not connected",
|
||||
|
@ -166,8 +166,6 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request)
|
|||
ErrCode: err.Error(),
|
||||
})
|
||||
return
|
||||
} else {
|
||||
user.SetSession(&sess)
|
||||
}
|
||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
||||
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")
|
||||
wasConnected := true
|
||||
sess, err := user.Conn.Disconnect()
|
||||
err := user.Conn.Disconnect()
|
||||
if err == whatsapp.ErrNotConnected {
|
||||
wasConnected = false
|
||||
} else if err != nil {
|
||||
user.log.Warnln("Error while disconnecting:", err)
|
||||
} else {
|
||||
user.SetSession(&sess)
|
||||
}
|
||||
|
||||
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)
|
||||
if err == whatsapp.ErrInvalidSession {
|
||||
if user.Session != nil {
|
||||
user.log.Debugln("Got invalid session error when reconnecting, but user has session. Retrying using RestoreWithSession()...")
|
||||
sess, err = user.Conn.RestoreWithSession(*user.Session)
|
||||
if err == nil {
|
||||
user.SetSession(&sess)
|
||||
}
|
||||
user.Conn.SetSession(*user.Session)
|
||||
err = user.Conn.Restore(true, r.Context())
|
||||
} else {
|
||||
jsonResponse(w, http.StatusForbidden, Error{
|
||||
Error: "You're not logged in",
|
||||
|
@ -216,7 +210,8 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
return
|
||||
}
|
||||
} else if err == whatsapp.ErrLoginInProgress {
|
||||
}
|
||||
if err == whatsapp.ErrLoginInProgress {
|
||||
jsonResponse(w, http.StatusConflict, Error{
|
||||
Error: "A login or reconnection is already in progress.",
|
||||
ErrCode: "login in progress",
|
||||
|
@ -231,23 +226,14 @@ func (prov *ProvisioningAPI) Reconnect(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
if err != nil {
|
||||
user.log.Warnln("Error while reconnecting:", err)
|
||||
if errors.Is(err, whatsapp.ErrRestoreSessionTimeout) {
|
||||
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),
|
||||
ErrCode: err.Error(),
|
||||
})
|
||||
}
|
||||
jsonResponse(w, http.StatusInternalServerError, Error{
|
||||
Error: fmt.Sprintf("Unknown error while reconnecting: %v", err),
|
||||
ErrCode: err.Error(),
|
||||
})
|
||||
user.log.Debugln("Disconnecting due to failed session restore in reconnect command...")
|
||||
sess, err := user.Conn.Disconnect()
|
||||
err = user.Conn.Disconnect()
|
||||
if err != nil {
|
||||
user.log.Errorln("Failed to disconnect after failed session restore in reconnect command:", err)
|
||||
} else {
|
||||
user.SetSession(&sess)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -339,7 +325,7 @@ func (prov *ProvisioningAPI) Logout(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
user.Disconnect()
|
||||
user.DeleteConnection()
|
||||
}
|
||||
|
||||
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")
|
||||
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"
|
||||
if err != nil {
|
||||
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."
|
||||
} else if errors.Is(err, whatsapp.ErrInvalidWebsocket) {
|
||||
msg = "WhatsApp connection error. Please try again."
|
||||
user.Disconnect()
|
||||
// TODO might need to make sure it reconnects?
|
||||
} else {
|
||||
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
|
||||
}
|
||||
user.log.Debugln("Successful login via provisioning API")
|
||||
user.log.Debugln("Successful login as", jid, "via provisioning API")
|
||||
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.SetSession(&session)
|
||||
_ = c.WriteJSON(map[string]interface{}{
|
||||
|
|
|
@ -291,8 +291,8 @@ func (puppet *Puppet) Sync(source *User, contact whatsapp.Contact) {
|
|||
puppet.log.Errorln("Failed to ensure registered:", err)
|
||||
}
|
||||
|
||||
if contact.Jid == source.JID {
|
||||
contact.Notify = source.Conn.Info.Pushname
|
||||
if contact.JID == source.JID {
|
||||
contact.Notify = source.pushName
|
||||
}
|
||||
|
||||
update := false
|
||||
|
|
191
user.go
191
user.go
|
@ -17,6 +17,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -61,6 +62,7 @@ type User struct {
|
|||
cleanDisconnection bool
|
||||
batteryWarningsSent int
|
||||
lastReconnection int64
|
||||
pushName string
|
||||
|
||||
chatListReceived chan struct{}
|
||||
syncPortalsDone chan struct{}
|
||||
|
@ -71,8 +73,9 @@ type User struct {
|
|||
syncStart chan struct{}
|
||||
syncWait sync.WaitGroup
|
||||
|
||||
mgmtCreateLock sync.Mutex
|
||||
connLock sync.Mutex
|
||||
mgmtCreateLock sync.Mutex
|
||||
connLock sync.Mutex
|
||||
cancelReconnect func()
|
||||
}
|
||||
|
||||
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 {
|
||||
user.connLock.Lock()
|
||||
if user.Conn != nil && user.Conn.IsConnected() {
|
||||
if user.Conn != nil {
|
||||
user.connLock.Unlock()
|
||||
return true
|
||||
if user.Conn.IsConnected() {
|
||||
return true
|
||||
} else {
|
||||
return user.RestoreSession()
|
||||
}
|
||||
} else if !evenIfNoSession && user.Session == nil {
|
||||
user.connLock.Unlock()
|
||||
return false
|
||||
}
|
||||
if user.Conn != nil {
|
||||
user.Disconnect()
|
||||
}
|
||||
user.log.Debugln("Connecting to WhatsApp")
|
||||
timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout)
|
||||
if timeout == 0 {
|
||||
timeout = 20
|
||||
}
|
||||
conn, err := whatsapp.NewConnWithOptions(&whatsapp.Options{
|
||||
user.Conn = whatsapp.NewConn(&whatsapp.Options{
|
||||
Timeout: timeout * time.Second,
|
||||
LongClientName: user.bridge.Config.WhatsApp.OSName,
|
||||
ShortClientName: user.bridge.Config.WhatsApp.BrowserName,
|
||||
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()
|
||||
return user.RestoreSession()
|
||||
}
|
||||
|
||||
func (user *User) Disconnect() {
|
||||
sess, err := user.Conn.Disconnect()
|
||||
func (user *User) DeleteConnection() {
|
||||
user.connLock.Lock()
|
||||
if user.Conn == nil {
|
||||
user.connLock.Unlock()
|
||||
return
|
||||
}
|
||||
err := user.Conn.Disconnect()
|
||||
if err != nil && err != whatsapp.ErrNotConnected {
|
||||
user.log.Warnln("Error disconnecting: %v", err)
|
||||
}
|
||||
user.SetSession(&sess)
|
||||
user.Conn.RemoveHandlers()
|
||||
user.Conn = nil
|
||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
||||
user.connLock.Unlock()
|
||||
}
|
||||
|
||||
func (user *User) RestoreSession() bool {
|
||||
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 {
|
||||
return true
|
||||
} else if err != nil {
|
||||
|
@ -290,24 +294,23 @@ func (user *User) RestoreSession() bool {
|
|||
if errors.Is(err, whatsapp.ErrUnpaired) {
|
||||
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " +
|
||||
"To re-pair your phone, log in again.")
|
||||
user.Disconnect()
|
||||
user.removeFromJIDMap()
|
||||
//user.JID = ""
|
||||
user.SetSession(nil)
|
||||
user.DeleteConnection()
|
||||
return false
|
||||
} else {
|
||||
user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " +
|
||||
"on your phone is reachable and use `reconnect` to try connecting again.")
|
||||
}
|
||||
user.log.Debugln("Disconnecting due to failed session restore...")
|
||||
_, err := user.Conn.Disconnect()
|
||||
err = user.Conn.Disconnect()
|
||||
if err != nil {
|
||||
user.log.Errorln("Failed to disconnect after failed session restore:", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
user.ConnectionErrors = 0
|
||||
user.SetSession(&sess)
|
||||
user.log.Debugln("Session restored successfully")
|
||||
user.PostLogin()
|
||||
}
|
||||
|
@ -382,7 +385,7 @@ func (user *User) Login(ce *CommandEvent) {
|
|||
qrChan := make(chan string, 3)
|
||||
eventIDChan := make(chan id.EventID, 1)
|
||||
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"
|
||||
if err != nil {
|
||||
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
|
||||
// Also between the two logout methods (commands.go and provisioning.go)
|
||||
user.log.Debugln("Successful login as", jid, "via command")
|
||||
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.SetSession(&session)
|
||||
ce.Reply("Successfully logged in, synchronizing chats...")
|
||||
|
@ -499,31 +503,31 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface
|
|||
}
|
||||
}
|
||||
|
||||
func (user *User) postConnPing(conn *whatsapp.Conn) bool {
|
||||
if user.Conn != conn {
|
||||
user.log.Warnln("Connection changed before scheduled post-connection ping, canceling ping")
|
||||
return false
|
||||
}
|
||||
func (user *User) postConnPing() bool {
|
||||
user.log.Debugln("Making post-connection ping")
|
||||
err := conn.AdminTest()
|
||||
if err != nil {
|
||||
user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err)
|
||||
sess, disconnectErr := conn.Disconnect()
|
||||
if disconnectErr != nil {
|
||||
user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr)
|
||||
var err error
|
||||
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 {
|
||||
user.Session = &sess
|
||||
break
|
||||
}
|
||||
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err))
|
||||
}()
|
||||
return false
|
||||
} else {
|
||||
user.log.Debugln("Post-connection ping OK")
|
||||
return true
|
||||
}
|
||||
user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err)
|
||||
disconnectErr := user.Conn.Disconnect()
|
||||
if disconnectErr != nil {
|
||||
user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr)
|
||||
}
|
||||
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err))
|
||||
}()
|
||||
return false
|
||||
}
|
||||
|
||||
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")
|
||||
case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
|
||||
user.log.Warnln("Timed out waiting for chat list to arrive!")
|
||||
user.postConnPing(conn)
|
||||
user.postConnPing()
|
||||
return
|
||||
}
|
||||
|
||||
if !user.postConnPing(conn) {
|
||||
if !user.postConnPing() {
|
||||
user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.")
|
||||
return
|
||||
}
|
||||
|
@ -557,16 +561,14 @@ func (user *User) intPostLogin(conn *whatsapp.Conn) {
|
|||
}
|
||||
}
|
||||
|
||||
type InfoGetter interface {
|
||||
type NormalMessage interface {
|
||||
GetInfo() whatsapp.MessageInfo
|
||||
}
|
||||
|
||||
func (user *User) HandleEvent(event interface{}) {
|
||||
switch v := event.(type) {
|
||||
case whatsapp.TextMessage, whatsapp.ImageMessage, whatsapp.StickerMessage, whatsapp.VideoMessage,
|
||||
whatsapp.AudioMessage, whatsapp.DocumentMessage, whatsapp.ContactMessage, whatsapp.StubMessage,
|
||||
whatsapp.LocationMessage:
|
||||
info := v.(InfoGetter).GetInfo()
|
||||
case NormalMessage:
|
||||
info := v.GetInfo()
|
||||
user.messageInput <- PortalMessage{info.RemoteJid, user, v, info.Timestamp}
|
||||
case whatsapp.MessageRevocation:
|
||||
user.messageInput <- PortalMessage{v.RemoteJid, user, v, 0}
|
||||
|
@ -596,6 +598,8 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
user.HandleCommand(v)
|
||||
case whatsapp.ChatUpdate:
|
||||
user.HandleChatUpdate(v)
|
||||
case whatsapp.ConnInfo:
|
||||
user.HandleConnInfo(v)
|
||||
case json.RawMessage:
|
||||
user.HandleJSONMessage(v)
|
||||
case *waProto.WebMessageInfo:
|
||||
|
@ -614,11 +618,11 @@ func (user *User) HandleStreamEvent(evt whatsapp.StreamEvent) {
|
|||
if user.lastReconnection+60 > time.Now().Unix() {
|
||||
user.lastReconnection = 0
|
||||
user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds")
|
||||
conn := user.Conn
|
||||
go func() {
|
||||
time.Sleep(20 * time.Second)
|
||||
// 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 {
|
||||
|
@ -630,10 +634,10 @@ func (user *User) HandleChatList(chats []whatsapp.Chat) {
|
|||
user.log.Infoln("Chat list received")
|
||||
chatMap := make(map[string]whatsapp.Chat)
|
||||
for _, chat := range user.Conn.Store.Chats {
|
||||
chatMap[chat.Jid] = chat
|
||||
chatMap[chat.JID] = chat
|
||||
}
|
||||
for _, chat := range chats {
|
||||
chatMap[chat.Jid] = chat
|
||||
chatMap[chat.JID] = chat
|
||||
}
|
||||
select {
|
||||
case user.chatListReceived <- struct{}{}:
|
||||
|
@ -655,14 +659,14 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
|||
for _, chat := range chatMap {
|
||||
ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
|
||||
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
|
||||
}
|
||||
portal := user.GetPortalByJID(chat.Jid)
|
||||
portal := user.GetPortalByJID(chat.JID)
|
||||
|
||||
chats = append(chats, Chat{
|
||||
Portal: portal,
|
||||
Contact: user.Conn.Store.Contacts[chat.Jid],
|
||||
Contact: user.Conn.Store.Contacts[chat.JID],
|
||||
LastMessageTime: ts,
|
||||
})
|
||||
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) {
|
||||
contactMap := make(map[string]whatsapp.Contact)
|
||||
for _, contact := range contacts {
|
||||
contactMap[contact.Jid] = contact
|
||||
contactMap[contact.JID] = contact
|
||||
}
|
||||
go user.syncPuppets(contactMap)
|
||||
}
|
||||
|
@ -786,10 +790,20 @@ func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
|
|||
if contacts == nil {
|
||||
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")
|
||||
for jid, contact := range contacts {
|
||||
if strings.HasSuffix(jid, whatsapp.NewUserSuffix) {
|
||||
puppet := user.bridge.GetPuppetByJID(contact.Jid)
|
||||
puppet := user.bridge.GetPuppetByJID(contact.JID)
|
||||
puppet.Sync(user, contact)
|
||||
}
|
||||
}
|
||||
|
@ -846,14 +860,18 @@ func (user *User) tryReconnect(msg string) {
|
|||
baseDelay = -baseDelay + 1
|
||||
}
|
||||
delay := baseDelay
|
||||
conn := user.Conn
|
||||
takeover := false
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
user.cancelReconnect = cancel
|
||||
for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||
if user.Conn != conn {
|
||||
user.log.Debugln("Connection was recreated, aborting reconnection attempts")
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
user.log.Debugln("tryReconnect context cancelled, aborting reconnection attempts")
|
||||
return
|
||||
default:
|
||||
}
|
||||
err := conn.Restore(takeover)
|
||||
err := user.Conn.Restore(takeover, ctx)
|
||||
takeover = true
|
||||
if err == nil {
|
||||
user.ConnectionErrors = 0
|
||||
|
@ -863,14 +881,23 @@ func (user *User) tryReconnect(msg string) {
|
|||
user.PostLogin()
|
||||
return
|
||||
} else if errors.Is(err, whatsapp.ErrBadRequest) {
|
||||
user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...")
|
||||
sess, err := conn.Disconnect()
|
||||
user.log.Warnln("Got init 400 error when trying to reconnect, resetting connection...")
|
||||
err = user.Conn.Disconnect()
|
||||
if err != nil {
|
||||
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++
|
||||
user.ConnectionErrors++
|
||||
if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
|
||||
|
@ -930,10 +957,10 @@ func (user *User) handleMessageLoop() {
|
|||
|
||||
func (user *User) HandleNewContact(contact whatsapp.Contact) {
|
||||
user.log.Debugfln("Contact message: %+v", contact)
|
||||
if strings.HasSuffix(contact.Jid, whatsapp.OldUserSuffix) {
|
||||
contact.Jid = strings.Replace(contact.Jid, whatsapp.OldUserSuffix, whatsapp.NewUserSuffix, -1)
|
||||
if strings.HasSuffix(contact.JID, whatsapp.OldUserSuffix) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -1176,11 +1203,23 @@ func (user *User) HandleChatUpdate(cmd whatsapp.ChatUpdate) {
|
|||
go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, nil, cmd.Data.UserChange.JIDs)
|
||||
case whatsapp.ChatActionIntroduce:
|
||||
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) {
|
||||
if !json.Valid(message) {
|
||||
return
|
||||
|
|
Loading…
Reference in a new issue