From 83ee19ea02e1b358f18380cf96d158bb1dc4da6c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Feb 2024 16:16:32 +0200 Subject: [PATCH] Add support for getting facebook user device list --- receipt.go | 2 +- send.go | 2 +- sendfb.go | 2 +- user.go | 89 ++++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/receipt.go b/receipt.go index a6fdf0e2..598e917d 100644 --- a/receipt.go +++ b/receipt.go @@ -156,7 +156,7 @@ func (cli *Client) MarkRead(ids []types.MessageID, timestamp time.Time, chat, se // TODO change played to played-self? } } - if !sender.IsEmpty() && chat.Server != types.DefaultUserServer { + if !sender.IsEmpty() && chat.Server != types.DefaultUserServer && chat.Server != types.MessengerServer { node.Attrs["participant"] = sender.ToNonAD() } if len(ids) > 1 { diff --git a/send.go b/send.go index 38b4b1a8..35cbed2b 100644 --- a/send.go +++ b/send.go @@ -282,7 +282,7 @@ func (cli *Client) BuildMessageKey(chat, sender types.JID, id types.MessageID) * } if !sender.IsEmpty() && sender.User != cli.getOwnID().User { key.FromMe = proto.Bool(false) - if chat.Server != types.DefaultUserServer { + if chat.Server != types.DefaultUserServer && chat.Server != types.MessengerServer { key.Participant = proto.String(sender.ToNonAD().String()) } } diff --git a/sendfb.go b/sendfb.go index 25e73dc4..56a614d7 100644 --- a/sendfb.go +++ b/sendfb.go @@ -117,7 +117,7 @@ func (cli *Client) SendFBMessage( switch to.Server { case types.GroupServer: phash, data, err = cli.sendGroupV3(ctx, to, ownID, req.ID, messageApp, msgAttrs, frankingTag, &resp.DebugTimings) - case types.DefaultUserServer: + case types.DefaultUserServer, types.MessengerServer: if req.Peer { err = fmt.Errorf("peer messages to fb are not yet supported") //data, err = cli.sendPeerMessage(to, req.ID, message, &resp.DebugTimings) diff --git a/user.go b/user.go index d6408fb6..e54e4b01 100644 --- a/user.go +++ b/user.go @@ -238,34 +238,50 @@ func (cli *Client) GetUserDevicesContext(ctx context.Context, jids []types.JID) cli.userDevicesCacheLock.Lock() defer cli.userDevicesCacheLock.Unlock() - var devices, jidsToSync []types.JID + var devices, jidsToSync, fbJIDsToSync []types.JID for _, jid := range jids { cached, ok := cli.userDevicesCache[jid] if ok && len(cached) > 0 { devices = append(devices, cached...) + } else if jid.Server == types.MessengerServer { + fbJIDsToSync = append(fbJIDsToSync, jid) } else { jidsToSync = append(jidsToSync, jid) } } - if len(jidsToSync) == 0 { - return devices, nil - } + if len(jidsToSync) > 0 { + list, err := cli.usync(ctx, jidsToSync, "query", "message", []waBinary.Node{ + {Tag: "devices", Attrs: waBinary.Attrs{"version": "2"}}, + }) + if err != nil { + return nil, err + } - list, err := cli.usync(ctx, jidsToSync, "query", "message", []waBinary.Node{ - {Tag: "devices", Attrs: waBinary.Attrs{"version": "2"}}, - }) - if err != nil { - return nil, err + for _, user := range list.GetChildren() { + jid, jidOK := user.Attrs["jid"].(types.JID) + if user.Tag != "user" || !jidOK { + continue + } + userDevices := parseDeviceList(jid.User, user.GetChildByTag("devices")) + cli.userDevicesCache[jid] = userDevices + devices = append(devices, userDevices...) + } } - for _, user := range list.GetChildren() { - jid, jidOK := user.Attrs["jid"].(types.JID) - if user.Tag != "user" || !jidOK { - continue + if len(fbJIDsToSync) > 0 { + list, err := cli.getFBIDDevices(ctx, fbJIDsToSync) + if err != nil { + return nil, err + } + for _, user := range list.GetChildren() { + jid, jidOK := user.Attrs["jid"].(types.JID) + if user.Tag != "user" || !jidOK { + continue + } + userDevices := parseFBDeviceList(jid, user.GetChildByTag("devices")) + cli.userDevicesCache[jid] = userDevices + devices = append(devices, userDevices...) } - userDevices := parseDeviceList(jid.User, user.GetChildByTag("devices")) - cli.userDevicesCache[jid] = userDevices - devices = append(devices, userDevices...) } return devices, nil @@ -473,6 +489,47 @@ func parseDeviceList(user string, deviceNode waBinary.Node) []types.JID { return devices } +func parseFBDeviceList(user types.JID, deviceList waBinary.Node) []types.JID { + children := deviceList.GetChildren() + devices := make([]types.JID, 0, len(children)) + for _, device := range children { + deviceID, ok := device.AttrGetter().GetInt64("id", true) + if device.Tag != "device" || !ok { + continue + } + user.Device = uint16(deviceID) + devices = append(devices, user) + // TODO take identities here too? + } + // TODO do something with the icdc blob? + return devices +} + +func (cli *Client) getFBIDDevices(ctx context.Context, jids []types.JID) (*waBinary.Node, error) { + users := make([]waBinary.Node, len(jids)) + for i, jid := range jids { + users[i].Tag = "user" + users[i].Attrs = waBinary.Attrs{"jid": jid} + } + resp, err := cli.sendIQ(infoQuery{ + Context: ctx, + Namespace: "fbid:devices", + Type: iqGet, + To: types.ServerJID, + Content: []waBinary.Node{{ + Tag: "users", + Content: users, + }}, + }) + if err != nil { + return nil, fmt.Errorf("failed to send usync query: %w", err) + } else if list, ok := resp.GetOptionalChildByTag("users"); !ok { + return nil, &ElementMissingError{Tag: "users", In: "response to fbid devices query"} + } else { + return &list, err + } +} + func (cli *Client) usync(ctx context.Context, jids []types.JID, mode, context string, query []waBinary.Node) (*waBinary.Node, error) { userList := make([]waBinary.Node, len(jids)) for i, jid := range jids {