mirror of
https://github.com/mautrix/whatsapp.git
synced 2026-07-11 08:26:08 -04:00
Compare commits
No commits in common. "main" and "v0.2606.0" have entirely different histories.
16 changed files with 40 additions and 185 deletions
4
go.mod
4
go.mod
|
|
@ -12,13 +12,13 @@ require (
|
||||||
github.com/tidwall/gjson v1.19.0
|
github.com/tidwall/gjson v1.19.0
|
||||||
go.mau.fi/util v0.9.10
|
go.mau.fi/util v0.9.10
|
||||||
go.mau.fi/webp v0.3.0
|
go.mau.fi/webp v0.3.0
|
||||||
go.mau.fi/whatsmeow v0.0.0-20260709092057-73fe7355f59f
|
go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537
|
||||||
golang.org/x/image v0.42.0
|
golang.org/x/image v0.42.0
|
||||||
golang.org/x/net v0.56.0
|
golang.org/x/net v0.56.0
|
||||||
golang.org/x/sync v0.21.0
|
golang.org/x/sync v0.21.0
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
maunium.net/go/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895
|
maunium.net/go/mautrix v0.28.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
|
||||||
8
go.sum
8
go.sum
|
|
@ -75,8 +75,8 @@ go.mau.fi/util v0.9.10 h1:wzvz5iDHyqDXB8vgisD4d3SzucLXNM3iNY+1O1RoHtg=
|
||||||
go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A=
|
go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A=
|
||||||
go.mau.fi/webp v0.3.0 h1:gVHQZtz21Ziwj+CDuklbX9mqpsnDIFKxs/BJyV7iZzA=
|
go.mau.fi/webp v0.3.0 h1:gVHQZtz21Ziwj+CDuklbX9mqpsnDIFKxs/BJyV7iZzA=
|
||||||
go.mau.fi/webp v0.3.0/go.mod h1:rlZFTev+dYxhvk+XNBP/5GcTt4gXmzAB4DU0aGUYIQo=
|
go.mau.fi/webp v0.3.0/go.mod h1:rlZFTev+dYxhvk+XNBP/5GcTt4gXmzAB4DU0aGUYIQo=
|
||||||
go.mau.fi/whatsmeow v0.0.0-20260709092057-73fe7355f59f h1:VZkwFBEQ9TbB9IWsldw8BJDhHbEBElm9VD2VQXbSc9s=
|
go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537 h1:LhxtFShA1NXJWz4yNygwtl3Eycp9XsfMqJeDdcLyKmk=
|
||||||
go.mau.fi/whatsmeow v0.0.0-20260709092057-73fe7355f59f/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U=
|
go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U=
|
||||||
go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU=
|
go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU=
|
||||||
go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w=
|
go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w=
|
||||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||||
|
|
@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
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/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895 h1:oe87/mP1vo4LnA6CJDEfyg5ZiAOar2quJNypfO8eS4Y=
|
maunium.net/go/mautrix v0.28.1 h1:Hic3oDMPbLbQu1fhboTRAKZcORMjzzkjxsa+SGk60b0=
|
||||||
maunium.net/go/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY=
|
maunium.net/go/mautrix v0.28.1/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY=
|
||||||
|
|
|
||||||
|
|
@ -400,8 +400,8 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wrappedInfo, err := wa.getChatInfo(ctx, conv.ChatJID, conv, true)
|
wrappedInfo, err := wa.getChatInfo(ctx, conv.ChatJID, conv, true)
|
||||||
if errors.Is(err, whatsmeow.ErrNotInGroup) || errors.Is(err, whatsmeow.ErrGroupNotFound) {
|
if errors.Is(err, whatsmeow.ErrNotInGroup) {
|
||||||
log.Debug().Err(err).Stringer("chat_jid", conv.ChatJID).
|
log.Debug().Stringer("chat_jid", conv.ChatJID).
|
||||||
Msg("Skipping creating room because the user is not a participant")
|
Msg("Skipping creating room because the user is not a participant")
|
||||||
//err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, conv.ChatJID)
|
//err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, conv.ChatJID)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
|
|
@ -750,7 +750,7 @@ func (wa *WhatsAppClient) convertHistorySyncMessage(
|
||||||
// TODO use proper intent
|
// TODO use proper intent
|
||||||
intent := wa.Main.Bridge.Bot
|
intent := wa.Main.Bridge.Bot
|
||||||
wrapped := &bridgev2.BackfillMessage{
|
wrapped := &bridgev2.BackfillMessage{
|
||||||
ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, rawMsg, info, nil, isViewOnce, true, nil),
|
ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, rawMsg, info, isViewOnce, true, nil),
|
||||||
Sender: wa.makeEventSender(ctx, info.Sender),
|
Sender: wa.makeEventSender(ctx, info.Sender),
|
||||||
ID: waid.MakeMessageID(info.Chat, info.Sender, info.ID),
|
ID: waid.MakeMessageID(info.Chat, info.Sender, info.ID),
|
||||||
TxnID: networkid.TransactionID(waid.MakeMessageID(info.Chat, info.Sender, info.ID)),
|
TxnID: networkid.TransactionID(waid.MakeMessageID(info.Chat, info.Sender, info.ID)),
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ func (wa *WhatsAppClient) getPortalKeyByMessageSource(ms types.MessageSource) ne
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageInfoWrapper struct {
|
type MessageInfoWrapper struct {
|
||||||
OrigSource types.MessageSource
|
|
||||||
Info types.MessageInfo
|
Info types.MessageInfo
|
||||||
wa *WhatsAppClient
|
wa *WhatsAppClient
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +200,7 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, msgconv.ContextKeyEditTargetID, evt.Message.GetProtocolMessage().GetKey().GetID())
|
ctx = context.WithValue(ctx, msgconv.ContextKeyEditTargetID, evt.Message.GetProtocolMessage().GetKey().GetID())
|
||||||
cm := evt.wa.Main.MsgConv.ToMatrix(
|
cm := evt.wa.Main.MsgConv.ToMatrix(
|
||||||
ctx, portal, evt.wa.Client, intent, editedMsg, evt.MsgEvent.RawMessage, &evt.Info, &evt.OrigSource, evt.isViewOnce(), false, previouslyConvertedPart,
|
ctx, portal, evt.wa.Client, intent, editedMsg, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), false, previouslyConvertedPart,
|
||||||
)
|
)
|
||||||
if evt.isUndecryptableUpsertSubEvent && isFailedMedia(cm) {
|
if evt.isUndecryptableUpsertSubEvent && isFailedMedia(cm) {
|
||||||
evt.postHandle = func() {
|
evt.postHandle = func() {
|
||||||
|
|
@ -286,7 +285,7 @@ func (evt *WAMessageEvent) HandleExisting(ctx context.Context, portal *bridgev2.
|
||||||
func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
|
func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
|
||||||
evt.wa.EnqueuePortalResync(portal, false)
|
evt.wa.EnqueuePortalResync(portal, false)
|
||||||
converted := evt.wa.Main.MsgConv.ToMatrix(
|
converted := evt.wa.Main.MsgConv.ToMatrix(
|
||||||
ctx, portal, evt.wa.Client, intent, evt.Message, evt.MsgEvent.RawMessage, &evt.Info, &evt.OrigSource, evt.isViewOnce(), false, nil,
|
ctx, portal, evt.wa.Client, intent, evt.Message, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), false, nil,
|
||||||
)
|
)
|
||||||
if isFailedMedia(converted) {
|
if isFailedMedia(converted) {
|
||||||
evt.postHandle = func() {
|
evt.postHandle = func() {
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev
|
||||||
defer wa.mcTrack(msg, time.Now(), &retErr)
|
defer wa.mcTrack(msg, time.Now(), &retErr)
|
||||||
var req whatsmeow.SendRequestExtra
|
var req whatsmeow.SendRequestExtra
|
||||||
if msg.Portal.Metadata.(*waid.PortalMetadata).CommunityAnnouncementGroup {
|
if msg.Portal.Metadata.(*waid.PortalMetadata).CommunityAnnouncementGroup {
|
||||||
reactionMsg.EncReactionMessage, err = wa.Client.EncryptReaction(ctx, msgconv.MessageIDToInfo(ctx, wa.Client, messageID), reactionMsg.ReactionMessage)
|
reactionMsg.EncReactionMessage, err = wa.Client.EncryptReaction(ctx, msgconv.MessageIDToInfo(wa.Client, messageID), reactionMsg.ReactionMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to encrypt reaction: %w", err)
|
return nil, fmt.Errorf("failed to encrypt reaction: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,20 +126,6 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) {
|
||||||
success = wa.handleWANewsletterLeave(evt)
|
success = wa.handleWANewsletterLeave(evt)
|
||||||
case *events.Picture:
|
case *events.Picture:
|
||||||
success = wa.handleWAPictureUpdate(ctx, evt)
|
success = wa.handleWAPictureUpdate(ctx, evt)
|
||||||
case *events.NotifyAccountReachoutTimelock:
|
|
||||||
wa.UserLogin.TrackAnalytics("WhatsApp Account Reachout Timelock", map[string]any{
|
|
||||||
"enforcement_type": evt.EnforcementType,
|
|
||||||
"is_active": evt.IsActive,
|
|
||||||
"time_enforcement_ends": evt.TimeEnforcementEnds.Time,
|
|
||||||
})
|
|
||||||
wa.UserLogin.Metadata.(*waid.UserLoginMetadata).ReachoutTimelockUntil = evt.TimeEnforcementEnds.Time
|
|
||||||
if wa.UserLogin.BridgeState.GetPrevUnsent().StateEvent == status.StateConnected {
|
|
||||||
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
|
|
||||||
}
|
|
||||||
err := wa.UserLogin.Save(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("Failed to save user login metadata after reachout timelock update")
|
|
||||||
}
|
|
||||||
|
|
||||||
case *events.AppStateSyncComplete:
|
case *events.AppStateSyncComplete:
|
||||||
wa.handleWAAppStateSyncComplete(ctx, evt)
|
wa.handleWAAppStateSyncComplete(ctx, evt)
|
||||||
|
|
@ -358,7 +344,6 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa
|
||||||
evt.UnwrapRaw()
|
evt.UnwrapRaw()
|
||||||
parsedMessageType = getMessageType(evt.Message)
|
parsedMessageType = getMessageType(evt.Message)
|
||||||
}
|
}
|
||||||
origSource := evt.Info.MessageSource
|
|
||||||
wa.rerouteWAMessage(ctx, "message", &evt.Info.MessageSource, evt.Info.ID)
|
wa.rerouteWAMessage(ctx, "message", &evt.Info.MessageSource, evt.Info.ID)
|
||||||
wa.UserLogin.Log.Trace().
|
wa.UserLogin.Log.Trace().
|
||||||
Any("info", evt.Info).
|
Any("info", evt.Info).
|
||||||
|
|
@ -410,7 +395,6 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa
|
||||||
|
|
||||||
res := wa.UserLogin.QueueRemoteEvent(&WAMessageEvent{
|
res := wa.UserLogin.QueueRemoteEvent(&WAMessageEvent{
|
||||||
MessageInfoWrapper: &MessageInfoWrapper{
|
MessageInfoWrapper: &MessageInfoWrapper{
|
||||||
OrigSource: origSource,
|
|
||||||
Info: evt.Info,
|
Info: evt.Info,
|
||||||
wa: wa,
|
wa: wa,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package connector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -13,7 +12,6 @@ import (
|
||||||
"go.mau.fi/util/exsync"
|
"go.mau.fi/util/exsync"
|
||||||
"go.mau.fi/util/jsontime"
|
"go.mau.fi/util/jsontime"
|
||||||
"go.mau.fi/whatsmeow"
|
"go.mau.fi/whatsmeow"
|
||||||
"go.mau.fi/whatsmeow/types"
|
|
||||||
"go.mau.fi/whatsmeow/types/events"
|
"go.mau.fi/whatsmeow/types/events"
|
||||||
waLog "go.mau.fi/whatsmeow/util/log"
|
waLog "go.mau.fi/whatsmeow/util/log"
|
||||||
"maunium.net/go/mautrix/bridgev2"
|
"maunium.net/go/mautrix/bridgev2"
|
||||||
|
|
@ -27,7 +25,6 @@ const (
|
||||||
LoginStepIDQR = "fi.mau.whatsapp.login.qr"
|
LoginStepIDQR = "fi.mau.whatsapp.login.qr"
|
||||||
LoginStepIDPhoneNumber = "fi.mau.whatsapp.login.phone"
|
LoginStepIDPhoneNumber = "fi.mau.whatsapp.login.phone"
|
||||||
LoginStepIDCode = "fi.mau.whatsapp.login.code"
|
LoginStepIDCode = "fi.mau.whatsapp.login.code"
|
||||||
LoginStepIDPasskey = "fi.mau.whatsapp.login.passkey"
|
|
||||||
LoginStepIDComplete = "fi.mau.whatsapp.login.complete"
|
LoginStepIDComplete = "fi.mau.whatsapp.login.complete"
|
||||||
|
|
||||||
LoginFlowIDQR = "qr"
|
LoginFlowIDQR = "qr"
|
||||||
|
|
@ -96,8 +93,6 @@ func (wa *WhatsAppConnector) CreateLogin(_ context.Context, user *bridgev2.User,
|
||||||
|
|
||||||
WaitForQRs: exsync.NewEvent(),
|
WaitForQRs: exsync.NewEvent(),
|
||||||
LoginComplete: exsync.NewEvent(),
|
LoginComplete: exsync.NewEvent(),
|
||||||
PasskeyRequest: exsync.NewEvent(),
|
|
||||||
PasskeyConfirmation: exsync.NewEvent(),
|
|
||||||
Received515: exsync.NewEvent(),
|
Received515: exsync.NewEvent(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -119,11 +114,6 @@ type WALogin struct {
|
||||||
Received515 *exsync.Event
|
Received515 *exsync.Event
|
||||||
PrevQRIndex atomic.Int32
|
PrevQRIndex atomic.Int32
|
||||||
|
|
||||||
PasskeyRequest *exsync.Event
|
|
||||||
PasskeyRequestData *events.PairPasskeyRequest
|
|
||||||
PasskeyConfirmation *exsync.Event
|
|
||||||
PasskeyConfirmationData *events.PairPasskeyConfirmation
|
|
||||||
|
|
||||||
Closed atomic.Bool
|
Closed atomic.Bool
|
||||||
EventHandlerID uint32
|
EventHandlerID uint32
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +122,6 @@ var (
|
||||||
_ bridgev2.LoginProcessDisplayAndWait = (*WALogin)(nil)
|
_ bridgev2.LoginProcessDisplayAndWait = (*WALogin)(nil)
|
||||||
_ bridgev2.LoginProcessUserInput = (*WALogin)(nil)
|
_ bridgev2.LoginProcessUserInput = (*WALogin)(nil)
|
||||||
_ bridgev2.LoginProcessWithOverride = (*WALogin)(nil)
|
_ bridgev2.LoginProcessWithOverride = (*WALogin)(nil)
|
||||||
_ bridgev2.LoginProcessWebAuthn = (*WALogin)(nil)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const LoginConnectWait = 15 * time.Second
|
const LoginConnectWait = 15 * time.Second
|
||||||
|
|
@ -273,17 +262,6 @@ func (wl *WALogin) handleEvent(rawEvt any) {
|
||||||
case *events.ClientOutdated:
|
case *events.ClientOutdated:
|
||||||
wl.Log.Error().Msg("Got client outdated error")
|
wl.Log.Error().Msg("Got client outdated error")
|
||||||
wl.LoginError = ErrLoginClientOutdated
|
wl.LoginError = ErrLoginClientOutdated
|
||||||
case *events.PairPasskeyRequest:
|
|
||||||
wl.PasskeyRequestData = evt
|
|
||||||
wl.PasskeyRequest.Set()
|
|
||||||
return
|
|
||||||
case *events.PairPasskeyConfirmation:
|
|
||||||
wl.PasskeyConfirmationData = evt
|
|
||||||
wl.PasskeyConfirmation.Set()
|
|
||||||
return
|
|
||||||
case *events.PairPasskeyError:
|
|
||||||
wl.Log.Err(evt.Error).Msg("Got passkey error")
|
|
||||||
wl.LoginError = evt.Error
|
|
||||||
case *events.PairSuccess:
|
case *events.PairSuccess:
|
||||||
wl.Log.Info().Any("event_data", evt).Msg("Got pair successful event")
|
wl.Log.Info().Any("event_data", evt).Msg("Got pair successful event")
|
||||||
wl.LoginSuccess = evt
|
wl.LoginSuccess = evt
|
||||||
|
|
@ -311,14 +289,10 @@ func (wl *WALogin) handleEvent(rawEvt any) {
|
||||||
|
|
||||||
func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
|
func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
|
||||||
if wl.PhoneCode {
|
if wl.PhoneCode {
|
||||||
select {
|
err := wl.LoginComplete.Wait(ctx)
|
||||||
case <-ctx.Done():
|
if err != nil {
|
||||||
wl.Cancel()
|
wl.Cancel()
|
||||||
return nil, ctx.Err()
|
return nil, err
|
||||||
case <-wl.PasskeyRequest.GetChan():
|
|
||||||
return wl.makePasskeyStep()
|
|
||||||
case <-wl.LoginComplete.GetChan():
|
|
||||||
// continue
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prevIndex := int(wl.PrevQRIndex.Load())
|
prevIndex := int(wl.PrevQRIndex.Load())
|
||||||
|
|
@ -345,16 +319,9 @@ func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
wl.Cancel()
|
wl.Cancel()
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
case <-wl.PasskeyRequest.GetChan():
|
|
||||||
return wl.makePasskeyStep()
|
|
||||||
case <-wl.LoginComplete.GetChan():
|
case <-wl.LoginComplete.GetChan():
|
||||||
// continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wl.onLoginComplete(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wl *WALogin) onLoginComplete(ctx context.Context) (*bridgev2.LoginStep, error) {
|
|
||||||
if wl.LoginError != nil {
|
if wl.LoginError != nil {
|
||||||
wl.Log.Debug().Err(wl.LoginError).Msg("Login completed with error")
|
wl.Log.Debug().Err(wl.LoginError).Msg("Login completed with error")
|
||||||
wl.Cancel()
|
wl.Cancel()
|
||||||
|
|
@ -405,66 +372,6 @@ func (wl *WALogin) onLoginComplete(ctx context.Context) (*bridgev2.LoginStep, er
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wl *WALogin) makePasskeyStep() (*bridgev2.LoginStep, error) {
|
|
||||||
pubkeyData, err := json.Marshal(wl.PasskeyRequestData.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
wl.Log.Err(err).Msg("Failed to marshal public key data")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &bridgev2.LoginStep{
|
|
||||||
Type: bridgev2.LoginStepTypeWebAuthn,
|
|
||||||
StepID: LoginStepIDPasskey,
|
|
||||||
WebAuthnParams: &bridgev2.LoginWebAuthnParams{
|
|
||||||
URL: "https://web.whatsapp.com",
|
|
||||||
PublicKey: pubkeyData,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wl *WALogin) SubmitWebAuthnResponse(ctx context.Context, rawResp json.RawMessage) (*bridgev2.LoginStep, error) {
|
|
||||||
var resp types.WebAuthnResponse
|
|
||||||
err := json.Unmarshal(rawResp, &resp)
|
|
||||||
if err != nil {
|
|
||||||
wl.Log.Err(err).Msg("Failed to unmarshal WebAuthn response")
|
|
||||||
wl.Cancel()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = wl.Client.SendPasskeyResponse(ctx, &resp)
|
|
||||||
if err != nil {
|
|
||||||
wl.Log.Err(err).Msg("Failed to send WebAuthn response")
|
|
||||||
wl.Cancel()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
wl.Cancel()
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case <-wl.PasskeyConfirmation.GetChan():
|
|
||||||
if !wl.PasskeyConfirmationData.SkipHandoffUX {
|
|
||||||
wl.Cancel()
|
|
||||||
return nil, bridgev2.RespError{
|
|
||||||
ErrCode: "FI.MAU.WHATSAPP.NOT_IMPLEMENTED",
|
|
||||||
Err: "Displaying WebAuthn pairing confirmation codes is not yet implemented",
|
|
||||||
StatusCode: http.StatusBadRequest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = wl.Client.SendPasskeyConfirmation(ctx)
|
|
||||||
if err != nil {
|
|
||||||
wl.Log.Err(err).Msg("Failed to send WebAuthn confirmation")
|
|
||||||
wl.Cancel()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case <-wl.LoginComplete.GetChan():
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
wl.Cancel()
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case <-wl.LoginComplete.GetChan():
|
|
||||||
}
|
|
||||||
return wl.onLoginComplete(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wl *WALogin) Cancel() {
|
func (wl *WALogin) Cancel() {
|
||||||
wl.Closed.Store(true)
|
wl.Closed.Store(true)
|
||||||
wl.Client.RemoveEventHandler(wl.EventHandlerID)
|
wl.Client.RemoveEventHandler(wl.EventHandlerID)
|
||||||
|
|
|
||||||
|
|
@ -37,13 +37,6 @@ func (wa *WhatsAppClient) FillBridgeState(state status.BridgeState) status.Bridg
|
||||||
state.Error = WAPhoneOffline
|
state.Error = WAPhoneOffline
|
||||||
state.UserAction = status.UserActionOpenNative
|
state.UserAction = status.UserActionOpenNative
|
||||||
}
|
}
|
||||||
rtu := wa.UserLogin.Metadata.(*waid.UserLoginMetadata).ReachoutTimelockUntil
|
|
||||||
if !rtu.IsZero() {
|
|
||||||
if state.Info == nil {
|
|
||||||
state.Info = make(map[string]any)
|
|
||||||
}
|
|
||||||
state.Info["reachout_timelock_until"] = rtu
|
|
||||||
}
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,7 @@ var ResyncLoopInterval = 4 * time.Hour
|
||||||
var ResyncJitterSeconds = 3600
|
var ResyncJitterSeconds = 3600
|
||||||
|
|
||||||
func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) {
|
func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) {
|
||||||
lastSync := ghost.Metadata.(*waid.GhostMetadata).LastSync.Time
|
if ghost.Metadata.(*waid.GhostMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) {
|
||||||
if lastSync.Add(ResyncMinInterval).After(time.Now()) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wa.resyncQueueLock.Lock()
|
wa.resyncQueueLock.Lock()
|
||||||
|
|
@ -44,7 +43,6 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) {
|
||||||
wa.UserLogin.Log.Debug().
|
wa.UserLogin.Log.Debug().
|
||||||
Stringer("jid", jid).
|
Stringer("jid", jid).
|
||||||
Str("next_resync_in", nextResyncIn).
|
Str("next_resync_in", nextResyncIn).
|
||||||
Time("last_ghost_resync", lastSync).
|
|
||||||
Msg("Enqueued resync for ghost")
|
Msg("Enqueued resync for ghost")
|
||||||
}
|
}
|
||||||
wa.resyncQueueLock.Unlock()
|
wa.resyncQueueLock.Unlock()
|
||||||
|
|
@ -52,8 +50,7 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) {
|
||||||
|
|
||||||
func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM bool) {
|
func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM bool) {
|
||||||
jid, _ := waid.ParsePortalID(portal.ID)
|
jid, _ := waid.ParsePortalID(portal.ID)
|
||||||
lastSync := portal.Metadata.(*waid.PortalMetadata).LastSync.Time
|
if portal.Metadata.(*waid.PortalMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) {
|
||||||
if lastSync.Add(ResyncMinInterval).After(time.Now()) {
|
|
||||||
return
|
return
|
||||||
} else if !allowDM && jid.Server != types.GroupServer {
|
} else if !allowDM && jid.Server != types.GroupServer {
|
||||||
return
|
return
|
||||||
|
|
@ -64,7 +61,6 @@ func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM b
|
||||||
wa.UserLogin.Log.Debug().
|
wa.UserLogin.Log.Debug().
|
||||||
Stringer("jid", jid).
|
Stringer("jid", jid).
|
||||||
Stringer("next_resync_in", time.Until(wa.nextResync)).
|
Stringer("next_resync_in", time.Until(wa.nextResync)).
|
||||||
Time("last_portal_resync", lastSync).
|
|
||||||
Msg("Enqueued resync for portal")
|
Msg("Enqueued resync for portal")
|
||||||
}
|
}
|
||||||
wa.resyncQueueLock.Unlock()
|
wa.resyncQueueLock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,10 @@ func getMessageType(waMsg *waE2E.Message) string {
|
||||||
return "encrypted comment"
|
return "encrypted comment"
|
||||||
case waMsg.CommentMessage != nil:
|
case waMsg.CommentMessage != nil:
|
||||||
return "comment"
|
return "comment"
|
||||||
case waMsg.PollCreationMessage != nil || waMsg.PollCreationMessageV2 != nil || waMsg.PollCreationMessageV3 != nil ||
|
case waMsg.PollCreationMessage != nil || waMsg.PollCreationMessageV2 != nil || waMsg.PollCreationMessageV3 != nil:
|
||||||
waMsg.PollCreationMessageV5 != nil || waMsg.PollCreationMessageV6 != nil:
|
|
||||||
return "poll create"
|
return "poll create"
|
||||||
case waMsg.PollCreationMessageV4 != nil:
|
case waMsg.PollCreationMessageV4 != nil || waMsg.PollCreationMessageV5 != nil:
|
||||||
return "poll create (v4)"
|
return "poll create (vNext)"
|
||||||
case waMsg.PollUpdateMessage != nil:
|
case waMsg.PollUpdateMessage != nil:
|
||||||
return "poll update"
|
return "poll update"
|
||||||
case waMsg.ProtocolMessage != nil:
|
case waMsg.ProtocolMessage != nil:
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ func (mc *MessageConverter) ToWhatsApp(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to parse message ID: %w", err)
|
return nil, nil, fmt.Errorf("failed to parse message ID: %w", err)
|
||||||
}
|
}
|
||||||
rootMsgInfo := MessageIDToInfo(ctx, client, parsedID)
|
rootMsgInfo := MessageIDToInfo(client, parsedID)
|
||||||
message, err = client.EncryptComment(ctx, rootMsgInfo, message)
|
message, err = client.EncryptComment(ctx, rootMsgInfo, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to encrypt comment: %w", err)
|
return nil, nil, fmt.Errorf("failed to encrypt comment: %w", err)
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,6 @@ func (mc *MessageConverter) ToMatrix(
|
||||||
waMsg *waE2E.Message,
|
waMsg *waE2E.Message,
|
||||||
rawWaMsg *waE2E.Message,
|
rawWaMsg *waE2E.Message,
|
||||||
info *types.MessageInfo,
|
info *types.MessageInfo,
|
||||||
origSource *types.MessageSource,
|
|
||||||
isViewOnce bool,
|
isViewOnce bool,
|
||||||
isBackfill bool,
|
isBackfill bool,
|
||||||
previouslyConvertedPart *bridgev2.ConvertedMessagePart,
|
previouslyConvertedPart *bridgev2.ConvertedMessagePart,
|
||||||
|
|
@ -176,14 +175,8 @@ func (mc *MessageConverter) ToMatrix(
|
||||||
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV2)
|
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV2)
|
||||||
case waMsg.PollCreationMessageV3 != nil:
|
case waMsg.PollCreationMessageV3 != nil:
|
||||||
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV3)
|
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV3)
|
||||||
//case waMsg.PollCreationMessageV4 != nil:
|
|
||||||
// part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV4)
|
|
||||||
case waMsg.PollCreationMessageV5 != nil:
|
|
||||||
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV5)
|
|
||||||
case waMsg.PollCreationMessageV6 != nil:
|
|
||||||
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV6)
|
|
||||||
case waMsg.PollUpdateMessage != nil:
|
case waMsg.PollUpdateMessage != nil:
|
||||||
part, contextInfo = mc.convertPollUpdateMessage(ctx, info, origSource, waMsg.PollUpdateMessage)
|
part, contextInfo = mc.convertPollUpdateMessage(ctx, info, waMsg.PollUpdateMessage)
|
||||||
case waMsg.EventMessage != nil:
|
case waMsg.EventMessage != nil:
|
||||||
part, contextInfo = mc.convertEventMessage(ctx, waMsg.EventMessage)
|
part, contextInfo = mc.convertEventMessage(ctx, waMsg.EventMessage)
|
||||||
case waMsg.PinInChatMessage != nil:
|
case waMsg.PinInChatMessage != nil:
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ func (mc *MessageConverter) PollVoteToWhatsApp(
|
||||||
zerolog.Ctx(ctx).Err(err).Msg("Failed to parse message ID")
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to parse message ID")
|
||||||
return nil, fmt.Errorf("failed to parse message ID")
|
return nil, fmt.Errorf("failed to parse message ID")
|
||||||
}
|
}
|
||||||
pollMsgInfo := MessageIDToInfo(ctx, client, parsedMsgID)
|
pollMsgInfo := MessageIDToInfo(client, parsedMsgID)
|
||||||
pollMsgInfo.Type = "poll"
|
pollMsgInfo.Type = "poll"
|
||||||
optionHashes := make([][]byte, 0, len(content.Response.Answers))
|
optionHashes := make([][]byte, 0, len(content.Response.Answers))
|
||||||
if pollMsg.Metadata.(*waid.MessageMetadata).IsMatrixPoll {
|
if pollMsg.Metadata.(*waid.MessageMetadata).IsMatrixPoll {
|
||||||
|
|
@ -146,23 +146,13 @@ func (mc *MessageConverter) PollVoteToWhatsApp(
|
||||||
return &waE2E.Message{PollUpdateMessage: pollUpdate}, err
|
return &waE2E.Message{PollUpdateMessage: pollUpdate}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func MessageIDToInfo(ctx context.Context, client *whatsmeow.Client, parsedMsgID *waid.ParsedMessageID) *types.MessageInfo {
|
func MessageIDToInfo(client *whatsmeow.Client, parsedMsgID *waid.ParsedMessageID) *types.MessageInfo {
|
||||||
chat := parsedMsgID.Chat
|
|
||||||
sender := parsedMsgID.Sender
|
|
||||||
if client.Store.LIDMigrationTimestamp != 0 && chat.Server == types.DefaultUserServer {
|
|
||||||
chatLID, _ := client.Store.LIDs.GetLIDForPN(ctx, chat)
|
|
||||||
senderLID, _ := client.Store.LIDs.GetLIDForPN(ctx, sender)
|
|
||||||
if !chatLID.IsEmpty() && !senderLID.IsEmpty() {
|
|
||||||
chat = chatLID
|
|
||||||
sender = senderLID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &types.MessageInfo{
|
return &types.MessageInfo{
|
||||||
MessageSource: types.MessageSource{
|
MessageSource: types.MessageSource{
|
||||||
Chat: chat,
|
Chat: parsedMsgID.Chat,
|
||||||
Sender: sender,
|
Sender: parsedMsgID.Sender,
|
||||||
IsFromMe: sender.User == client.Store.GetLID().User || sender.User == client.Store.GetJID().User,
|
IsFromMe: parsedMsgID.Sender.User == client.Store.GetLID().User || parsedMsgID.Sender.User == client.Store.GetJID().User,
|
||||||
IsGroup: chat.Server == types.GroupServer,
|
IsGroup: parsedMsgID.Chat.Server == types.GroupServer,
|
||||||
},
|
},
|
||||||
ID: parsedMsgID.ID,
|
ID: parsedMsgID.ID,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.mau.fi/util/exfmt"
|
|
||||||
"go.mau.fi/util/exmime"
|
"go.mau.fi/util/exmime"
|
||||||
"go.mau.fi/util/exslices"
|
"go.mau.fi/util/exslices"
|
||||||
"go.mau.fi/whatsmeow"
|
"go.mau.fi/whatsmeow"
|
||||||
|
|
@ -130,10 +129,10 @@ func getMediaIDVersion(msg MediaMessage) []byte {
|
||||||
func (mc *MessageConverter) convertAlbumMessage(ctx context.Context, msg *waE2E.AlbumMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) {
|
func (mc *MessageConverter) convertAlbumMessage(ctx context.Context, msg *waE2E.AlbumMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) {
|
||||||
parts := make([]string, 0, 2)
|
parts := make([]string, 0, 2)
|
||||||
if msg.GetExpectedImageCount() > 0 {
|
if msg.GetExpectedImageCount() > 0 {
|
||||||
parts = append(parts, exfmt.Pluralizable("image")(int(msg.GetExpectedImageCount())))
|
parts = append(parts, fmt.Sprintf("%d images", msg.GetExpectedImageCount()))
|
||||||
}
|
}
|
||||||
if msg.GetExpectedVideoCount() > 0 {
|
if msg.GetExpectedVideoCount() > 0 {
|
||||||
parts = append(parts, exfmt.Pluralizable("video")(int(msg.GetExpectedVideoCount())))
|
parts = append(parts, fmt.Sprintf("%d videos", msg.GetExpectedVideoCount()))
|
||||||
}
|
}
|
||||||
var partDesc string
|
var partDesc string
|
||||||
if len(parts) > 0 {
|
if len(parts) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ var failedPollUpdatePart = &bridgev2.ConvertedMessagePart{
|
||||||
DontBridge: true,
|
DontBridge: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MessageConverter) convertPollUpdateMessage(ctx context.Context, info *types.MessageInfo, origSource *types.MessageSource, msg *waE2E.PollUpdateMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) {
|
func (mc *MessageConverter) convertPollUpdateMessage(ctx context.Context, info *types.MessageInfo, msg *waE2E.PollUpdateMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) {
|
||||||
log := zerolog.Ctx(ctx)
|
log := zerolog.Ctx(ctx)
|
||||||
pollMessageID := KeyToMessageID(ctx, getClient(ctx), info.Chat, info.Sender, msg.PollCreationMessageKey)
|
pollMessageID := KeyToMessageID(ctx, getClient(ctx), info.Chat, info.Sender, msg.PollCreationMessageKey)
|
||||||
pollMessage, err := mc.Bridge.DB.Message.GetPartByID(ctx, getPortal(ctx).Receiver, pollMessageID, "")
|
pollMessage, err := mc.Bridge.DB.Message.GetPartByID(ctx, getPortal(ctx).Receiver, pollMessageID, "")
|
||||||
|
|
@ -181,12 +181,8 @@ func (mc *MessageConverter) convertPollUpdateMessage(ctx context.Context, info *
|
||||||
log.Warn().Str("target_message_id", string(pollMessageID)).Msg("Poll update target message not found")
|
log.Warn().Str("target_message_id", string(pollMessageID)).Msg("Poll update target message not found")
|
||||||
return failedPollUpdatePart, nil
|
return failedPollUpdatePart, nil
|
||||||
}
|
}
|
||||||
infoForDecrypt := *info
|
|
||||||
if origSource != nil {
|
|
||||||
infoForDecrypt.MessageSource = *origSource
|
|
||||||
}
|
|
||||||
vote, err := getClient(ctx).DecryptPollVote(ctx, &events.Message{
|
vote, err := getClient(ctx).DecryptPollVote(ctx, &events.Message{
|
||||||
Info: infoForDecrypt,
|
Info: *info,
|
||||||
Message: &waE2E.Message{PollUpdateMessage: msg},
|
Message: &waE2E.Message{PollUpdateMessage: msg},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ type UserLoginMetadata struct {
|
||||||
AppStateRecoveryAttempted map[appstate.WAPatchName]time.Time `json:"app_state_recovery_attempted,omitempty"`
|
AppStateRecoveryAttempted map[appstate.WAPatchName]time.Time `json:"app_state_recovery_attempted,omitempty"`
|
||||||
|
|
||||||
HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"`
|
HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"`
|
||||||
ReachoutTimelockUntil time.Time `json:"reachout_timelock_until,omitempty"`
|
|
||||||
|
|
||||||
MData json.RawMessage `json:"mdata,omitempty"`
|
MData json.RawMessage `json:"mdata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue