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 true <- 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}
339 def unreact_with_emoji(user, reaction_id, options \\ []) do
340 with local <- Keyword.get(options, :local, true),
341 activity_id <- Keyword.get(options, :activity_id, nil),
342 user_ap_id <- user.ap_id,
343 %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
344 object <- Object.normalize(reaction_activity),
345 unreact_data <- make_undo_data(user, reaction_activity, activity_id),
346 {:ok, activity} <- insert(unreact_data, local),
347 {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
348 :ok <- maybe_federate(activity) do
349 {:ok, activity, object}
355 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
357 %User{ap_id: ap_id} = user,
358 %Object{data: %{"id" => _}} = object,
362 with nil <- get_existing_like(ap_id, object),
363 like_data <- make_like_data(user, object, activity_id),
364 {:ok, activity} <- insert(like_data, local),
365 {:ok, object} <- add_like_to_object(activity, object),
366 :ok <- maybe_federate(activity) do
367 {:ok, activity, object}
369 %Activity{} = activity -> {:ok, activity, object}
370 error -> {:error, error}
374 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
375 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
376 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
377 {:ok, unlike_activity} <- insert(unlike_data, local),
378 {:ok, _activity} <- Repo.delete(like_activity),
379 {:ok, object} <- remove_like_from_object(like_activity, object),
380 :ok <- maybe_federate(unlike_activity) do
381 {:ok, unlike_activity, like_activity, object}
388 %User{ap_id: _} = user,
389 %Object{data: %{"id" => _}} = object,
394 with true <- is_announceable?(object, user, public),
395 announce_data <- make_announce_data(user, object, activity_id, public),
396 {:ok, activity} <- insert(announce_data, local),
397 {:ok, object} <- add_announce_to_object(activity, object),
398 :ok <- maybe_federate(activity) do
399 {:ok, activity, object}
401 error -> {:error, error}
411 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
412 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
413 {:ok, unannounce_activity} <- insert(unannounce_data, local),
414 :ok <- maybe_federate(unannounce_activity),
415 {:ok, _activity} <- Repo.delete(announce_activity),
416 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
417 {:ok, unannounce_activity, object}
423 def follow(follower, followed, activity_id \\ nil, local \\ true) do
424 with data <- make_follow_data(follower, followed, activity_id),
425 {:ok, activity} <- insert(data, local),
426 :ok <- maybe_federate(activity),
427 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
432 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
433 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
434 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
435 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
436 {:ok, activity} <- insert(unfollow_data, local),
437 :ok <- maybe_federate(activity) do
442 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
444 "to" => [follower_address],
447 "object" => %{"type" => "Person", "id" => ap_id}
449 {:ok, activity} <- insert(data, true, true, true),
450 :ok <- maybe_federate(activity) do
455 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
456 local = Keyword.get(options, :local, true)
457 activity_id = Keyword.get(options, :activity_id, nil)
458 actor = Keyword.get(options, :actor, actor)
460 user = User.get_cached_by_ap_id(actor)
461 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
463 with create_activity <- Activity.get_create_by_object_ap_id(id),
470 "deleted_activity_id" => create_activity && create_activity.id
472 |> maybe_put("id", activity_id),
473 {:ok, activity} <- insert(data, local, false),
474 {:ok, object, _create_activity} <- Object.delete(object),
475 stream_out_participations(object, user),
476 _ <- decrease_replies_count_if_reply(object),
477 {:ok, _actor} <- decrease_note_count_if_public(user, object),
478 :ok <- maybe_federate(activity) do
483 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
484 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
485 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
486 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
488 if unfollow_blocked do
489 follow_activity = fetch_latest_follow(blocker, blocked)
490 if follow_activity, do: unfollow(blocker, blocked, nil, local)
493 with true <- outgoing_blocks,
494 block_data <- make_block_data(blocker, blocked, activity_id),
495 {:ok, activity} <- insert(block_data, local),
496 :ok <- maybe_federate(activity) do
503 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
504 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
505 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
506 {:ok, activity} <- insert(unblock_data, local),
507 :ok <- maybe_federate(activity) do
512 @spec flag(map()) :: {:ok, Activity.t()} | any
522 # only accept false as false value
523 local = !(params[:local] == false)
524 forward = !(params[:forward] == false)
526 additional = params[:additional] || %{}
530 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
532 Map.merge(additional, %{"to" => [], "cc" => []})
535 with flag_data <- make_flag_data(params, additional),
536 {:ok, activity} <- insert(flag_data, local),
537 {:ok, stripped_activity} <- strip_report_status_data(activity),
538 :ok <- maybe_federate(stripped_activity) do
539 Enum.each(User.all_superusers(), fn superuser ->
541 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
542 |> Pleroma.Emails.Mailer.deliver_async()
549 def move(%User{} = origin, %User{} = target, local \\ true) do
552 "actor" => origin.ap_id,
553 "object" => origin.ap_id,
554 "target" => target.ap_id
557 with true <- origin.ap_id in target.also_known_as,
558 {:ok, activity} <- insert(params, local) do
559 maybe_federate(activity)
561 BackgroundWorker.enqueue("move_following", %{
562 "origin_id" => origin.id,
563 "target_id" => target.id
568 false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
573 defp fetch_activities_for_context_query(context, opts) do
574 public = [Pleroma.Constants.as_public()]
578 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
581 from(activity in Activity)
582 |> maybe_preload_objects(opts)
583 |> maybe_preload_bookmarks(opts)
584 |> maybe_set_thread_muted_field(opts)
585 |> restrict_blocked(opts)
586 |> restrict_recipients(recipients, opts["user"])
590 "?->>'type' = ? and ?->>'context' = ?",
597 |> exclude_poll_votes(opts)
599 |> order_by([activity], desc: activity.id)
602 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
603 def fetch_activities_for_context(context, opts \\ %{}) do
605 |> fetch_activities_for_context_query(opts)
609 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
610 FlakeId.Ecto.CompatType.t() | nil
611 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
613 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
619 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
620 opts = Map.drop(opts, ["user"])
622 [Pleroma.Constants.as_public()]
623 |> fetch_activities_query(opts)
624 |> restrict_unlisted()
625 |> Pagination.fetch_paginated(opts, pagination)
628 @valid_visibilities ~w[direct unlisted public private]
630 defp restrict_visibility(query, %{visibility: visibility})
631 when is_list(visibility) do
632 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
638 "activity_visibility(?, ?, ?) = ANY (?)",
648 Logger.error("Could not restrict visibility to #{visibility}")
652 defp restrict_visibility(query, %{visibility: visibility})
653 when visibility in @valid_visibilities do
657 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
661 defp restrict_visibility(_query, %{visibility: visibility})
662 when visibility not in @valid_visibilities do
663 Logger.error("Could not restrict visibility to #{visibility}")
666 defp restrict_visibility(query, _visibility), do: query
668 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
669 when is_list(visibility) do
670 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
675 "activity_visibility(?, ?, ?) = ANY (?)",
683 Logger.error("Could not exclude visibility to #{visibility}")
688 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
689 when visibility in @valid_visibilities do
694 "activity_visibility(?, ?, ?) = ?",
703 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
704 when visibility not in @valid_visibilities do
705 Logger.error("Could not exclude visibility to #{visibility}")
709 defp exclude_visibility(query, _visibility), do: query
711 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
714 defp restrict_thread_visibility(
716 %{"user" => %User{skip_thread_containment: true}},
721 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
724 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
728 defp restrict_thread_visibility(query, _, _), do: query
730 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
733 |> Map.put("user", reading_user)
734 |> Map.put("actor_id", user.ap_id)
737 user_activities_recipients(%{
738 "godmode" => params["godmode"],
739 "reading_user" => reading_user
742 fetch_activities(recipients, params)
746 def fetch_user_activities(user, reading_user, params \\ %{}) do
749 |> Map.put("type", ["Create", "Announce"])
750 |> Map.put("user", reading_user)
751 |> Map.put("actor_id", user.ap_id)
752 |> Map.put("pinned_activity_ids", user.pinned_activities)
755 if User.blocks?(reading_user, user) do
759 |> Map.put("blocking_user", reading_user)
760 |> Map.put("muting_user", reading_user)
764 user_activities_recipients(%{
765 "godmode" => params["godmode"],
766 "reading_user" => reading_user
769 fetch_activities(recipients, params)
773 def fetch_statuses(reading_user, params) do
776 |> Map.put("type", ["Create", "Announce"])
779 user_activities_recipients(%{
780 "godmode" => params["godmode"],
781 "reading_user" => reading_user
784 fetch_activities(recipients, params, :offset)
788 defp user_activities_recipients(%{"godmode" => true}) do
792 defp user_activities_recipients(%{"reading_user" => reading_user}) do
794 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
796 [Pleroma.Constants.as_public()]
800 defp restrict_since(query, %{"since_id" => ""}), do: query
802 defp restrict_since(query, %{"since_id" => since_id}) do
803 from(activity in query, where: activity.id > ^since_id)
806 defp restrict_since(query, _), do: query
808 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
809 raise "Can't use the child object without preloading!"
812 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
813 when is_list(tag_reject) and tag_reject != [] do
815 [_activity, object] in query,
816 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
820 defp restrict_tag_reject(query, _), do: query
822 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
823 raise "Can't use the child object without preloading!"
826 defp restrict_tag_all(query, %{"tag_all" => tag_all})
827 when is_list(tag_all) and tag_all != [] do
829 [_activity, object] in query,
830 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
834 defp restrict_tag_all(query, _), do: query
836 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
837 raise "Can't use the child object without preloading!"
840 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
842 [_activity, object] in query,
843 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
847 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
849 [_activity, object] in query,
850 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
854 defp restrict_tag(query, _), do: query
856 defp restrict_recipients(query, [], _user), do: query
858 defp restrict_recipients(query, recipients, nil) do
859 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
862 defp restrict_recipients(query, recipients, user) do
865 where: fragment("? && ?", ^recipients, activity.recipients),
866 or_where: activity.actor == ^user.ap_id
870 defp restrict_local(query, %{"local_only" => true}) do
871 from(activity in query, where: activity.local == true)
874 defp restrict_local(query, _), do: query
876 defp restrict_actor(query, %{"actor_id" => actor_id}) do
877 from(activity in query, where: activity.actor == ^actor_id)
880 defp restrict_actor(query, _), do: query
882 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
883 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
886 defp restrict_type(query, %{"type" => type}) do
887 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
890 defp restrict_type(query, _), do: query
892 defp restrict_state(query, %{"state" => state}) do
893 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
896 defp restrict_state(query, _), do: query
898 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
900 [_activity, object] in query,
901 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
905 defp restrict_favorited_by(query, _), do: query
907 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
908 raise "Can't use the child object without preloading!"
911 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
913 [_activity, object] in query,
914 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
918 defp restrict_media(query, _), do: query
920 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
922 [_activity, object] in query,
923 where: fragment("?->>'inReplyTo' is null", object.data)
927 defp restrict_replies(query, _), do: query
929 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
930 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
933 defp restrict_reblogs(query, _), do: query
935 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
937 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
938 mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
941 from([activity] in query,
942 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
943 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
946 unless opts["skip_preload"] do
947 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
953 defp restrict_muted(query, _), do: query
955 defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
956 blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
957 domain_blocks = user.domain_blocks || []
959 following_ap_ids = User.get_friends_ap_ids(user)
962 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
965 [activity, object: o] in query,
966 where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
967 where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
970 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
977 "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
985 "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
994 defp restrict_blocked(query, _), do: query
996 defp restrict_unlisted(query) do
1001 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
1003 ^[Pleroma.Constants.as_public()]
1008 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
1009 from(activity in query, where: activity.id in ^ids)
1012 defp restrict_pinned(query, _), do: query
1014 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
1015 muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
1021 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
1029 defp restrict_muted_reblogs(query, _), do: query
1031 defp restrict_instance(query, %{"instance" => instance}) do
1036 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
1040 from(activity in query, where: activity.actor in ^users)
1043 defp restrict_instance(query, _), do: query
1045 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
1047 defp exclude_poll_votes(query, _) do
1048 if has_named_binding?(query, :object) do
1049 from([activity, object: o] in query,
1050 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1057 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1058 from(activity in query, where: activity.id != ^id)
1061 defp exclude_id(query, _), do: query
1063 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1065 defp maybe_preload_objects(query, _) do
1067 |> Activity.with_preloaded_object()
1070 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1072 defp maybe_preload_bookmarks(query, opts) do
1074 |> Activity.with_preloaded_bookmark(opts["user"])
1077 defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
1079 |> Activity.with_preloaded_report_notes()
1082 defp maybe_preload_report_notes(query, _), do: query
1084 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1086 defp maybe_set_thread_muted_field(query, opts) do
1088 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1091 defp maybe_order(query, %{order: :desc}) do
1093 |> order_by(desc: :id)
1096 defp maybe_order(query, %{order: :asc}) do
1098 |> order_by(asc: :id)
1101 defp maybe_order(query, _), do: query
1103 defp fetch_activities_query_ap_ids_ops(opts) do
1104 source_user = opts["muting_user"]
1105 ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
1109 if opts["blocking_user"] && opts["blocking_user"] == source_user do
1115 preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
1117 restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
1118 restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
1120 restrict_muted_reblogs_opts =
1121 Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
1123 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
1126 def fetch_activities_query(recipients, opts \\ %{}) do
1127 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
1128 fetch_activities_query_ap_ids_ops(opts)
1131 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1135 |> maybe_preload_objects(opts)
1136 |> maybe_preload_bookmarks(opts)
1137 |> maybe_preload_report_notes(opts)
1138 |> maybe_set_thread_muted_field(opts)
1139 |> maybe_order(opts)
1140 |> restrict_recipients(recipients, opts["user"])
1141 |> restrict_tag(opts)
1142 |> restrict_tag_reject(opts)
1143 |> restrict_tag_all(opts)
1144 |> restrict_since(opts)
1145 |> restrict_local(opts)
1146 |> restrict_actor(opts)
1147 |> restrict_type(opts)
1148 |> restrict_state(opts)
1149 |> restrict_favorited_by(opts)
1150 |> restrict_blocked(restrict_blocked_opts)
1151 |> restrict_muted(restrict_muted_opts)
1152 |> restrict_media(opts)
1153 |> restrict_visibility(opts)
1154 |> restrict_thread_visibility(opts, config)
1155 |> restrict_replies(opts)
1156 |> restrict_reblogs(opts)
1157 |> restrict_pinned(opts)
1158 |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
1159 |> restrict_instance(opts)
1160 |> Activity.restrict_deactivated_users()
1161 |> exclude_poll_votes(opts)
1162 |> exclude_visibility(opts)
1165 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1166 list_memberships = Pleroma.List.memberships(opts["user"])
1168 fetch_activities_query(recipients ++ list_memberships, opts)
1169 |> Pagination.fetch_paginated(opts, pagination)
1171 |> maybe_update_cc(list_memberships, opts["user"])
1175 Fetch favorites activities of user with order by sort adds to favorites
1177 @spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
1178 def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
1180 |> Activity.Queries.by_actor()
1181 |> Activity.Queries.by_type("Like")
1182 |> Activity.with_joined_object()
1183 |> Object.with_joined_activity()
1184 |> select([_like, object, activity], %{activity | object: object})
1185 |> order_by([like, _, _], desc: like.id)
1186 |> Pagination.fetch_paginated(
1187 Map.merge(params, %{"skip_order" => true}),
1193 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1194 when is_list(list_memberships) and length(list_memberships) > 0 do
1195 Enum.map(activities, fn
1196 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1197 if Enum.any?(bcc, &(&1 in list_memberships)) do
1198 update_in(activity.data["cc"], &[user_ap_id | &1])
1208 defp maybe_update_cc(activities, _, _), do: activities
1210 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1211 from(activity in query,
1213 fragment("? && ?", activity.recipients, ^recipients) or
1214 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1215 ^Pleroma.Constants.as_public() in activity.recipients)
1219 def fetch_activities_bounded(
1221 recipients_with_public,
1223 pagination \\ :keyset
1225 fetch_activities_query([], opts)
1226 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1227 |> Pagination.fetch_paginated(opts, pagination)
1231 def upload(file, opts \\ []) do
1232 with {:ok, data} <- Upload.store(file, opts) do
1235 Map.put(data, "actor", opts[:actor])
1240 Repo.insert(%Object{data: obj_data})
1244 defp object_to_user_data(data) do
1246 data["icon"]["url"] &&
1249 "url" => [%{"href" => data["icon"]["url"]}]
1253 data["image"]["url"] &&
1256 "url" => [%{"href" => data["image"]["url"]}]
1261 |> Map.get("attachment", [])
1262 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1263 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1265 locked = data["manuallyApprovesFollowers"] || false
1266 data = Transmogrifier.maybe_fix_user_object(data)
1267 discoverable = data["discoverable"] || false
1268 invisible = data["invisible"] || false
1269 actor_type = data["type"] || "Person"
1278 discoverable: discoverable,
1279 invisible: invisible,
1282 follower_address: data["followers"],
1283 following_address: data["following"],
1284 bio: data["summary"],
1285 actor_type: actor_type,
1286 also_known_as: Map.get(data, "alsoKnownAs", [])
1289 # nickname can be nil because of virtual actors
1291 if data["preferredUsername"] do
1295 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1298 Map.put(user_data, :nickname, nil)
1304 def fetch_follow_information_for_user(user) do
1305 with {:ok, following_data} <-
1306 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1307 {:ok, hide_follows} <- collection_private(following_data),
1308 {:ok, followers_data} <-
1309 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1310 {:ok, hide_followers} <- collection_private(followers_data) do
1313 hide_follows: hide_follows,
1314 follower_count: normalize_counter(followers_data["totalItems"]),
1315 following_count: normalize_counter(following_data["totalItems"]),
1316 hide_followers: hide_followers
1319 {:error, _} = e -> e
1324 defp normalize_counter(counter) when is_integer(counter), do: counter
1325 defp normalize_counter(_), do: 0
1327 defp maybe_update_follow_information(data) do
1328 with {:enabled, true} <-
1329 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1330 {:ok, info} <- fetch_follow_information_for_user(data) do
1331 info = Map.merge(data[:info] || %{}, info)
1332 Map.put(data, :info, info)
1334 {:enabled, false} ->
1339 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1346 defp collection_private(%{"first" => %{"type" => type}})
1347 when type in ["CollectionPage", "OrderedCollectionPage"],
1350 defp collection_private(%{"first" => first}) do
1351 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1352 Fetcher.fetch_and_contain_remote_object_from_id(first) do
1355 {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
1356 {:error, _} = e -> e
1361 defp collection_private(_data), do: {:ok, true}
1363 def user_data_from_user_object(data) do
1364 with {:ok, data} <- MRF.filter(data),
1365 {:ok, data} <- object_to_user_data(data) do
1372 def fetch_and_prepare_user_from_ap_id(ap_id) do
1373 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1374 {:ok, data} <- user_data_from_user_object(data),
1375 data <- maybe_update_follow_information(data) do
1378 {:error, "Object has been deleted"} = e ->
1379 Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1383 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1388 def make_user_from_ap_id(ap_id) do
1389 if _user = User.get_cached_by_ap_id(ap_id) do
1390 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1392 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1393 User.insert_or_update_user(data)
1400 def make_user_from_nickname(nickname) do
1401 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1402 make_user_from_ap_id(ap_id)
1404 _e -> {:error, "No AP id in WebFinger"}
1408 # filter out broken threads
1409 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1410 entire_thread_visible_for_user?(activity, user)
1413 # do post-processing on a specific activity
1414 def contain_activity(%Activity{} = activity, %User{} = user) do
1415 contain_broken_threads(activity, user)
1418 def fetch_direct_messages_query do
1420 |> restrict_type(%{"type" => "Create"})
1421 |> restrict_visibility(%{visibility: "direct"})
1422 |> order_by([activity], asc: activity.id)