Amarula.Plugins.MessageStore (amarula v0.1.0)

View Source

Example/reference plugin: a persistent message store.

Amarula is a library — it does not keep long-term message history itself. This optional plugin shows the intended pattern for a consumer-side store:

  • subscribe to the socket's {:whatsapp, :messages_upsert, ..} events and persist incoming messages,
  • remember our own outgoing messages,
  • expose get_message/2, which the library calls (via the :get_message config callback) to re-encrypt + resend a message on a type="retry" receipt when its built-in recent-message cache has already evicted it.

It's a GenServer backed by DETS so history survives restarts. A real consumer would likely use its own database; this is the minimal working reference.

Wiring

{:ok, store} = Amarula.Plugins.MessageStore.start_link(dir: "./amarula_store")

config =
  base_config
  |> Map.put(:get_message, Amarula.Plugins.MessageStore.get_message_fun(store))

# Make the store the socket's parent_pid so it sees the events directly,
# or forward `{:whatsapp, :messages_upsert, ..}` to it from your own process.
{:ok, socket} = Amarula.new(config) |> Amarula.connect(parent_pid: store)

Summary

Functions

Returns a specification to start this module under a supervisor.

Fetch a stored message by id: {recipient_jid, %Proto.Message{}} or nil.

A fn msg_id -> {recipient_jid, message} | nil closure to pass as the socket's :get_message config callback.

Record an outgoing message we sent (so a retry can re-fetch it). key is a map with at least :remote_jid and :id.

Start the store. :dir sets where the DETS file lives (default ./amarula_store).

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

get_message(store, msg_id)

@spec get_message(GenServer.server(), String.t()) :: {String.t(), struct()} | nil

Fetch a stored message by id: {recipient_jid, %Proto.Message{}} or nil.

get_message_fun(store)

@spec get_message_fun(GenServer.server()) :: (String.t() ->
                                          {String.t(), struct()} | nil)

A fn msg_id -> {recipient_jid, message} | nil closure to pass as the socket's :get_message config callback.

put(store, map, message)

@spec put(GenServer.server(), %{remote_jid: String.t(), id: String.t()}, struct()) ::
  :ok

Record an outgoing message we sent (so a retry can re-fetch it). key is a map with at least :remote_jid and :id.

start_link(opts \\ [])

Start the store. :dir sets where the DETS file lives (default ./amarula_store).