diff options
author | Hubert Chathi <hubert@uhoreg.ca> | 2019-10-07 22:38:48 -0400 |
---|---|---|
committer | Hubert Chathi <hubert@uhoreg.ca> | 2019-10-07 22:38:48 -0400 |
commit | 040ac4289cf94c903445c96b378976b6a64bd54d (patch) | |
tree | c22132305f1f4b52f16fedfd7d3be750c93055a7 | |
parent | make sync more robust, and send messages with connection status (diff) |
send more information to listeners and add support for joining
-rw-r--r-- | lib/polyjuice/client.ex | 21 | ||||
-rw-r--r-- | lib/polyjuice/client/endpoint/post_join.ex | 86 | ||||
-rw-r--r-- | lib/polyjuice/client/sync.ex | 66 | ||||
-rw-r--r-- | test/polyjuice/client/endpoint/post_join_test.exs | 87 |
4 files changed, 256 insertions, 4 deletions
diff --git a/lib/polyjuice/client.ex b/lib/polyjuice/client.ex index c6a9282..b737d93 100644 --- a/lib/polyjuice/client.ex +++ b/lib/polyjuice/client.ex @@ -218,4 +218,25 @@ defmodule Polyjuice.Client do } ) end + + @doc """ + Join a room. + """ + @spec join_room( + client_api :: Polyjuice.Client.API.t(), + room :: String.t(), + servers :: list(String.t()), + third_party_join :: map | nil + ) :: Any + def join_room(client_api, room, servers \\ [], third_party_signed \\ nil) + when is_binary(room) and is_list(servers) and (is_map(third_party_signed) or third_party_signed == nil) do + Polyjuice.Client.API.call( + client_api, + %Polyjuice.Client.Endpoint.PostJoin{ + room: room, + servers: servers, + third_party_signed: third_party_signed + } + ) + end end diff --git a/lib/polyjuice/client/endpoint/post_join.ex b/lib/polyjuice/client/endpoint/post_join.ex new file mode 100644 index 0000000..8fb742c --- /dev/null +++ b/lib/polyjuice/client/endpoint/post_join.ex @@ -0,0 +1,86 @@ +# Copyright 2019 Hubert Chathi <hubert@uhoreg.ca> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +defmodule Polyjuice.Client.Endpoint.PostJoin do + @moduledoc """ + Join a room. + + https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-join-roomidoralias + """ + + @type t :: %__MODULE__{ + room: String.t(), + servers: [String.t()], + third_party_signed: map | nil + } + + @enforce_keys [:room] + defstruct [ + :room, + :third_party_signed, + servers: [] + ] + + defimpl Polyjuice.Client.Endpoint.Proto do + def http_spec( + %{ + room: room, + servers: servers, + third_party_signed: third_party_signed + }, + base_url + ) do + e = &URI.encode_www_form/1 + body = Poison.encode!( + if third_party_signed do + %{"third_party_signed" => third_party_signed} + else + %{} + end + ) + + url = %{ + URI.merge( + base_url, + "#{Polyjuice.Client.prefix_r0()}/join/#{e.(room)}" + ) | query: if servers == [] do + nil + else + Enum.join(Enum.map(servers, &("server_name=#{e.(&1)}")), "&") + end + } + + %Polyjuice.Client.Endpoint.HttpSpec{ + method: :post, + headers: [ + {"Accept", "application/json"}, + {"Content-Type", "application/json"} + ], + url: to_string(url), + body: body, + transform: &Polyjuice.Client.Endpoint.PostJoin.transform/3 + } + end + end + + def transform(status_code, _resp_headers, body) do + case status_code do + 200 -> + {:ok, body |> Poison.decode!() |> Map.get("room_id")} + + _ -> + {:error, status_code, body} + end + end +end diff --git a/lib/polyjuice/client/sync.ex b/lib/polyjuice/client/sync.ex index ac81568..1f4c2f8 100644 --- a/lib/polyjuice/client/sync.ex +++ b/lib/polyjuice/client/sync.ex @@ -272,20 +272,41 @@ defmodule Polyjuice.Client.Sync do end defp process_body(body, state) do - body - |> Map.get("rooms", %{}) + rooms = Map.get(body, "rooms", %{}) + + rooms |> Map.get("join", []) |> Enum.each(fn {k, v} -> process_room(k, v, state) end) + + rooms + |> Map.get("invite", []) + |> Enum.each(fn {k, v} -> process_invite(k, v, state) end) + + rooms + |> Map.get("leave", []) + |> Enum.each( + fn {k, v} -> + process_room(k, v, state) + send(state.listener, {:left, k}) + end + ) end defp process_room(roomname, room, state) do + timeline = Map.get(room, "timeline", %{}) + + if Map.get(timeline, "limited", false) do + with {:ok, prev_batch} <- Map.get(timeline, "prev_batch") do + send(state.listener, {:limited, room, prev_batch}) + end + end + room |> Map.get("state", %{}) |> Map.get("events", []) |> Enum.each(&process_event(&1, roomname, state)) - room - |> Map.get("timeline", %{}) + timeline |> Map.get("events", []) |> Enum.each(&process_event(&1, roomname, state)) end @@ -321,4 +342,41 @@ defmodule Polyjuice.Client.Sync do defp process_event(_, _, state) do state end + + defp process_invite(roomname, room, state) do + Logger.debug("invite received #{inspect(room)}") + # The invite state is a map from state type to state key to event. + invite_state = Enum.reduce( + Map.get(room, "invite_state", %{}) |> Map.get("events", []), + %{}, + fn + %{ + "type" => type, + "state_key" => state_key + } = val, acc -> + Map.get(acc, type, %{}) + |> Map.put(state_key, val) + |> (&Map.put(acc, type, &1)).() + + _, acc -> + acc + end + ) + + Logger.debug("state #{inspect(invite_state)}") + + inviter = invite_state + |> Map.get("m.room.member", %{}) + |> Map.get(state.user_id, %{}) + |> Map.get("sender") + + if inviter do + send( + state.listener, + {:invite, roomname, inviter, invite_state} + ) + end + + state + end end diff --git a/test/polyjuice/client/endpoint/post_join_test.exs b/test/polyjuice/client/endpoint/post_join_test.exs new file mode 100644 index 0000000..71010bf --- /dev/null +++ b/test/polyjuice/client/endpoint/post_join_test.exs @@ -0,0 +1,87 @@ +# Copyright 2019 Hubert Chathi <hubert@uhoreg.ca> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +defmodule Polyjuice.Client.Endpoint.PostJoinTest do + use ExUnit.Case + + test "POST join/{roomIdOrAlias}" do + with endpoint = %Polyjuice.Client.Endpoint.PostJoin{ + room: "!room", + third_party_signed: %{ + "sender" => "@alice:example.org", + "mxid" => "@bob:example.org", + "token" => "random8nonce", + "signatures" => %{ + "example.org" => %{ + "ed25519:0" => "some9signature" + } + } + } + } do + + http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") + + assert %{http_spec | transform: nil, body: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + auth_required: true, + body: nil, + headers: [ + {"Accept", "application/json"}, + {"Content-Type", "application/json"} + ], + method: :post, + transform: nil, + url: "https://example.com/_matrix/client/r0/join/%21room" + } + + assert Poison.decode!(http_spec.body) == %{ + "third_party_signed" => %{ + "sender" => "@alice:example.org", + "mxid" => "@bob:example.org", + "token" => "random8nonce", + "signatures" => %{ + "example.org" => %{ + "ed25519:0" => "some9signature" + } + } + } + } + + assert http_spec.transform.(200, [], ~s({"room_id":"!room"})) == {:ok, "!room"} + + assert http_spec.transform.(500, [], "Aaah!") == {:error, 500, "Aaah!"} + end + + with endpoint = %Polyjuice.Client.Endpoint.PostJoin{ + room: "!room", + servers: ["example.com", "example.org"] + } do + + http_spec = Polyjuice.Client.Endpoint.Proto.http_spec(endpoint, "https://example.com") + + assert %{http_spec | transform: nil} == %Polyjuice.Client.Endpoint.HttpSpec{ + auth_required: true, + body: "{}", + headers: [ + {"Accept", "application/json"}, + {"Content-Type", "application/json"} + ], + method: :post, + transform: nil, + url: "https://example.com/_matrix/client/r0/join/%21room?server_name=example.com&server_name=example.org" + } + + assert http_spec.transform.(200, [], ~s({"room_id":"!room"})) == {:ok, "!room"} + end + end +end |