Why don't you ask channel instead, just spawn synchronous task, join task to group to which you expect to receive result, then push message to channel and then return result back to controller as task completion result.
Let say this is controller action
defmodule SomeController do
#...
def some_action(conn, params) do
task = Task.async(fn ->
MyApp.Endpoint.subscribe("topic:123:presence_list")
MyApp.Endpoint.broadcast("topic:123", :presence_list, %{})
receive do
{:presence_list, list} ->
{:ok, list}
after
5000 ->
{:error, timeout}
end
end)
{:ok, list} = Task.await(task)
# do something with a list
end
end
Then in channel just handle broadcast and return Presence.list(socket)
This should be cleaner way instead of deigning into presence internals that may change in future.
Second option is to create your own tracker and plug it into app then ask your tracker to provide you a list of present users for specific channel. You can find some details how to implement tracker behaviour here