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
16 alias Pleroma.SubscriptionNotification
19 alias Pleroma.Web.ActivityPub.MRF
20 alias Pleroma.Web.ActivityPub.Transmogrifier
21 alias Pleroma.Web.Streamer
22 alias Pleroma.Web.WebFinger
23 alias Pleroma.Workers.BackgroundWorker
26 import Pleroma.Web.ActivityPub.Utils
27 import Pleroma.Web.ActivityPub.Visibility
30 require Pleroma.Constants
32 # For Announce activities, we filter the recipients based on following status for any actors
33 # that match actual users. See issue #164 for more information about why this is necessary.
34 defp get_recipients(%{"type" => "Announce"} = data) do
35 to = Map.get(data, "to", [])
36 cc = Map.get(data, "cc", [])
37 bcc = Map.get(data, "bcc", [])
38 actor = User.get_cached_by_ap_id(data["actor"])
41 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
42 case User.get_cached_by_ap_id(recipient) do
44 user -> User.following?(user, actor)
51 defp get_recipients(%{"type" => "Create"} = data) do
52 to = Map.get(data, "to", [])
53 cc = Map.get(data, "cc", [])
54 bcc = Map.get(data, "bcc", [])
55 actor = Map.get(data, "actor", [])
56 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
60 defp get_recipients(data) do
61 to = Map.get(data, "to", [])
62 cc = Map.get(data, "cc", [])
63 bcc = Map.get(data, "bcc", [])
64 recipients = Enum.concat([to, cc, bcc])
68 defp check_actor_is_active(actor) do
69 if not is_nil(actor) do
70 with user <- User.get_cached_by_ap_id(actor),
71 false <- user.info.deactivated do
81 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
82 limit = Config.get([:instance, :remote_limit])
83 String.length(content) <= limit
86 defp check_remote_limit(_), do: true
88 def increase_note_count_if_public(actor, object) do
89 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
92 def decrease_note_count_if_public(actor, object) do
93 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
96 def increase_replies_count_if_reply(%{
97 "object" => %{"inReplyTo" => reply_ap_id} = object,
100 if is_public?(object) do
101 Object.increase_replies_count(reply_ap_id)
105 def increase_replies_count_if_reply(_create_data), do: :noop
107 def decrease_replies_count_if_reply(%Object{
108 data: %{"inReplyTo" => reply_ap_id} = object
110 if is_public?(object) do
111 Object.decrease_replies_count(reply_ap_id)
115 def decrease_replies_count_if_reply(_object), do: :noop
117 def increase_poll_votes_if_vote(%{
118 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
121 Object.increase_vote_count(reply_ap_id, name)
124 def increase_poll_votes_if_vote(_create_data), do: :noop
126 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
127 with nil <- Activity.normalize(map),
128 map <- lazy_put_activity_defaults(map, fake),
129 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
130 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
131 {:ok, map} <- MRF.filter(map),
132 {recipients, _, _} = get_recipients(map),
133 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
134 :ok <- Containment.contain_child(map),
135 {:ok, map, object} <- insert_full_object(map) do
137 Repo.insert(%Activity{
141 recipients: recipients
144 # Splice in the child object if we have one.
146 if not is_nil(object) do
147 Map.put(activity, :object, object)
152 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
154 Notification.create_notifications(activity)
155 SubscriptionNotification.create_notifications(activity)
159 |> Conversation.create_or_bump_for()
160 |> get_participations()
163 stream_out_participations(participations)
166 %Activity{} = activity ->
169 {:fake, true, map, recipients} ->
170 activity = %Activity{
174 recipients: recipients,
178 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
186 defp get_participations({:ok, %{participations: participations}}), do: participations
187 defp get_participations(_), do: []
189 def stream_out_participations(participations) do
192 |> Repo.preload(:user)
194 Streamer.stream("participation", participations)
197 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
198 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
199 conversation = Repo.preload(conversation, :participations),
201 fetch_latest_activity_id_for_context(conversation.ap_id, %{
203 "blocking_user" => user
205 if last_activity_id do
206 stream_out_participations(conversation.participations)
211 def stream_out_participations(_, _), do: :noop
213 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
214 when data_type in ["Create", "Announce", "Delete"] do
216 |> Topics.get_activity_topics()
217 |> Streamer.stream(activity)
220 def stream_out(_activity) do
224 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
225 additional = params[:additional] || %{}
226 # only accept false as false value
227 local = !(params[:local] == false)
228 published = params[:published]
232 %{to: to, actor: actor, published: published, context: context, object: object},
235 {:ok, activity} <- insert(create_data, local, fake),
236 {:fake, false, activity} <- {:fake, fake, activity},
237 _ <- increase_replies_count_if_reply(create_data),
238 _ <- increase_poll_votes_if_vote(create_data),
239 # Changing note count prior to enqueuing federation task in order to avoid
240 # race conditions on updating user.info
241 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
242 :ok <- maybe_federate(activity) do
245 {:fake, true, activity} ->
253 def accept(%{to: to, actor: actor, object: object} = params) do
254 # only accept false as false value
255 local = !(params[:local] == false)
257 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
258 {:ok, activity} <- insert(data, local),
259 :ok <- maybe_federate(activity) do
264 def reject(%{to: to, actor: actor, object: object} = params) do
265 # only accept false as false value
266 local = !(params[:local] == false)
268 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
269 {:ok, activity} <- insert(data, local),
270 :ok <- maybe_federate(activity) do
275 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
276 # only accept false as false value
277 local = !(params[:local] == false)
286 {:ok, activity} <- insert(data, local),
287 :ok <- maybe_federate(activity) do
292 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
294 %User{ap_id: ap_id} = user,
295 %Object{data: %{"id" => _}} = object,
299 with nil <- get_existing_like(ap_id, object),
300 like_data <- make_like_data(user, object, activity_id),
301 {:ok, activity} <- insert(like_data, local),
302 {:ok, object} <- add_like_to_object(activity, object),
303 :ok <- maybe_federate(activity) do
304 {:ok, activity, object}
306 %Activity{} = activity -> {:ok, activity, object}
307 error -> {:error, error}
311 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
312 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
313 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
314 {:ok, unlike_activity} <- insert(unlike_data, local),
315 {:ok, _activity} <- Repo.delete(like_activity),
316 {:ok, object} <- remove_like_from_object(like_activity, object),
317 :ok <- maybe_federate(unlike_activity) do
318 {:ok, unlike_activity, like_activity, object}
325 %User{ap_id: _} = user,
326 %Object{data: %{"id" => _}} = object,
331 with true <- is_public?(object),
332 announce_data <- make_announce_data(user, object, activity_id, public),
333 {:ok, activity} <- insert(announce_data, local),
334 {:ok, object} <- add_announce_to_object(activity, object),
335 :ok <- maybe_federate(activity) do
336 {:ok, activity, object}
338 error -> {:error, error}
348 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
349 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
350 {:ok, unannounce_activity} <- insert(unannounce_data, local),
351 :ok <- maybe_federate(unannounce_activity),
352 {:ok, _activity} <- Repo.delete(announce_activity),
353 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
354 {:ok, unannounce_activity, object}
360 def follow(follower, followed, activity_id \\ nil, local \\ true) do
361 with data <- make_follow_data(follower, followed, activity_id),
362 {:ok, activity} <- insert(data, local),
363 :ok <- maybe_federate(activity),
364 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
369 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
370 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
371 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
372 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
373 {:ok, activity} <- insert(unfollow_data, local),
374 :ok <- maybe_federate(activity) do
379 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
381 "to" => [follower_address],
384 "object" => %{"type" => "Person", "id" => ap_id}
386 {:ok, activity} <- insert(data, true, true, true),
387 :ok <- maybe_federate(activity) do
392 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
393 user = User.get_cached_by_ap_id(actor)
394 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
396 with {:ok, object, activity} <- Object.delete(object),
402 "deleted_activity_id" => activity && activity.id
404 {:ok, activity} <- insert(data, local, false),
405 stream_out_participations(object, user),
406 _ <- decrease_replies_count_if_reply(object),
407 # Changing note count prior to enqueuing federation task in order to avoid
408 # race conditions on updating user.info
409 {:ok, _actor} <- decrease_note_count_if_public(user, object),
410 :ok <- maybe_federate(activity) do
415 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
416 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
417 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
418 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
420 if unfollow_blocked do
421 follow_activity = fetch_latest_follow(blocker, blocked)
422 if follow_activity, do: unfollow(blocker, blocked, nil, local)
425 with true <- outgoing_blocks,
426 block_data <- make_block_data(blocker, blocked, activity_id),
427 {:ok, activity} <- insert(block_data, local),
428 :ok <- maybe_federate(activity) do
435 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
436 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
437 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
438 {:ok, activity} <- insert(unblock_data, local),
439 :ok <- maybe_federate(activity) do
444 @spec flag(map()) :: {:ok, Activity.t()} | any
454 # only accept false as false value
455 local = !(params[:local] == false)
456 forward = !(params[:forward] == false)
458 additional = params[:additional] || %{}
462 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
464 Map.merge(additional, %{"to" => [], "cc" => []})
467 with flag_data <- make_flag_data(params, additional),
468 {:ok, activity} <- insert(flag_data, local),
469 :ok <- maybe_federate(activity) do
470 Enum.each(User.all_superusers(), fn superuser ->
472 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
473 |> Pleroma.Emails.Mailer.deliver_async()
480 defp fetch_activities_for_context_query(context, opts) do
481 public = [Pleroma.Constants.as_public()]
484 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
486 from(activity in Activity)
487 |> maybe_preload_objects(opts)
488 |> maybe_preload_bookmarks(opts)
489 |> maybe_set_thread_muted_field(opts)
490 |> restrict_blocked(opts)
491 |> restrict_recipients(recipients, opts["user"])
495 "?->>'type' = ? and ?->>'context' = ?",
502 |> exclude_poll_votes(opts)
504 |> order_by([activity], desc: activity.id)
507 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
508 def fetch_activities_for_context(context, opts \\ %{}) do
510 |> fetch_activities_for_context_query(opts)
514 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
515 FlakeId.Ecto.CompatType.t() | nil
516 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
518 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
524 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
525 opts = Map.drop(opts, ["user"])
527 [Pleroma.Constants.as_public()]
528 |> fetch_activities_query(opts)
529 |> restrict_unlisted()
530 |> Pagination.fetch_paginated(opts, pagination)
534 @valid_visibilities ~w[direct unlisted public private]
536 defp restrict_visibility(query, %{visibility: visibility})
537 when is_list(visibility) do
538 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
544 "activity_visibility(?, ?, ?) = ANY (?)",
554 Logger.error("Could not restrict visibility to #{visibility}")
558 defp restrict_visibility(query, %{visibility: visibility})
559 when visibility in @valid_visibilities do
563 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
567 defp restrict_visibility(_query, %{visibility: visibility})
568 when visibility not in @valid_visibilities do
569 Logger.error("Could not restrict visibility to #{visibility}")
572 defp restrict_visibility(query, _visibility), do: query
574 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
577 defp restrict_thread_visibility(
579 %{"user" => %User{info: %{skip_thread_containment: true}}},
584 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
587 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
591 defp restrict_thread_visibility(query, _, _), do: query
593 def fetch_user_activities(user, reading_user, params \\ %{}) do
596 |> Map.put("type", ["Create", "Announce"])
597 |> Map.put("user", reading_user)
598 |> Map.put("actor_id", user.ap_id)
599 |> Map.put("whole_db", true)
600 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
603 user_activities_recipients(%{
604 "godmode" => params["godmode"],
605 "reading_user" => reading_user
608 fetch_activities(recipients, params)
612 defp user_activities_recipients(%{"godmode" => true}) do
616 defp user_activities_recipients(%{"reading_user" => reading_user}) do
618 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
620 [Pleroma.Constants.as_public()]
624 defp restrict_since(query, %{"since_id" => ""}), do: query
626 defp restrict_since(query, %{"since_id" => since_id}) do
627 from(activity in query, where: activity.id > ^since_id)
630 defp restrict_since(query, _), do: query
632 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
633 raise "Can't use the child object without preloading!"
636 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
637 when is_list(tag_reject) and tag_reject != [] do
639 [_activity, object] in query,
640 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
644 defp restrict_tag_reject(query, _), do: query
646 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
647 raise "Can't use the child object without preloading!"
650 defp restrict_tag_all(query, %{"tag_all" => tag_all})
651 when is_list(tag_all) and tag_all != [] do
653 [_activity, object] in query,
654 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
658 defp restrict_tag_all(query, _), do: query
660 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
661 raise "Can't use the child object without preloading!"
664 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
666 [_activity, object] in query,
667 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
671 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
673 [_activity, object] in query,
674 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
678 defp restrict_tag(query, _), do: query
680 defp restrict_recipients(query, [], _user), do: query
682 defp restrict_recipients(query, recipients, nil) do
683 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
686 defp restrict_recipients(query, recipients, user) do
689 where: fragment("? && ?", ^recipients, activity.recipients),
690 or_where: activity.actor == ^user.ap_id
694 defp restrict_local(query, %{"local_only" => true}) do
695 from(activity in query, where: activity.local == true)
698 defp restrict_local(query, _), do: query
700 defp restrict_actor(query, %{"actor_id" => actor_id}) do
701 from(activity in query, where: activity.actor == ^actor_id)
704 defp restrict_actor(query, _), do: query
706 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
707 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
710 defp restrict_type(query, %{"type" => type}) do
711 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
714 defp restrict_type(query, _), do: query
716 defp restrict_state(query, %{"state" => state}) do
717 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
720 defp restrict_state(query, _), do: query
722 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
724 [_activity, object] in query,
725 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
729 defp restrict_favorited_by(query, _), do: query
731 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
732 raise "Can't use the child object without preloading!"
735 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
737 [_activity, object] in query,
738 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
742 defp restrict_media(query, _), do: query
744 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
747 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
751 defp restrict_replies(query, _), do: query
753 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
754 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
757 defp restrict_reblogs(query, _), do: query
759 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
761 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
765 from([activity] in query,
766 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
767 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
770 unless opts["skip_preload"] do
771 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
777 defp restrict_muted(query, _), do: query
779 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
780 blocks = info.blocks || []
781 domain_blocks = info.domain_blocks || []
784 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
787 [activity, object: o] in query,
788 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
789 where: fragment("not (? && ?)", activity.recipients, ^blocks),
792 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
797 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
798 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
802 defp restrict_blocked(query, _), do: query
804 defp restrict_unlisted(query) do
809 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
811 ^[Pleroma.Constants.as_public()]
816 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
817 from(activity in query, where: activity.id in ^ids)
820 defp restrict_pinned(query, _), do: query
822 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
823 muted_reblogs = info.muted_reblogs || []
829 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
837 defp restrict_muted_reblogs(query, _), do: query
839 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
841 defp exclude_poll_votes(query, _) do
842 if has_named_binding?(query, :object) do
843 from([activity, object: o] in query,
844 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
851 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
852 from(activity in query, where: activity.id != ^id)
855 defp exclude_id(query, _), do: query
857 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
859 defp maybe_preload_objects(query, _) do
861 |> Activity.with_preloaded_object()
864 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
866 defp maybe_preload_bookmarks(query, opts) do
868 |> Activity.with_preloaded_bookmark(opts["user"])
871 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
873 defp maybe_set_thread_muted_field(query, opts) do
875 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
878 defp maybe_order(query, %{order: :desc}) do
880 |> order_by(desc: :id)
883 defp maybe_order(query, %{order: :asc}) do
885 |> order_by(asc: :id)
888 defp maybe_order(query, _), do: query
890 def fetch_activities_query(recipients, opts \\ %{}) do
892 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
896 |> maybe_preload_objects(opts)
897 |> maybe_preload_bookmarks(opts)
898 |> maybe_set_thread_muted_field(opts)
900 |> restrict_recipients(recipients, opts["user"])
901 |> restrict_tag(opts)
902 |> restrict_tag_reject(opts)
903 |> restrict_tag_all(opts)
904 |> restrict_since(opts)
905 |> restrict_local(opts)
906 |> restrict_actor(opts)
907 |> restrict_type(opts)
908 |> restrict_state(opts)
909 |> restrict_favorited_by(opts)
910 |> restrict_blocked(opts)
911 |> restrict_muted(opts)
912 |> restrict_media(opts)
913 |> restrict_visibility(opts)
914 |> restrict_thread_visibility(opts, config)
915 |> restrict_replies(opts)
916 |> restrict_reblogs(opts)
917 |> restrict_pinned(opts)
918 |> restrict_muted_reblogs(opts)
919 |> Activity.restrict_deactivated_users()
920 |> exclude_poll_votes(opts)
923 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
924 list_memberships = Pleroma.List.memberships(opts["user"])
926 fetch_activities_query(recipients ++ list_memberships, opts)
927 |> Pagination.fetch_paginated(opts, pagination)
929 |> maybe_update_cc(list_memberships, opts["user"])
932 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
933 when is_list(list_memberships) and length(list_memberships) > 0 do
934 Enum.map(activities, fn
935 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
936 if Enum.any?(bcc, &(&1 in list_memberships)) do
937 update_in(activity.data["cc"], &[user_ap_id | &1])
947 defp maybe_update_cc(activities, _, _), do: activities
949 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
950 from(activity in query,
952 fragment("? && ?", activity.recipients, ^recipients) or
953 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
954 ^Pleroma.Constants.as_public() in activity.recipients)
958 def fetch_activities_bounded(
960 recipients_with_public,
962 pagination \\ :keyset
964 fetch_activities_query([], opts)
965 |> fetch_activities_bounded_query(recipients, recipients_with_public)
966 |> Pagination.fetch_paginated(opts, pagination)
970 def upload(file, opts \\ []) do
971 with {:ok, data} <- Upload.store(file, opts) do
974 Map.put(data, "actor", opts[:actor])
979 Repo.insert(%Object{data: obj_data})
983 defp object_to_user_data(data) do
985 data["icon"]["url"] &&
988 "url" => [%{"href" => data["icon"]["url"]}]
992 data["image"]["url"] &&
995 "url" => [%{"href" => data["image"]["url"]}]
1000 |> Map.get("attachment", [])
1001 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1002 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1004 locked = data["manuallyApprovesFollowers"] || false
1005 data = Transmogrifier.maybe_fix_user_object(data)
1006 discoverable = data["discoverable"] || false
1016 discoverable: discoverable
1020 follower_address: data["followers"],
1021 following_address: data["following"],
1022 bio: data["summary"]
1025 # nickname can be nil because of virtual actors
1027 if data["preferredUsername"] do
1031 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1034 Map.put(user_data, :nickname, nil)
1040 def fetch_follow_information_for_user(user) do
1041 with {:ok, following_data} <-
1042 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1043 following_count when is_integer(following_count) <- following_data["totalItems"],
1044 {:ok, hide_follows} <- collection_private(following_data),
1045 {:ok, followers_data} <-
1046 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1047 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1048 {:ok, hide_followers} <- collection_private(followers_data) do
1051 hide_follows: hide_follows,
1052 follower_count: followers_count,
1053 following_count: following_count,
1054 hide_followers: hide_followers
1065 defp maybe_update_follow_information(data) do
1066 with {:enabled, true} <-
1067 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1068 {:ok, info} <- fetch_follow_information_for_user(data) do
1069 info = Map.merge(data.info, info)
1070 Map.put(data, :info, info)
1072 {:enabled, false} ->
1077 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1084 defp collection_private(data) do
1085 if is_map(data["first"]) and
1086 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1089 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1090 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1093 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1105 def user_data_from_user_object(data) do
1106 with {:ok, data} <- MRF.filter(data),
1107 {:ok, data} <- object_to_user_data(data) do
1114 def fetch_and_prepare_user_from_ap_id(ap_id) do
1115 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1116 {:ok, data} <- user_data_from_user_object(data),
1117 data <- maybe_update_follow_information(data) do
1120 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1124 def make_user_from_ap_id(ap_id) do
1125 if _user = User.get_cached_by_ap_id(ap_id) do
1126 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1128 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1129 User.insert_or_update_user(data)
1136 def make_user_from_nickname(nickname) do
1137 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1138 make_user_from_ap_id(ap_id)
1140 _e -> {:error, "No AP id in WebFinger"}
1144 # filter out broken threads
1145 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1146 entire_thread_visible_for_user?(activity, user)
1149 # do post-processing on a specific activity
1150 def contain_activity(%Activity{} = activity, %User{} = user) do
1151 contain_broken_threads(activity, user)
1154 def fetch_direct_messages_query do
1156 |> restrict_type(%{"type" => "Create"})
1157 |> restrict_visibility(%{visibility: "direct"})
1158 |> order_by([activity], asc: activity.id)