Gamend.Chat (gamend_sdk v1.0.1114)

Copy Markdown View Source

Context for chat messaging across lobbies, groups, and friend DMs.

Chat types

  • "lobby" — messages within a lobby. chat_ref_id is the lobby id.
  • "group" — messages within a group. chat_ref_id is the group id.
  • "friend" — direct messages between two friends. chat_ref_id is the other user's id (each user stores the other user's id so queries work symmetrically).

PubSub topics

  • "chat:lobby:<id>" — lobby chat events
  • "chat:group:<id>" — group chat events
  • "chat:friend:<low>:<high>" — friend DM events (sorted pair of user ids)

Hooks

  • before_chat_message/2 — pipeline hook (user, attrs){:ok, attrs} | {:error, reason}

  • after_chat_message/1 — fire-and-forget after a message is persisted

Note: This is an SDK stub. Calling these functions will raise an error. The actual implementation runs on the Gamend.

Summary

Functions

Admin: delete a single message by id.

Returns :ok when user can access the chat conversation.

Delete all chat data (messages + read cursors) for a given conversation.

Delete all friend DM messages and read cursors between two users.

Count all messages matching filters (admin).

Count total friend DM messages between two users.

Count total messages in a chat conversation.

Count messages grouped by chat_type.

Count mutes matching filters.

Count reports matching filters.

Count distinct users who have sent at least one chat message.

Count unread messages for a user in a specific chat conversation.

Count unread friend DMs between two users for a specific user.

Count unread friend DMs for a user across all friends.

Count unread messages for a user in multiple group chats.

Delete all messages for a given chat conversation.

Delete a chat message owned by the given user.

Delete all read cursors for a given chat conversation.

Every blocklist entry matching content, as [{word, severity, match_mode}].

Get a single message by id.

Get the read cursor for a user in a chat conversation.

List all messages (admin). Supports filters: sender_id, chat_type, chat_ref_id, content.

List friend DM messages between two users.

List messages for a chat conversation.

List mutes. Filters: :user_id, :scope, :scope_ref_id, :active.

List reports. Filters: :status, :reported_user_id, :reporter_id.

Mark a chat conversation as read up to a given message id.

Mute user_id in scope ("global", "lobby", "group" or "party").

Whether user_id is currently muted for the given chat.

File a report about a message on behalf of reporter_id.

Resolve a report: set its status, with an optional note and resolver.

Claim a report for review, moving it from open to reviewing.

Send a chat message.

Subscribe to chat events for a friend DM conversation.

Subscribe to chat events for a group.

Subscribe to chat events for a lobby.

Subscribe to chat events for a party.

Lift a mute. Returns {:ok, count} — 0 when the user was not muted.

Unsubscribe from friend DM chat events.

Unsubscribe from group chat events.

Unsubscribe from lobby chat events.

Unsubscribe from party chat events.

Update a chat message owned by the given user.

Functions

admin_delete_message(id)

@spec admin_delete_message(Ecto.UUID.t()) ::
  {:ok, Gamend.Chat.Message.t()} | {:error, term()}

Admin: delete a single message by id.

authorize_access(user_id, chat_type, chat_ref_id)

@spec authorize_access(Ecto.UUID.t(), String.t(), Ecto.UUID.t()) ::
  :ok | {:error, atom()}

Returns :ok when user can access the chat conversation.

cleanup_chat(chat_type, chat_ref_id)

@spec cleanup_chat(String.t(), Ecto.UUID.t()) :: :ok

Delete all chat data (messages + read cursors) for a given conversation.

cleanup_friend_chat(user_a_id, user_b_id)

@spec cleanup_friend_chat(Ecto.UUID.t(), Ecto.UUID.t()) :: :ok

Delete all friend DM messages and read cursors between two users.

Friend messages are stored bidirectionally (each user's messages use the other's id as chat_ref_id), so both directions must be cleaned up.

count_all_messages(filters)

@spec count_all_messages(map()) :: non_neg_integer()

Count all messages matching filters (admin).

count_friend_messages(user_a_id, user_b_id)

@spec count_friend_messages(String.t(), String.t()) :: non_neg_integer()

Count total friend DM messages between two users.

count_messages(chat_type, chat_ref_id)

@spec count_messages(String.t(), Ecto.UUID.t()) :: non_neg_integer()

Count total messages in a chat conversation.

count_messages_by_type()

@spec count_messages_by_type() :: map()

Count messages grouped by chat_type.

Returns a map like %{"lobby" => 10, "group" => 5, "friend" => 3}.

count_mutes(filters)

@spec count_mutes(map()) :: non_neg_integer()

Count mutes matching filters.

count_reports(filters)

@spec count_reports(map()) :: non_neg_integer()

Count reports matching filters.

count_unique_senders()

@spec count_unique_senders() :: non_neg_integer()

Count distinct users who have sent at least one chat message.

count_unread(user_id, chat_type, chat_ref_id)

@spec count_unread(Ecto.UUID.t(), String.t(), Ecto.UUID.t()) :: non_neg_integer()

Count unread messages for a user in a specific chat conversation.

Returns 0 if the user has read all messages or has no cursor (all are unread in which case count_messages/2 should be used instead).

count_unread_friend(user_id, friend_id)

@spec count_unread_friend(Ecto.UUID.t(), Ecto.UUID.t()) :: non_neg_integer()

Count unread friend DMs between two users for a specific user.

count_unread_friends_batch(user_id, friend_ids)

@spec count_unread_friends_batch(Ecto.UUID.t(), [Ecto.UUID.t()]) :: %{
  required(Ecto.UUID.t()) => non_neg_integer()
}

Count unread friend DMs for a user across all friends.

Returns a map of %{friend_id => unread_count} for friends that have at least one unread message.

count_unread_groups_batch(user_id, group_ids)

@spec count_unread_groups_batch(Ecto.UUID.t(), [Ecto.UUID.t()]) :: %{
  required(Ecto.UUID.t()) => non_neg_integer()
}

Count unread messages for a user in multiple group chats.

Returns a map of %{group_id => unread_count}.

delete_messages(chat_type, chat_ref_id)

@spec delete_messages(String.t(), Ecto.UUID.t()) :: {non_neg_integer(), nil}

Delete all messages for a given chat conversation.

delete_own_message(user_id, message_id)

@spec delete_own_message(Ecto.UUID.t(), Ecto.UUID.t()) ::
  {:ok, Gamend.Chat.Message.t()} | {:error, term()}

Delete a chat message owned by the given user.

Returns {:error, :not_found} if the message does not exist or {:error, :forbidden} if the caller is not the sender.

delete_read_cursors(chat_type, chat_ref_id)

@spec delete_read_cursors(String.t(), Ecto.UUID.t()) :: {non_neg_integer(), nil}

Delete all read cursors for a given chat conversation.

filter_hits(content)

@spec filter_hits(String.t()) :: [{String.t(), String.t(), String.t()}]

Every blocklist entry matching content, as [{word, severity, match_mode}].

get_message(id)

@spec get_message(Ecto.UUID.t()) :: Gamend.Chat.Message.t() | nil

Get a single message by id.

get_read_cursor(user_id, chat_type, chat_ref_id)

@spec get_read_cursor(Ecto.UUID.t(), String.t(), Ecto.UUID.t()) ::
  Gamend.Chat.ReadCursor.t() | nil

Get the read cursor for a user in a chat conversation.

Returns nil if the user has never opened this conversation.

list_all_messages(filters, opts)

@spec list_all_messages(
  map(),
  keyword()
) :: [Gamend.Chat.Message.t()]

List all messages (admin). Supports filters: sender_id, chat_type, chat_ref_id, content.

list_friend_messages(user_a_id, user_b_id, opts)

@spec list_friend_messages(String.t(), String.t(), keyword()) :: [
  Gamend.Chat.Message.t()
]

List friend DM messages between two users.

Convenience wrapper that queries messages in both directions.

## Options

* `:page`  page number (default 1)
* `:page_size`  items per page (default 25)

list_messages(chat_type, chat_ref_id, opts)

@spec list_messages(String.t(), Ecto.UUID.t(), keyword()) :: [Gamend.Chat.Message.t()]

List messages for a chat conversation.

## Options

* `:page`  page number (default 1)
* `:page_size`  items per page (default 25)

Returns a list of %Message{} structs ordered by inserted_at descending (newest first).

list_mutes(filters, opts)

@spec list_mutes(
  map(),
  keyword()
) :: [Gamend.Chat.Mute.t()]

List mutes. Filters: :user_id, :scope, :scope_ref_id, :active.

list_reports(filters, opts)

@spec list_reports(
  map(),
  keyword()
) :: [Gamend.Chat.Report.t()]

List reports. Filters: :status, :reported_user_id, :reporter_id.

mark_read(user_id, chat_type, chat_ref_id, message_id)

@spec mark_read(Ecto.UUID.t(), String.t(), Ecto.UUID.t(), Ecto.UUID.t()) ::
  {:ok, Gamend.Chat.ReadCursor.t()} | {:error, term()}

Mark a chat conversation as read up to a given message id.

Uses an upsert to create or update the read cursor.

mute_user(user_id, scope, scope_ref_id, attrs)

@spec mute_user(Ecto.UUID.t(), String.t(), Ecto.UUID.t() | nil, map()) ::
  {:ok, Gamend.Chat.Mute.t()} | {:error, term()}

Mute user_id in scope ("global", "lobby", "group" or "party").

scope_ref_id is the room id, or nil for a global mute. attrs may carry expires_at (nil means permanent), reason and muted_by.

muted?(user_id, chat_type, chat_ref_id)

@spec muted?(Ecto.UUID.t(), String.t(), Ecto.UUID.t() | nil) :: boolean()

Whether user_id is currently muted for the given chat.

report_message(reporter_id, message_id, reason)

@spec report_message(Ecto.UUID.t(), Ecto.UUID.t(), String.t() | nil) ::
  {:ok, Gamend.Chat.Report.t()} | {:error, term()}

File a report about a message on behalf of reporter_id.

resolve_report(report, status, attrs)

@spec resolve_report(Gamend.Chat.Report.t() | Ecto.UUID.t(), String.t(), map()) ::
  {:ok, Gamend.Chat.Report.t()} | {:error, term()}

Resolve a report: set its status, with an optional note and resolver.

review_report(report)

@spec review_report(Gamend.Chat.Report.t() | Ecto.UUID.t()) ::
  {:ok, Gamend.Chat.Report.t()} | {:error, term()}

Claim a report for review, moving it from open to reviewing.

send_message(map, attrs)

@spec send_message(map(), map()) :: {:ok, Gamend.Chat.Message.t()} | {:error, term()}

Send a chat message.

## Parameters

* `scope`  `%{user: %User{}}` (current_scope)
* `attrs`  map with `"chat_type"`, `"chat_ref_id"`, `"content"`, optional `"metadata"`

## Returns

* `{:ok, %Message{}}` on success
* `{:error, reason}` on failure

Moderation runs before the before_chat_message hook: a muted sender is rejected with {:error, :muted} and a blocked word with {:error, :blocked_content}, so a plugin never sees a message core already refused. The hook can then modify attrs or reject the message itself. The after_chat_message hook fires asynchronously after the message is persisted.

subscribe_friend_chat(user_a_id, user_b_id)

@spec subscribe_friend_chat(Ecto.UUID.t(), Ecto.UUID.t()) :: :ok | {:error, term()}

Subscribe to chat events for a friend DM conversation.

subscribe_group_chat(group_id)

@spec subscribe_group_chat(Ecto.UUID.t()) :: :ok | {:error, term()}

Subscribe to chat events for a group.

subscribe_lobby_chat(lobby_id)

@spec subscribe_lobby_chat(Ecto.UUID.t()) :: :ok | {:error, term()}

Subscribe to chat events for a lobby.

subscribe_party_chat(party_id)

@spec subscribe_party_chat(Ecto.UUID.t()) :: :ok | {:error, term()}

Subscribe to chat events for a party.

unmute_user(user_id, scope, scope_ref_id)

@spec unmute_user(Ecto.UUID.t(), String.t(), Ecto.UUID.t() | nil) ::
  {:ok, non_neg_integer()}

Lift a mute. Returns {:ok, count} — 0 when the user was not muted.

unsubscribe_friend_chat(user_a_id, user_b_id)

@spec unsubscribe_friend_chat(Ecto.UUID.t(), Ecto.UUID.t()) :: :ok

Unsubscribe from friend DM chat events.

unsubscribe_group_chat(group_id)

@spec unsubscribe_group_chat(Ecto.UUID.t()) :: :ok

Unsubscribe from group chat events.

unsubscribe_lobby_chat(lobby_id)

@spec unsubscribe_lobby_chat(Ecto.UUID.t()) :: :ok

Unsubscribe from lobby chat events.

unsubscribe_party_chat(party_id)

@spec unsubscribe_party_chat(Ecto.UUID.t()) :: :ok

Unsubscribe from party chat events.

update_message(user_id, message_id, attrs)

@spec update_message(Ecto.UUID.t(), Ecto.UUID.t(), map()) ::
  {:ok, Gamend.Chat.Message.t()} | {:error, term()}

Update a chat message owned by the given user.

Only the content and metadata fields can be changed. Returns {:error, :not_found} if the message does not exist or {:error, :forbidden} if the caller is not the sender.