Skip to content

Commit

Permalink
Add support for getting facebook user device list
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Feb 7, 2024
1 parent af6a315 commit 83ee19e
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 19 deletions.
2 changes: 1 addition & 1 deletion receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion send.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}
Expand Down
2 changes: 1 addition & 1 deletion sendfb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
89 changes: 73 additions & 16 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 83ee19e

Please sign in to comment.