Skip to content

Commit

Permalink
feature(v2): Cleaner code-style (thanks sumner!), W->M: revoke & edit
Browse files Browse the repository at this point in the history
  • Loading branch information
purpshell committed Aug 19, 2024
1 parent 639b6e6 commit 14f08c7
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 60 deletions.
5 changes: 3 additions & 2 deletions pkg/connector/chatinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import (
var moderatorPL = 50

func (wa *WhatsAppClient) GetChatInfo(_ context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) {
myJID := wa.Client.Store.ID.ToNonAD()
members := &bridgev2.ChatMemberList{
IsFull: true,
Members: []bridgev2.ChatMember{
{
EventSender: wa.makeEventSender(wa.Client.Store.ID),
EventSender: wa.makeEventSender(&myJID),
Membership: event.MembershipJoin,
PowerLevel: &moderatorPL,
},
Expand Down Expand Up @@ -62,7 +63,7 @@ func (wa *WhatsAppClient) contactToUserInfo(jid types.JID, contact types.Contact
}
name := wa.Main.Config.FormatDisplayname(jid, contact)
ui.Name = &name
ui.Identifiers = append(ui.Identifiers, "tel:"+jid.User)
ui.Identifiers = append(ui.Identifiers, "tel:+"+jid.User)
// TODO: implement Profile picture stuff
return ui
}
17 changes: 14 additions & 3 deletions pkg/connector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import (
"maunium.net/go/mautrix-whatsapp/pkg/waid"
)

const (
WANotLoggedIn status.BridgeStateErrorCode = "wa-not-logged-in"
)

type respGetProxy struct {
ProxyURL string `json:"proxy_url"`
}
Expand Down Expand Up @@ -140,8 +144,6 @@ type WhatsAppClient struct {
UserLogin *bridgev2.UserLogin
Client *whatsmeow.Client
Device *store.Device

State status.BridgeState
}

var whatsappCaps = &bridgev2.NetworkRoomCapabilities{
Expand Down Expand Up @@ -227,7 +229,16 @@ func (wa *WhatsAppClient) IsThisUser(_ context.Context, userID networkid.UserID)
}

func (wa *WhatsAppClient) Connect(_ context.Context) error {
return wa.Client.Connect()
if wa.Client != nil {
return wa.Client.Connect()
} else {
state := status.BridgeState{
StateEvent: status.StateBadCredentials,
Error: WANotLoggedIn,
}
wa.UserLogin.BridgeState.Send(state)
return nil
}
}

func (wa *WhatsAppClient) Disconnect() {
Expand Down
18 changes: 6 additions & 12 deletions pkg/connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
"go.mau.fi/whatsmeow/proto/waCompanionReg"
"go.mau.fi/whatsmeow/store"
"go.mau.fi/whatsmeow/store/sqlstore"
"go.mau.fi/whatsmeow/types"
waLog "go.mau.fi/whatsmeow/util/log"
"google.golang.org/protobuf/proto"
"maunium.net/go/mautrix/bridgev2"

"maunium.net/go/mautrix-whatsapp/pkg/msgconv"
"maunium.net/go/mautrix-whatsapp/pkg/waid"
)

type WhatsAppConnector struct {
Expand Down Expand Up @@ -110,13 +110,10 @@ func (wa *WhatsAppConnector) Start(_ context.Context) error {
func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.UserLogin) error {
loginMetadata := login.Metadata.(*UserLoginMetadata)

jid, err := types.ParseJID(string(login.ID))
if err == nil {
jid.Device = loginMetadata.WADeviceID
}
jid := waid.ParseWAUserLoginID(login.ID)
jid.Device = loginMetadata.WADeviceID

device, err := wa.DeviceStore.GetDevice(jid)

if err != nil {
return err
}
Expand All @@ -129,14 +126,11 @@ func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.Us

log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger()

w.MakeNewClient(log)

err = w.Client.Connect()

if err != nil {
login.Log.Err(err).Msg("Error connecting to WhatsApp")
if device != nil {
w.MakeNewClient(log)
}

login.Client = w

return nil
}
76 changes: 75 additions & 1 deletion pkg/connector/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"

"github.com/rs/zerolog"
"go.mau.fi/whatsmeow/types"
"go.mau.fi/whatsmeow/types/events"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database"
"maunium.net/go/mautrix/bridgev2/networkid"

"maunium.net/go/mautrix-whatsapp/pkg/waid"
Expand All @@ -20,13 +22,85 @@ type WAMessageEvent struct {
var (
_ bridgev2.RemoteMessage = (*WAMessageEvent)(nil)
_ bridgev2.RemoteEventThatMayCreatePortal = (*WAMessageEvent)(nil)
_ bridgev2.RemoteReaction = (*WAMessageEvent)(nil)
_ bridgev2.RemoteReactionRemove = (*WAMessageEvent)(nil)
_ bridgev2.RemoteEdit = (*WAMessageEvent)(nil)
_ bridgev2.RemoteMessageRemove = (*WAMessageEvent)(nil)
)

func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, existing []*database.Message) (*bridgev2.ConvertedEdit, error) {
// only change text and captions
if len(existing) > 1 {
zerolog.Ctx(ctx).Warn().Msg("Got edit to message with multiple parts")
}
editedMsg := evt.Message.Message.GetProtocolMessage().GetEditedMessage()

cm := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, editedMsg, nil) // for media messages, it is better not to do this
return &bridgev2.ConvertedEdit{
ModifiedParts: []*bridgev2.ConvertedEditPart{cm.Parts[0].ToEditPart(existing[0])},
}, nil
}

func (evt *WAMessageEvent) GetTargetMessage() networkid.MessageID {
if reactionMsg := evt.Message.Message.GetReactionMessage(); reactionMsg != nil {
key := reactionMsg.GetKey()
senderID := key.GetParticipant()
if senderID == "" {
if key.GetFromMe() == true {
senderID = evt.Info.Sender.ToNonAD().String()
} else {
senderID = evt.wa.Client.Store.ID.ToNonAD().String() // could be false in groups
}
}
senderJID, _ := types.ParseJID(senderID)
return waid.MakeMessageID(evt.Info.Chat, senderJID, *key.ID)
} else if protocolMsg := evt.Message.Message.GetProtocolMessage(); protocolMsg != nil {
key := protocolMsg.GetKey()
senderID := key.GetParticipant()
if senderID == "" {
if key.GetFromMe() == true {
senderID = evt.Info.Sender.ToNonAD().String()
} else {
senderID = evt.wa.Client.Store.ID.ToNonAD().String() // could be false in groups
}
}
senderJID, _ := types.ParseJID(senderID)
return waid.MakeMessageID(evt.Info.Chat, senderJID, *key.ID)
}
return ""
}

func (evt *WAMessageEvent) GetReactionEmoji() (string, networkid.EmojiID) {
if reactionMsg := evt.Message.Message.GetReactionMessage(); reactionMsg != nil {
return reactionMsg.GetText(), ""
} else {
return "", ""
}
}

func (evt *WAMessageEvent) GetRemovedEmojiID() networkid.EmojiID {
return ""
}

func (evt *WAMessageEvent) ShouldCreatePortal() bool {
return true
}

func (evt *WAMessageEvent) GetType() bridgev2.RemoteEventType {
waMsg := evt.Message.Message
if reactionMsg := waMsg.GetReactionMessage(); reactionMsg != nil {
if reactionMsg.GetText() == "" {
return bridgev2.RemoteEventReactionRemove
}
return bridgev2.RemoteEventReaction
} else if protocolMsg := waMsg.GetProtocolMessage(); protocolMsg != nil {
protocolType := protocolMsg.GetType()
if protocolType == 0 { // REVOKE (message deletes)
return bridgev2.RemoteEventMessageRemove
} else if protocolType == 14 { // Message edits
return bridgev2.RemoteEventEdit
}
}
return bridgev2.RemoteEventMessage
}

Expand All @@ -47,5 +121,5 @@ func (evt *WAMessageEvent) GetID() networkid.MessageID {
}

func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
return evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message), nil
return evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message.Message, &evt.Message.Info), nil
}
2 changes: 1 addition & 1 deletion pkg/connector/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ proxy_only_login: false
# {{.PushName}} - nickname set by the WhatsApp user
# {{.BusinessName}} - validated WhatsApp business name
# {{.Phone}} - phone number (international format)
displayname_template: "{{or .BusinessName .PushName .JID}} (WA)"
displayname_template: "{{or .BusinessName .PushName .Phone}} (WA)"

# Should incoming calls send a message to the Matrix room?
call_start_notices: true
Expand Down
2 changes: 0 additions & 2 deletions pkg/connector/handlematrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2
func (wa *WhatsAppClient) PreHandleMatrixReaction(_ context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) {
return bridgev2.MatrixReactionPreResponse{
SenderID: networkid.UserID(wa.UserLogin.ID),
EmojiID: "",
Emoji: variationselector.Remove(msg.Content.RelatesTo.Key),
MaxReactions: 1,
}, nil
Expand Down Expand Up @@ -114,7 +113,6 @@ func (wa *WhatsAppClient) HandleMatrixEdit(ctx context.Context, edit *bridgev2.M
}

portalJID, err := types.ParseJID(string(edit.Portal.ID))

if err != nil {
return err
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/connector/handlewhatsapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
})
case *events.Connected:
log.Debug().Msg("Connected to WhatsApp socket")
wa.State = status.BridgeState{StateEvent: status.StateConnected}
wa.UserLogin.BridgeState.Send(wa.State)
state := status.BridgeState{StateEvent: status.StateConnected}
wa.UserLogin.BridgeState.Send(state)
case *events.Disconnected:
log.Debug().Msg("Disconnected from WhatsApp socket")
wa.State = status.BridgeState{
state := status.BridgeState{
StateEvent: status.StateTransientDisconnect,
Error: WADisconnected,
}
wa.UserLogin.BridgeState.Send(wa.State)
wa.UserLogin.BridgeState.Send(state)
case events.PermanentDisconnect:
switch e := evt.(type) {
case *events.LoggedOut:
Expand Down Expand Up @@ -108,12 +108,12 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
}
}

wa.State = status.BridgeState{
state := status.BridgeState{
StateEvent: status.StateUnknownError,
Error: WAPermanentError,
Message: evt.PermanentDisconnectDescription(),
}
wa.UserLogin.BridgeState.Send(wa.State)
wa.UserLogin.BridgeState.Send(state)
default:
log.Debug().Type("event_type", rawEvt).Msg("Unhandled WhatsApp event")
}
Expand Down
26 changes: 12 additions & 14 deletions pkg/connector/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func makeQRChan(connector *WhatsAppConnector, user *bridgev2.User) (client *what
}

func makeUserLogin(ctx context.Context, user *bridgev2.User, client *whatsmeow.Client) (ul *bridgev2.UserLogin, err error) {
newLoginID := waid.MakeWAUserLoginID(client.Store.ID)
newLoginID := waid.MakeUserLoginID(client.Store.ID)
ul, err = user.NewLogin(ctx, &database.UserLogin{
ID: newLoginID,
RemoteName: client.Store.PushName,
Expand All @@ -98,10 +98,7 @@ var _ bridgev2.LoginProcessDisplayAndWait = (*QRLogin)(nil)

func (qr *QRLogin) Cancel() {
qr.cancelChan()
go func() {
for range qr.QRChan {
}
}()
qr.Client.Disconnect()
}

const (
Expand Down Expand Up @@ -129,7 +126,7 @@ func (qr *QRLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
return nil, fmt.Errorf("invalid QR channel event: %s", resp.Event)
}
case <-ctx.Done():
qr.cancelChan()
qr.Cancel()
return nil, ctx.Err()
}

Expand Down Expand Up @@ -162,22 +159,22 @@ func (qr *QRLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
},
}, nil
} else if resp.Event != "success" {
qr.cancelChan()
qr.Cancel()
return nil, fmt.Errorf("did not pair properly, err: %s", resp.Event)
}
case <-ctx.Done():
qr.cancelChan()
qr.Cancel()
return nil, ctx.Err()
}

defer qr.cancelChan()
defer qr.Cancel()

ul, err := makeUserLogin(ctx, qr.User, qr.Client)
if err != nil {
return nil, fmt.Errorf("failed to create user login: %w", err)
}

err = qr.Main.LoadUserLogin(context.Background(), ul)
err = ul.Client.Connect(ul.Log.WithContext(context.Background()))

if err != nil {
return nil, fmt.Errorf("failed to connect after login: %w", err)
Expand Down Expand Up @@ -208,6 +205,7 @@ var _ bridgev2.LoginProcessUserInput = (*PairingCodeLogin)(nil)
var _ bridgev2.LoginProcessDisplayAndWait = (*PairingCodeLogin)(nil)

func (pc *PairingCodeLogin) Cancel() {
pc.Client.Disconnect()
pc.cancelChan()
go func() {
for range pc.QRChan {
Expand Down Expand Up @@ -278,22 +276,22 @@ func (pc *PairingCodeLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, erro
},
}, nil
} else if resp.Event != "success" {
pc.cancelChan()
pc.Cancel()
return nil, fmt.Errorf("did not pair properly, err: %s", resp.Event)
}
case <-ctx.Done():
pc.cancelChan()
pc.Cancel()
return nil, ctx.Err()
}

defer pc.cancelChan()
defer pc.Cancel()

ul, err := makeUserLogin(ctx, pc.User, pc.Client)
if err != nil {
return nil, fmt.Errorf("failed to create user login: %w", err)
}

err = pc.Main.LoadUserLogin(context.Background(), ul)
err = ul.Client.Connect(ul.Log.WithContext(context.Background()))

if err != nil {
return nil, fmt.Errorf("failed to connect after login: %w", err)
Expand Down
Loading

0 comments on commit 14f08c7

Please sign in to comment.