1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.ActivityPub do
8 alias Pleroma.Conversation
9 alias Pleroma.Notification
11 alias Pleroma.Object.Containment
12 alias Pleroma.Object.Fetcher
13 alias Pleroma.Pagination
17 alias Pleroma.Web.ActivityPub.MRF
18 alias Pleroma.Web.ActivityPub.Transmogrifier
19 alias Pleroma.Web.WebFinger
20 alias Pleroma.Workers.BackgroundWorker
23 import Pleroma.Web.ActivityPub.Utils
24 import Pleroma.Web.ActivityPub.Visibility
27 require Pleroma.Constants
29 defdelegate worker_args(queue), to: Pleroma.Workers.Helper
31 # For Announce activities, we filter the recipients based on following status for any actors
32 # that match actual users. See issue #164 for more information about why this is necessary.
33 defp get_recipients(%{"type" => "Announce"} = data) do
34 to = Map.get(data, "to", [])
35 cc = Map.get(data, "cc", [])
36 bcc = Map.get(data, "bcc", [])
37 actor = User.get_cached_by_ap_id(data["actor"])
40 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
41 case User.get_cached_by_ap_id(recipient) do
43 user -> User.following?(user, actor)
50 defp get_recipients(%{"type" => "Create"} = data) do
51 to = Map.get(data, "to", [])
52 cc = Map.get(data, "cc", [])
53 bcc = Map.get(data, "bcc", [])
54 actor = Map.get(data, "actor", [])
55 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
59 defp get_recipients(data) do
60 to = Map.get(data, "to", [])
61 cc = Map.get(data, "cc", [])
62 bcc = Map.get(data, "bcc", [])
63 recipients = Enum.concat([to, cc, bcc])
67 defp check_actor_is_active(actor) do
68 if not is_nil(actor) do
69 with user <- User.get_cached_by_ap_id(actor),
70 false <- user.info.deactivated do
80 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
81 limit = Config.get([:instance, :remote_limit])
82 String.length(content) <= limit
85 defp check_remote_limit(_), do: true
87 def increase_note_count_if_public(actor, object) do
88 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
91 def decrease_note_count_if_public(actor, object) do
92 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
95 def increase_replies_count_if_reply(%{
96 "object" => %{"inReplyTo" => reply_ap_id} = object,
99 if is_public?(object) do
100 Object.increase_replies_count(reply_ap_id)
104 def increase_replies_count_if_reply(_create_data), do: :noop
106 def decrease_replies_count_if_reply(%Object{
107 data: %{"inReplyTo" => reply_ap_id} = object
109 if is_public?(object) do
110 Object.decrease_replies_count(reply_ap_id)
114 def decrease_replies_count_if_reply(_object), do: :noop
116 def increase_poll_votes_if_vote(%{
117 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
120 Object.increase_vote_count(reply_ap_id, name)
123 def increase_poll_votes_if_vote(_create_data), do: :noop
125 def insert(map, local \\ true, fake \\ false) when is_map(map) do
126 with nil <- Activity.normalize(map),
127 map <- lazy_put_activity_defaults(map, fake),
128 :ok <- check_actor_is_active(map["actor"]),
129 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
130 {:ok, map} <- MRF.filter(map),
131 {recipients, _, _} = get_recipients(map),
132 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
133 :ok <- Containment.contain_child(map),
134 {:ok, map, object} <- insert_full_object(map) do
136 Repo.insert(%Activity{
140 recipients: recipients
143 # Splice in the child object if we have one.
145 if !is_nil(object) do
146 Map.put(activity, :object, object)
151 %{"op" => "fetch_data_for_activity", "activity_id" => activity.id}
152 |> BackgroundWorker.new(worker_args(:background))
155 Notification.create_notifications(activity)
159 |> Conversation.create_or_bump_for()
160 |> get_participations()
163 stream_out_participations(participations)
166 %Activity{} = activity ->
169 {:fake, true, map, recipients} ->
170 activity = %Activity{
174 recipients: recipients,
178 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
186 defp get_participations({:ok, %{participations: participations}}), do: participations
187 defp get_participations(_), do: []
189 def stream_out_participations(participations) do
192 |> Repo.preload(:user)
194 Enum.each(participations, fn participation ->
195 Pleroma.Web.Streamer.stream("participation", participation)
199 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
200 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
201 conversation = Repo.preload(conversation, :participations),
203 fetch_latest_activity_id_for_context(conversation.ap_id, %{
205 "blocking_user" => user
207 if last_activity_id do
208 stream_out_participations(conversation.participations)
213 def stream_out_participations(_, _), do: :noop
215 def stream_out(activity) do
216 if activity.data["type"] in ["Create", "Announce", "Delete"] do
217 object = Object.normalize(activity)
218 # Do not stream out poll replies
219 unless object.data["type"] == "Answer" do
220 Pleroma.Web.Streamer.stream("user", activity)
221 Pleroma.Web.Streamer.stream("list", activity)
223 if get_visibility(activity) == "public" do
224 Pleroma.Web.Streamer.stream("public", activity)
227 Pleroma.Web.Streamer.stream("public:local", activity)
230 if activity.data["type"] in ["Create"] do
232 |> Map.get("tag", [])
233 |> Enum.filter(fn tag -> is_bitstring(tag) end)
234 |> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
236 if object.data["attachment"] != [] do
237 Pleroma.Web.Streamer.stream("public:media", activity)
240 Pleroma.Web.Streamer.stream("public:local:media", activity)
245 if get_visibility(activity) == "direct",
246 do: Pleroma.Web.Streamer.stream("direct", activity)
252 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
253 additional = params[:additional] || %{}
254 # only accept false as false value
255 local = !(params[:local] == false)
256 published = params[:published]
260 %{to: to, actor: actor, published: published, context: context, object: object},
263 {:ok, activity} <- insert(create_data, local, fake),
264 {:fake, false, activity} <- {:fake, fake, activity},
265 _ <- increase_replies_count_if_reply(create_data),
266 _ <- increase_poll_votes_if_vote(create_data),
267 # Changing note count prior to enqueuing federation task in order to avoid
268 # race conditions on updating user.info
269 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
270 :ok <- maybe_federate(activity) do
273 {:fake, true, activity} ->
281 def accept(%{to: to, actor: actor, object: object} = params) do
282 # only accept false as false value
283 local = !(params[:local] == false)
285 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
286 {:ok, activity} <- insert(data, local),
287 :ok <- maybe_federate(activity) do
292 def reject(%{to: to, actor: actor, object: object} = params) do
293 # only accept false as false value
294 local = !(params[:local] == false)
296 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
297 {:ok, activity} <- insert(data, local),
298 :ok <- maybe_federate(activity) do
303 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
304 # only accept false as false value
305 local = !(params[:local] == false)
314 {:ok, activity} <- insert(data, local),
315 :ok <- maybe_federate(activity) do
320 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
322 %User{ap_id: ap_id} = user,
323 %Object{data: %{"id" => _}} = object,
327 with nil <- get_existing_like(ap_id, object),
328 like_data <- make_like_data(user, object, activity_id),
329 {:ok, activity} <- insert(like_data, local),
330 {:ok, object} <- add_like_to_object(activity, object),
331 :ok <- maybe_federate(activity) do
332 {:ok, activity, object}
334 %Activity{} = activity -> {:ok, activity, object}
335 error -> {:error, error}
345 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
346 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
347 {:ok, unlike_activity} <- insert(unlike_data, local),
348 {:ok, _activity} <- Repo.delete(like_activity),
349 {:ok, object} <- remove_like_from_object(like_activity, object),
350 :ok <- maybe_federate(unlike_activity) do
351 {:ok, unlike_activity, like_activity, object}
358 %User{ap_id: _} = user,
359 %Object{data: %{"id" => _}} = object,
364 with true <- is_public?(object),
365 announce_data <- make_announce_data(user, object, activity_id, public),
366 {:ok, activity} <- insert(announce_data, local),
367 {:ok, object} <- add_announce_to_object(activity, object),
368 :ok <- maybe_federate(activity) do
369 {:ok, activity, object}
371 error -> {:error, error}
381 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
382 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
383 {:ok, unannounce_activity} <- insert(unannounce_data, local),
384 :ok <- maybe_federate(unannounce_activity),
385 {:ok, _activity} <- Repo.delete(announce_activity),
386 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
387 {:ok, unannounce_activity, object}
393 def follow(follower, followed, activity_id \\ nil, local \\ true) do
394 with data <- make_follow_data(follower, followed, activity_id),
395 {:ok, activity} <- insert(data, local),
396 :ok <- maybe_federate(activity) do
401 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
402 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
403 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
404 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
405 {:ok, activity} <- insert(unfollow_data, local),
406 :ok <- maybe_federate(activity) do
411 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
413 "to" => [follower_address],
416 "object" => %{"type" => "Person", "id" => ap_id}
418 {:ok, activity} <- insert(data, true, true),
419 :ok <- maybe_federate(activity) do
424 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
425 user = User.get_cached_by_ap_id(actor)
426 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
428 with {:ok, object, activity} <- Object.delete(object),
434 "deleted_activity_id" => activity && activity.id
436 {:ok, activity} <- insert(data, local, false),
437 stream_out_participations(object, user),
438 _ <- decrease_replies_count_if_reply(object),
439 # Changing note count prior to enqueuing federation task in order to avoid
440 # race conditions on updating user.info
441 {:ok, _actor} <- decrease_note_count_if_public(user, object),
442 :ok <- maybe_federate(activity) do
447 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
448 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
449 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
451 if unfollow_blocked do
452 follow_activity = fetch_latest_follow(blocker, blocked)
453 if follow_activity, do: unfollow(blocker, blocked, nil, local)
456 with true <- outgoing_blocks,
457 block_data <- make_block_data(blocker, blocked, activity_id),
458 {:ok, activity} <- insert(block_data, local),
459 :ok <- maybe_federate(activity) do
466 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
467 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
468 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
469 {:ok, activity} <- insert(unblock_data, local),
470 :ok <- maybe_federate(activity) do
484 # only accept false as false value
485 local = !(params[:local] == false)
486 forward = !(params[:forward] == false)
488 additional = params[:additional] || %{}
500 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
502 Map.merge(additional, %{"to" => [], "cc" => []})
505 with flag_data <- make_flag_data(params, additional),
506 {:ok, activity} <- insert(flag_data, local),
507 :ok <- maybe_federate(activity) do
508 Enum.each(User.all_superusers(), fn superuser ->
510 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
511 |> Pleroma.Emails.Mailer.deliver_async()
518 defp fetch_activities_for_context_query(context, opts) do
519 public = [Pleroma.Constants.as_public()]
522 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
524 from(activity in Activity)
525 |> maybe_preload_objects(opts)
526 |> maybe_preload_bookmarks(opts)
527 |> maybe_set_thread_muted_field(opts)
528 |> restrict_blocked(opts)
529 |> restrict_recipients(recipients, opts["user"])
533 "?->>'type' = ? and ?->>'context' = ?",
540 |> exclude_poll_votes(opts)
542 |> order_by([activity], desc: activity.id)
545 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
546 def fetch_activities_for_context(context, opts \\ %{}) do
548 |> fetch_activities_for_context_query(opts)
552 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
553 Pleroma.FlakeId.t() | nil
554 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
556 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
562 def fetch_public_activities(opts \\ %{}) do
563 q = fetch_activities_query([Pleroma.Constants.as_public()], opts)
566 |> restrict_unlisted()
567 |> Pagination.fetch_paginated(opts)
571 @valid_visibilities ~w[direct unlisted public private]
573 defp restrict_visibility(query, %{visibility: visibility})
574 when is_list(visibility) do
575 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
581 "activity_visibility(?, ?, ?) = ANY (?)",
591 Logger.error("Could not restrict visibility to #{visibility}")
595 defp restrict_visibility(query, %{visibility: visibility})
596 when visibility in @valid_visibilities do
600 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
604 defp restrict_visibility(_query, %{visibility: visibility})
605 when visibility not in @valid_visibilities do
606 Logger.error("Could not restrict visibility to #{visibility}")
609 defp restrict_visibility(query, _visibility), do: query
611 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
614 defp restrict_thread_visibility(
616 %{"user" => %User{info: %{skip_thread_containment: true}}},
621 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
624 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
628 defp restrict_thread_visibility(query, _, _), do: query
630 def fetch_user_activities(user, reading_user, params \\ %{}) do
633 |> Map.put("type", ["Create", "Announce"])
634 |> Map.put("user", reading_user)
635 |> Map.put("actor_id", user.ap_id)
636 |> Map.put("whole_db", true)
637 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
640 user_activities_recipients(%{
641 "godmode" => params["godmode"],
642 "reading_user" => reading_user
645 fetch_activities(recipients, params)
649 defp user_activities_recipients(%{"godmode" => true}) do
653 defp user_activities_recipients(%{"reading_user" => reading_user}) do
655 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
657 [Pleroma.Constants.as_public()]
661 defp restrict_since(query, %{"since_id" => ""}), do: query
663 defp restrict_since(query, %{"since_id" => since_id}) do
664 from(activity in query, where: activity.id > ^since_id)
667 defp restrict_since(query, _), do: query
669 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
670 raise "Can't use the child object without preloading!"
673 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
674 when is_list(tag_reject) and tag_reject != [] do
676 [_activity, object] in query,
677 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
681 defp restrict_tag_reject(query, _), do: query
683 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
684 raise "Can't use the child object without preloading!"
687 defp restrict_tag_all(query, %{"tag_all" => tag_all})
688 when is_list(tag_all) and tag_all != [] do
690 [_activity, object] in query,
691 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
695 defp restrict_tag_all(query, _), do: query
697 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
698 raise "Can't use the child object without preloading!"
701 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
703 [_activity, object] in query,
704 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
708 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
710 [_activity, object] in query,
711 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
715 defp restrict_tag(query, _), do: query
717 defp restrict_recipients(query, [], _user), do: query
719 defp restrict_recipients(query, recipients, nil) do
720 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
723 defp restrict_recipients(query, recipients, user) do
726 where: fragment("? && ?", ^recipients, activity.recipients),
727 or_where: activity.actor == ^user.ap_id
731 defp restrict_local(query, %{"local_only" => true}) do
732 from(activity in query, where: activity.local == true)
735 defp restrict_local(query, _), do: query
737 defp restrict_actor(query, %{"actor_id" => actor_id}) do
738 from(activity in query, where: activity.actor == ^actor_id)
741 defp restrict_actor(query, _), do: query
743 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
744 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
747 defp restrict_type(query, %{"type" => type}) do
748 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
751 defp restrict_type(query, _), do: query
753 defp restrict_state(query, %{"state" => state}) do
754 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
757 defp restrict_state(query, _), do: query
759 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
761 [_activity, object] in query,
762 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
766 defp restrict_favorited_by(query, _), do: query
768 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
769 raise "Can't use the child object without preloading!"
772 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
774 [_activity, object] in query,
775 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
779 defp restrict_media(query, _), do: query
781 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
784 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
788 defp restrict_replies(query, _), do: query
790 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
791 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
794 defp restrict_reblogs(query, _), do: query
796 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
798 defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
803 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
804 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
808 defp restrict_muted(query, _), do: query
810 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
811 blocks = info.blocks || []
812 domain_blocks = info.domain_blocks || []
815 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
818 [activity, object: o] in query,
819 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
820 where: fragment("not (? && ?)", activity.recipients, ^blocks),
823 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
828 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
829 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
833 defp restrict_blocked(query, _), do: query
835 defp restrict_unlisted(query) do
840 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
842 ^[Pleroma.Constants.as_public()]
847 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
848 from(activity in query, where: activity.id in ^ids)
851 defp restrict_pinned(query, _), do: query
853 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
854 muted_reblogs = info.muted_reblogs || []
860 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
868 defp restrict_muted_reblogs(query, _), do: query
870 defp exclude_poll_votes(query, %{"include_poll_votes" => "true"}), do: query
872 defp exclude_poll_votes(query, _) do
873 if has_named_binding?(query, :object) do
874 from([activity, object: o] in query,
875 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
882 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
883 from(activity in query, where: activity.id != ^id)
886 defp exclude_id(query, _), do: query
888 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
890 defp maybe_preload_objects(query, _) do
892 |> Activity.with_preloaded_object()
895 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
897 defp maybe_preload_bookmarks(query, opts) do
899 |> Activity.with_preloaded_bookmark(opts["user"])
902 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
904 defp maybe_set_thread_muted_field(query, opts) do
906 |> Activity.with_set_thread_muted_field(opts["user"])
909 defp maybe_order(query, %{order: :desc}) do
911 |> order_by(desc: :id)
914 defp maybe_order(query, %{order: :asc}) do
916 |> order_by(asc: :id)
919 defp maybe_order(query, _), do: query
921 def fetch_activities_query(recipients, opts \\ %{}) do
923 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
927 |> maybe_preload_objects(opts)
928 |> maybe_preload_bookmarks(opts)
929 |> maybe_set_thread_muted_field(opts)
931 |> restrict_recipients(recipients, opts["user"])
932 |> restrict_tag(opts)
933 |> restrict_tag_reject(opts)
934 |> restrict_tag_all(opts)
935 |> restrict_since(opts)
936 |> restrict_local(opts)
937 |> restrict_actor(opts)
938 |> restrict_type(opts)
939 |> restrict_state(opts)
940 |> restrict_favorited_by(opts)
941 |> restrict_blocked(opts)
942 |> restrict_muted(opts)
943 |> restrict_media(opts)
944 |> restrict_visibility(opts)
945 |> restrict_thread_visibility(opts, config)
946 |> restrict_replies(opts)
947 |> restrict_reblogs(opts)
948 |> restrict_pinned(opts)
949 |> restrict_muted_reblogs(opts)
950 |> Activity.restrict_deactivated_users()
951 |> exclude_poll_votes(opts)
954 def fetch_activities(recipients, opts \\ %{}) do
955 list_memberships = Pleroma.List.memberships(opts["user"])
957 fetch_activities_query(recipients ++ list_memberships, opts)
958 |> Pagination.fetch_paginated(opts)
960 |> maybe_update_cc(list_memberships, opts["user"])
963 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
964 when is_list(list_memberships) and length(list_memberships) > 0 do
965 Enum.map(activities, fn
966 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
967 if Enum.any?(bcc, &(&1 in list_memberships)) do
968 update_in(activity.data["cc"], &[user_ap_id | &1])
978 defp maybe_update_cc(activities, _, _), do: activities
980 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
981 from(activity in query,
983 fragment("? && ?", activity.recipients, ^recipients) or
984 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
985 ^Pleroma.Constants.as_public() in activity.recipients)
989 def fetch_activities_bounded(recipients, recipients_with_public, opts \\ %{}) do
990 fetch_activities_query([], opts)
991 |> fetch_activities_bounded_query(recipients, recipients_with_public)
992 |> Pagination.fetch_paginated(opts)
996 def upload(file, opts \\ []) do
997 with {:ok, data} <- Upload.store(file, opts) do
1000 Map.put(data, "actor", opts[:actor])
1005 Repo.insert(%Object{data: obj_data})
1009 defp object_to_user_data(data) do
1011 data["icon"]["url"] &&
1014 "url" => [%{"href" => data["icon"]["url"]}]
1018 data["image"]["url"] &&
1021 "url" => [%{"href" => data["image"]["url"]}]
1024 locked = data["manuallyApprovesFollowers"] || false
1025 data = Transmogrifier.maybe_fix_user_object(data)
1037 follower_address: data["followers"],
1038 following_address: data["following"],
1039 bio: data["summary"]
1042 # nickname can be nil because of virtual actors
1044 if data["preferredUsername"] do
1048 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1051 Map.put(user_data, :nickname, nil)
1057 def fetch_follow_information_for_user(user) do
1058 with {:ok, following_data} <-
1059 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1060 following_count when is_integer(following_count) <- following_data["totalItems"],
1061 {:ok, hide_follows} <- collection_private(following_data),
1062 {:ok, followers_data} <-
1063 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1064 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1065 {:ok, hide_followers} <- collection_private(followers_data) do
1068 hide_follows: hide_follows,
1069 follower_count: followers_count,
1070 following_count: following_count,
1071 hide_followers: hide_followers
1082 defp maybe_update_follow_information(data) do
1083 with {:enabled, true} <-
1084 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1085 {:ok, info} <- fetch_follow_information_for_user(data) do
1086 info = Map.merge(data.info, info)
1087 Map.put(data, :info, info)
1089 {:enabled, false} ->
1094 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1101 defp collection_private(data) do
1102 if is_map(data["first"]) and
1103 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1106 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1107 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1110 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1122 def user_data_from_user_object(data) do
1123 with {:ok, data} <- MRF.filter(data),
1124 {:ok, data} <- object_to_user_data(data) do
1131 def fetch_and_prepare_user_from_ap_id(ap_id) do
1132 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1133 {:ok, data} <- user_data_from_user_object(data),
1134 data <- maybe_update_follow_information(data) do
1137 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1141 def make_user_from_ap_id(ap_id) do
1142 if _user = User.get_cached_by_ap_id(ap_id) do
1143 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1145 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1146 User.insert_or_update_user(data)
1153 def make_user_from_nickname(nickname) do
1154 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1155 make_user_from_ap_id(ap_id)
1157 _e -> {:error, "No AP id in WebFinger"}
1161 # filter out broken threads
1162 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1163 entire_thread_visible_for_user?(activity, user)
1166 # do post-processing on a specific activity
1167 def contain_activity(%Activity{} = activity, %User{} = user) do
1168 contain_broken_threads(activity, user)
1171 def fetch_direct_messages_query do
1173 |> restrict_type(%{"type" => "Create"})
1174 |> restrict_visibility(%{visibility: "direct"})
1175 |> order_by([activity], asc: activity.id)