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 <- maybe_federate(activity) do
507 Enum.each(User.all_superusers(), fn superuser ->
509 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
510 |> Pleroma.Emails.Mailer.deliver_async()
517 defp fetch_activities_for_context_query(context, opts) do
518 public = [Pleroma.Constants.as_public()]
521 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
523 from(activity in Activity)
524 |> maybe_preload_objects(opts)
525 |> maybe_preload_bookmarks(opts)
526 |> maybe_set_thread_muted_field(opts)
527 |> restrict_blocked(opts)
528 |> restrict_recipients(recipients, opts["user"])
532 "?->>'type' = ? and ?->>'context' = ?",
539 |> exclude_poll_votes(opts)
541 |> order_by([activity], desc: activity.id)
544 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
545 def fetch_activities_for_context(context, opts \\ %{}) do
547 |> fetch_activities_for_context_query(opts)
551 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
552 FlakeId.Ecto.CompatType.t() | nil
553 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
555 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
561 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
562 opts = Map.drop(opts, ["user"])
564 [Pleroma.Constants.as_public()]
565 |> fetch_activities_query(opts)
566 |> restrict_unlisted()
567 |> Pagination.fetch_paginated(opts, pagination)
571 @valid_visibilities ~w[direct unlisted public private]
573 defp restrict_visibility(query, %{visibility: visibility})
574 when is_list(visibility) do
575 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
581 "activity_visibility(?, ?, ?) = ANY (?)",
591 Logger.error("Could not restrict visibility to #{visibility}")
595 defp restrict_visibility(query, %{visibility: visibility})
596 when visibility in @valid_visibilities do
600 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
604 defp restrict_visibility(_query, %{visibility: visibility})
605 when visibility not in @valid_visibilities do
606 Logger.error("Could not restrict visibility to #{visibility}")
609 defp restrict_visibility(query, _visibility), do: query
611 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
612 when is_list(visibility) do
613 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
618 "activity_visibility(?, ?, ?) = ANY (?)",
626 Logger.error("Could not exclude visibility to #{visibility}")
631 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
632 when visibility in @valid_visibilities do
637 "activity_visibility(?, ?, ?) = ?",
646 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
647 when visibility not in @valid_visibilities do
648 Logger.error("Could not exclude visibility to #{visibility}")
652 defp exclude_visibility(query, _visibility), do: query
654 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
657 defp restrict_thread_visibility(
659 %{"user" => %User{skip_thread_containment: true}},
664 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
667 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
671 defp restrict_thread_visibility(query, _, _), do: query
673 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
676 |> Map.put("user", reading_user)
677 |> Map.put("actor_id", user.ap_id)
678 |> Map.put("whole_db", true)
681 user_activities_recipients(%{
682 "godmode" => params["godmode"],
683 "reading_user" => reading_user
686 fetch_activities(recipients, params)
690 def fetch_user_activities(user, reading_user, params \\ %{}) do
693 |> Map.put("type", ["Create", "Announce"])
694 |> Map.put("user", reading_user)
695 |> Map.put("actor_id", user.ap_id)
696 |> Map.put("whole_db", true)
697 |> Map.put("pinned_activity_ids", user.pinned_activities)
700 user_activities_recipients(%{
701 "godmode" => params["godmode"],
702 "reading_user" => reading_user
705 fetch_activities(recipients, params)
709 defp user_activities_recipients(%{"godmode" => true}) do
713 defp user_activities_recipients(%{"reading_user" => reading_user}) do
715 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
717 [Pleroma.Constants.as_public()]
721 defp restrict_since(query, %{"since_id" => ""}), do: query
723 defp restrict_since(query, %{"since_id" => since_id}) do
724 from(activity in query, where: activity.id > ^since_id)
727 defp restrict_since(query, _), do: query
729 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
730 raise "Can't use the child object without preloading!"
733 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
734 when is_list(tag_reject) and tag_reject != [] do
736 [_activity, object] in query,
737 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
741 defp restrict_tag_reject(query, _), do: query
743 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
744 raise "Can't use the child object without preloading!"
747 defp restrict_tag_all(query, %{"tag_all" => tag_all})
748 when is_list(tag_all) and tag_all != [] do
750 [_activity, object] in query,
751 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
755 defp restrict_tag_all(query, _), do: query
757 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
758 raise "Can't use the child object without preloading!"
761 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
763 [_activity, object] in query,
764 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
768 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
770 [_activity, object] in query,
771 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
775 defp restrict_tag(query, _), do: query
777 defp restrict_recipients(query, [], _user), do: query
779 defp restrict_recipients(query, recipients, nil) do
780 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
783 defp restrict_recipients(query, recipients, user) do
786 where: fragment("? && ?", ^recipients, activity.recipients),
787 or_where: activity.actor == ^user.ap_id
791 defp restrict_local(query, %{"local_only" => true}) do
792 from(activity in query, where: activity.local == true)
795 defp restrict_local(query, _), do: query
797 defp restrict_actor(query, %{"actor_id" => actor_id}) do
798 from(activity in query, where: activity.actor == ^actor_id)
801 defp restrict_actor(query, _), do: query
803 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
804 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
807 defp restrict_type(query, %{"type" => type}) do
808 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
811 defp restrict_type(query, _), do: query
813 defp restrict_state(query, %{"state" => state}) do
814 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
817 defp restrict_state(query, _), do: query
819 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
821 [_activity, object] in query,
822 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
826 defp restrict_favorited_by(query, _), do: query
828 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
829 raise "Can't use the child object without preloading!"
832 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
834 [_activity, object] in query,
835 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
839 defp restrict_media(query, _), do: query
841 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
843 [_activity, object] in query,
844 where: fragment("?->>'inReplyTo' is null", object.data)
848 defp restrict_replies(query, _), do: query
850 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
851 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
854 defp restrict_reblogs(query, _), do: query
856 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
858 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
862 from([activity] in query,
863 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
864 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
867 unless opts["skip_preload"] do
868 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
874 defp restrict_muted(query, _), do: query
876 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
877 blocks = user.blocks || []
878 domain_blocks = user.domain_blocks || []
881 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
884 [activity, object: o] in query,
885 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
886 where: fragment("not (? && ?)", activity.recipients, ^blocks),
889 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
894 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
895 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
899 defp restrict_blocked(query, _), do: query
901 defp restrict_unlisted(query) do
906 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
908 ^[Pleroma.Constants.as_public()]
913 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
914 from(activity in query, where: activity.id in ^ids)
917 defp restrict_pinned(query, _), do: query
919 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
920 muted_reblogs = user.muted_reblogs || []
926 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
934 defp restrict_muted_reblogs(query, _), do: query
936 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
938 defp exclude_poll_votes(query, _) do
939 if has_named_binding?(query, :object) do
940 from([activity, object: o] in query,
941 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
948 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
949 from(activity in query, where: activity.id != ^id)
952 defp exclude_id(query, _), do: query
954 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
956 defp maybe_preload_objects(query, _) do
958 |> Activity.with_preloaded_object()
961 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
963 defp maybe_preload_bookmarks(query, opts) do
965 |> Activity.with_preloaded_bookmark(opts["user"])
968 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
970 defp maybe_set_thread_muted_field(query, opts) do
972 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
975 defp maybe_order(query, %{order: :desc}) do
977 |> order_by(desc: :id)
980 defp maybe_order(query, %{order: :asc}) do
982 |> order_by(asc: :id)
985 defp maybe_order(query, _), do: query
987 def fetch_activities_query(recipients, opts \\ %{}) do
989 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
993 |> maybe_preload_objects(opts)
994 |> maybe_preload_bookmarks(opts)
995 |> maybe_set_thread_muted_field(opts)
997 |> restrict_recipients(recipients, opts["user"])
998 |> restrict_tag(opts)
999 |> restrict_tag_reject(opts)
1000 |> restrict_tag_all(opts)
1001 |> restrict_since(opts)
1002 |> restrict_local(opts)
1003 |> restrict_actor(opts)
1004 |> restrict_type(opts)
1005 |> restrict_state(opts)
1006 |> restrict_favorited_by(opts)
1007 |> restrict_blocked(opts)
1008 |> restrict_muted(opts)
1009 |> restrict_media(opts)
1010 |> restrict_visibility(opts)
1011 |> restrict_thread_visibility(opts, config)
1012 |> restrict_replies(opts)
1013 |> restrict_reblogs(opts)
1014 |> restrict_pinned(opts)
1015 |> restrict_muted_reblogs(opts)
1016 |> Activity.restrict_deactivated_users()
1017 |> exclude_poll_votes(opts)
1018 |> exclude_visibility(opts)
1021 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1022 list_memberships = Pleroma.List.memberships(opts["user"])
1024 fetch_activities_query(recipients ++ list_memberships, opts)
1025 |> Pagination.fetch_paginated(opts, pagination)
1027 |> maybe_update_cc(list_memberships, opts["user"])
1030 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1031 when is_list(list_memberships) and length(list_memberships) > 0 do
1032 Enum.map(activities, fn
1033 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1034 if Enum.any?(bcc, &(&1 in list_memberships)) do
1035 update_in(activity.data["cc"], &[user_ap_id | &1])
1045 defp maybe_update_cc(activities, _, _), do: activities
1047 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1048 from(activity in query,
1050 fragment("? && ?", activity.recipients, ^recipients) or
1051 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1052 ^Pleroma.Constants.as_public() in activity.recipients)
1056 def fetch_activities_bounded(
1058 recipients_with_public,
1060 pagination \\ :keyset
1062 fetch_activities_query([], opts)
1063 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1064 |> Pagination.fetch_paginated(opts, pagination)
1068 def upload(file, opts \\ []) do
1069 with {:ok, data} <- Upload.store(file, opts) do
1072 Map.put(data, "actor", opts[:actor])
1077 Repo.insert(%Object{data: obj_data})
1081 defp object_to_user_data(data) do
1083 data["icon"]["url"] &&
1086 "url" => [%{"href" => data["icon"]["url"]}]
1090 data["image"]["url"] &&
1093 "url" => [%{"href" => data["image"]["url"]}]
1098 |> Map.get("attachment", [])
1099 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1100 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1102 locked = data["manuallyApprovesFollowers"] || false
1103 data = Transmogrifier.maybe_fix_user_object(data)
1104 discoverable = data["discoverable"] || false
1105 invisible = data["invisible"] || false
1114 discoverable: discoverable,
1115 invisible: invisible,
1118 follower_address: data["followers"],
1119 following_address: data["following"],
1120 bio: data["summary"]
1123 # nickname can be nil because of virtual actors
1125 if data["preferredUsername"] do
1129 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1132 Map.put(user_data, :nickname, nil)
1138 def fetch_follow_information_for_user(user) do
1139 with {:ok, following_data} <-
1140 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1141 following_count when is_integer(following_count) <- following_data["totalItems"],
1142 {:ok, hide_follows} <- collection_private(following_data),
1143 {:ok, followers_data} <-
1144 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1145 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1146 {:ok, hide_followers} <- collection_private(followers_data) do
1149 hide_follows: hide_follows,
1150 follower_count: followers_count,
1151 following_count: following_count,
1152 hide_followers: hide_followers
1163 defp maybe_update_follow_information(data) do
1164 with {:enabled, true} <-
1165 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1166 {:ok, info} <- fetch_follow_information_for_user(data) do
1167 info = Map.merge(data[:info] || %{}, info)
1168 Map.put(data, :info, info)
1170 {:enabled, false} ->
1175 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1182 defp collection_private(data) do
1183 if is_map(data["first"]) and
1184 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1187 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1188 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1191 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1203 def user_data_from_user_object(data) do
1204 with {:ok, data} <- MRF.filter(data),
1205 {:ok, data} <- object_to_user_data(data) do
1212 def fetch_and_prepare_user_from_ap_id(ap_id) do
1213 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1214 {:ok, data} <- user_data_from_user_object(data),
1215 data <- maybe_update_follow_information(data) do
1219 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1224 def make_user_from_ap_id(ap_id) do
1225 if _user = User.get_cached_by_ap_id(ap_id) do
1226 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1228 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1229 User.insert_or_update_user(data)
1236 def make_user_from_nickname(nickname) do
1237 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1238 make_user_from_ap_id(ap_id)
1240 _e -> {:error, "No AP id in WebFinger"}
1244 # filter out broken threads
1245 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1246 entire_thread_visible_for_user?(activity, user)
1249 # do post-processing on a specific activity
1250 def contain_activity(%Activity{} = activity, %User{} = user) do
1251 contain_broken_threads(activity, user)
1254 def fetch_direct_messages_query do
1256 |> restrict_type(%{"type" => "Create"})
1257 |> restrict_visibility(%{visibility: "direct"})
1258 |> order_by([activity], asc: activity.id)