Relay message feature for PhoenixGenApi.
Provides group-based message relaying where a user sends a message to a group and all members (including the sender) receive it.
Group Types
:public
- Anyone can join immediately as
:active. - All members can send and receive messages.
:private
- New members join with
:pendingstatus. - Any existing active member can accept pending members.
- Only
:activemembers can send and receive messages.
:strict_private
- New members join with
:pendingstatus. - Only
:adminmembers can accept pending members. - Admins can
:muteand:unmutemembers. - Muted members can receive but cannot send messages.
Architecture
ETS table (
:phoenix_gen_api_relay_groups) stores group metadata:{group_id, group_type, members_map}wheremembers_mapis%{user_id => %{roles: MapSet, status: atom, joined_at: DateTime}}.Registry (
PhoenixGenApi.RelayRegistry) with:duplicatekeys mapsgroup_idto{user_id, channel_pid}for dispatching messages to channel processes viasend/2.
Summary
Functions
Accepts a pending member into a group.
Creates a new group. The creator becomes the admin with :active status.
Deletes a group from ETS. Registry entries are cleaned up when channel processes terminate.
Returns group info including type and members map.
Handles a relay message request. This is the MFA target configured in
FunConfig for request_type: "relay_msg".
Joins a user to a group.
Removes a user from a group and unregisters them from the Registry.
Mutes a member in a strict_private group. Only admins can mute. Muted members can receive but cannot send messages.
Returns the ETS table name for group metadata storage.
Unmutes a member in a strict_private group. Only admins can unmute.
Types
@type group_type() :: :public | :private | :strict_private
@type member_info() :: %{ roles: MapSet.t(atom()), status: member_status(), joined_at: DateTime.t() }
@type member_status() :: :active | :pending | :muted
Functions
@spec accept_member(String.t(), String.t(), String.t()) :: :ok | {:error, :not_found | :user_not_in_group | :not_admin | :user_not_pending}
Accepts a pending member into a group.
- private: any active member can accept.
- strict_private: only admin can accept.
Returns :ok or {:error, reason}.
@spec create_group(String.t(), group_type(), String.t(), pid()) :: :ok | {:error, :already_exists}
Creates a new group. The creator becomes the admin with :active status.
Returns :ok or {:error, :already_exists}.
@spec delete_group(String.t()) :: :ok | {:error, :not_found}
Deletes a group from ETS. Registry entries are cleaned up when channel processes terminate.
Returns :ok or {:error, :not_found}.
Returns group info including type and members map.
@spec handle_relay( PhoenixGenApi.Structs.Request.t(), PhoenixGenApi.Structs.FunConfig.t() ) :: PhoenixGenApi.Structs.Response.t()
Handles a relay message request. This is the MFA target configured in
FunConfig for request_type: "relay_msg".
Extracts group_id and message from request.args, validates the
sender's membership and permissions, then sends {:relay_message, response}
to all group members' channel pids via the Registry.
Returns a Response with relay status.
@spec join_group(String.t(), String.t(), pid()) :: {:ok, :active | :pending} | {:error, :not_found | :already_member}
Joins a user to a group.
- public: user becomes
:activeimmediately. - private / strict_private: user becomes
:pending, needs acceptance.
Returns {:ok, :active} or {:ok, :pending} depending on group type,
or {:error, reason}.
Removes a user from a group and unregisters them from the Registry.
Returns :ok or {:error, reason}.
@spec mute_member(String.t(), String.t(), String.t()) :: :ok | {:error, :not_found | :not_strict_private | :user_not_in_group | :not_admin | :cannot_mute}
Mutes a member in a strict_private group. Only admins can mute. Muted members can receive but cannot send messages.
Returns :ok or {:error, reason}.
@spec table() :: atom()
Returns the ETS table name for group metadata storage.
@spec unmute_member(String.t(), String.t(), String.t()) :: :ok | {:error, :not_found | :not_strict_private | :user_not_in_group | :not_admin | :not_muted}
Unmutes a member in a strict_private group. Only admins can unmute.
Returns :ok or {:error, reason}.