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 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
327 %User{ap_id: ap_id} = user,
328 %Object{data: %{"id" => _}} = object,
332 with nil <- get_existing_like(ap_id, object),
333 like_data <- make_like_data(user, object, activity_id),
334 {:ok, activity} <- insert(like_data, local),
335 {:ok, object} <- add_like_to_object(activity, object),
336 :ok <- maybe_federate(activity) do
337 {:ok, activity, object}
339 %Activity{} = activity -> {:ok, activity, object}
340 error -> {:error, error}
344 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
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_announceable?(object, user, public),
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),
397 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
402 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
403 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
404 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
405 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
406 {:ok, activity} <- insert(unfollow_data, local),
407 :ok <- maybe_federate(activity) do
412 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
414 "to" => [follower_address],
417 "object" => %{"type" => "Person", "id" => ap_id}
419 {:ok, activity} <- insert(data, true, true, true),
420 :ok <- maybe_federate(activity) do
425 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
426 local = Keyword.get(options, :local, true)
427 activity_id = Keyword.get(options, :activity_id, nil)
428 actor = Keyword.get(options, :actor, actor)
430 user = User.get_cached_by_ap_id(actor)
431 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
433 with {:ok, object, activity} <- Object.delete(object),
440 "deleted_activity_id" => activity && activity.id
442 |> maybe_put("id", activity_id),
443 {:ok, activity} <- insert(data, local, false),
444 stream_out_participations(object, user),
445 _ <- decrease_replies_count_if_reply(object),
446 {:ok, _actor} <- decrease_note_count_if_public(user, object),
447 :ok <- maybe_federate(activity) do
452 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
453 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
454 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
455 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
457 if unfollow_blocked do
458 follow_activity = fetch_latest_follow(blocker, blocked)
459 if follow_activity, do: unfollow(blocker, blocked, nil, local)
462 with true <- outgoing_blocks,
463 block_data <- make_block_data(blocker, blocked, activity_id),
464 {:ok, activity} <- insert(block_data, local),
465 :ok <- maybe_federate(activity) do
472 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
473 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
474 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
475 {:ok, activity} <- insert(unblock_data, local),
476 :ok <- maybe_federate(activity) do
481 @spec flag(map()) :: {:ok, Activity.t()} | any
491 # only accept false as false value
492 local = !(params[:local] == false)
493 forward = !(params[:forward] == false)
495 additional = params[:additional] || %{}
499 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
501 Map.merge(additional, %{"to" => [], "cc" => []})
504 with flag_data <- make_flag_data(params, additional),
505 {:ok, activity} <- insert(flag_data, local),
506 {:ok, stripped_activity} <- strip_report_status_data(activity),
507 :ok <- maybe_federate(stripped_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()]
523 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
526 from(activity in Activity)
527 |> maybe_preload_objects(opts)
528 |> maybe_preload_bookmarks(opts)
529 |> maybe_set_thread_muted_field(opts)
530 |> restrict_blocked(opts)
531 |> restrict_recipients(recipients, opts["user"])
535 "?->>'type' = ? and ?->>'context' = ?",
542 |> exclude_poll_votes(opts)
544 |> order_by([activity], desc: activity.id)
547 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
548 def fetch_activities_for_context(context, opts \\ %{}) do
550 |> fetch_activities_for_context_query(opts)
554 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
555 FlakeId.Ecto.CompatType.t() | nil
556 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
558 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
564 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
565 opts = Map.drop(opts, ["user"])
567 [Pleroma.Constants.as_public()]
568 |> fetch_activities_query(opts)
569 |> restrict_unlisted()
570 |> Pagination.fetch_paginated(opts, pagination)
573 @valid_visibilities ~w[direct unlisted public private]
575 defp restrict_visibility(query, %{visibility: visibility})
576 when is_list(visibility) do
577 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
583 "activity_visibility(?, ?, ?) = ANY (?)",
593 Logger.error("Could not restrict visibility to #{visibility}")
597 defp restrict_visibility(query, %{visibility: visibility})
598 when visibility in @valid_visibilities do
602 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
606 defp restrict_visibility(_query, %{visibility: visibility})
607 when visibility not in @valid_visibilities do
608 Logger.error("Could not restrict visibility to #{visibility}")
611 defp restrict_visibility(query, _visibility), do: query
613 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
614 when is_list(visibility) do
615 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
620 "activity_visibility(?, ?, ?) = ANY (?)",
628 Logger.error("Could not exclude visibility to #{visibility}")
633 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
634 when visibility in @valid_visibilities do
639 "activity_visibility(?, ?, ?) = ?",
648 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
649 when visibility not in @valid_visibilities do
650 Logger.error("Could not exclude visibility to #{visibility}")
654 defp exclude_visibility(query, _visibility), do: query
656 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
659 defp restrict_thread_visibility(
661 %{"user" => %User{skip_thread_containment: true}},
666 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
669 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
673 defp restrict_thread_visibility(query, _, _), do: query
675 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
678 |> Map.put("user", reading_user)
679 |> Map.put("actor_id", user.ap_id)
680 |> Map.put("whole_db", true)
683 user_activities_recipients(%{
684 "godmode" => params["godmode"],
685 "reading_user" => reading_user
688 fetch_activities(recipients, params)
692 def fetch_user_activities(user, reading_user, params \\ %{}) do
695 |> Map.put("type", ["Create", "Announce"])
696 |> Map.put("user", reading_user)
697 |> Map.put("actor_id", user.ap_id)
698 |> Map.put("whole_db", true)
699 |> Map.put("pinned_activity_ids", user.pinned_activities)
702 user_activities_recipients(%{
703 "godmode" => params["godmode"],
704 "reading_user" => reading_user
707 fetch_activities(recipients, params)
711 def fetch_instance_activities(params) do
714 |> Map.put("type", ["Create", "Announce"])
715 |> Map.put("instance", params["instance"])
716 |> Map.put("whole_db", true)
718 fetch_activities([Pleroma.Constants.as_public()], params, :offset)
722 defp user_activities_recipients(%{"godmode" => true}) do
726 defp user_activities_recipients(%{"reading_user" => reading_user}) do
728 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
730 [Pleroma.Constants.as_public()]
734 defp restrict_since(query, %{"since_id" => ""}), do: query
736 defp restrict_since(query, %{"since_id" => since_id}) do
737 from(activity in query, where: activity.id > ^since_id)
740 defp restrict_since(query, _), do: query
742 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
743 raise "Can't use the child object without preloading!"
746 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
747 when is_list(tag_reject) and tag_reject != [] do
749 [_activity, object] in query,
750 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
754 defp restrict_tag_reject(query, _), do: query
756 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
757 raise "Can't use the child object without preloading!"
760 defp restrict_tag_all(query, %{"tag_all" => tag_all})
761 when is_list(tag_all) and tag_all != [] do
763 [_activity, object] in query,
764 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
768 defp restrict_tag_all(query, _), do: query
770 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
771 raise "Can't use the child object without preloading!"
774 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
776 [_activity, object] in query,
777 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
781 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
783 [_activity, object] in query,
784 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
788 defp restrict_tag(query, _), do: query
790 defp restrict_recipients(query, [], _user), do: query
792 defp restrict_recipients(query, recipients, nil) do
793 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
796 defp restrict_recipients(query, recipients, user) do
799 where: fragment("? && ?", ^recipients, activity.recipients),
800 or_where: activity.actor == ^user.ap_id
804 defp restrict_local(query, %{"local_only" => true}) do
805 from(activity in query, where: activity.local == true)
808 defp restrict_local(query, _), do: query
810 defp restrict_actor(query, %{"actor_id" => actor_id}) do
811 from(activity in query, where: activity.actor == ^actor_id)
814 defp restrict_actor(query, _), do: query
816 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
817 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
820 defp restrict_type(query, %{"type" => type}) do
821 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
824 defp restrict_type(query, _), do: query
826 defp restrict_state(query, %{"state" => state}) do
827 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
830 defp restrict_state(query, _), do: query
832 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
834 [_activity, object] in query,
835 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
839 defp restrict_favorited_by(query, _), do: query
841 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
842 raise "Can't use the child object without preloading!"
845 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
847 [_activity, object] in query,
848 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
852 defp restrict_media(query, _), do: query
854 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
856 [_activity, object] in query,
857 where: fragment("?->>'inReplyTo' is null", object.data)
861 defp restrict_replies(query, _), do: query
863 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
864 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
867 defp restrict_reblogs(query, _), do: query
869 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
871 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
875 from([activity] in query,
876 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
877 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
880 unless opts["skip_preload"] do
881 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
887 defp restrict_muted(query, _), do: query
889 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
890 blocks = user.blocks || []
891 domain_blocks = user.domain_blocks || []
894 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
897 [activity, object: o] in query,
898 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
899 where: fragment("not (? && ?)", activity.recipients, ^blocks),
902 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
907 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
908 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
912 defp restrict_blocked(query, _), do: query
914 defp restrict_unlisted(query) do
919 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
921 ^[Pleroma.Constants.as_public()]
926 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
927 from(activity in query, where: activity.id in ^ids)
930 defp restrict_pinned(query, _), do: query
932 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
933 muted_reblogs = user.muted_reblogs || []
939 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
947 defp restrict_muted_reblogs(query, _), do: query
949 defp restrict_instance(query, %{"instance" => instance}) do
954 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
958 from(activity in query, where: activity.actor in ^users)
961 defp restrict_instance(query, _), do: query
963 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
965 defp exclude_poll_votes(query, _) do
966 if has_named_binding?(query, :object) do
967 from([activity, object: o] in query,
968 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
975 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
976 from(activity in query, where: activity.id != ^id)
979 defp exclude_id(query, _), do: query
981 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
983 defp maybe_preload_objects(query, _) do
985 |> Activity.with_preloaded_object()
988 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
990 defp maybe_preload_bookmarks(query, opts) do
992 |> Activity.with_preloaded_bookmark(opts["user"])
995 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
997 defp maybe_set_thread_muted_field(query, opts) do
999 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1002 defp maybe_order(query, %{order: :desc}) do
1004 |> order_by(desc: :id)
1007 defp maybe_order(query, %{order: :asc}) do
1009 |> order_by(asc: :id)
1012 defp maybe_order(query, _), do: query
1014 def fetch_activities_query(recipients, opts \\ %{}) do
1016 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1020 |> maybe_preload_objects(opts)
1021 |> maybe_preload_bookmarks(opts)
1022 |> maybe_set_thread_muted_field(opts)
1023 |> maybe_order(opts)
1024 |> restrict_recipients(recipients, opts["user"])
1025 |> restrict_tag(opts)
1026 |> restrict_tag_reject(opts)
1027 |> restrict_tag_all(opts)
1028 |> restrict_since(opts)
1029 |> restrict_local(opts)
1030 |> restrict_actor(opts)
1031 |> restrict_type(opts)
1032 |> restrict_state(opts)
1033 |> restrict_favorited_by(opts)
1034 |> restrict_blocked(opts)
1035 |> restrict_muted(opts)
1036 |> restrict_media(opts)
1037 |> restrict_visibility(opts)
1038 |> restrict_thread_visibility(opts, config)
1039 |> restrict_replies(opts)
1040 |> restrict_reblogs(opts)
1041 |> restrict_pinned(opts)
1042 |> restrict_muted_reblogs(opts)
1043 |> restrict_instance(opts)
1044 |> Activity.restrict_deactivated_users()
1045 |> exclude_poll_votes(opts)
1046 |> exclude_visibility(opts)
1049 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1050 list_memberships = Pleroma.List.memberships(opts["user"])
1052 fetch_activities_query(recipients ++ list_memberships, opts)
1053 |> Pagination.fetch_paginated(opts, pagination)
1055 |> maybe_update_cc(list_memberships, opts["user"])
1058 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1059 when is_list(list_memberships) and length(list_memberships) > 0 do
1060 Enum.map(activities, fn
1061 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1062 if Enum.any?(bcc, &(&1 in list_memberships)) do
1063 update_in(activity.data["cc"], &[user_ap_id | &1])
1073 defp maybe_update_cc(activities, _, _), do: activities
1075 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1076 from(activity in query,
1078 fragment("? && ?", activity.recipients, ^recipients) or
1079 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1080 ^Pleroma.Constants.as_public() in activity.recipients)
1084 def fetch_activities_bounded(
1086 recipients_with_public,
1088 pagination \\ :keyset
1090 fetch_activities_query([], opts)
1091 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1092 |> Pagination.fetch_paginated(opts, pagination)
1096 def upload(file, opts \\ []) do
1097 with {:ok, data} <- Upload.store(file, opts) do
1100 Map.put(data, "actor", opts[:actor])
1105 Repo.insert(%Object{data: obj_data})
1109 defp object_to_user_data(data) do
1111 data["icon"]["url"] &&
1114 "url" => [%{"href" => data["icon"]["url"]}]
1118 data["image"]["url"] &&
1121 "url" => [%{"href" => data["image"]["url"]}]
1126 |> Map.get("attachment", [])
1127 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1128 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1130 locked = data["manuallyApprovesFollowers"] || false
1131 data = Transmogrifier.maybe_fix_user_object(data)
1132 discoverable = data["discoverable"] || false
1133 invisible = data["invisible"] || false
1142 discoverable: discoverable,
1143 invisible: invisible,
1146 follower_address: data["followers"],
1147 following_address: data["following"],
1148 bio: data["summary"]
1151 # nickname can be nil because of virtual actors
1153 if data["preferredUsername"] do
1157 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1160 Map.put(user_data, :nickname, nil)
1166 def fetch_follow_information_for_user(user) do
1167 with {:ok, following_data} <-
1168 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1169 following_count when is_integer(following_count) <- following_data["totalItems"],
1170 {:ok, hide_follows} <- collection_private(following_data),
1171 {:ok, followers_data} <-
1172 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1173 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1174 {:ok, hide_followers} <- collection_private(followers_data) do
1177 hide_follows: hide_follows,
1178 follower_count: followers_count,
1179 following_count: following_count,
1180 hide_followers: hide_followers
1191 defp maybe_update_follow_information(data) do
1192 with {:enabled, true} <-
1193 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1194 {:ok, info} <- fetch_follow_information_for_user(data) do
1195 info = Map.merge(data[:info] || %{}, info)
1196 Map.put(data, :info, info)
1198 {:enabled, false} ->
1203 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1210 defp collection_private(data) do
1211 if is_map(data["first"]) and
1212 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1215 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1216 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1219 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1231 def user_data_from_user_object(data) do
1232 with {:ok, data} <- MRF.filter(data),
1233 {:ok, data} <- object_to_user_data(data) do
1240 def fetch_and_prepare_user_from_ap_id(ap_id) do
1241 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1242 {:ok, data} <- user_data_from_user_object(data),
1243 data <- maybe_update_follow_information(data) do
1247 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1252 def make_user_from_ap_id(ap_id) do
1253 if _user = User.get_cached_by_ap_id(ap_id) do
1254 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1256 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1257 User.insert_or_update_user(data)
1264 def make_user_from_nickname(nickname) do
1265 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1266 make_user_from_ap_id(ap_id)
1268 _e -> {:error, "No AP id in WebFinger"}
1272 # filter out broken threads
1273 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1274 entire_thread_visible_for_user?(activity, user)
1277 # do post-processing on a specific activity
1278 def contain_activity(%Activity{} = activity, %User{} = user) do
1279 contain_broken_threads(activity, user)
1282 def fetch_direct_messages_query do
1284 |> restrict_type(%{"type" => "Create"})
1285 |> restrict_visibility(%{visibility: "direct"})
1286 |> order_by([activity], asc: activity.id)