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.ActivityPub.Utils
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)
158 |> Conversation.create_or_bump_for()
159 |> get_participations()
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 get_participations({:ok, %{participations: participations}}), do: participations
186 defp get_participations(_), do: []
188 def stream_out_participations(participations) do
191 |> Repo.preload(:user)
193 Streamer.stream("participation", participations)
196 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
197 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
198 conversation = Repo.preload(conversation, :participations),
200 fetch_latest_activity_id_for_context(conversation.ap_id, %{
202 "blocking_user" => user
204 if last_activity_id do
205 stream_out_participations(conversation.participations)
210 def stream_out_participations(_, _), do: :noop
212 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
213 when data_type in ["Create", "Announce", "Delete"] do
215 |> Topics.get_activity_topics()
216 |> Streamer.stream(activity)
219 def stream_out(_activity) do
223 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
224 additional = params[:additional] || %{}
225 # only accept false as false value
226 local = !(params[:local] == false)
227 published = params[:published]
231 %{to: to, actor: actor, published: published, context: context, object: object},
234 {:ok, activity} <- insert(create_data, local, fake),
235 {:fake, false, activity} <- {:fake, fake, activity},
236 _ <- increase_replies_count_if_reply(create_data),
237 _ <- increase_poll_votes_if_vote(create_data),
238 # Changing note count prior to enqueuing federation task in order to avoid
239 # race conditions on updating user.info
240 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
241 :ok <- maybe_federate(activity) do
244 {:fake, true, activity} ->
252 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
253 additional = params[:additional] || %{}
254 # only accept false as false value
255 local = !(params[:local] == false)
256 published = params[:published]
260 %{to: to, actor: actor, published: published, context: context, object: object},
263 {:ok, activity} <- insert(listen_data, local),
264 :ok <- maybe_federate(activity) do
272 def accept(%{to: to, actor: actor, object: object} = params) do
273 # only accept false as false value
274 local = !(params[:local] == false)
276 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
277 {:ok, activity} <- insert(data, local),
278 :ok <- maybe_federate(activity) do
283 def reject(%{to: to, actor: actor, object: object} = params) do
284 # only accept false as false value
285 local = !(params[:local] == false)
287 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
288 {:ok, activity} <- insert(data, local),
289 :ok <- maybe_federate(activity) do
294 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
295 local = !(params[:local] == false)
296 activity_id = params[:activity_id]
305 data <- Utils.maybe_put(data, "id", activity_id),
306 {:ok, activity} <- insert(data, local),
307 :ok <- maybe_federate(activity) do
312 def react_with_emoji(user, object, emoji, options \\ []) do
313 with local <- Keyword.get(options, :local, true),
314 activity_id <- Keyword.get(options, :activity_id, nil),
315 Pleroma.Emoji.is_unicode_emoji?(emoji),
316 reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
317 {:ok, activity} <- insert(reaction_data, local),
318 {:ok, object} <- add_emoji_reaction_to_object(activity, object),
319 :ok <- maybe_federate(activity) do
320 {:ok, activity, object}
324 def unreact_with_emoji(user, reaction_id, options \\ []) do
325 with local <- Keyword.get(options, :local, true),
326 activity_id <- Keyword.get(options, :activity_id, nil),
327 user_ap_id <- user.ap_id,
328 %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
329 object <- Object.normalize(reaction_activity),
330 unreact_data <- make_undo_data(user, reaction_activity, activity_id),
331 {:ok, activity} <- insert(unreact_data, local),
332 {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
333 :ok <- maybe_federate(activity) do
334 {:ok, activity, object}
338 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
340 %User{ap_id: ap_id} = user,
341 %Object{data: %{"id" => _}} = object,
345 with nil <- get_existing_like(ap_id, object),
346 like_data <- make_like_data(user, object, activity_id),
347 {:ok, activity} <- insert(like_data, local),
348 {:ok, object} <- add_like_to_object(activity, object),
349 :ok <- maybe_federate(activity) do
350 {:ok, activity, object}
352 %Activity{} = activity -> {:ok, activity, object}
353 error -> {:error, error}
357 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
358 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
359 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
360 {:ok, unlike_activity} <- insert(unlike_data, local),
361 {:ok, _activity} <- Repo.delete(like_activity),
362 {:ok, object} <- remove_like_from_object(like_activity, object),
363 :ok <- maybe_federate(unlike_activity) do
364 {:ok, unlike_activity, like_activity, object}
371 %User{ap_id: _} = user,
372 %Object{data: %{"id" => _}} = object,
377 with true <- is_announceable?(object, user, public),
378 announce_data <- make_announce_data(user, object, activity_id, public),
379 {:ok, activity} <- insert(announce_data, local),
380 {:ok, object} <- add_announce_to_object(activity, object),
381 :ok <- maybe_federate(activity) do
382 {:ok, activity, object}
384 error -> {:error, error}
394 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
395 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
396 {:ok, unannounce_activity} <- insert(unannounce_data, local),
397 :ok <- maybe_federate(unannounce_activity),
398 {:ok, _activity} <- Repo.delete(announce_activity),
399 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
400 {:ok, unannounce_activity, object}
406 def follow(follower, followed, activity_id \\ nil, local \\ true) do
407 with data <- make_follow_data(follower, followed, activity_id),
408 {:ok, activity} <- insert(data, local),
409 :ok <- maybe_federate(activity),
410 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
415 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
416 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
417 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
418 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
419 {:ok, activity} <- insert(unfollow_data, local),
420 :ok <- maybe_federate(activity) do
425 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
427 "to" => [follower_address],
430 "object" => %{"type" => "Person", "id" => ap_id}
432 {:ok, activity} <- insert(data, true, true, true),
433 :ok <- maybe_federate(activity) do
438 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
439 user = User.get_cached_by_ap_id(actor)
440 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
442 with {:ok, object, activity} <- Object.delete(object),
448 "deleted_activity_id" => activity && activity.id
450 {:ok, activity} <- insert(data, local, false),
451 stream_out_participations(object, user),
452 _ <- decrease_replies_count_if_reply(object),
453 # Changing note count prior to enqueuing federation task in order to avoid
454 # race conditions on updating user.info
455 {:ok, _actor} <- decrease_note_count_if_public(user, object),
456 :ok <- maybe_federate(activity) do
461 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
462 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
463 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
464 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
466 if unfollow_blocked do
467 follow_activity = fetch_latest_follow(blocker, blocked)
468 if follow_activity, do: unfollow(blocker, blocked, nil, local)
471 with true <- outgoing_blocks,
472 block_data <- make_block_data(blocker, blocked, activity_id),
473 {:ok, activity} <- insert(block_data, local),
474 :ok <- maybe_federate(activity) do
481 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
482 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
483 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
484 {:ok, activity} <- insert(unblock_data, local),
485 :ok <- maybe_federate(activity) do
490 @spec flag(map()) :: {:ok, Activity.t()} | any
500 # only accept false as false value
501 local = !(params[:local] == false)
502 forward = !(params[:forward] == false)
504 additional = params[:additional] || %{}
508 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
510 Map.merge(additional, %{"to" => [], "cc" => []})
513 with flag_data <- make_flag_data(params, additional),
514 {:ok, activity} <- insert(flag_data, local),
515 :ok <- maybe_federate(activity) do
516 Enum.each(User.all_superusers(), fn superuser ->
518 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
519 |> Pleroma.Emails.Mailer.deliver_async()
526 defp fetch_activities_for_context_query(context, opts) do
527 public = [Pleroma.Constants.as_public()]
530 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
532 from(activity in Activity)
533 |> maybe_preload_objects(opts)
534 |> maybe_preload_bookmarks(opts)
535 |> maybe_set_thread_muted_field(opts)
536 |> restrict_blocked(opts)
537 |> restrict_recipients(recipients, opts["user"])
541 "?->>'type' = ? and ?->>'context' = ?",
548 |> exclude_poll_votes(opts)
550 |> order_by([activity], desc: activity.id)
553 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
554 def fetch_activities_for_context(context, opts \\ %{}) do
556 |> fetch_activities_for_context_query(opts)
560 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
561 FlakeId.Ecto.CompatType.t() | nil
562 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
564 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
570 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
571 opts = Map.drop(opts, ["user"])
573 [Pleroma.Constants.as_public()]
574 |> fetch_activities_query(opts)
575 |> restrict_unlisted()
576 |> Pagination.fetch_paginated(opts, pagination)
580 @valid_visibilities ~w[direct unlisted public private]
582 defp restrict_visibility(query, %{visibility: visibility})
583 when is_list(visibility) do
584 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
590 "activity_visibility(?, ?, ?) = ANY (?)",
600 Logger.error("Could not restrict visibility to #{visibility}")
604 defp restrict_visibility(query, %{visibility: visibility})
605 when visibility in @valid_visibilities do
609 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
613 defp restrict_visibility(_query, %{visibility: visibility})
614 when visibility not in @valid_visibilities do
615 Logger.error("Could not restrict visibility to #{visibility}")
618 defp restrict_visibility(query, _visibility), do: query
620 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
623 defp restrict_thread_visibility(
625 %{"user" => %User{info: %{skip_thread_containment: true}}},
630 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
633 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
637 defp restrict_thread_visibility(query, _, _), do: query
639 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
642 |> Map.put("user", reading_user)
643 |> Map.put("actor_id", user.ap_id)
644 |> Map.put("whole_db", true)
647 user_activities_recipients(%{
648 "godmode" => params["godmode"],
649 "reading_user" => reading_user
652 fetch_activities(recipients, params)
656 def fetch_user_activities(user, reading_user, params \\ %{}) do
659 |> Map.put("type", ["Create", "Announce"])
660 |> Map.put("user", reading_user)
661 |> Map.put("actor_id", user.ap_id)
662 |> Map.put("whole_db", true)
663 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
666 user_activities_recipients(%{
667 "godmode" => params["godmode"],
668 "reading_user" => reading_user
671 fetch_activities(recipients, params)
675 defp user_activities_recipients(%{"godmode" => true}) do
679 defp user_activities_recipients(%{"reading_user" => reading_user}) do
681 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
683 [Pleroma.Constants.as_public()]
687 defp restrict_since(query, %{"since_id" => ""}), do: query
689 defp restrict_since(query, %{"since_id" => since_id}) do
690 from(activity in query, where: activity.id > ^since_id)
693 defp restrict_since(query, _), do: query
695 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
696 raise "Can't use the child object without preloading!"
699 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
700 when is_list(tag_reject) and tag_reject != [] do
702 [_activity, object] in query,
703 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
707 defp restrict_tag_reject(query, _), do: query
709 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
710 raise "Can't use the child object without preloading!"
713 defp restrict_tag_all(query, %{"tag_all" => tag_all})
714 when is_list(tag_all) and tag_all != [] do
716 [_activity, object] in query,
717 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
721 defp restrict_tag_all(query, _), do: query
723 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
724 raise "Can't use the child object without preloading!"
727 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
729 [_activity, object] in query,
730 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
734 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
736 [_activity, object] in query,
737 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
741 defp restrict_tag(query, _), do: query
743 defp restrict_recipients(query, [], _user), do: query
745 defp restrict_recipients(query, recipients, nil) do
746 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
749 defp restrict_recipients(query, recipients, user) do
752 where: fragment("? && ?", ^recipients, activity.recipients),
753 or_where: activity.actor == ^user.ap_id
757 defp restrict_local(query, %{"local_only" => true}) do
758 from(activity in query, where: activity.local == true)
761 defp restrict_local(query, _), do: query
763 defp restrict_actor(query, %{"actor_id" => actor_id}) do
764 from(activity in query, where: activity.actor == ^actor_id)
767 defp restrict_actor(query, _), do: query
769 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
770 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
773 defp restrict_type(query, %{"type" => type}) do
774 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
777 defp restrict_type(query, _), do: query
779 defp restrict_state(query, %{"state" => state}) do
780 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
783 defp restrict_state(query, _), do: query
785 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
787 [_activity, object] in query,
788 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
792 defp restrict_favorited_by(query, _), do: query
794 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
795 raise "Can't use the child object without preloading!"
798 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
800 [_activity, object] in query,
801 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
805 defp restrict_media(query, _), do: query
807 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
810 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
814 defp restrict_replies(query, _), do: query
816 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
817 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
820 defp restrict_reblogs(query, _), do: query
822 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
824 defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
828 from([activity] in query,
829 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
830 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
833 unless opts["skip_preload"] do
834 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
840 defp restrict_muted(query, _), do: query
842 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
843 blocks = info.blocks || []
844 domain_blocks = info.domain_blocks || []
847 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
850 [activity, object: o] in query,
851 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
852 where: fragment("not (? && ?)", activity.recipients, ^blocks),
855 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
860 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
861 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
865 defp restrict_blocked(query, _), do: query
867 defp restrict_unlisted(query) do
872 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
874 ^[Pleroma.Constants.as_public()]
879 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
880 from(activity in query, where: activity.id in ^ids)
883 defp restrict_pinned(query, _), do: query
885 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
886 muted_reblogs = info.muted_reblogs || []
892 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
900 defp restrict_muted_reblogs(query, _), do: query
902 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
904 defp exclude_poll_votes(query, _) do
905 if has_named_binding?(query, :object) do
906 from([activity, object: o] in query,
907 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
914 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
915 from(activity in query, where: activity.id != ^id)
918 defp exclude_id(query, _), do: query
920 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
922 defp maybe_preload_objects(query, _) do
924 |> Activity.with_preloaded_object()
927 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
929 defp maybe_preload_bookmarks(query, opts) do
931 |> Activity.with_preloaded_bookmark(opts["user"])
934 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
936 defp maybe_set_thread_muted_field(query, opts) do
938 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
941 defp maybe_order(query, %{order: :desc}) do
943 |> order_by(desc: :id)
946 defp maybe_order(query, %{order: :asc}) do
948 |> order_by(asc: :id)
951 defp maybe_order(query, _), do: query
953 def fetch_activities_query(recipients, opts \\ %{}) do
955 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
959 |> maybe_preload_objects(opts)
960 |> maybe_preload_bookmarks(opts)
961 |> maybe_set_thread_muted_field(opts)
963 |> restrict_recipients(recipients, opts["user"])
964 |> restrict_tag(opts)
965 |> restrict_tag_reject(opts)
966 |> restrict_tag_all(opts)
967 |> restrict_since(opts)
968 |> restrict_local(opts)
969 |> restrict_actor(opts)
970 |> restrict_type(opts)
971 |> restrict_state(opts)
972 |> restrict_favorited_by(opts)
973 |> restrict_blocked(opts)
974 |> restrict_muted(opts)
975 |> restrict_media(opts)
976 |> restrict_visibility(opts)
977 |> restrict_thread_visibility(opts, config)
978 |> restrict_replies(opts)
979 |> restrict_reblogs(opts)
980 |> restrict_pinned(opts)
981 |> restrict_muted_reblogs(opts)
982 |> Activity.restrict_deactivated_users()
983 |> exclude_poll_votes(opts)
986 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
987 list_memberships = Pleroma.List.memberships(opts["user"])
989 fetch_activities_query(recipients ++ list_memberships, opts)
990 |> Pagination.fetch_paginated(opts, pagination)
992 |> maybe_update_cc(list_memberships, opts["user"])
995 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
996 when is_list(list_memberships) and length(list_memberships) > 0 do
997 Enum.map(activities, fn
998 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
999 if Enum.any?(bcc, &(&1 in list_memberships)) do
1000 update_in(activity.data["cc"], &[user_ap_id | &1])
1010 defp maybe_update_cc(activities, _, _), do: activities
1012 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1013 from(activity in query,
1015 fragment("? && ?", activity.recipients, ^recipients) or
1016 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1017 ^Pleroma.Constants.as_public() in activity.recipients)
1021 def fetch_activities_bounded(
1023 recipients_with_public,
1025 pagination \\ :keyset
1027 fetch_activities_query([], opts)
1028 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1029 |> Pagination.fetch_paginated(opts, pagination)
1033 def upload(file, opts \\ []) do
1034 with {:ok, data} <- Upload.store(file, opts) do
1037 Map.put(data, "actor", opts[:actor])
1042 Repo.insert(%Object{data: obj_data})
1046 defp object_to_user_data(data) do
1048 data["icon"]["url"] &&
1051 "url" => [%{"href" => data["icon"]["url"]}]
1055 data["image"]["url"] &&
1058 "url" => [%{"href" => data["image"]["url"]}]
1063 |> Map.get("attachment", [])
1064 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1065 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1067 locked = data["manuallyApprovesFollowers"] || false
1068 data = Transmogrifier.maybe_fix_user_object(data)
1069 discoverable = data["discoverable"] || false
1079 discoverable: discoverable
1083 follower_address: data["followers"],
1084 following_address: data["following"],
1085 bio: data["summary"]
1088 # nickname can be nil because of virtual actors
1090 if data["preferredUsername"] do
1094 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1097 Map.put(user_data, :nickname, nil)
1103 def fetch_follow_information_for_user(user) do
1104 with {:ok, following_data} <-
1105 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1106 following_count when is_integer(following_count) <- following_data["totalItems"],
1107 {:ok, hide_follows} <- collection_private(following_data),
1108 {:ok, followers_data} <-
1109 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1110 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1111 {:ok, hide_followers} <- collection_private(followers_data) do
1114 hide_follows: hide_follows,
1115 follower_count: followers_count,
1116 following_count: following_count,
1117 hide_followers: hide_followers
1128 defp maybe_update_follow_information(data) do
1129 with {:enabled, true} <-
1130 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1131 {:ok, info} <- fetch_follow_information_for_user(data) do
1132 info = Map.merge(data.info, info)
1133 Map.put(data, :info, info)
1135 {:enabled, false} ->
1140 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1147 defp collection_private(data) do
1148 if is_map(data["first"]) and
1149 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1152 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1153 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1156 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1168 def user_data_from_user_object(data) do
1169 with {:ok, data} <- MRF.filter(data),
1170 {:ok, data} <- object_to_user_data(data) do
1177 def fetch_and_prepare_user_from_ap_id(ap_id) do
1178 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1179 {:ok, data} <- user_data_from_user_object(data),
1180 data <- maybe_update_follow_information(data) do
1183 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1187 def make_user_from_ap_id(ap_id) do
1188 if _user = User.get_cached_by_ap_id(ap_id) do
1189 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1191 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1192 User.insert_or_update_user(data)
1199 def make_user_from_nickname(nickname) do
1200 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1201 make_user_from_ap_id(ap_id)
1203 _e -> {:error, "No AP id in WebFinger"}
1207 # filter out broken threads
1208 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1209 entire_thread_visible_for_user?(activity, user)
1212 # do post-processing on a specific activity
1213 def contain_activity(%Activity{} = activity, %User{} = user) do
1214 contain_broken_threads(activity, user)
1217 def fetch_direct_messages_query do
1219 |> restrict_type(%{"type" => "Create"})
1220 |> restrict_visibility(%{visibility: "direct"})
1221 |> order_by([activity], asc: activity.id)