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
7 alias Pleroma.Activity.Ir.Topics
9 alias Pleroma.Conversation
10 alias Pleroma.Conversation.Participation
11 alias Pleroma.Notification
13 alias Pleroma.Object.Containment
14 alias Pleroma.Object.Fetcher
15 alias Pleroma.Pagination
19 alias Pleroma.Web.ActivityPub.MRF
20 alias Pleroma.Web.ActivityPub.Transmogrifier
21 alias Pleroma.Web.ActivityPub.Utils
22 alias Pleroma.Web.Streamer
23 alias Pleroma.Web.WebFinger
24 alias Pleroma.Workers.BackgroundWorker
27 import Pleroma.Web.ActivityPub.Utils
28 import Pleroma.Web.ActivityPub.Visibility
31 require Pleroma.Constants
33 # For Announce activities, we filter the recipients based on following status for any actors
34 # that match actual users. See issue #164 for more information about why this is necessary.
35 defp get_recipients(%{"type" => "Announce"} = data) do
36 to = Map.get(data, "to", [])
37 cc = Map.get(data, "cc", [])
38 bcc = Map.get(data, "bcc", [])
39 actor = User.get_cached_by_ap_id(data["actor"])
42 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
43 case User.get_cached_by_ap_id(recipient) do
45 user -> User.following?(user, actor)
52 defp get_recipients(%{"type" => "Create"} = data) do
53 to = Map.get(data, "to", [])
54 cc = Map.get(data, "cc", [])
55 bcc = Map.get(data, "bcc", [])
56 actor = Map.get(data, "actor", [])
57 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
61 defp get_recipients(data) do
62 to = Map.get(data, "to", [])
63 cc = Map.get(data, "cc", [])
64 bcc = Map.get(data, "bcc", [])
65 recipients = Enum.concat([to, cc, bcc])
69 defp check_actor_is_active(actor) do
70 if not is_nil(actor) do
71 with user <- User.get_cached_by_ap_id(actor),
72 false <- user.deactivated do
82 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
83 limit = Config.get([:instance, :remote_limit])
84 String.length(content) <= limit
87 defp check_remote_limit(_), do: true
89 def increase_note_count_if_public(actor, object) do
90 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
93 def decrease_note_count_if_public(actor, object) do
94 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
97 def increase_replies_count_if_reply(%{
98 "object" => %{"inReplyTo" => reply_ap_id} = object,
101 if is_public?(object) do
102 Object.increase_replies_count(reply_ap_id)
106 def increase_replies_count_if_reply(_create_data), do: :noop
108 def decrease_replies_count_if_reply(%Object{
109 data: %{"inReplyTo" => reply_ap_id} = object
111 if is_public?(object) do
112 Object.decrease_replies_count(reply_ap_id)
116 def decrease_replies_count_if_reply(_object), do: :noop
118 def increase_poll_votes_if_vote(%{
119 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
122 Object.increase_vote_count(reply_ap_id, name)
125 def increase_poll_votes_if_vote(_create_data), do: :noop
127 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
128 with nil <- Activity.normalize(map),
129 map <- lazy_put_activity_defaults(map, fake),
130 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
131 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
132 {:ok, map} <- MRF.filter(map),
133 {recipients, _, _} = get_recipients(map),
134 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
135 {:containment, :ok} <- {:containment, Containment.contain_child(map)},
136 {:ok, map, object} <- insert_full_object(map) do
138 Repo.insert(%Activity{
142 recipients: recipients
145 # Splice in the child object if we have one.
147 if not is_nil(object) do
148 Map.put(activity, :object, object)
153 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
155 Notification.create_notifications(activity)
157 conversation = create_or_bump_conversation(activity, map["actor"])
158 participations = get_participations(conversation)
160 stream_out_participations(participations)
163 %Activity{} = activity ->
166 {:fake, true, map, recipients} ->
167 activity = %Activity{
171 recipients: recipients,
175 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
183 defp create_or_bump_conversation(activity, actor) do
184 with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
185 %User{} = user <- User.get_cached_by_ap_id(actor),
186 Participation.mark_as_read(user, conversation) do
191 defp get_participations({:ok, conversation}) do
193 |> Repo.preload(:participations, force: true)
194 |> Map.get(:participations)
197 defp get_participations(_), do: []
199 def stream_out_participations(participations) do
202 |> Repo.preload(:user)
204 Streamer.stream("participation", participations)
207 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
208 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
209 conversation = Repo.preload(conversation, :participations),
211 fetch_latest_activity_id_for_context(conversation.ap_id, %{
213 "blocking_user" => user
215 if last_activity_id do
216 stream_out_participations(conversation.participations)
221 def stream_out_participations(_, _), do: :noop
223 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
224 when data_type in ["Create", "Announce", "Delete"] do
226 |> Topics.get_activity_topics()
227 |> Streamer.stream(activity)
230 def stream_out(_activity) do
234 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
235 additional = params[:additional] || %{}
236 # only accept false as false value
237 local = !(params[:local] == false)
238 published = params[:published]
239 quick_insert? = Pleroma.Config.get([:env]) == :benchmark
243 %{to: to, actor: actor, published: published, context: context, object: object},
246 {:ok, activity} <- insert(create_data, local, fake),
247 {:fake, false, activity} <- {:fake, fake, activity},
248 _ <- increase_replies_count_if_reply(create_data),
249 _ <- increase_poll_votes_if_vote(create_data),
250 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
251 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
252 :ok <- maybe_federate(activity) do
255 {:quick_insert, true, activity} ->
258 {:fake, true, activity} ->
266 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
267 additional = params[:additional] || %{}
268 # only accept false as false value
269 local = !(params[:local] == false)
270 published = params[:published]
274 %{to: to, actor: actor, published: published, context: context, object: object},
277 {:ok, activity} <- insert(listen_data, local),
278 :ok <- maybe_federate(activity) do
286 def accept(params) do
287 accept_or_reject("Accept", params)
290 def reject(params) do
291 accept_or_reject("Reject", params)
294 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
295 local = Map.get(params, :local, true)
296 activity_id = Map.get(params, :activity_id, nil)
299 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
300 |> Utils.maybe_put("id", activity_id),
301 {:ok, activity} <- insert(data, local),
302 :ok <- maybe_federate(activity) do
307 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
308 local = !(params[:local] == false)
309 activity_id = params[:activity_id]
318 data <- Utils.maybe_put(data, "id", activity_id),
319 {:ok, activity} <- insert(data, local),
320 :ok <- maybe_federate(activity) do
325 def react_with_emoji(user, object, emoji, options \\ []) do
326 with local <- Keyword.get(options, :local, true),
327 activity_id <- Keyword.get(options, :activity_id, nil),
328 Pleroma.Emoji.is_unicode_emoji?(emoji),
329 reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
330 {:ok, activity} <- insert(reaction_data, local),
331 {:ok, object} <- add_emoji_reaction_to_object(activity, object),
332 :ok <- maybe_federate(activity) do
333 {:ok, activity, object}
337 def unreact_with_emoji(user, reaction_id, options \\ []) do
338 with local <- Keyword.get(options, :local, true),
339 activity_id <- Keyword.get(options, :activity_id, nil),
340 user_ap_id <- user.ap_id,
341 %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
342 object <- Object.normalize(reaction_activity),
343 unreact_data <- make_undo_data(user, reaction_activity, activity_id),
344 {:ok, activity} <- insert(unreact_data, local),
345 {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
346 :ok <- maybe_federate(activity) do
347 {:ok, activity, object}
351 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
353 %User{ap_id: ap_id} = user,
354 %Object{data: %{"id" => _}} = object,
358 with nil <- get_existing_like(ap_id, object),
359 like_data <- make_like_data(user, object, activity_id),
360 {:ok, activity} <- insert(like_data, local),
361 {:ok, object} <- add_like_to_object(activity, object),
362 :ok <- maybe_federate(activity) do
363 {:ok, activity, object}
365 %Activity{} = activity -> {:ok, activity, object}
366 error -> {:error, error}
370 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
371 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
372 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
373 {:ok, unlike_activity} <- insert(unlike_data, local),
374 {:ok, _activity} <- Repo.delete(like_activity),
375 {:ok, object} <- remove_like_from_object(like_activity, object),
376 :ok <- maybe_federate(unlike_activity) do
377 {:ok, unlike_activity, like_activity, object}
384 %User{ap_id: _} = user,
385 %Object{data: %{"id" => _}} = object,
390 with true <- is_announceable?(object, user, public),
391 announce_data <- make_announce_data(user, object, activity_id, public),
392 {:ok, activity} <- insert(announce_data, local),
393 {:ok, object} <- add_announce_to_object(activity, object),
394 :ok <- maybe_federate(activity) do
395 {:ok, activity, object}
397 error -> {:error, error}
407 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
408 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
409 {:ok, unannounce_activity} <- insert(unannounce_data, local),
410 :ok <- maybe_federate(unannounce_activity),
411 {:ok, _activity} <- Repo.delete(announce_activity),
412 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
413 {:ok, unannounce_activity, object}
419 def follow(follower, followed, activity_id \\ nil, local \\ true) do
420 with data <- make_follow_data(follower, followed, activity_id),
421 {:ok, activity} <- insert(data, local),
422 :ok <- maybe_federate(activity),
423 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
428 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
429 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
430 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
431 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
432 {:ok, activity} <- insert(unfollow_data, local),
433 :ok <- maybe_federate(activity) do
438 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
440 "to" => [follower_address],
443 "object" => %{"type" => "Person", "id" => ap_id}
445 {:ok, activity} <- insert(data, true, true, true),
446 :ok <- maybe_federate(activity) do
451 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
452 local = Keyword.get(options, :local, true)
453 activity_id = Keyword.get(options, :activity_id, nil)
454 actor = Keyword.get(options, :actor, actor)
456 user = User.get_cached_by_ap_id(actor)
457 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
459 with {:ok, object, activity} <- Object.delete(object),
466 "deleted_activity_id" => activity && activity.id
468 |> maybe_put("id", activity_id),
469 {:ok, activity} <- insert(data, local, false),
470 stream_out_participations(object, user),
471 _ <- decrease_replies_count_if_reply(object),
472 {:ok, _actor} <- decrease_note_count_if_public(user, object),
473 :ok <- maybe_federate(activity) do
478 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
479 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
480 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
481 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
483 if unfollow_blocked do
484 follow_activity = fetch_latest_follow(blocker, blocked)
485 if follow_activity, do: unfollow(blocker, blocked, nil, local)
488 with true <- outgoing_blocks,
489 block_data <- make_block_data(blocker, blocked, activity_id),
490 {:ok, activity} <- insert(block_data, local),
491 :ok <- maybe_federate(activity) do
498 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
499 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
500 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
501 {:ok, activity} <- insert(unblock_data, local),
502 :ok <- maybe_federate(activity) do
507 @spec flag(map()) :: {:ok, Activity.t()} | any
517 # only accept false as false value
518 local = !(params[:local] == false)
519 forward = !(params[:forward] == false)
521 additional = params[:additional] || %{}
525 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
527 Map.merge(additional, %{"to" => [], "cc" => []})
530 with flag_data <- make_flag_data(params, additional),
531 {:ok, activity} <- insert(flag_data, local),
532 {:ok, stripped_activity} <- strip_report_status_data(activity),
533 :ok <- maybe_federate(stripped_activity) do
534 Enum.each(User.all_superusers(), fn superuser ->
536 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
537 |> Pleroma.Emails.Mailer.deliver_async()
544 defp fetch_activities_for_context_query(context, opts) do
545 public = [Pleroma.Constants.as_public()]
549 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
552 from(activity in Activity)
553 |> maybe_preload_objects(opts)
554 |> maybe_preload_bookmarks(opts)
555 |> maybe_set_thread_muted_field(opts)
556 |> restrict_blocked(opts)
557 |> restrict_recipients(recipients, opts["user"])
561 "?->>'type' = ? and ?->>'context' = ?",
568 |> exclude_poll_votes(opts)
570 |> order_by([activity], desc: activity.id)
573 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
574 def fetch_activities_for_context(context, opts \\ %{}) do
576 |> fetch_activities_for_context_query(opts)
580 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
581 FlakeId.Ecto.CompatType.t() | nil
582 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
584 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
590 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
591 opts = Map.drop(opts, ["user"])
593 [Pleroma.Constants.as_public()]
594 |> fetch_activities_query(opts)
595 |> restrict_unlisted()
596 |> Pagination.fetch_paginated(opts, pagination)
599 @valid_visibilities ~w[direct unlisted public private]
601 defp restrict_visibility(query, %{visibility: visibility})
602 when is_list(visibility) do
603 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
609 "activity_visibility(?, ?, ?) = ANY (?)",
619 Logger.error("Could not restrict visibility to #{visibility}")
623 defp restrict_visibility(query, %{visibility: visibility})
624 when visibility in @valid_visibilities do
628 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
632 defp restrict_visibility(_query, %{visibility: visibility})
633 when visibility not in @valid_visibilities do
634 Logger.error("Could not restrict visibility to #{visibility}")
637 defp restrict_visibility(query, _visibility), do: query
639 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
640 when is_list(visibility) do
641 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
646 "activity_visibility(?, ?, ?) = ANY (?)",
654 Logger.error("Could not exclude visibility to #{visibility}")
659 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
660 when visibility in @valid_visibilities do
665 "activity_visibility(?, ?, ?) = ?",
674 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
675 when visibility not in @valid_visibilities do
676 Logger.error("Could not exclude visibility to #{visibility}")
680 defp exclude_visibility(query, _visibility), do: query
682 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
685 defp restrict_thread_visibility(
687 %{"user" => %User{skip_thread_containment: true}},
692 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
695 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
699 defp restrict_thread_visibility(query, _, _), do: query
701 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
704 |> Map.put("user", reading_user)
705 |> Map.put("actor_id", user.ap_id)
706 |> Map.put("whole_db", true)
709 user_activities_recipients(%{
710 "godmode" => params["godmode"],
711 "reading_user" => reading_user
714 fetch_activities(recipients, params)
718 def fetch_user_activities(user, reading_user, params \\ %{}) do
721 |> Map.put("type", ["Create", "Announce"])
722 |> Map.put("user", reading_user)
723 |> Map.put("actor_id", user.ap_id)
724 |> Map.put("whole_db", true)
725 |> Map.put("pinned_activity_ids", user.pinned_activities)
728 user_activities_recipients(%{
729 "godmode" => params["godmode"],
730 "reading_user" => reading_user
733 fetch_activities(recipients, params)
737 def fetch_instance_activities(params) do
740 |> Map.put("type", ["Create", "Announce"])
741 |> Map.put("instance", params["instance"])
742 |> Map.put("whole_db", true)
744 fetch_activities([Pleroma.Constants.as_public()], params, :offset)
748 defp user_activities_recipients(%{"godmode" => true}) do
752 defp user_activities_recipients(%{"reading_user" => reading_user}) do
754 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
756 [Pleroma.Constants.as_public()]
760 defp restrict_since(query, %{"since_id" => ""}), do: query
762 defp restrict_since(query, %{"since_id" => since_id}) do
763 from(activity in query, where: activity.id > ^since_id)
766 defp restrict_since(query, _), do: query
768 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
769 raise "Can't use the child object without preloading!"
772 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
773 when is_list(tag_reject) and tag_reject != [] do
775 [_activity, object] in query,
776 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
780 defp restrict_tag_reject(query, _), do: query
782 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
783 raise "Can't use the child object without preloading!"
786 defp restrict_tag_all(query, %{"tag_all" => tag_all})
787 when is_list(tag_all) and tag_all != [] do
789 [_activity, object] in query,
790 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
794 defp restrict_tag_all(query, _), do: query
796 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
797 raise "Can't use the child object without preloading!"
800 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
802 [_activity, object] in query,
803 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
807 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
809 [_activity, object] in query,
810 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
814 defp restrict_tag(query, _), do: query
816 defp restrict_recipients(query, [], _user), do: query
818 defp restrict_recipients(query, recipients, nil) do
819 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
822 defp restrict_recipients(query, recipients, user) do
825 where: fragment("? && ?", ^recipients, activity.recipients),
826 or_where: activity.actor == ^user.ap_id
830 defp restrict_local(query, %{"local_only" => true}) do
831 from(activity in query, where: activity.local == true)
834 defp restrict_local(query, _), do: query
836 defp restrict_actor(query, %{"actor_id" => actor_id}) do
837 from(activity in query, where: activity.actor == ^actor_id)
840 defp restrict_actor(query, _), do: query
842 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
843 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
846 defp restrict_type(query, %{"type" => type}) do
847 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
850 defp restrict_type(query, _), do: query
852 defp restrict_state(query, %{"state" => state}) do
853 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
856 defp restrict_state(query, _), do: query
858 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
860 [_activity, object] in query,
861 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
865 defp restrict_favorited_by(query, _), do: query
867 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
868 raise "Can't use the child object without preloading!"
871 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
873 [_activity, object] in query,
874 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
878 defp restrict_media(query, _), do: query
880 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
882 [_activity, object] in query,
883 where: fragment("?->>'inReplyTo' is null", object.data)
887 defp restrict_replies(query, _), do: query
889 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
890 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
893 defp restrict_reblogs(query, _), do: query
895 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
897 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
901 from([activity] in query,
902 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
903 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
906 unless opts["skip_preload"] do
907 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
913 defp restrict_muted(query, _), do: query
915 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
916 blocks = user.blocks || []
917 domain_blocks = user.domain_blocks || []
920 |> User.get_friends_ap_ids()
923 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
926 [activity, object: o] in query,
927 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
928 where: fragment("not (? && ?)", activity.recipients, ^blocks),
931 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
936 where: fragment("(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)", activity.actor, ^domain_blocks, activity.actor, ^following_ap_ids),
937 where: fragment("(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)", o.data, ^domain_blocks, o.data, ^following_ap_ids)
941 defp restrict_blocked(query, _), do: query
943 defp restrict_unlisted(query) do
948 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
950 ^[Pleroma.Constants.as_public()]
955 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
956 from(activity in query, where: activity.id in ^ids)
959 defp restrict_pinned(query, _), do: query
961 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
962 muted_reblogs = user.muted_reblogs || []
968 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
976 defp restrict_muted_reblogs(query, _), do: query
978 defp restrict_instance(query, %{"instance" => instance}) do
983 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
987 from(activity in query, where: activity.actor in ^users)
990 defp restrict_instance(query, _), do: query
992 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
994 defp exclude_poll_votes(query, _) do
995 if has_named_binding?(query, :object) do
996 from([activity, object: o] in query,
997 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1004 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1005 from(activity in query, where: activity.id != ^id)
1008 defp exclude_id(query, _), do: query
1010 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1012 defp maybe_preload_objects(query, _) do
1014 |> Activity.with_preloaded_object()
1017 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1019 defp maybe_preload_bookmarks(query, opts) do
1021 |> Activity.with_preloaded_bookmark(opts["user"])
1024 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1026 defp maybe_set_thread_muted_field(query, opts) do
1028 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1031 defp maybe_order(query, %{order: :desc}) do
1033 |> order_by(desc: :id)
1036 defp maybe_order(query, %{order: :asc}) do
1038 |> order_by(asc: :id)
1041 defp maybe_order(query, _), do: query
1043 def fetch_activities_query(recipients, opts \\ %{}) do
1045 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1049 |> maybe_preload_objects(opts)
1050 |> maybe_preload_bookmarks(opts)
1051 |> maybe_set_thread_muted_field(opts)
1052 |> maybe_order(opts)
1053 |> restrict_recipients(recipients, opts["user"])
1054 |> restrict_tag(opts)
1055 |> restrict_tag_reject(opts)
1056 |> restrict_tag_all(opts)
1057 |> restrict_since(opts)
1058 |> restrict_local(opts)
1059 |> restrict_actor(opts)
1060 |> restrict_type(opts)
1061 |> restrict_state(opts)
1062 |> restrict_favorited_by(opts)
1063 |> restrict_blocked(opts)
1064 |> restrict_muted(opts)
1065 |> restrict_media(opts)
1066 |> restrict_visibility(opts)
1067 |> restrict_thread_visibility(opts, config)
1068 |> restrict_replies(opts)
1069 |> restrict_reblogs(opts)
1070 |> restrict_pinned(opts)
1071 |> restrict_muted_reblogs(opts)
1072 |> restrict_instance(opts)
1073 |> Activity.restrict_deactivated_users()
1074 |> exclude_poll_votes(opts)
1075 |> exclude_visibility(opts)
1078 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1079 list_memberships = Pleroma.List.memberships(opts["user"])
1081 fetch_activities_query(recipients ++ list_memberships, opts)
1082 |> Pagination.fetch_paginated(opts, pagination)
1084 |> maybe_update_cc(list_memberships, opts["user"])
1087 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1088 when is_list(list_memberships) and length(list_memberships) > 0 do
1089 Enum.map(activities, fn
1090 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1091 if Enum.any?(bcc, &(&1 in list_memberships)) do
1092 update_in(activity.data["cc"], &[user_ap_id | &1])
1102 defp maybe_update_cc(activities, _, _), do: activities
1104 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1105 from(activity in query,
1107 fragment("? && ?", activity.recipients, ^recipients) or
1108 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1109 ^Pleroma.Constants.as_public() in activity.recipients)
1113 def fetch_activities_bounded(
1115 recipients_with_public,
1117 pagination \\ :keyset
1119 fetch_activities_query([], opts)
1120 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1121 |> Pagination.fetch_paginated(opts, pagination)
1125 def upload(file, opts \\ []) do
1126 with {:ok, data} <- Upload.store(file, opts) do
1129 Map.put(data, "actor", opts[:actor])
1134 Repo.insert(%Object{data: obj_data})
1138 defp object_to_user_data(data) do
1140 data["icon"]["url"] &&
1143 "url" => [%{"href" => data["icon"]["url"]}]
1147 data["image"]["url"] &&
1150 "url" => [%{"href" => data["image"]["url"]}]
1155 |> Map.get("attachment", [])
1156 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1157 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1159 locked = data["manuallyApprovesFollowers"] || false
1160 data = Transmogrifier.maybe_fix_user_object(data)
1161 discoverable = data["discoverable"] || false
1162 invisible = data["invisible"] || false
1171 discoverable: discoverable,
1172 invisible: invisible,
1175 follower_address: data["followers"],
1176 following_address: data["following"],
1177 bio: data["summary"]
1180 # nickname can be nil because of virtual actors
1182 if data["preferredUsername"] do
1186 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1189 Map.put(user_data, :nickname, nil)
1195 def fetch_follow_information_for_user(user) do
1196 with {:ok, following_data} <-
1197 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1198 following_count when is_integer(following_count) <- following_data["totalItems"],
1199 {:ok, hide_follows} <- collection_private(following_data),
1200 {:ok, followers_data} <-
1201 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1202 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1203 {:ok, hide_followers} <- collection_private(followers_data) do
1206 hide_follows: hide_follows,
1207 follower_count: followers_count,
1208 following_count: following_count,
1209 hide_followers: hide_followers
1220 defp maybe_update_follow_information(data) do
1221 with {:enabled, true} <-
1222 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1223 {:ok, info} <- fetch_follow_information_for_user(data) do
1224 info = Map.merge(data[:info] || %{}, info)
1225 Map.put(data, :info, info)
1227 {:enabled, false} ->
1232 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1239 defp collection_private(data) do
1240 if is_map(data["first"]) and
1241 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1244 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1245 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1248 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1260 def user_data_from_user_object(data) do
1261 with {:ok, data} <- MRF.filter(data),
1262 {:ok, data} <- object_to_user_data(data) do
1269 def fetch_and_prepare_user_from_ap_id(ap_id) do
1270 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1271 {:ok, data} <- user_data_from_user_object(data),
1272 data <- maybe_update_follow_information(data) do
1276 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1281 def make_user_from_ap_id(ap_id) do
1282 if _user = User.get_cached_by_ap_id(ap_id) do
1283 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1285 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1286 User.insert_or_update_user(data)
1293 def make_user_from_nickname(nickname) do
1294 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1295 make_user_from_ap_id(ap_id)
1297 _e -> {:error, "No AP id in WebFinger"}
1301 # filter out broken threads
1302 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1303 entire_thread_visible_for_user?(activity, user)
1306 # do post-processing on a specific activity
1307 def contain_activity(%Activity{} = activity, %User{} = user) do
1308 contain_broken_threads(activity, user)
1311 def fetch_direct_messages_query do
1313 |> restrict_type(%{"type" => "Create"})
1314 |> restrict_visibility(%{visibility: "direct"})
1315 |> order_by([activity], asc: activity.id)