diff --git a/README.md b/README.md index d0553d9..00d5c62 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Here, implemented and some unimplemented features are listed. - PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId} - GET /_matrix/client/r0/rooms/{roomId}/messages: Except filtering. - GET /_matrix/client/r0/rooms/{roomId}/state +- GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey} - GET /_matrix/client/r0/directory/list/room/{roomId} - PUT /_matrix/client/r0/directory/list/room/{roomId} - GET /_matrix/client/r0/capabilities diff --git a/lib/architex/room_server.ex b/lib/architex/room_server.ex index b4a029e..31c8d6f 100644 --- a/lib/architex/room_server.ex +++ b/lib/architex/room_server.ex @@ -200,6 +200,12 @@ defmodule Architex.RoomServer do GenServer.call(pid, {:get_current_state, account}) end + @spec get_state_event(pid(), Account.t(), String.t(), String.t()) :: + {:ok, map()} | {:error, :unauthorized} | {:error, :not_found} + def get_state_event(pid, account, event_type, state_key) do + GenServer.call(pid, {:get_state_event, account, event_type, state_key}) + end + ### Implementation @impl true @@ -445,6 +451,10 @@ defmodule Architex.RoomServer do %Event{content: %{"membership" => "leave"}} = event -> # Get the state of the room, after leaving. + # TODO: This does not work properly, as a user's membership can change to "leave" + # even after they left/are banned. + # I think it is best to seperately keep track when a user left, maybe in the + # Membership table. state_set = StateResolution.resolve(event) {:reply, {:ok, Map.values(state_set)}, state} @@ -453,6 +463,34 @@ defmodule Architex.RoomServer do end end + def handle_call( + {:get_state_event, account, event_type, state_key}, + _from, + %{state_set: state_set} = state + ) do + mxid = Account.get_mxid(account) + + case state_set[{"m.room.member", mxid}] do + %Event{content: %{"membership" => "join"}} -> + case state_set[{event_type, state_key}] do + %Event{content: content} -> {:reply, {:ok, content}, state} + nil -> {:reply, {:error, :not_found}, state} + end + + %Event{content: %{"membership" => "leave"}} = event -> + # TODO: See get_current_state. + state_set = StateResolution.resolve(event) + + case state_set[{event_type, state_key}] do + %Event{content: content} -> {:reply, {:ok, content}, state} + nil -> {:reply, {:error, :not_found}, state} + end + + _ -> + {:reply, {:error, :unauthorized}, state} + end + end + @spec process_event_with_txn(t(), Room.t(), Device.t(), %Event{}, String.t()) :: (() -> {t(), Room.t(), String.t()} | {:error, atom()}) defp process_event_with_txn( diff --git a/lib/architex_web/client/controllers/room_controller.ex b/lib/architex_web/client/controllers/room_controller.ex index f713010..95aa10f 100644 --- a/lib/architex_web/client/controllers/room_controller.ex +++ b/lib/architex_web/client/controllers/room_controller.ex @@ -317,7 +317,7 @@ defmodule ArchitexWeb.Client.RoomController do Action for GET /_matrix/client/r0/rooms/{roomId}/state. """ - def state(%Conn{assigns: %{account: account}} = conn, %{"room_id" => room_id}) do + def get_state(%Conn{assigns: %{account: account}} = conn, %{"room_id" => room_id}) do case RoomServer.get_room_server(room_id) do {:ok, pid} -> case RoomServer.get_current_state(pid, account) do @@ -340,4 +340,46 @@ defmodule ArchitexWeb.Client.RoomController do put_error(conn, :not_found, "The given room was not found.") end end + + @doc """ + Looks up the contents of a state event in a room. + + Action for GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}. + """ + def get_state_event(conn, %{"state_key" => [state_key | _]} = params) do + do_get_state_event(conn, params, state_key) + end + + def get_state_event(conn, params) do + do_get_state_event(conn, params, "") + end + + defp do_get_state_event( + %Conn{assigns: %{account: account}} = conn, + %{"room_id" => room_id, "event_type" => event_type}, + state_key + ) do + case RoomServer.get_room_server(room_id) do + {:ok, pid} -> + case RoomServer.get_state_event(pid, account, event_type, state_key) do + {:ok, content} -> + conn + |> put_status(200) + |> json(content) + + {:error, :unauthorized} -> + put_error( + conn, + :forbidden, + "You aren't a member of the room and weren't previously a member of the room." + ) + + {:error, :not_found} -> + put_error(conn, :not_found, "The room has no state with the given type or key.") + end + + {:error, :not_found} -> + put_error(conn, :not_found, "The given room was not found.") + end + end end diff --git a/lib/architex_web/router.ex b/lib/architex_web/router.ex index 757d557..44f4cd8 100644 --- a/lib/architex_web/router.ex +++ b/lib/architex_web/router.ex @@ -61,13 +61,16 @@ defmodule ArchitexWeb.Router do scope "/r0" do get "/account/whoami", AccountController, :whoami - post "/logout", AccountController, :logout - post "/logout/all", AccountController, :logout_all post "/createRoom", RoomController, :create get "/joined_rooms", RoomController, :joined_rooms get "/capabilities", InfoController, :capabilities get "/sync", SyncController, :sync + scope "/logout" do + post "/", AccountController, :logout + post "/all", AccountController, :logout_all + end + scope "/profile/:user_id" do put "/avatar_url", ProfileController, :set_avatar_url put "/displayname", ProfileController, :set_displayname @@ -89,8 +92,12 @@ defmodule ArchitexWeb.Router do get "/messages", RoomController, :messages scope "/state" do - get "/", RoomController, :state - put "/:event_type/*state_key", RoomController, :send_state_event + get "/", RoomController, :get_state + + scope "/:event_type/*state_key" do + get "/", RoomController, :get_state_event + put "/", RoomController, :send_state_event + end end end end