Amarula.Address (amarula v0.1.0)

View Source

A WhatsApp address — the consumer-facing way to name who/what a message is for or from. The boundary abstraction over the wire JID string.

Three kinds, distinguished by :kind:

  • :pn — a phone-number identity (<number>@s.whatsapp.net).
  • :lid — a privacy "Linked ID" (<id>@lid). WhatsApp's wire-preferred identity; the same person has both a PN and a LID.
  • :group — a group chat (<id>@g.us). A container of participants, not a person; its members are fetched separately (group metadata), not stored here.
  • :none — the empty address (empty/0): "no identity". A total value for "we don't have one yet" (e.g. Amarula.own_address/1 before login), so callers never have to nil-guard. It names nothing: every is_*? is false, it is never same_account? with anything, and rendering it to a wire jid is only allowed via the bang (to_jid!/1 raises; to_jid/1 returns {:error, :no_jid}).

:device is the device number (nil = account-level / primary). An address with device: nil names the whole account; with a device it names one client.

Parsed-only

An Address is a pure value — only what's deterministic from the string. It does NOT carry resolved data (a PN's LID, a group's participants, device lists): those need connection state, are lazy, and can change, so they stay internal. Don't expect Address.pn(...) to "know" its LID.

Boundary

parse/1 (wire string → Address) and to_jid/1 (Address → wire string) are the border crossing, delegating to Amarula.Protocol.Binary.JID. Above the border (public API, events) everything speaks Address; the wire/protocol layer speaks JID strings. The public API also accepts a raw string and parses it, so Amarula.send_text(conn, "5511...@s.whatsapp.net", ...) and Amarula.send_text(conn, Address.pn("5511..."), ...) both work.

Summary

Functions

The empty address — "no identity". A total stand-in (see the :none kind).

Whether this is the empty address (empty/0, kind :none).

A group address from a bare id or full @g.us jid string.

A LID address from a bare id or full jid string.

The account-level address (device stripped).

Parse a wire jid into an Address (via JID.decode/1). Accepts a string or an already-parsed Address (passed through), so it's safe to call at the API boundary on either. Returns nil for an unparseable/unknown-server string. Use parse!/1 when a bad jid should raise instead.

Like parse/1 but raises ArgumentError on an unparseable jid (never nil).

A PN address from a bare number or full jid string.

Whether two addresses name the same account (same user+kind, ignoring device). The empty address (:none) names nothing, so it is never the same account as anything — including another empty.

Render an Address to its wire jid string (via JID.encode/1). Total: the empty address (:none) has no wire form and returns {:error, :no_jid}. Use to_jid!/1 when you have a real address and want the bare string.

Like to_jid/1 but returns the bare string, raising on the empty address.

Coerce a string-or-Address to a wire jid string (boundary → wire). Total; mirrors to_jid/1 (a binary passes through as {:ok, binary}). Use to_wire!/1 for the bare string.

Like to_wire/1 but returns the bare string, raising on the empty address.

Types

kind()

@type kind() :: :pn | :lid | :group | :none

t()

@type t() :: %Amarula.Address{
  device: non_neg_integer() | nil,
  kind: kind(),
  user: String.t()
}

Functions

empty()

@spec empty() :: t()

The empty address — "no identity". A total stand-in (see the :none kind).

empty?(arg1)

@spec empty?(t()) :: boolean()

Whether this is the empty address (empty/0, kind :none).

group(id)

@spec group(String.t()) :: t()

A group address from a bare id or full @g.us jid string.

group?(arg1)

@spec group?(t()) :: boolean()

lid(user)

@spec lid(String.t()) :: t()

A LID address from a bare id or full jid string.

lid?(arg1)

@spec lid?(t()) :: boolean()

normalize(a)

@spec normalize(t()) :: t()

The account-level address (device stripped).

parse(address)

@spec parse(String.t() | t()) :: t() | nil

Parse a wire jid into an Address (via JID.decode/1). Accepts a string or an already-parsed Address (passed through), so it's safe to call at the API boundary on either. Returns nil for an unparseable/unknown-server string. Use parse!/1 when a bad jid should raise instead.

parse!(jid)

@spec parse!(String.t() | t()) :: t()

Like parse/1 but raises ArgumentError on an unparseable jid (never nil).

pn(user)

@spec pn(String.t()) :: t()

A PN address from a bare number or full jid string.

pn?(arg1)

@spec pn?(t()) :: boolean()

same_account?(arg1, arg2)

@spec same_account?(t(), t()) :: boolean()

Whether two addresses name the same account (same user+kind, ignoring device). The empty address (:none) names nothing, so it is never the same account as anything — including another empty.

to_jid(address)

@spec to_jid(t()) :: {:ok, String.t()} | {:error, :no_jid}

Render an Address to its wire jid string (via JID.encode/1). Total: the empty address (:none) has no wire form and returns {:error, :no_jid}. Use to_jid!/1 when you have a real address and want the bare string.

to_jid!(addr)

@spec to_jid!(t()) :: String.t()

Like to_jid/1 but returns the bare string, raising on the empty address.

to_wire(a)

@spec to_wire(String.t() | t()) :: {:ok, String.t()} | {:error, :no_jid}

Coerce a string-or-Address to a wire jid string (boundary → wire). Total; mirrors to_jid/1 (a binary passes through as {:ok, binary}). Use to_wire!/1 for the bare string.

to_wire!(a)

@spec to_wire!(String.t() | t()) :: String.t()

Like to_wire/1 but returns the bare string, raising on the empty address.