Amarula.Contacts (amarula v0.4.4)
View SourceContact discovery via USync. The consumer-facing half of Baileys' onWhatsApp
/ fetchStatus.
Both functions build a single USync iq (see Amarula.Protocol.USync), send it
through the connection's generic IQ primitive (Amarula.Connection.query_iq/3),
and turn the reply into friendly maps carrying Amarula.Address values — never
raw jid strings, matching Amarula.Msg/Amarula.Group.
Call them on this module: on_whatsapp/2, fetch_status/2, resolve_lid/2.
Summary
Types
One resolve_lid/2 result: the contact's LID and PN addresses.
One on_whatsapp/2 result: the resolved address and whether it's on WhatsApp.
One fetch_status/2 result: the address, its status/bio text, and when it was set.
Functions
Fetch the status/bio text of the given users.
Look up a contact's LID from their PN in the local mapping store — the
inverse of pn_for_lid/2. A cheap read; nil when unmapped.
Check which of the given phone numbers are on WhatsApp.
Look up a contact's PN from their LID in the local mapping store — a
cheap read, no server query. Returns an Amarula.Address (:pn) or nil when
the mapping isn't known yet.
Resolve each phone number to its privacy LID (<n>@lid) and persist the
LID↔PN mapping, so the LID/PN mapping (and the Signal addressing the send
pipeline uses) resolve that contact afterwards.
Types
@type conn() :: GenServer.server()
@type lid_pair() :: %{lid: Amarula.Address.t(), pn: Amarula.Address.t()}
One resolve_lid/2 result: the contact's LID and PN addresses.
@type presence() :: %{address: Amarula.Address.t() | nil, exists: boolean()}
One on_whatsapp/2 result: the resolved address and whether it's on WhatsApp.
@type status() :: %{ address: Amarula.Address.t() | nil, status: String.t() | nil, set_at: DateTime.t() | nil }
One fetch_status/2 result: the address, its status/bio text, and when it was set.
Functions
@spec fetch_status( conn(), [String.t() | Amarula.Address.t()] | String.t() | Amarula.Address.t() ) :: {:ok, [status()]} | {:error, term()}
Fetch the status/bio text of the given users.
jids are wire jid strings or Amarula.Address values. Returns one result per
user with the status text (nil when not visible to you, "" when explicitly
empty) and the time it was set.
@spec lid_for_pn(conn(), String.t() | Amarula.Address.t()) :: Amarula.Address.t() | nil
Look up a contact's LID from their PN in the local mapping store — the
inverse of pn_for_lid/2. A cheap read; nil when unmapped.
Check which of the given phone numbers are on WhatsApp.
phones are bare numbers or +-prefixed (e.g. "15551234567"); each is sent
as a USync contact lookup. Returns one result per number with the resolved
Amarula.Address and an exists flag.
Amarula.Contacts.on_whatsapp(conn, ["15551234567"])
#=> {:ok, [%{address: %Amarula.Address{...}, exists: true}]}
@spec pn_for_lid(conn(), String.t() | Amarula.Address.t()) :: Amarula.Address.t() | nil
Look up a contact's PN from their LID in the local mapping store — a
cheap read, no server query. Returns an Amarula.Address (:pn) or nil when
the mapping isn't known yet.
Amarula auto-populates this store from group metadata, the send pipeline, and
resolve_lid/2. So after a :messages_upsert from a group member you can map
their LID back to a PN without hitting WhatsApp — call resolve_lid/2 first if
the mapping is missing.
Amarula.Contacts.pn_for_lid(conn, "12345@lid")
#=> %Amarula.Address{kind: :pn, ...} | nilReplying to a LID-addressed message
You do not need the PN to reply — msg.channel (a @lid address) is a
valid send target; the send pipeline resolves the LID on the wire. Use this
lookup only when your logic needs to identify the sender by phone number
(Baileys #2205). For an inbound DM whose mapping isn't cached yet, run
resolve_lid/2 once to populate it.
Resolve each phone number to its privacy LID (<n>@lid) and persist the
LID↔PN mapping, so the LID/PN mapping (and the Signal addressing the send
pipeline uses) resolve that contact afterwards.
on_whatsapp/2 returns only the PN, so it can't establish a mapping; this runs
a :lid+:contact USync (the only query that returns the pairing) and feeds the
result into the same mapping store Amarula auto-populates from group metadata and
the send pipeline. Returns one entry per number that resolved to a LID; numbers
not on WhatsApp (no LID in the reply) are omitted.
Amarula.Contacts.resolve_lid(conn, ["15551234567"])
#=> {:ok, [%{lid: %Amarula.Address{kind: :lid, ...},
# pn: %Amarula.Address{kind: :pn, ...}}]}