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.Notification
12 alias Pleroma.Object.Containment
13 alias Pleroma.Object.Fetcher
14 alias Pleroma.Pagination
18 alias Pleroma.Web.ActivityPub.MRF
19 alias Pleroma.Web.ActivityPub.Transmogrifier
20 alias Pleroma.Web.Streamer
21 alias Pleroma.Web.WebFinger
22 alias Pleroma.Workers.BackgroundWorker
25 import Pleroma.Web.ActivityPub.Utils
26 import Pleroma.Web.ActivityPub.Visibility
29 require Pleroma.Constants
31 # For Announce activities, we filter the recipients based on following status for any actors
32 # that match actual users. See issue #164 for more information about why this is necessary.
33 defp get_recipients(%{"type" => "Announce"} = data) do
34 to = Map.get(data, "to", [])
35 cc = Map.get(data, "cc", [])
36 bcc = Map.get(data, "bcc", [])
37 actor = User.get_cached_by_ap_id(data["actor"])
40 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
41 case User.get_cached_by_ap_id(recipient) do
43 user -> User.following?(user, actor)
50 defp get_recipients(%{"type" => "Create"} = data) do
51 to = Map.get(data, "to", [])
52 cc = Map.get(data, "cc", [])
53 bcc = Map.get(data, "bcc", [])
54 actor = Map.get(data, "actor", [])
55 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
59 defp get_recipients(data) do
60 to = Map.get(data, "to", [])
61 cc = Map.get(data, "cc", [])
62 bcc = Map.get(data, "bcc", [])
63 recipients = Enum.concat([to, cc, bcc])
67 defp check_actor_is_active(actor) do
68 if not is_nil(actor) do
69 with user <- User.get_cached_by_ap_id(actor),
70 false <- user.info.deactivated do
80 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
81 limit = Config.get([:instance, :remote_limit])
82 String.length(content) <= limit
85 defp check_remote_limit(_), do: true
87 def increase_note_count_if_public(actor, object) do
88 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
91 def decrease_note_count_if_public(actor, object) do
92 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
95 def increase_replies_count_if_reply(%{
96 "object" => %{"inReplyTo" => reply_ap_id} = object,
99 if is_public?(object) do
100 Object.increase_replies_count(reply_ap_id)
104 def increase_replies_count_if_reply(_create_data), do: :noop
106 def decrease_replies_count_if_reply(%Object{
107 data: %{"inReplyTo" => reply_ap_id} = object
109 if is_public?(object) do
110 Object.decrease_replies_count(reply_ap_id)
114 def decrease_replies_count_if_reply(_object), do: :noop
116 def increase_poll_votes_if_vote(%{
117 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
120 Object.increase_vote_count(reply_ap_id, name)
123 def increase_poll_votes_if_vote(_create_data), do: :noop
125 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
126 with nil <- Activity.normalize(map),
127 map <- lazy_put_activity_defaults(map, fake),
128 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
129 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
130 {:ok, map} <- MRF.filter(map),
131 {recipients, _, _} = get_recipients(map),
132 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
133 :ok <- Containment.contain_child(map),
134 {:ok, map, object} <- insert_full_object(map) do
136 Repo.insert(%Activity{
140 recipients: recipients
143 # Splice in the child object if we have one.
145 if not is_nil(object) do
146 Map.put(activity, :object, object)
151 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
153 Notification.create_notifications(activity)
157 |> Conversation.create_or_bump_for()
158 |> get_participations()
161 stream_out_participations(participations)
164 %Activity{} = activity ->
167 {:fake, true, map, recipients} ->
168 activity = %Activity{
172 recipients: recipients,
176 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
184 defp get_participations({:ok, %{participations: participations}}), do: participations
185 defp get_participations(_), do: []
187 def stream_out_participations(participations) do
190 |> Repo.preload(:user)
192 Streamer.stream("participation", participations)
195 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
196 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
197 conversation = Repo.preload(conversation, :participations),
199 fetch_latest_activity_id_for_context(conversation.ap_id, %{
201 "blocking_user" => user
203 if last_activity_id do
204 stream_out_participations(conversation.participations)
209 def stream_out_participations(_, _), do: :noop
211 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
212 when data_type in ["Create", "Announce", "Delete"] do
214 |> Topics.get_activity_topics()
215 |> Streamer.stream(activity)
218 def stream_out(_activity) do
222 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
223 additional = params[:additional] || %{}
224 # only accept false as false value
225 local = !(params[:local] == false)
226 published = params[:published]
230 %{to: to, actor: actor, published: published, context: context, object: object},
233 {:ok, activity} <- insert(create_data, local, fake),
234 {:fake, false, activity} <- {:fake, fake, activity},
235 _ <- increase_replies_count_if_reply(create_data),
236 _ <- increase_poll_votes_if_vote(create_data),
237 # Changing note count prior to enqueuing federation task in order to avoid
238 # race conditions on updating user.info
239 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
240 :ok <- maybe_federate(activity) do
243 {:fake, true, activity} ->
251 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
252 additional = params[:additional] || %{}
253 # only accept false as false value
254 local = !(params[:local] == false)
255 published = params[:published]
259 %{to: to, actor: actor, published: published, context: context, object: object},
262 {:ok, activity} <- insert(listen_data, local),
263 :ok <- maybe_federate(activity) do
271 def accept(%{to: to, actor: actor, object: object} = params) do
272 # only accept false as false value
273 local = !(params[:local] == false)
275 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
276 {:ok, activity} <- insert(data, local),
277 :ok <- maybe_federate(activity) do
282 def reject(%{to: to, actor: actor, object: object} = params) do
283 # only accept false as false value
284 local = !(params[:local] == false)
286 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
287 {:ok, activity} <- insert(data, local),
288 :ok <- maybe_federate(activity) do
293 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
294 # only accept false as false value
295 local = !(params[:local] == false)
304 {:ok, activity} <- insert(data, local),
305 :ok <- maybe_federate(activity) do
310 def react_with_emoji(user, object, emoji, options \\ []) do
311 with local <- Keyword.get(options, :local, true),
312 activity_id <- Keyword.get(options, :activity_id, nil),
313 Pleroma.Emoji.is_unicode_emoji?(emoji),
314 reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
315 {:ok, activity} <- insert(reaction_data, local),
316 {:ok, object} <- add_emoji_reaction_to_object(activity, object),
317 :ok <- maybe_federate(activity) do
318 {:ok, activity, object}
322 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
324 %User{ap_id: ap_id} = user,
325 %Object{data: %{"id" => _}} = object,
329 with nil <- get_existing_like(ap_id, object),
330 like_data <- make_like_data(user, object, activity_id),
331 {:ok, activity} <- insert(like_data, local),
332 {:ok, object} <- add_like_to_object(activity, object),
333 :ok <- maybe_federate(activity) do
334 {:ok, activity, object}
336 %Activity{} = activity -> {:ok, activity, object}
337 error -> {:error, error}
341 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
342 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
343 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
344 {:ok, unlike_activity} <- insert(unlike_data, local),
345 {:ok, _activity} <- Repo.delete(like_activity),
346 {:ok, object} <- remove_like_from_object(like_activity, object),
347 :ok <- maybe_federate(unlike_activity) do
348 {:ok, unlike_activity, like_activity, object}
355 %User{ap_id: _} = user,
356 %Object{data: %{"id" => _}} = object,
361 with true <- is_public?(object),
362 announce_data <- make_announce_data(user, object, activity_id, public),
363 {:ok, activity} <- insert(announce_data, local),
364 {:ok, object} <- add_announce_to_object(activity, object),
365 :ok <- maybe_federate(activity) do
366 {:ok, activity, object}
368 error -> {:error, error}
378 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
379 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
380 {:ok, unannounce_activity} <- insert(unannounce_data, local),
381 :ok <- maybe_federate(unannounce_activity),
382 {:ok, _activity} <- Repo.delete(announce_activity),
383 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
384 {:ok, unannounce_activity, object}
390 def follow(follower, followed, activity_id \\ nil, local \\ true) do
391 with data <- make_follow_data(follower, followed, activity_id),
392 {:ok, activity} <- insert(data, local),
393 :ok <- maybe_federate(activity),
394 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
399 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
400 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
401 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
402 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
403 {:ok, activity} <- insert(unfollow_data, local),
404 :ok <- maybe_federate(activity) do
409 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
411 "to" => [follower_address],
414 "object" => %{"type" => "Person", "id" => ap_id}
416 {:ok, activity} <- insert(data, true, true, true),
417 :ok <- maybe_federate(activity) do
422 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
423 user = User.get_cached_by_ap_id(actor)
424 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
426 with {:ok, object, activity} <- Object.delete(object),
432 "deleted_activity_id" => activity && activity.id
434 {:ok, activity} <- insert(data, local, false),
435 stream_out_participations(object, user),
436 _ <- decrease_replies_count_if_reply(object),
437 # Changing note count prior to enqueuing federation task in order to avoid
438 # race conditions on updating user.info
439 {:ok, _actor} <- decrease_note_count_if_public(user, object),
440 :ok <- maybe_federate(activity) do
445 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
446 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
447 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
448 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
450 if unfollow_blocked do
451 follow_activity = fetch_latest_follow(blocker, blocked)
452 if follow_activity, do: unfollow(blocker, blocked, nil, local)
455 with true <- outgoing_blocks,
456 block_data <- make_block_data(blocker, blocked, activity_id),
457 {:ok, activity} <- insert(block_data, local),
458 :ok <- maybe_federate(activity) do
465 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
466 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
467 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
468 {:ok, activity} <- insert(unblock_data, local),
469 :ok <- maybe_federate(activity) do
474 @spec flag(map()) :: {:ok, Activity.t()} | any
484 # only accept false as false value
485 local = !(params[:local] == false)
486 forward = !(params[:forward] == false)
488 additional = params[:additional] || %{}
492 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
494 Map.merge(additional, %{"to" => [], "cc" => []})
497 with flag_data <- make_flag_data(params, additional),
498 {:ok, activity} <- insert(flag_data, local),
499 :ok <- maybe_federate(activity) do
500 Enum.each(User.all_superusers(), fn superuser ->
502 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
503 |> Pleroma.Emails.Mailer.deliver_async()
510 defp fetch_activities_for_context_query(context, opts) do
511 public = [Pleroma.Constants.as_public()]
514 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
516 from(activity in Activity)
517 |> maybe_preload_objects(opts)
518 |> maybe_preload_bookmarks(opts)
519 |> maybe_set_thread_muted_field(opts)
520 |> restrict_blocked(opts)
521 |> restrict_recipients(recipients, opts["user"])
525 "?->>'type' = ? and ?->>'context' = ?",
532 |> exclude_poll_votes(opts)
534 |> order_by([activity], desc: activity.id)
537 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
538 def fetch_activities_for_context(context, opts \\ %{}) do
540 |> fetch_activities_for_context_query(opts)
544 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
545 FlakeId.Ecto.CompatType.t() | nil
546 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
548 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
554 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
555 opts = Map.drop(opts, ["user"])
557 [Pleroma.Constants.as_public()]
558 |> fetch_activities_query(opts)
559 |> restrict_unlisted()
560 |> Pagination.fetch_paginated(opts, pagination)
564 @valid_visibilities ~w[direct unlisted public private]
566 defp restrict_visibility(query, %{visibility: visibility})
567 when is_list(visibility) do
568 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
574 "activity_visibility(?, ?, ?) = ANY (?)",
584 Logger.error("Could not restrict visibility to #{visibility}")
588 defp restrict_visibility(query, %{visibility: visibility})
589 when visibility in @valid_visibilities do
593 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
597 defp restrict_visibility(_query, %{visibility: visibility})
598 when visibility not in @valid_visibilities do
599 Logger.error("Could not restrict visibility to #{visibility}")
602 defp restrict_visibility(query, _visibility), do: query
604 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
607 defp restrict_thread_visibility(
609 %{"user" => %User{info: %{skip_thread_containment: true}}},
614 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
617 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
621 defp restrict_thread_visibility(query, _, _), do: query
623 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
626 |> Map.put("user", reading_user)
627 |> Map.put("actor_id", user.ap_id)
628 |> Map.put("whole_db", true)
631 user_activities_recipients(%{
632 "godmode" => params["godmode"],
633 "reading_user" => reading_user
636 fetch_activities(recipients, params)
640 def fetch_user_activities(user, reading_user, params \\ %{}) do
643 |> Map.put("type", ["Create", "Announce"])
644 |> Map.put("user", reading_user)
645 |> Map.put("actor_id", user.ap_id)
646 |> Map.put("whole_db", true)
647 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
650 user_activities_recipients(%{
651 "godmode" => params["godmode"],
652 "reading_user" => reading_user
655 fetch_activities(recipients, params)
659 defp user_activities_recipients(%{"godmode" => true}) do
663 defp user_activities_recipients(%{"reading_user" => reading_user}) do
665 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
667 [Pleroma.Constants.as_public()]
671 defp restrict_since(query, %{"since_id" => ""}), do: query
673 defp restrict_since(query, %{"since_id" => since_id}) do
674 from(activity in query, where: activity.id > ^since_id)
677 defp restrict_since(query, _), do: query
679 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
680 raise "Can't use the child object without preloading!"
683 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
684 when is_list(tag_reject) and tag_reject != [] do
686 [_activity, object] in query,
687 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
691 defp restrict_tag_reject(query, _), do: query
693 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
694 raise "Can't use the child object without preloading!"
697 defp restrict_tag_all(query, %{"tag_all" => tag_all})
698 when is_list(tag_all) and tag_all != [] do
700 [_activity, object] in query,
701 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
705 defp restrict_tag_all(query, _), do: query
707 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
708 raise "Can't use the child object without preloading!"
711 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
713 [_activity, object] in query,
714 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
718 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
720 [_activity, object] in query,
721 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
725 defp restrict_tag(query, _), do: query
727 defp restrict_recipients(query, [], _user), do: query
729 defp restrict_recipients(query, recipients, nil) do
730 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
733 defp restrict_recipients(query, recipients, user) do
736 where: fragment("? && ?", ^recipients, activity.recipients),
737 or_where: activity.actor == ^user.ap_id
741 defp restrict_local(query, %{"local_only" => true}) do
742 from(activity in query, where: activity.local == true)
745 defp restrict_local(query, _), do: query
747 defp restrict_actor(query, %{"actor_id" => actor_id}) do
748 from(activity in query, where: activity.actor == ^actor_id)
751 defp restrict_actor(query, _), do: query
753 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
754 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
757 defp restrict_type(query, %{"type" => type}) do
758 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
761 defp restrict_type(query, _), do: query
763 defp restrict_state(query, %{"state" => state}) do
764 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
767 defp restrict_state(query, _), do: query
769 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
771 [_activity, object] in query,
772 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
776 defp restrict_favorited_by(query, _), do: query
778 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
779 raise "Can't use the child object without preloading!"
782 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
784 [_activity, object] in query,
785 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
789 defp restrict_media(query, _), do: query
791 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
794 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
798 defp restrict_replies(query, _), do: query
800 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
801 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
804 defp restrict_reblogs(query, _), do: query
806 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
808 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
812 from([activity] in query,
813 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
814 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
817 unless opts["skip_preload"] do
818 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
824 defp restrict_muted(query, _), do: query
826 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
827 blocks = info.blocks || []
828 domain_blocks = info.domain_blocks || []
831 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
834 [activity, object: o] in query,
835 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
836 where: fragment("not (? && ?)", activity.recipients, ^blocks),
839 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
844 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
845 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
849 defp restrict_blocked(query, _), do: query
851 defp restrict_unlisted(query) do
856 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
858 ^[Pleroma.Constants.as_public()]
863 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
864 from(activity in query, where: activity.id in ^ids)
867 defp restrict_pinned(query, _), do: query
869 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
870 muted_reblogs = info.muted_reblogs || []
876 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
884 defp restrict_muted_reblogs(query, _), do: query
886 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
888 defp exclude_poll_votes(query, _) do
889 if has_named_binding?(query, :object) do
890 from([activity, object: o] in query,
891 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
898 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
899 from(activity in query, where: activity.id != ^id)
902 defp exclude_id(query, _), do: query
904 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
906 defp maybe_preload_objects(query, _) do
908 |> Activity.with_preloaded_object()
911 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
913 defp maybe_preload_bookmarks(query, opts) do
915 |> Activity.with_preloaded_bookmark(opts["user"])
918 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
920 defp maybe_set_thread_muted_field(query, opts) do
922 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
925 defp maybe_order(query, %{order: :desc}) do
927 |> order_by(desc: :id)
930 defp maybe_order(query, %{order: :asc}) do
932 |> order_by(asc: :id)
935 defp maybe_order(query, _), do: query
937 def fetch_activities_query(recipients, opts \\ %{}) do
939 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
943 |> maybe_preload_objects(opts)
944 |> maybe_preload_bookmarks(opts)
945 |> maybe_set_thread_muted_field(opts)
947 |> restrict_recipients(recipients, opts["user"])
948 |> restrict_tag(opts)
949 |> restrict_tag_reject(opts)
950 |> restrict_tag_all(opts)
951 |> restrict_since(opts)
952 |> restrict_local(opts)
953 |> restrict_actor(opts)
954 |> restrict_type(opts)
955 |> restrict_state(opts)
956 |> restrict_favorited_by(opts)
957 |> restrict_blocked(opts)
958 |> restrict_muted(opts)
959 |> restrict_media(opts)
960 |> restrict_visibility(opts)
961 |> restrict_thread_visibility(opts, config)
962 |> restrict_replies(opts)
963 |> restrict_reblogs(opts)
964 |> restrict_pinned(opts)
965 |> restrict_muted_reblogs(opts)
966 |> Activity.restrict_deactivated_users()
967 |> exclude_poll_votes(opts)
970 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
971 list_memberships = Pleroma.List.memberships(opts["user"])
973 fetch_activities_query(recipients ++ list_memberships, opts)
974 |> Pagination.fetch_paginated(opts, pagination)
976 |> maybe_update_cc(list_memberships, opts["user"])
979 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
980 when is_list(list_memberships) and length(list_memberships) > 0 do
981 Enum.map(activities, fn
982 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
983 if Enum.any?(bcc, &(&1 in list_memberships)) do
984 update_in(activity.data["cc"], &[user_ap_id | &1])
994 defp maybe_update_cc(activities, _, _), do: activities
996 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
997 from(activity in query,
999 fragment("? && ?", activity.recipients, ^recipients) or
1000 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1001 ^Pleroma.Constants.as_public() in activity.recipients)
1005 def fetch_activities_bounded(
1007 recipients_with_public,
1009 pagination \\ :keyset
1011 fetch_activities_query([], opts)
1012 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1013 |> Pagination.fetch_paginated(opts, pagination)
1017 def upload(file, opts \\ []) do
1018 with {:ok, data} <- Upload.store(file, opts) do
1021 Map.put(data, "actor", opts[:actor])
1026 Repo.insert(%Object{data: obj_data})
1030 defp object_to_user_data(data) do
1032 data["icon"]["url"] &&
1035 "url" => [%{"href" => data["icon"]["url"]}]
1039 data["image"]["url"] &&
1042 "url" => [%{"href" => data["image"]["url"]}]
1047 |> Map.get("attachment", [])
1048 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1049 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1051 locked = data["manuallyApprovesFollowers"] || false
1052 data = Transmogrifier.maybe_fix_user_object(data)
1053 discoverable = data["discoverable"] || false
1063 discoverable: discoverable
1067 follower_address: data["followers"],
1068 following_address: data["following"],
1069 bio: data["summary"]
1072 # nickname can be nil because of virtual actors
1074 if data["preferredUsername"] do
1078 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1081 Map.put(user_data, :nickname, nil)
1087 def fetch_follow_information_for_user(user) do
1088 with {:ok, following_data} <-
1089 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1090 following_count when is_integer(following_count) <- following_data["totalItems"],
1091 {:ok, hide_follows} <- collection_private(following_data),
1092 {:ok, followers_data} <-
1093 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1094 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1095 {:ok, hide_followers} <- collection_private(followers_data) do
1098 hide_follows: hide_follows,
1099 follower_count: followers_count,
1100 following_count: following_count,
1101 hide_followers: hide_followers
1112 defp maybe_update_follow_information(data) do
1113 with {:enabled, true} <-
1114 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1115 {:ok, info} <- fetch_follow_information_for_user(data) do
1116 info = Map.merge(data.info, info)
1117 Map.put(data, :info, info)
1119 {:enabled, false} ->
1124 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1131 defp collection_private(data) do
1132 if is_map(data["first"]) and
1133 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1136 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1137 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1140 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1152 def user_data_from_user_object(data) do
1153 with {:ok, data} <- MRF.filter(data),
1154 {:ok, data} <- object_to_user_data(data) do
1161 def fetch_and_prepare_user_from_ap_id(ap_id) do
1162 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1163 {:ok, data} <- user_data_from_user_object(data),
1164 data <- maybe_update_follow_information(data) do
1167 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1171 def make_user_from_ap_id(ap_id) do
1172 if _user = User.get_cached_by_ap_id(ap_id) do
1173 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1175 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1176 User.insert_or_update_user(data)
1183 def make_user_from_nickname(nickname) do
1184 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1185 make_user_from_ap_id(ap_id)
1187 _e -> {:error, "No AP id in WebFinger"}
1191 # filter out broken threads
1192 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1193 entire_thread_visible_for_user?(activity, user)
1196 # do post-processing on a specific activity
1197 def contain_activity(%Activity{} = activity, %User{} = user) do
1198 contain_broken_threads(activity, user)
1201 def fetch_direct_messages_query do
1203 |> restrict_type(%{"type" => "Create"})
1204 |> restrict_visibility(%{visibility: "direct"})
1205 |> order_by([activity], asc: activity.id)