Remote Timeline: add Streaming support
authorAlex Gleason <alex@alexgleason.me>
Fri, 9 Oct 2020 01:01:48 +0000 (20:01 -0500)
committerAlex Gleason <alex@alexgleason.me>
Fri, 9 Oct 2020 01:07:03 +0000 (20:07 -0500)
CHANGELOG.md
docs/API/differences_in_mastoapi_responses.md
lib/pleroma/activity/ir/topics.ex
lib/pleroma/web/streamer/streamer.ex
test/activity/ir/topics_test.exs
test/integration/mastodon_websocket_test.exs
test/web/streamer/streamer_test.exs

index 8fc1750d181088c9476145faa312b42ae8d3c939..0eeffb72fdb81cf47ce619fb10e776ea675555ca 100644 (file)
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mix tasks for controlling user account confirmation status in bulk (`mix pleroma.user confirm_all` and `mix pleroma.user unconfirm_all`)
 - Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`)
 - Mix task option for force-unfollowing relays
+- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`
 
 ### Changed
 
index 38865dc68a8b4393a7c40d23eceacebf1a7bf434..bb1000b0bd893bde927d98c1fc1d3ae76375c941 100644 (file)
@@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas
 ## Timelines
 
 Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
+
 Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
+
 Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
 
+Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
+
 ## Statuses
 
 - `visibility`: has an additional possible value `list`
@@ -249,6 +253,8 @@ Has these additional fields under the `pleroma` object:
 
 There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
 
+For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
+
 ## Not implemented
 
 Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
index 9e65bedade120e9daecc35fa4bfc132fdabc2fbb..fe2e8cb5c900d7b84f0e92bc1ba42a74c85d685b 100644 (file)
@@ -40,7 +40,8 @@ defmodule Pleroma.Activity.Ir.Topics do
   end
 
   defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
-    tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
+    tags ++
+      remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
   end
 
   defp item_creation_tags(tags, _, _) do
@@ -55,9 +56,19 @@ defmodule Pleroma.Activity.Ir.Topics do
 
   defp hashtags_to_topics(_), do: []
 
+  defp remote_topics(%{local: true}), do: []
+
+  defp remote_topics(%{actor: actor}) when is_binary(actor),
+    do: ["public:remote:" <> URI.parse(actor).host]
+
+  defp remote_topics(_), do: []
+
   defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
 
   defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
 
+  defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
+    do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
+
   defp attachment_topics(_object, _act), do: ["public:media"]
 end
index 5475f18a6d1e7b2878262a3fbb5d2b4285dad069..d774f0dd9ecd21a8c6d9491c04856c0501e6e33a 100644 (file)
@@ -57,6 +57,15 @@ defmodule Pleroma.Web.Streamer do
     {:ok, "hashtag:" <> tag}
   end
 
+  # Allow remote instance streams.
+  def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
+    {:ok, "public:remote:" <> instance}
+  end
+
+  def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
+    {:ok, "public:remote:media:" <> instance}
+  end
+
   # Expand user streams.
   def get_topic(
         stream,
index 14a6e6b71e8d10f2a9c3048d45740eda0c434dfa..c8dcb28cccbd52b118e5cd6c2aeb5b0e5b608b99 100644 (file)
@@ -93,6 +93,13 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
 
       refute Enum.member?(topics, "hashtag:2")
     end
+
+    test "non-local action produces public:remote topic", %{activity: activity} do
+      activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
+      topics = Topics.get_activity_topics(activity)
+
+      assert Enum.member?(topics, "public:remote:lain.com")
+    end
   end
 
   describe "public visibility create events with attachments" do
@@ -124,6 +131,13 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
 
       refute Enum.member?(topics, "public:local:media")
     end
+
+    test "non-local action produces public:remote:media topic", %{activity: activity} do
+      activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
+      topics = Topics.get_activity_topics(activity)
+
+      assert Enum.member?(topics, "public:remote:media:lain.com")
+    end
   end
 
   describe "non-public visibility" do
index 0f2e6cc2b0a1169e84cdb62fc55523133dc0013d..bb8e795b723c5f1bf7e31aeee47a901e8cbe9e03 100644 (file)
@@ -49,6 +49,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
   test "allows public streams without authentication" do
     assert {:ok, _} = start_socket("?stream=public")
     assert {:ok, _} = start_socket("?stream=public:local")
+    assert {:ok, _} = start_socket("?stream=public:remote&instance=lain.com")
     assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
   end
 
index 185724a9fce49d1b197466f6e4c85b3e1b5ee431..1495ed1245544aa1cf4d7aef7fe55efc2b9f0779 100644 (file)
@@ -29,6 +29,14 @@ defmodule Pleroma.Web.StreamerTest do
       assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)
     end
 
+    test "allows instance streams" do
+      assert {:ok, "public:remote:lain.com"} =
+               Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"})
+
+      assert {:ok, "public:remote:media:lain.com"} =
+               Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"})
+    end
+
     test "allows hashtag streams" do
       assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"})
     end