X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fstreamer.ex;h=05f877438dbd103a0238f7962a2eec7578b82615;hb=89fbed88212657e3dcd4bbcb2c0718b07802037f;hp=d81732a1a5889b43d88c541e5ac1797c076e635c;hpb=d2099c849d355a9b8d3b1f2e0342dfe3c7e3497c;p=akkoma diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index d81732a1a..05f877438 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -1,13 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Streamer do use GenServer require Logger - alias Pleroma.{User, Notification} + alias Pleroma.{User, Notification, Activity, Object, Repo} + alias Pleroma.Web.ActivityPub.ActivityPub + + @keepalive_interval :timer.seconds(30) def start_link do - spawn(fn -> - Process.sleep(1000 * 30) # 30 seconds - GenServer.cast(__MODULE__, %{action: :ping}) - end) GenServer.start_link(__MODULE__, %{}, name: __MODULE__) end @@ -23,41 +26,107 @@ defmodule Pleroma.Web.Streamer do GenServer.cast(__MODULE__, %{action: :stream, topic: topic, item: item}) end + def init(args) do + spawn(fn -> + # 30 seconds + Process.sleep(@keepalive_interval) + GenServer.cast(__MODULE__, %{action: :ping}) + end) + + {:ok, args} + end + def handle_cast(%{action: :ping}, topics) do Map.values(topics) - |> List.flatten - |> Enum.each(fn (socket) -> + |> List.flatten() + |> Enum.each(fn socket -> Logger.debug("Sending keepalive ping") - send socket.transport_pid, {:text, ""} + send(socket.transport_pid, {:text, ""}) end) + spawn(fn -> - Process.sleep(1000 * 30) # 30 seconds + # 30 seconds + Process.sleep(@keepalive_interval) GenServer.cast(__MODULE__, %{action: :ping}) end) + + {:noreply, topics} + end + + def handle_cast(%{action: :stream, topic: "direct", item: item}, topics) do + recipient_topics = + User.get_recipients_from_activity(item) + |> Enum.map(fn %{id: id} -> "direct:#{id}" end) + + Enum.each(recipient_topics || [], fn user_topic -> + Logger.debug("Trying to push direct message to #{user_topic}\n\n") + push_to_socket(topics, user_topic, item) + end) + + {:noreply, topics} + end + + def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do + # filter the recipient list if the activity is not public, see #270. + recipient_lists = + case ActivityPub.is_public?(item) do + true -> + Pleroma.List.get_lists_from_activity(item) + + _ -> + Pleroma.List.get_lists_from_activity(item) + |> Enum.filter(fn list -> + owner = Repo.get(User, list.user_id) + + ActivityPub.visible_for_user?(item, owner) + end) + end + + recipient_topics = + recipient_lists + |> Enum.map(fn %{id: id} -> "list:#{id}" end) + + Enum.each(recipient_topics || [], fn list_topic -> + Logger.debug("Trying to push message to #{list_topic}\n\n") + push_to_socket(topics, list_topic, item) + end) + {:noreply, topics} end def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do topic = "user:#{item.user_id}" - Enum.each(topics[topic] || [], fn (socket) -> - json = %{ - event: "notification", - payload: Pleroma.Web.MastodonAPI.MastodonAPIController.render_notification(socket.assigns["user"], item) |> Jason.encode! - } |> Jason.encode! - send socket.transport_pid, {:text, json} + Enum.each(topics[topic] || [], fn socket -> + json = + %{ + event: "notification", + payload: + Pleroma.Web.MastodonAPI.MastodonAPIController.render_notification( + socket.assigns["user"], + item + ) + |> Jason.encode!() + } + |> Jason.encode!() + + send(socket.transport_pid, {:text, json}) end) + {:noreply, topics} end def handle_cast(%{action: :stream, topic: "user", item: item}, topics) do Logger.debug("Trying to push to users") - recipient_topics = User.get_recipients_from_activity(item) - |> Enum.map(fn (%{id: id}) -> "user:#{id}" end) - Enum.each(recipient_topics, fn (topic) -> + recipient_topics = + User.get_recipients_from_activity(item) + |> Enum.map(fn %{id: id} -> "user:#{id}" end) + + Enum.each(recipient_topics, fn topic -> push_to_socket(topics, topic, item) end) + {:noreply, topics} end @@ -91,19 +160,69 @@ defmodule Pleroma.Web.Streamer do {:noreply, state} end - def push_to_socket(topics, topic, item) do - Enum.each(topics[topic] || [], fn (socket) -> - json = %{ - event: "update", - payload: Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: item, for: socket.assigns[:user]) |> Jason.encode! - } |> Jason.encode! + defp represent_update(%Activity{} = activity, %User{} = user) do + %{ + event: "update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "status.json", + activity: activity, + for: user + ) + |> Jason.encode!() + } + |> Jason.encode!() + end + + defp represent_update(%Activity{} = activity) do + %{ + event: "update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "status.json", + activity: activity + ) + |> Jason.encode!() + } + |> Jason.encode!() + end - send socket.transport_pid, {:text, json} + def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do + Enum.each(topics[topic] || [], fn socket -> + # Get the current user so we have up-to-date blocks etc. + if socket.assigns[:user] do + user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id) + blocks = user.info.blocks || [] + + parent = Object.normalize(item.data["object"]) + + unless is_nil(parent) or item.actor in blocks or parent.data["actor"] in blocks do + send(socket.transport_pid, {:text, represent_update(item, user)}) + end + else + send(socket.transport_pid, {:text, represent_update(item)}) + end + end) + end + + def push_to_socket(topics, topic, item) do + Enum.each(topics[topic] || [], fn socket -> + # Get the current user so we have up-to-date blocks etc. + if socket.assigns[:user] do + user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id) + blocks = user.info.blocks || [] + + unless item.actor in blocks do + send(socket.transport_pid, {:text, represent_update(item, user)}) + end + else + send(socket.transport_pid, {:text, represent_update(item)}) + end end) end - defp internal_topic("user", socket) do - "user:#{socket.assigns[:user].id}" + defp internal_topic(topic, socket) when topic in ~w[user direct] do + "#{topic}:#{socket.assigns[:user].id}" end defp internal_topic(topic, _), do: topic