1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.ActivityPub do
7 alias Pleroma.Activity.Ir.Topics
9 alias Pleroma.Conversation
10 alias Pleroma.Conversation.Participation
11 alias Pleroma.Notification
13 alias Pleroma.Object.Containment
14 alias Pleroma.Object.Fetcher
15 alias Pleroma.Pagination
19 alias Pleroma.Web.ActivityPub.MRF
20 alias Pleroma.Web.ActivityPub.Transmogrifier
21 alias Pleroma.Web.ActivityPub.Utils
22 alias Pleroma.Web.Streamer
23 alias Pleroma.Web.WebFinger
24 alias Pleroma.Workers.BackgroundWorker
27 import Pleroma.Web.ActivityPub.Utils
28 import Pleroma.Web.ActivityPub.Visibility
31 require Pleroma.Constants
33 # For Announce activities, we filter the recipients based on following status for any actors
34 # that match actual users. See issue #164 for more information about why this is necessary.
35 defp get_recipients(%{"type" => "Announce"} = data) do
36 to = Map.get(data, "to", [])
37 cc = Map.get(data, "cc", [])
38 bcc = Map.get(data, "bcc", [])
39 actor = User.get_cached_by_ap_id(data["actor"])
42 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
43 case User.get_cached_by_ap_id(recipient) do
45 user -> User.following?(user, actor)
52 defp get_recipients(%{"type" => "Create"} = data) do
53 to = Map.get(data, "to", [])
54 cc = Map.get(data, "cc", [])
55 bcc = Map.get(data, "bcc", [])
56 actor = Map.get(data, "actor", [])
57 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
61 defp get_recipients(data) do
62 to = Map.get(data, "to", [])
63 cc = Map.get(data, "cc", [])
64 bcc = Map.get(data, "bcc", [])
65 recipients = Enum.concat([to, cc, bcc])
69 defp check_actor_is_active(actor) do
70 if not is_nil(actor) do
71 with user <- User.get_cached_by_ap_id(actor),
72 false <- user.deactivated do
82 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
83 limit = Config.get([:instance, :remote_limit])
84 String.length(content) <= limit
87 defp check_remote_limit(_), do: true
89 def increase_note_count_if_public(actor, object) do
90 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
93 def decrease_note_count_if_public(actor, object) do
94 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
97 def increase_replies_count_if_reply(%{
98 "object" => %{"inReplyTo" => reply_ap_id} = object,
101 if is_public?(object) do
102 Object.increase_replies_count(reply_ap_id)
106 def increase_replies_count_if_reply(_create_data), do: :noop
108 def decrease_replies_count_if_reply(%Object{
109 data: %{"inReplyTo" => reply_ap_id} = object
111 if is_public?(object) do
112 Object.decrease_replies_count(reply_ap_id)
116 def decrease_replies_count_if_reply(_object), do: :noop
118 def increase_poll_votes_if_vote(%{
119 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
122 Object.increase_vote_count(reply_ap_id, name)
125 def increase_poll_votes_if_vote(_create_data), do: :noop
127 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
128 with nil <- Activity.normalize(map),
129 map <- lazy_put_activity_defaults(map, fake),
130 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
131 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
132 {:ok, map} <- MRF.filter(map),
133 {recipients, _, _} = get_recipients(map),
134 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
135 {:containment, :ok} <- {:containment, Containment.contain_child(map)},
136 {:ok, map, object} <- insert_full_object(map) do
138 Repo.insert(%Activity{
142 recipients: recipients
145 # Splice in the child object if we have one.
147 if not is_nil(object) do
148 Map.put(activity, :object, object)
153 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
155 Notification.create_notifications(activity)
157 conversation = create_or_bump_conversation(activity, map["actor"])
158 participations = get_participations(conversation)
160 stream_out_participations(participations)
163 %Activity{} = activity ->
166 {:fake, true, map, recipients} ->
167 activity = %Activity{
171 recipients: recipients,
175 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
183 defp create_or_bump_conversation(activity, actor) do
184 with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
185 %User{} = user <- User.get_cached_by_ap_id(actor),
186 Participation.mark_as_read(user, conversation) do
191 defp get_participations({:ok, conversation}) do
193 |> Repo.preload(:participations, force: true)
194 |> Map.get(:participations)
197 defp get_participations(_), do: []
199 def stream_out_participations(participations) do
202 |> Repo.preload(:user)
204 Streamer.stream("participation", participations)
207 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
208 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
209 conversation = Repo.preload(conversation, :participations),
211 fetch_latest_activity_id_for_context(conversation.ap_id, %{
213 "blocking_user" => user
215 if last_activity_id do
216 stream_out_participations(conversation.participations)
221 def stream_out_participations(_, _), do: :noop
223 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
224 when data_type in ["Create", "Announce", "Delete"] do
226 |> Topics.get_activity_topics()
227 |> Streamer.stream(activity)
230 def stream_out(_activity) do
234 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
235 additional = params[:additional] || %{}
236 # only accept false as false value
237 local = !(params[:local] == false)
238 published = params[:published]
239 quick_insert? = Pleroma.Config.get([:env]) == :benchmark
243 %{to: to, actor: actor, published: published, context: context, object: object},
246 {:ok, activity} <- insert(create_data, local, fake),
247 {:fake, false, activity} <- {:fake, fake, activity},
248 _ <- increase_replies_count_if_reply(create_data),
249 _ <- increase_poll_votes_if_vote(create_data),
250 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
251 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
252 :ok <- maybe_federate(activity) do
255 {:quick_insert, true, activity} ->
258 {:fake, true, activity} ->
266 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
267 additional = params[:additional] || %{}
268 # only accept false as false value
269 local = !(params[:local] == false)
270 published = params[:published]
274 %{to: to, actor: actor, published: published, context: context, object: object},
277 {:ok, activity} <- insert(listen_data, local),
278 :ok <- maybe_federate(activity) do
286 def accept(params) do
287 accept_or_reject("Accept", params)
290 def reject(params) do
291 accept_or_reject("Reject", params)
294 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
295 local = Map.get(params, :local, true)
296 activity_id = Map.get(params, :activity_id, nil)
299 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
300 |> Utils.maybe_put("id", activity_id),
301 {:ok, activity} <- insert(data, local),
302 :ok <- maybe_federate(activity) do
307 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
308 local = !(params[:local] == false)
309 activity_id = params[:activity_id]
318 data <- Utils.maybe_put(data, "id", activity_id),
319 {:ok, activity} <- insert(data, local),
320 :ok <- maybe_federate(activity) do
325 def react_with_emoji(user, object, emoji, options \\ []) do
326 with local <- Keyword.get(options, :local, true),
327 activity_id <- Keyword.get(options, :activity_id, nil),
328 Pleroma.Emoji.is_unicode_emoji?(emoji),
329 reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
330 {:ok, activity} <- insert(reaction_data, local),
331 {:ok, object} <- add_emoji_reaction_to_object(activity, object),
332 :ok <- maybe_federate(activity) do
333 {:ok, activity, object}
337 def unreact_with_emoji(user, reaction_id, options \\ []) do
338 with local <- Keyword.get(options, :local, true),
339 activity_id <- Keyword.get(options, :activity_id, nil),
340 user_ap_id <- user.ap_id,
341 %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
342 object <- Object.normalize(reaction_activity),
343 unreact_data <- make_undo_data(user, reaction_activity, activity_id),
344 {:ok, activity} <- insert(unreact_data, local),
345 {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
346 :ok <- maybe_federate(activity) do
347 {:ok, activity, object}
351 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
353 %User{ap_id: ap_id} = user,
354 %Object{data: %{"id" => _}} = object,
358 with nil <- get_existing_like(ap_id, object),
359 like_data <- make_like_data(user, object, activity_id),
360 {:ok, activity} <- insert(like_data, local),
361 {:ok, object} <- add_like_to_object(activity, object),
362 :ok <- maybe_federate(activity) do
363 {:ok, activity, object}
365 %Activity{} = activity -> {:ok, activity, object}
366 error -> {:error, error}
370 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
371 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
372 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
373 {:ok, unlike_activity} <- insert(unlike_data, local),
374 {:ok, _activity} <- Repo.delete(like_activity),
375 {:ok, object} <- remove_like_from_object(like_activity, object),
376 :ok <- maybe_federate(unlike_activity) do
377 {:ok, unlike_activity, like_activity, object}
384 %User{ap_id: _} = user,
385 %Object{data: %{"id" => _}} = object,
390 with true <- is_announceable?(object, user, public),
391 announce_data <- make_announce_data(user, object, activity_id, public),
392 {:ok, activity} <- insert(announce_data, local),
393 {:ok, object} <- add_announce_to_object(activity, object),
394 :ok <- maybe_federate(activity) do
395 {:ok, activity, object}
397 error -> {:error, error}
407 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
408 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
409 {:ok, unannounce_activity} <- insert(unannounce_data, local),
410 :ok <- maybe_federate(unannounce_activity),
411 {:ok, _activity} <- Repo.delete(announce_activity),
412 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
413 {:ok, unannounce_activity, object}
419 def follow(follower, followed, activity_id \\ nil, local \\ true) do
420 with data <- make_follow_data(follower, followed, activity_id),
421 {:ok, activity} <- insert(data, local),
422 :ok <- maybe_federate(activity),
423 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
428 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
429 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
430 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
431 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
432 {:ok, activity} <- insert(unfollow_data, local),
433 :ok <- maybe_federate(activity) do
438 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
440 "to" => [follower_address],
443 "object" => %{"type" => "Person", "id" => ap_id}
445 {:ok, activity} <- insert(data, true, true, true),
446 :ok <- maybe_federate(activity) do
451 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
452 local = Keyword.get(options, :local, true)
453 activity_id = Keyword.get(options, :activity_id, nil)
454 actor = Keyword.get(options, :actor, actor)
456 user = User.get_cached_by_ap_id(actor)
457 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
459 with create_activity <- Activity.get_create_by_object_ap_id(id),
466 "deleted_activity_id" => create_activity && create_activity.id
468 |> maybe_put("id", activity_id),
469 {:ok, activity} <- insert(data, local, false),
470 {:ok, object, _create_activity} <- Object.delete(object),
471 stream_out_participations(object, user),
472 _ <- decrease_replies_count_if_reply(object),
473 {:ok, _actor} <- decrease_note_count_if_public(user, object),
474 :ok <- maybe_federate(activity) do
479 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
480 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
481 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
482 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
484 if unfollow_blocked do
485 follow_activity = fetch_latest_follow(blocker, blocked)
486 if follow_activity, do: unfollow(blocker, blocked, nil, local)
489 with true <- outgoing_blocks,
490 block_data <- make_block_data(blocker, blocked, activity_id),
491 {:ok, activity} <- insert(block_data, local),
492 :ok <- maybe_federate(activity) do
499 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
500 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
501 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
502 {:ok, activity} <- insert(unblock_data, local),
503 :ok <- maybe_federate(activity) do
508 @spec flag(map()) :: {:ok, Activity.t()} | any
518 # only accept false as false value
519 local = !(params[:local] == false)
520 forward = !(params[:forward] == false)
522 additional = params[:additional] || %{}
526 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
528 Map.merge(additional, %{"to" => [], "cc" => []})
531 with flag_data <- make_flag_data(params, additional),
532 {:ok, activity} <- insert(flag_data, local),
533 {:ok, stripped_activity} <- strip_report_status_data(activity),
534 :ok <- maybe_federate(stripped_activity) do
535 Enum.each(User.all_superusers(), fn superuser ->
537 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
538 |> Pleroma.Emails.Mailer.deliver_async()
545 def move(%User{} = origin, %User{} = target, local \\ true) do
548 "actor" => origin.ap_id,
549 "object" => origin.ap_id,
550 "target" => target.ap_id
553 with true <- origin.ap_id in target.also_known_as,
554 {:ok, activity} <- insert(params, local) do
555 maybe_federate(activity)
557 BackgroundWorker.enqueue("move_following", %{
558 "origin_id" => origin.id,
559 "target_id" => target.id
564 false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
569 defp fetch_activities_for_context_query(context, opts) do
570 public = [Pleroma.Constants.as_public()]
574 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
577 from(activity in Activity)
578 |> maybe_preload_objects(opts)
579 |> maybe_preload_bookmarks(opts)
580 |> maybe_set_thread_muted_field(opts)
581 |> restrict_blocked(opts)
582 |> restrict_recipients(recipients, opts["user"])
586 "?->>'type' = ? and ?->>'context' = ?",
593 |> exclude_poll_votes(opts)
595 |> order_by([activity], desc: activity.id)
598 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
599 def fetch_activities_for_context(context, opts \\ %{}) do
601 |> fetch_activities_for_context_query(opts)
605 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
606 FlakeId.Ecto.CompatType.t() | nil
607 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
609 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
615 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
616 opts = Map.drop(opts, ["user"])
618 [Pleroma.Constants.as_public()]
619 |> fetch_activities_query(opts)
620 |> restrict_unlisted()
621 |> Pagination.fetch_paginated(opts, pagination)
624 @valid_visibilities ~w[direct unlisted public private]
626 defp restrict_visibility(query, %{visibility: visibility})
627 when is_list(visibility) do
628 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
634 "activity_visibility(?, ?, ?) = ANY (?)",
644 Logger.error("Could not restrict visibility to #{visibility}")
648 defp restrict_visibility(query, %{visibility: visibility})
649 when visibility in @valid_visibilities do
653 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
657 defp restrict_visibility(_query, %{visibility: visibility})
658 when visibility not in @valid_visibilities do
659 Logger.error("Could not restrict visibility to #{visibility}")
662 defp restrict_visibility(query, _visibility), do: query
664 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
665 when is_list(visibility) do
666 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
671 "activity_visibility(?, ?, ?) = ANY (?)",
679 Logger.error("Could not exclude visibility to #{visibility}")
684 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
685 when visibility in @valid_visibilities do
690 "activity_visibility(?, ?, ?) = ?",
699 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
700 when visibility not in @valid_visibilities do
701 Logger.error("Could not exclude visibility to #{visibility}")
705 defp exclude_visibility(query, _visibility), do: query
707 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
710 defp restrict_thread_visibility(
712 %{"user" => %User{skip_thread_containment: true}},
717 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
720 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
724 defp restrict_thread_visibility(query, _, _), do: query
726 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
729 |> Map.put("user", reading_user)
730 |> Map.put("actor_id", user.ap_id)
731 |> Map.put("whole_db", true)
734 user_activities_recipients(%{
735 "godmode" => params["godmode"],
736 "reading_user" => reading_user
739 fetch_activities(recipients, params)
743 def fetch_user_activities(user, reading_user, params \\ %{}) do
746 |> Map.put("type", ["Create", "Announce"])
747 |> Map.put("user", reading_user)
748 |> Map.put("actor_id", user.ap_id)
749 |> Map.put("whole_db", true)
750 |> Map.put("pinned_activity_ids", user.pinned_activities)
753 if User.blocks?(reading_user, user) do
757 |> Map.put("blocking_user", reading_user)
758 |> Map.put("muting_user", reading_user)
762 user_activities_recipients(%{
763 "godmode" => params["godmode"],
764 "reading_user" => reading_user
767 fetch_activities(recipients, params)
771 def fetch_instance_activities(params) do
774 |> Map.put("type", ["Create", "Announce"])
775 |> Map.put("instance", params["instance"])
776 |> Map.put("whole_db", true)
778 fetch_activities([Pleroma.Constants.as_public()], params, :offset)
782 defp user_activities_recipients(%{"godmode" => true}) do
786 defp user_activities_recipients(%{"reading_user" => reading_user}) do
788 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
790 [Pleroma.Constants.as_public()]
794 defp restrict_since(query, %{"since_id" => ""}), do: query
796 defp restrict_since(query, %{"since_id" => since_id}) do
797 from(activity in query, where: activity.id > ^since_id)
800 defp restrict_since(query, _), do: query
802 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
803 raise "Can't use the child object without preloading!"
806 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
807 when is_list(tag_reject) and tag_reject != [] do
809 [_activity, object] in query,
810 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
814 defp restrict_tag_reject(query, _), do: query
816 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
817 raise "Can't use the child object without preloading!"
820 defp restrict_tag_all(query, %{"tag_all" => tag_all})
821 when is_list(tag_all) and tag_all != [] do
823 [_activity, object] in query,
824 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
828 defp restrict_tag_all(query, _), do: query
830 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
831 raise "Can't use the child object without preloading!"
834 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
836 [_activity, object] in query,
837 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
841 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
843 [_activity, object] in query,
844 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
848 defp restrict_tag(query, _), do: query
850 defp restrict_recipients(query, [], _user), do: query
852 defp restrict_recipients(query, recipients, nil) do
853 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
856 defp restrict_recipients(query, recipients, user) do
859 where: fragment("? && ?", ^recipients, activity.recipients),
860 or_where: activity.actor == ^user.ap_id
864 defp restrict_local(query, %{"local_only" => true}) do
865 from(activity in query, where: activity.local == true)
868 defp restrict_local(query, _), do: query
870 defp restrict_actor(query, %{"actor_id" => actor_id}) do
871 from(activity in query, where: activity.actor == ^actor_id)
874 defp restrict_actor(query, _), do: query
876 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
877 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
880 defp restrict_type(query, %{"type" => type}) do
881 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
884 defp restrict_type(query, _), do: query
886 defp restrict_state(query, %{"state" => state}) do
887 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
890 defp restrict_state(query, _), do: query
892 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
894 [_activity, object] in query,
895 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
899 defp restrict_favorited_by(query, _), do: query
901 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
902 raise "Can't use the child object without preloading!"
905 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
907 [_activity, object] in query,
908 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
912 defp restrict_media(query, _), do: query
914 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
916 [_activity, object] in query,
917 where: fragment("?->>'inReplyTo' is null", object.data)
921 defp restrict_replies(query, _), do: query
923 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
924 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
927 defp restrict_reblogs(query, _), do: query
929 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
931 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
932 mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
935 from([activity] in query,
936 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
937 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
940 unless opts["skip_preload"] do
941 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
947 defp restrict_muted(query, _), do: query
949 defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
950 blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
951 domain_blocks = user.domain_blocks || []
953 following_ap_ids = User.get_friends_ap_ids(user)
956 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
959 [activity, object: o] in query,
960 where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
961 where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
964 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
971 "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
979 "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
988 defp restrict_blocked(query, _), do: query
990 defp restrict_unlisted(query) do
995 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
997 ^[Pleroma.Constants.as_public()]
1002 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
1003 from(activity in query, where: activity.id in ^ids)
1006 defp restrict_pinned(query, _), do: query
1008 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
1009 muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
1015 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
1023 defp restrict_muted_reblogs(query, _), do: query
1025 defp restrict_instance(query, %{"instance" => instance}) do
1030 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
1034 from(activity in query, where: activity.actor in ^users)
1037 defp restrict_instance(query, _), do: query
1039 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
1041 defp exclude_poll_votes(query, _) do
1042 if has_named_binding?(query, :object) do
1043 from([activity, object: o] in query,
1044 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1051 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1052 from(activity in query, where: activity.id != ^id)
1055 defp exclude_id(query, _), do: query
1057 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1059 defp maybe_preload_objects(query, _) do
1061 |> Activity.with_preloaded_object()
1064 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1066 defp maybe_preload_bookmarks(query, opts) do
1068 |> Activity.with_preloaded_bookmark(opts["user"])
1071 defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
1073 |> Activity.with_preloaded_report_notes()
1076 defp maybe_preload_report_notes(query, _), do: query
1078 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1080 defp maybe_set_thread_muted_field(query, opts) do
1082 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1085 defp maybe_order(query, %{order: :desc}) do
1087 |> order_by(desc: :id)
1090 defp maybe_order(query, %{order: :asc}) do
1092 |> order_by(asc: :id)
1095 defp maybe_order(query, _), do: query
1097 defp fetch_activities_query_ap_ids_ops(opts) do
1098 source_user = opts["muting_user"]
1099 ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
1103 if opts["blocking_user"] && opts["blocking_user"] == source_user do
1109 preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
1111 restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
1112 restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
1114 restrict_muted_reblogs_opts =
1115 Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
1117 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
1120 def fetch_activities_query(recipients, opts \\ %{}) do
1121 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
1122 fetch_activities_query_ap_ids_ops(opts)
1125 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1129 |> maybe_preload_objects(opts)
1130 |> maybe_preload_bookmarks(opts)
1131 |> maybe_preload_report_notes(opts)
1132 |> maybe_set_thread_muted_field(opts)
1133 |> maybe_order(opts)
1134 |> restrict_recipients(recipients, opts["user"])
1135 |> restrict_tag(opts)
1136 |> restrict_tag_reject(opts)
1137 |> restrict_tag_all(opts)
1138 |> restrict_since(opts)
1139 |> restrict_local(opts)
1140 |> restrict_actor(opts)
1141 |> restrict_type(opts)
1142 |> restrict_state(opts)
1143 |> restrict_favorited_by(opts)
1144 |> restrict_blocked(restrict_blocked_opts)
1145 |> restrict_muted(restrict_muted_opts)
1146 |> restrict_media(opts)
1147 |> restrict_visibility(opts)
1148 |> restrict_thread_visibility(opts, config)
1149 |> restrict_replies(opts)
1150 |> restrict_reblogs(opts)
1151 |> restrict_pinned(opts)
1152 |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
1153 |> restrict_instance(opts)
1154 |> Activity.restrict_deactivated_users()
1155 |> exclude_poll_votes(opts)
1156 |> exclude_visibility(opts)
1159 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1160 list_memberships = Pleroma.List.memberships(opts["user"])
1162 fetch_activities_query(recipients ++ list_memberships, opts)
1163 |> Pagination.fetch_paginated(opts, pagination)
1165 |> maybe_update_cc(list_memberships, opts["user"])
1169 Fetch favorites activities of user with order by sort adds to favorites
1171 @spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
1172 def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
1174 |> Activity.Queries.by_actor()
1175 |> Activity.Queries.by_type("Like")
1176 |> Activity.with_joined_object()
1177 |> Object.with_joined_activity()
1178 |> select([_like, object, activity], %{activity | object: object})
1179 |> order_by([like, _, _], desc: like.id)
1180 |> Pagination.fetch_paginated(
1181 Map.merge(params, %{"skip_order" => true}),
1187 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1188 when is_list(list_memberships) and length(list_memberships) > 0 do
1189 Enum.map(activities, fn
1190 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1191 if Enum.any?(bcc, &(&1 in list_memberships)) do
1192 update_in(activity.data["cc"], &[user_ap_id | &1])
1202 defp maybe_update_cc(activities, _, _), do: activities
1204 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1205 from(activity in query,
1207 fragment("? && ?", activity.recipients, ^recipients) or
1208 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1209 ^Pleroma.Constants.as_public() in activity.recipients)
1213 def fetch_activities_bounded(
1215 recipients_with_public,
1217 pagination \\ :keyset
1219 fetch_activities_query([], opts)
1220 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1221 |> Pagination.fetch_paginated(opts, pagination)
1225 def upload(file, opts \\ []) do
1226 with {:ok, data} <- Upload.store(file, opts) do
1229 Map.put(data, "actor", opts[:actor])
1234 Repo.insert(%Object{data: obj_data})
1238 defp object_to_user_data(data) do
1240 data["icon"]["url"] &&
1243 "url" => [%{"href" => data["icon"]["url"]}]
1247 data["image"]["url"] &&
1250 "url" => [%{"href" => data["image"]["url"]}]
1255 |> Map.get("attachment", [])
1256 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1257 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1259 locked = data["manuallyApprovesFollowers"] || false
1260 data = Transmogrifier.maybe_fix_user_object(data)
1261 discoverable = data["discoverable"] || false
1262 invisible = data["invisible"] || false
1263 actor_type = data["type"] || "Person"
1272 discoverable: discoverable,
1273 invisible: invisible,
1276 follower_address: data["followers"],
1277 following_address: data["following"],
1278 bio: data["summary"],
1279 actor_type: actor_type,
1280 also_known_as: Map.get(data, "alsoKnownAs", [])
1283 # nickname can be nil because of virtual actors
1285 if data["preferredUsername"] do
1289 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1292 Map.put(user_data, :nickname, nil)
1298 def fetch_follow_information_for_user(user) do
1299 with {:ok, following_data} <-
1300 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1301 {:ok, hide_follows} <- collection_private(following_data),
1302 {:ok, followers_data} <-
1303 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1304 {:ok, hide_followers} <- collection_private(followers_data) do
1307 hide_follows: hide_follows,
1308 follower_count: normalize_counter(followers_data["totalItems"]),
1309 following_count: normalize_counter(following_data["totalItems"]),
1310 hide_followers: hide_followers
1313 {:error, _} = e -> e
1318 defp normalize_counter(counter) when is_integer(counter), do: counter
1319 defp normalize_counter(_), do: 0
1321 defp maybe_update_follow_information(data) do
1322 with {:enabled, true} <-
1323 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1324 {:ok, info} <- fetch_follow_information_for_user(data) do
1325 info = Map.merge(data[:info] || %{}, info)
1326 Map.put(data, :info, info)
1328 {:enabled, false} ->
1333 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1340 defp collection_private(%{"first" => %{"type" => type}})
1341 when type in ["CollectionPage", "OrderedCollectionPage"],
1344 defp collection_private(%{"first" => first}) do
1345 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1346 Fetcher.fetch_and_contain_remote_object_from_id(first) do
1349 {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
1350 {:error, _} = e -> e
1355 defp collection_private(_data), do: {:ok, true}
1357 def user_data_from_user_object(data) do
1358 with {:ok, data} <- MRF.filter(data),
1359 {:ok, data} <- object_to_user_data(data) do
1366 def fetch_and_prepare_user_from_ap_id(ap_id) do
1367 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1368 {:ok, data} <- user_data_from_user_object(data),
1369 data <- maybe_update_follow_information(data) do
1373 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1378 def make_user_from_ap_id(ap_id) do
1379 if _user = User.get_cached_by_ap_id(ap_id) do
1380 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1382 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1383 User.insert_or_update_user(data)
1390 def make_user_from_nickname(nickname) do
1391 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1392 make_user_from_ap_id(ap_id)
1394 _e -> {:error, "No AP id in WebFinger"}
1398 # filter out broken threads
1399 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1400 entire_thread_visible_for_user?(activity, user)
1403 # do post-processing on a specific activity
1404 def contain_activity(%Activity{} = activity, %User{} = user) do
1405 contain_broken_threads(activity, user)
1408 def fetch_direct_messages_query do
1410 |> restrict_type(%{"type" => "Create"})
1411 |> restrict_visibility(%{visibility: "direct"})
1412 |> order_by([activity], asc: activity.id)