1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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.Constants
10 alias Pleroma.Conversation
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
14 alias Pleroma.Object.Containment
15 alias Pleroma.Object.Fetcher
16 alias Pleroma.Pagination
20 alias Pleroma.Web.ActivityPub.MRF
21 alias Pleroma.Web.ActivityPub.Transmogrifier
22 alias Pleroma.Web.ActivityPub.Utils
23 alias Pleroma.Web.Streamer
24 alias Pleroma.Web.WebFinger
25 alias Pleroma.Workers.BackgroundWorker
28 import Pleroma.Web.ActivityPub.Utils
29 import Pleroma.Web.ActivityPub.Visibility
32 require Pleroma.Constants
34 # For Announce activities, we filter the recipients based on following status for any actors
35 # that match actual users. See issue #164 for more information about why this is necessary.
36 defp get_recipients(%{"type" => "Announce"} = data) do
37 to = Map.get(data, "to", [])
38 cc = Map.get(data, "cc", [])
39 bcc = Map.get(data, "bcc", [])
40 actor = User.get_cached_by_ap_id(data["actor"])
43 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
44 case User.get_cached_by_ap_id(recipient) do
46 user -> User.following?(user, actor)
53 defp get_recipients(%{"type" => "Create"} = data) do
54 to = Map.get(data, "to", [])
55 cc = Map.get(data, "cc", [])
56 bcc = Map.get(data, "bcc", [])
57 actor = Map.get(data, "actor", [])
58 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
62 defp get_recipients(data) do
63 to = Map.get(data, "to", [])
64 cc = Map.get(data, "cc", [])
65 bcc = Map.get(data, "bcc", [])
66 recipients = Enum.concat([to, cc, bcc])
70 defp check_actor_is_active(actor) do
71 if not is_nil(actor) do
72 with user <- User.get_cached_by_ap_id(actor),
73 false <- user.deactivated do
83 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
84 limit = Config.get([:instance, :remote_limit])
85 String.length(content) <= limit
88 defp check_remote_limit(_), do: true
90 def increase_note_count_if_public(actor, object) do
91 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
94 def decrease_note_count_if_public(actor, object) do
95 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
98 def increase_replies_count_if_reply(%{
99 "object" => %{"inReplyTo" => reply_ap_id} = object,
102 if is_public?(object) do
103 Object.increase_replies_count(reply_ap_id)
107 def increase_replies_count_if_reply(_create_data), do: :noop
109 def decrease_replies_count_if_reply(%Object{
110 data: %{"inReplyTo" => reply_ap_id} = object
112 if is_public?(object) do
113 Object.decrease_replies_count(reply_ap_id)
117 def decrease_replies_count_if_reply(_object), do: :noop
119 def increase_poll_votes_if_vote(%{
120 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
123 Object.increase_vote_count(reply_ap_id, name)
126 def increase_poll_votes_if_vote(_create_data), do: :noop
128 @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
129 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
130 with nil <- Activity.normalize(map),
131 map <- lazy_put_activity_defaults(map, fake),
132 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
133 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
134 {:ok, map} <- MRF.filter(map),
135 {recipients, _, _} = get_recipients(map),
136 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
137 {:containment, :ok} <- {:containment, Containment.contain_child(map)},
138 {:ok, map, object} <- insert_full_object(map) do
140 Repo.insert(%Activity{
144 recipients: recipients
147 # Splice in the child object if we have one.
149 if not is_nil(object) do
150 Map.put(activity, :object, object)
155 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
157 Notification.create_notifications(activity)
159 conversation = create_or_bump_conversation(activity, map["actor"])
160 participations = get_participations(conversation)
162 stream_out_participations(participations)
165 %Activity{} = activity ->
168 {:fake, true, map, recipients} ->
169 activity = %Activity{
173 recipients: recipients,
177 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
185 defp create_or_bump_conversation(activity, actor) do
186 with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
187 %User{} = user <- User.get_cached_by_ap_id(actor),
188 Participation.mark_as_read(user, conversation) do
193 defp get_participations({:ok, conversation}) do
195 |> Repo.preload(:participations, force: true)
196 |> Map.get(:participations)
199 defp get_participations(_), do: []
201 def stream_out_participations(participations) do
204 |> Repo.preload(:user)
206 Streamer.stream("participation", participations)
209 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
210 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
211 conversation = Repo.preload(conversation, :participations),
213 fetch_latest_activity_id_for_context(conversation.ap_id, %{
215 "blocking_user" => user
217 if last_activity_id do
218 stream_out_participations(conversation.participations)
223 def stream_out_participations(_, _), do: :noop
225 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
226 when data_type in ["Create", "Announce", "Delete"] do
228 |> Topics.get_activity_topics()
229 |> Streamer.stream(activity)
232 def stream_out(_activity) do
236 @spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
237 def create(params, fake \\ false) do
238 with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do
243 defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do
244 additional = params[:additional] || %{}
245 # only accept false as false value
246 local = !(params[:local] == false)
247 published = params[:published]
248 quick_insert? = Config.get([:env]) == :benchmark
252 %{to: to, actor: actor, published: published, context: context, object: object},
255 {:ok, activity} <- insert(create_data, local, fake),
256 {:fake, false, activity} <- {:fake, fake, activity},
257 _ <- increase_replies_count_if_reply(create_data),
258 _ <- increase_poll_votes_if_vote(create_data),
259 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
260 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
261 :ok <- maybe_federate(activity) do
264 {:quick_insert, true, activity} ->
267 {:fake, true, activity} ->
271 Repo.rollback(message)
275 @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
276 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
277 additional = params[:additional] || %{}
278 # only accept false as false value
279 local = !(params[:local] == false)
280 published = params[:published]
284 %{to: to, actor: actor, published: published, context: context, object: object},
287 {:ok, activity} <- insert(listen_data, local),
288 :ok <- maybe_federate(activity) do
293 @spec accept(map()) :: {:ok, Activity.t()} | {:error, any()}
294 def accept(params) do
295 accept_or_reject("Accept", params)
298 @spec reject(map()) :: {:ok, Activity.t()} | {:error, any()}
299 def reject(params) do
300 accept_or_reject("Reject", params)
303 @spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
304 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
305 local = Map.get(params, :local, true)
306 activity_id = Map.get(params, :activity_id, nil)
309 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
310 |> Utils.maybe_put("id", activity_id),
311 {:ok, activity} <- insert(data, local),
312 :ok <- maybe_federate(activity) do
317 @spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
318 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
319 local = !(params[:local] == false)
320 activity_id = params[:activity_id]
329 data <- Utils.maybe_put(data, "id", activity_id),
330 {:ok, activity} <- insert(data, local),
331 :ok <- maybe_federate(activity) do
336 @spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) ::
337 {:ok, Activity.t(), Object.t()} | {:error, any()}
338 def react_with_emoji(user, object, emoji, options \\ []) do
339 with {:ok, result} <-
340 Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do
345 defp do_react_with_emoji(user, object, emoji, options) do
346 with local <- Keyword.get(options, :local, true),
347 activity_id <- Keyword.get(options, :activity_id, nil),
348 true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
349 reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
350 {:ok, activity} <- insert(reaction_data, local),
351 {:ok, object} <- add_emoji_reaction_to_object(activity, object),
352 :ok <- maybe_federate(activity) do
353 {:ok, activity, object}
355 false -> {:error, false}
356 {:error, error} -> Repo.rollback(error)
360 @spec unreact_with_emoji(User.t(), String.t(), keyword()) ::
361 {:ok, Activity.t(), Object.t()} | {:error, any()}
362 def unreact_with_emoji(user, reaction_id, options \\ []) do
363 with {:ok, result} <-
364 Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do
369 defp do_unreact_with_emoji(user, reaction_id, options) do
370 with local <- Keyword.get(options, :local, true),
371 activity_id <- Keyword.get(options, :activity_id, nil),
372 user_ap_id <- user.ap_id,
373 %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
374 object <- Object.normalize(reaction_activity),
375 unreact_data <- make_undo_data(user, reaction_activity, activity_id),
376 {:ok, activity} <- insert(unreact_data, local),
377 {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
378 :ok <- maybe_federate(activity) do
379 {:ok, activity, object}
381 {:error, error} -> Repo.rollback(error)
385 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
386 @spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
387 {:ok, Activity.t(), Object.t()} | {:error, any()}
388 def like(user, object, activity_id \\ nil, local \\ true) do
389 with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do
395 %User{ap_id: ap_id} = user,
396 %Object{data: %{"id" => _}} = object,
400 with nil <- get_existing_like(ap_id, object),
401 like_data <- make_like_data(user, object, activity_id),
402 {:ok, activity} <- insert(like_data, local),
403 {:ok, object} <- add_like_to_object(activity, object),
404 :ok <- maybe_federate(activity) do
405 {:ok, activity, object}
407 %Activity{} = activity ->
408 {:ok, activity, object}
415 @spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) ::
416 {:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
417 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
418 with {:ok, result} <-
419 Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do
424 defp do_unlike(actor, object, activity_id, local) do
425 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
426 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
427 {:ok, unlike_activity} <- insert(unlike_data, local),
428 {:ok, _activity} <- Repo.delete(like_activity),
429 {:ok, object} <- remove_like_from_object(like_activity, object),
430 :ok <- maybe_federate(unlike_activity) do
431 {:ok, unlike_activity, like_activity, object}
434 {:error, error} -> Repo.rollback(error)
438 @spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
439 {:ok, Activity.t(), Object.t()} | {:error, any()}
441 %User{ap_id: _} = user,
442 %Object{data: %{"id" => _}} = object,
447 with {:ok, result} <-
448 Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
453 defp do_announce(user, object, activity_id, local, public) do
454 with true <- is_announceable?(object, user, public),
455 announce_data <- make_announce_data(user, object, activity_id, public),
456 {:ok, activity} <- insert(announce_data, local),
457 {:ok, object} <- add_announce_to_object(activity, object),
458 :ok <- maybe_federate(activity) do
459 {:ok, activity, object}
461 false -> {:error, false}
462 {:error, error} -> Repo.rollback(error)
466 @spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) ::
467 {:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
474 with {:ok, result} <-
475 Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do
480 defp do_unannounce(actor, object, activity_id, local) do
481 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
482 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
483 {:ok, unannounce_activity} <- insert(unannounce_data, local),
484 :ok <- maybe_federate(unannounce_activity),
485 {:ok, _activity} <- Repo.delete(announce_activity),
486 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
487 {:ok, unannounce_activity, object}
490 {:error, error} -> Repo.rollback(error)
494 @spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
495 {:ok, Activity.t()} | {:error, any()}
496 def follow(follower, followed, activity_id \\ nil, local \\ true) do
497 with {:ok, result} <-
498 Repo.transaction(fn -> do_follow(follower, followed, activity_id, local) end) do
503 defp do_follow(follower, followed, activity_id, local) do
504 with data <- make_follow_data(follower, followed, activity_id),
505 {:ok, activity} <- insert(data, local),
506 :ok <- maybe_federate(activity) do
509 {:error, error} -> Repo.rollback(error)
513 @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
514 {:ok, Activity.t()} | nil | {:error, any()}
515 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
516 with {:ok, result} <-
517 Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
522 defp do_unfollow(follower, followed, activity_id, local) do
523 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
524 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
525 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
526 {:ok, activity} <- insert(unfollow_data, local),
527 :ok <- maybe_federate(activity) do
531 {:error, error} -> Repo.rollback(error)
535 @spec delete(User.t() | Object.t(), keyword()) :: {:ok, User.t() | Object.t()} | {:error, any()}
536 def delete(entity, options \\ []) do
537 with {:ok, result} <- Repo.transaction(fn -> do_delete(entity, options) end) do
542 defp do_delete(%User{ap_id: ap_id, follower_address: follower_address} = user, _) do
544 "to" => [follower_address],
547 "object" => %{"type" => "Person", "id" => ap_id}
549 {:ok, activity} <- insert(data, true, true, true),
550 :ok <- maybe_federate(activity) do
555 defp do_delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options) do
556 local = Keyword.get(options, :local, true)
557 activity_id = Keyword.get(options, :activity_id, nil)
558 actor = Keyword.get(options, :actor, actor)
560 user = User.get_cached_by_ap_id(actor)
561 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
563 with create_activity <- Activity.get_create_by_object_ap_id(id),
570 "deleted_activity_id" => create_activity && create_activity.id
572 |> maybe_put("id", activity_id),
573 {:ok, activity} <- insert(data, local, false),
574 {:ok, object, _create_activity} <- Object.delete(object),
575 stream_out_participations(object, user),
576 _ <- decrease_replies_count_if_reply(object),
577 {:ok, _actor} <- decrease_note_count_if_public(user, object),
578 :ok <- maybe_federate(activity) do
586 @spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
587 {:ok, Activity.t()} | {:error, any()}
588 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
589 with {:ok, result} <-
590 Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
595 defp do_block(blocker, blocked, activity_id, local) do
596 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
597 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
599 if unfollow_blocked do
600 follow_activity = fetch_latest_follow(blocker, blocked)
601 if follow_activity, do: unfollow(blocker, blocked, nil, local)
604 with true <- outgoing_blocks,
605 block_data <- make_block_data(blocker, blocked, activity_id),
606 {:ok, activity} <- insert(block_data, local),
607 :ok <- maybe_federate(activity) do
610 {:error, error} -> Repo.rollback(error)
614 @spec unblock(User.t(), User.t(), String.t() | nil, boolean()) ::
615 {:ok, Activity.t()} | {:error, any()} | nil
616 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
617 with {:ok, result} <-
618 Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do
623 defp do_unblock(blocker, blocked, activity_id, local) do
624 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
625 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
626 {:ok, activity} <- insert(unblock_data, local),
627 :ok <- maybe_federate(activity) do
631 {:error, error} -> Repo.rollback(error)
635 @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
645 # only accept false as false value
646 local = !(params[:local] == false)
647 forward = !(params[:forward] == false)
649 additional = params[:additional] || %{}
653 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
655 Map.merge(additional, %{"to" => [], "cc" => []})
658 with flag_data <- make_flag_data(params, additional),
659 {:ok, activity} <- insert(flag_data, local),
660 {:ok, stripped_activity} <- strip_report_status_data(activity),
661 :ok <- maybe_federate(stripped_activity) do
662 User.all_superusers()
663 |> Enum.filter(fn user -> not is_nil(user.email) end)
664 |> Enum.each(fn superuser ->
666 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
667 |> Pleroma.Emails.Mailer.deliver_async()
674 @spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
675 def move(%User{} = origin, %User{} = target, local \\ true) do
678 "actor" => origin.ap_id,
679 "object" => origin.ap_id,
680 "target" => target.ap_id
683 with true <- origin.ap_id in target.also_known_as,
684 {:ok, activity} <- insert(params, local) do
685 maybe_federate(activity)
687 BackgroundWorker.enqueue("move_following", %{
688 "origin_id" => origin.id,
689 "target_id" => target.id
694 false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
699 defp fetch_activities_for_context_query(context, opts) do
700 public = [Constants.as_public()]
704 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
707 from(activity in Activity)
708 |> maybe_preload_objects(opts)
709 |> maybe_preload_bookmarks(opts)
710 |> maybe_set_thread_muted_field(opts)
711 |> restrict_blocked(opts)
712 |> restrict_recipients(recipients, opts["user"])
716 "?->>'type' = ? and ?->>'context' = ?",
723 |> exclude_poll_votes(opts)
725 |> order_by([activity], desc: activity.id)
728 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
729 def fetch_activities_for_context(context, opts \\ %{}) do
731 |> fetch_activities_for_context_query(opts)
735 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
736 FlakeId.Ecto.CompatType.t() | nil
737 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
739 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
745 @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
746 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
747 opts = Map.drop(opts, ["user"])
749 [Constants.as_public()]
750 |> fetch_activities_query(opts)
751 |> restrict_unlisted()
752 |> Pagination.fetch_paginated(opts, pagination)
755 @valid_visibilities ~w[direct unlisted public private]
757 defp restrict_visibility(query, %{visibility: visibility})
758 when is_list(visibility) do
759 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
765 "activity_visibility(?, ?, ?) = ANY (?)",
775 Logger.error("Could not restrict visibility to #{visibility}")
779 defp restrict_visibility(query, %{visibility: visibility})
780 when visibility in @valid_visibilities do
784 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
788 defp restrict_visibility(_query, %{visibility: visibility})
789 when visibility not in @valid_visibilities do
790 Logger.error("Could not restrict visibility to #{visibility}")
793 defp restrict_visibility(query, _visibility), do: query
795 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
796 when is_list(visibility) do
797 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
802 "activity_visibility(?, ?, ?) = ANY (?)",
810 Logger.error("Could not exclude visibility to #{visibility}")
815 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
816 when visibility in @valid_visibilities do
821 "activity_visibility(?, ?, ?) = ?",
830 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
831 when visibility not in @valid_visibilities do
832 Logger.error("Could not exclude visibility to #{visibility}")
836 defp exclude_visibility(query, _visibility), do: query
838 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
841 defp restrict_thread_visibility(
843 %{"user" => %User{skip_thread_containment: true}},
848 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
851 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
855 defp restrict_thread_visibility(query, _, _), do: query
857 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
860 |> Map.put("user", reading_user)
861 |> Map.put("actor_id", user.ap_id)
864 user_activities_recipients(%{
865 "godmode" => params["godmode"],
866 "reading_user" => reading_user
869 fetch_activities(recipients, params)
873 def fetch_user_activities(user, reading_user, params \\ %{}) do
876 |> Map.put("type", ["Create", "Announce"])
877 |> Map.put("user", reading_user)
878 |> Map.put("actor_id", user.ap_id)
879 |> Map.put("pinned_activity_ids", user.pinned_activities)
882 if User.blocks?(reading_user, user) do
886 |> Map.put("blocking_user", reading_user)
887 |> Map.put("muting_user", reading_user)
891 user_activities_recipients(%{
892 "godmode" => params["godmode"],
893 "reading_user" => reading_user
896 fetch_activities(recipients, params)
900 def fetch_statuses(reading_user, params) do
903 |> Map.put("type", ["Create", "Announce"])
906 user_activities_recipients(%{
907 "godmode" => params["godmode"],
908 "reading_user" => reading_user
911 fetch_activities(recipients, params, :offset)
915 defp user_activities_recipients(%{"godmode" => true}) do
919 defp user_activities_recipients(%{"reading_user" => reading_user}) do
921 [Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
923 [Constants.as_public()]
927 defp restrict_since(query, %{"since_id" => ""}), do: query
929 defp restrict_since(query, %{"since_id" => since_id}) do
930 from(activity in query, where: activity.id > ^since_id)
933 defp restrict_since(query, _), do: query
935 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
936 raise "Can't use the child object without preloading!"
939 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
940 when is_list(tag_reject) and tag_reject != [] do
942 [_activity, object] in query,
943 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
947 defp restrict_tag_reject(query, _), do: query
949 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
950 raise "Can't use the child object without preloading!"
953 defp restrict_tag_all(query, %{"tag_all" => tag_all})
954 when is_list(tag_all) and tag_all != [] do
956 [_activity, object] in query,
957 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
961 defp restrict_tag_all(query, _), do: query
963 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
964 raise "Can't use the child object without preloading!"
967 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
969 [_activity, object] in query,
970 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
974 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
976 [_activity, object] in query,
977 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
981 defp restrict_tag(query, _), do: query
983 defp restrict_recipients(query, [], _user), do: query
985 defp restrict_recipients(query, recipients, nil) do
986 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
989 defp restrict_recipients(query, recipients, user) do
992 where: fragment("? && ?", ^recipients, activity.recipients),
993 or_where: activity.actor == ^user.ap_id
997 defp restrict_local(query, %{"local_only" => true}) do
998 from(activity in query, where: activity.local == true)
1001 defp restrict_local(query, _), do: query
1003 defp restrict_actor(query, %{"actor_id" => actor_id}) do
1004 from(activity in query, where: activity.actor == ^actor_id)
1007 defp restrict_actor(query, _), do: query
1009 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
1010 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
1013 defp restrict_type(query, %{"type" => type}) do
1014 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
1017 defp restrict_type(query, _), do: query
1019 defp restrict_state(query, %{"state" => state}) do
1020 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
1023 defp restrict_state(query, _), do: query
1025 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
1027 [_activity, object] in query,
1028 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
1032 defp restrict_favorited_by(query, _), do: query
1034 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
1035 raise "Can't use the child object without preloading!"
1038 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
1040 [_activity, object] in query,
1041 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
1045 defp restrict_media(query, _), do: query
1047 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
1049 [_activity, object] in query,
1050 where: fragment("?->>'inReplyTo' is null", object.data)
1054 defp restrict_replies(query, _), do: query
1056 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
1057 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
1060 defp restrict_reblogs(query, _), do: query
1062 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
1064 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
1065 mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
1068 from([activity] in query,
1069 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
1070 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
1073 unless opts["skip_preload"] do
1074 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
1080 defp restrict_muted(query, _), do: query
1082 defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
1083 blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
1084 domain_blocks = user.domain_blocks || []
1086 following_ap_ids = User.get_friends_ap_ids(user)
1089 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
1092 [activity, object: o] in query,
1093 where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
1094 where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
1097 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
1104 "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
1112 "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
1121 defp restrict_blocked(query, _), do: query
1123 defp restrict_unlisted(query) do
1128 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
1130 ^[Constants.as_public()]
1135 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
1136 from(activity in query, where: activity.id in ^ids)
1139 defp restrict_pinned(query, _), do: query
1141 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
1142 muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
1148 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
1156 defp restrict_muted_reblogs(query, _), do: query
1158 defp restrict_instance(query, %{"instance" => instance}) do
1163 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
1167 from(activity in query, where: activity.actor in ^users)
1170 defp restrict_instance(query, _), do: query
1172 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
1174 defp exclude_poll_votes(query, _) do
1175 if has_named_binding?(query, :object) do
1176 from([activity, object: o] in query,
1177 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1184 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1185 from(activity in query, where: activity.id != ^id)
1188 defp exclude_id(query, _), do: query
1190 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1192 defp maybe_preload_objects(query, _) do
1194 |> Activity.with_preloaded_object()
1197 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1199 defp maybe_preload_bookmarks(query, opts) do
1201 |> Activity.with_preloaded_bookmark(opts["user"])
1204 defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
1206 |> Activity.with_preloaded_report_notes()
1209 defp maybe_preload_report_notes(query, _), do: query
1211 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1213 defp maybe_set_thread_muted_field(query, opts) do
1215 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1218 defp maybe_order(query, %{order: :desc}) do
1220 |> order_by(desc: :id)
1223 defp maybe_order(query, %{order: :asc}) do
1225 |> order_by(asc: :id)
1228 defp maybe_order(query, _), do: query
1230 defp fetch_activities_query_ap_ids_ops(opts) do
1231 source_user = opts["muting_user"]
1232 ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
1236 if opts["blocking_user"] && opts["blocking_user"] == source_user do
1242 preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
1244 restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
1245 restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
1247 restrict_muted_reblogs_opts =
1248 Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
1250 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
1253 def fetch_activities_query(recipients, opts \\ %{}) do
1254 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
1255 fetch_activities_query_ap_ids_ops(opts)
1258 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1262 |> maybe_preload_objects(opts)
1263 |> maybe_preload_bookmarks(opts)
1264 |> maybe_preload_report_notes(opts)
1265 |> maybe_set_thread_muted_field(opts)
1266 |> maybe_order(opts)
1267 |> restrict_recipients(recipients, opts["user"])
1268 |> restrict_tag(opts)
1269 |> restrict_tag_reject(opts)
1270 |> restrict_tag_all(opts)
1271 |> restrict_since(opts)
1272 |> restrict_local(opts)
1273 |> restrict_actor(opts)
1274 |> restrict_type(opts)
1275 |> restrict_state(opts)
1276 |> restrict_favorited_by(opts)
1277 |> restrict_blocked(restrict_blocked_opts)
1278 |> restrict_muted(restrict_muted_opts)
1279 |> restrict_media(opts)
1280 |> restrict_visibility(opts)
1281 |> restrict_thread_visibility(opts, config)
1282 |> restrict_replies(opts)
1283 |> restrict_reblogs(opts)
1284 |> restrict_pinned(opts)
1285 |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
1286 |> restrict_instance(opts)
1287 |> Activity.restrict_deactivated_users()
1288 |> exclude_poll_votes(opts)
1289 |> exclude_visibility(opts)
1292 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1293 list_memberships = Pleroma.List.memberships(opts["user"])
1295 fetch_activities_query(recipients ++ list_memberships, opts)
1296 |> Pagination.fetch_paginated(opts, pagination)
1298 |> maybe_update_cc(list_memberships, opts["user"])
1302 Fetch favorites activities of user with order by sort adds to favorites
1304 @spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
1305 def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
1307 |> Activity.Queries.by_actor()
1308 |> Activity.Queries.by_type("Like")
1309 |> Activity.with_joined_object()
1310 |> Object.with_joined_activity()
1311 |> select([_like, object, activity], %{activity | object: object})
1312 |> order_by([like, _, _], desc: like.id)
1313 |> Pagination.fetch_paginated(
1314 Map.merge(params, %{"skip_order" => true}),
1320 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1321 when is_list(list_memberships) and length(list_memberships) > 0 do
1322 Enum.map(activities, fn
1323 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1324 if Enum.any?(bcc, &(&1 in list_memberships)) do
1325 update_in(activity.data["cc"], &[user_ap_id | &1])
1335 defp maybe_update_cc(activities, _, _), do: activities
1337 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1338 from(activity in query,
1340 fragment("? && ?", activity.recipients, ^recipients) or
1341 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1342 ^Constants.as_public() in activity.recipients)
1346 def fetch_activities_bounded(
1348 recipients_with_public,
1350 pagination \\ :keyset
1352 fetch_activities_query([], opts)
1353 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1354 |> Pagination.fetch_paginated(opts, pagination)
1358 @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
1359 def upload(file, opts \\ []) do
1360 with {:ok, data} <- Upload.store(file, opts) do
1363 Map.put(data, "actor", opts[:actor])
1368 Repo.insert(%Object{data: obj_data})
1372 defp object_to_user_data(data) do
1374 data["icon"]["url"] &&
1377 "url" => [%{"href" => data["icon"]["url"]}]
1381 data["image"]["url"] &&
1384 "url" => [%{"href" => data["image"]["url"]}]
1389 |> Map.get("attachment", [])
1390 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1391 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1393 locked = data["manuallyApprovesFollowers"] || false
1394 data = Transmogrifier.maybe_fix_user_object(data)
1395 discoverable = data["discoverable"] || false
1396 invisible = data["invisible"] || false
1397 actor_type = data["type"] || "Person"
1406 discoverable: discoverable,
1407 invisible: invisible,
1410 follower_address: data["followers"],
1411 following_address: data["following"],
1412 bio: data["summary"],
1413 actor_type: actor_type,
1414 also_known_as: Map.get(data, "alsoKnownAs", [])
1417 # nickname can be nil because of virtual actors
1419 if data["preferredUsername"] do
1423 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1426 Map.put(user_data, :nickname, nil)
1432 def fetch_follow_information_for_user(user) do
1433 with {:ok, following_data} <-
1434 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1435 {:ok, hide_follows} <- collection_private(following_data),
1436 {:ok, followers_data} <-
1437 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1438 {:ok, hide_followers} <- collection_private(followers_data) do
1441 hide_follows: hide_follows,
1442 follower_count: normalize_counter(followers_data["totalItems"]),
1443 following_count: normalize_counter(following_data["totalItems"]),
1444 hide_followers: hide_followers
1447 {:error, _} = e -> e
1452 defp normalize_counter(counter) when is_integer(counter), do: counter
1453 defp normalize_counter(_), do: 0
1455 defp maybe_update_follow_information(data) do
1456 with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
1457 {:ok, info} <- fetch_follow_information_for_user(data) do
1458 info = Map.merge(data[:info] || %{}, info)
1459 Map.put(data, :info, info)
1461 {:enabled, false} ->
1466 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1473 defp collection_private(%{"first" => %{"type" => type}})
1474 when type in ["CollectionPage", "OrderedCollectionPage"],
1477 defp collection_private(%{"first" => first}) do
1478 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1479 Fetcher.fetch_and_contain_remote_object_from_id(first) do
1482 {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
1483 {:error, _} = e -> e
1488 defp collection_private(_data), do: {:ok, true}
1490 def user_data_from_user_object(data) do
1491 with {:ok, data} <- MRF.filter(data),
1492 {:ok, data} <- object_to_user_data(data) do
1499 def fetch_and_prepare_user_from_ap_id(ap_id) do
1500 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1501 {:ok, data} <- user_data_from_user_object(data),
1502 data <- maybe_update_follow_information(data) do
1505 {:error, "Object has been deleted"} = e ->
1506 Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1510 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1515 def make_user_from_ap_id(ap_id) do
1516 if _user = User.get_cached_by_ap_id(ap_id) do
1517 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1519 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1520 User.insert_or_update_user(data)
1527 def make_user_from_nickname(nickname) do
1528 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1529 make_user_from_ap_id(ap_id)
1531 _e -> {:error, "No AP id in WebFinger"}
1535 # filter out broken threads
1536 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1537 entire_thread_visible_for_user?(activity, user)
1540 # do post-processing on a specific activity
1541 def contain_activity(%Activity{} = activity, %User{} = user) do
1542 contain_broken_threads(activity, user)
1545 def fetch_direct_messages_query do
1547 |> restrict_type(%{"type" => "Create"})
1548 |> restrict_visibility(%{visibility: "direct"})
1549 |> order_by([activity], asc: activity.id)