summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHubert Chathi <hubert@uhoreg.ca>2019-10-07 22:38:48 -0400
committerHubert Chathi <hubert@uhoreg.ca>2019-10-07 22:38:48 -0400
commit040ac4289cf94c903445c96b378976b6a64bd54d (patch)
treec22132305f1f4b52f16fedfd7d3be750c93055a7
parentmake 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.ex21
-rw-r--r--lib/polyjuice/client/endpoint/post_join.ex86
-rw-r--r--lib/polyjuice/client/sync.ex66
-rw-r--r--test/polyjuice/client/endpoint/post_join_test.exs87
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