4f7043c9240eff164f1bbfc8095e52eb68ceab55
[akkoma] / lib / pleroma / web / activity_pub / activity_pub.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.ActivityPub do
6 alias Pleroma.Activity
7 alias Pleroma.Activity.Ir.Topics
8 alias Pleroma.Config
9 alias Pleroma.Constants
10 alias Pleroma.Conversation
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Object.Containment
15 alias Pleroma.Object.Fetcher
16 alias Pleroma.Pagination
17 alias Pleroma.Repo
18 alias Pleroma.Upload
19 alias Pleroma.User
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
26
27 import Ecto.Query
28 import Pleroma.Web.ActivityPub.Utils
29 import Pleroma.Web.ActivityPub.Visibility
30
31 require Logger
32 require Pleroma.Constants
33
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"])
41
42 recipients =
43 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
44 case User.get_cached_by_ap_id(recipient) do
45 nil -> true
46 user -> User.following?(user, actor)
47 end
48 end)
49
50 {recipients, to, cc}
51 end
52
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()
59 {recipients, to, cc}
60 end
61
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])
67 {recipients, to, cc}
68 end
69
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
74 true
75 else
76 _e -> false
77 end
78 else
79 true
80 end
81 end
82
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
86 end
87
88 defp check_remote_limit(_), do: true
89
90 def increase_note_count_if_public(actor, object) do
91 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
92 end
93
94 def decrease_note_count_if_public(actor, object) do
95 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
96 end
97
98 def increase_replies_count_if_reply(%{
99 "object" => %{"inReplyTo" => reply_ap_id} = object,
100 "type" => "Create"
101 }) do
102 if is_public?(object) do
103 Object.increase_replies_count(reply_ap_id)
104 end
105 end
106
107 def increase_replies_count_if_reply(_create_data), do: :noop
108
109 def decrease_replies_count_if_reply(%Object{
110 data: %{"inReplyTo" => reply_ap_id} = object
111 }) do
112 if is_public?(object) do
113 Object.decrease_replies_count(reply_ap_id)
114 end
115 end
116
117 def decrease_replies_count_if_reply(_object), do: :noop
118
119 def increase_poll_votes_if_vote(%{
120 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
121 "type" => "Create",
122 "actor" => actor
123 }) do
124 Object.increase_vote_count(reply_ap_id, name, actor)
125 end
126
127 def increase_poll_votes_if_vote(_create_data), do: :noop
128
129 @object_types ["ChatMessage"]
130 @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
131 def persist(%{"type" => type} = object, meta) when type in @object_types do
132 with {:ok, object} <- Object.create(object) do
133 {:ok, object, meta}
134 end
135 end
136
137 def persist(object, meta) do
138 with local <- Keyword.fetch!(meta, :local),
139 {recipients, _, _} <- get_recipients(object),
140 {:ok, activity} <-
141 Repo.insert(%Activity{
142 data: object,
143 local: local,
144 recipients: recipients,
145 actor: object["actor"]
146 }) do
147 {:ok, activity, meta}
148 end
149 end
150
151 @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
152 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
153 with nil <- Activity.normalize(map),
154 map <- lazy_put_activity_defaults(map, fake),
155 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
156 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
157 {:ok, map} <- MRF.filter(map),
158 {recipients, _, _} = get_recipients(map),
159 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
160 {:containment, :ok} <- {:containment, Containment.contain_child(map)},
161 {:ok, map, object} <- insert_full_object(map) do
162 {:ok, activity} =
163 Repo.insert(%Activity{
164 data: map,
165 local: local,
166 actor: map["actor"],
167 recipients: recipients
168 })
169
170 # Splice in the child object if we have one.
171 activity =
172 if not is_nil(object) do
173 Map.put(activity, :object, object)
174 else
175 activity
176 end
177
178 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
179
180 {:ok, activity}
181 else
182 %Activity{} = activity ->
183 {:ok, activity}
184
185 {:fake, true, map, recipients} ->
186 activity = %Activity{
187 data: map,
188 local: local,
189 actor: map["actor"],
190 recipients: recipients,
191 id: "pleroma:fakeid"
192 }
193
194 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
195 {:ok, activity}
196
197 error ->
198 {:error, error}
199 end
200 end
201
202 def notify_and_stream(activity) do
203 Notification.create_notifications(activity)
204
205 conversation = create_or_bump_conversation(activity, activity.actor)
206 participations = get_participations(conversation)
207 stream_out(activity)
208 stream_out_participations(participations)
209 end
210
211 defp create_or_bump_conversation(activity, actor) do
212 with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
213 %User{} = user <- User.get_cached_by_ap_id(actor),
214 Participation.mark_as_read(user, conversation) do
215 {:ok, conversation}
216 end
217 end
218
219 defp get_participations({:ok, conversation}) do
220 conversation
221 |> Repo.preload(:participations, force: true)
222 |> Map.get(:participations)
223 end
224
225 defp get_participations(_), do: []
226
227 def stream_out_participations(participations) do
228 participations =
229 participations
230 |> Repo.preload(:user)
231
232 Streamer.stream("participation", participations)
233 end
234
235 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
236 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
237 conversation = Repo.preload(conversation, :participations),
238 last_activity_id =
239 fetch_latest_activity_id_for_context(conversation.ap_id, %{
240 "user" => user,
241 "blocking_user" => user
242 }) do
243 if last_activity_id do
244 stream_out_participations(conversation.participations)
245 end
246 end
247 end
248
249 def stream_out_participations(_, _), do: :noop
250
251 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
252 when data_type in ["Create", "Announce", "Delete"] do
253 activity
254 |> Topics.get_activity_topics()
255 |> Streamer.stream(activity)
256 end
257
258 def stream_out(_activity) do
259 :noop
260 end
261
262 @spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
263 def create(params, fake \\ false) do
264 with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do
265 result
266 end
267 end
268
269 defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do
270 additional = params[:additional] || %{}
271 # only accept false as false value
272 local = !(params[:local] == false)
273 published = params[:published]
274 quick_insert? = Config.get([:env]) == :benchmark
275
276 with create_data <-
277 make_create_data(
278 %{to: to, actor: actor, published: published, context: context, object: object},
279 additional
280 ),
281 {:ok, activity} <- insert(create_data, local, fake),
282 {:fake, false, activity} <- {:fake, fake, activity},
283 _ <- increase_replies_count_if_reply(create_data),
284 _ <- increase_poll_votes_if_vote(create_data),
285 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
286 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
287 _ <- notify_and_stream(activity),
288 :ok <- maybe_federate(activity) do
289 {:ok, activity}
290 else
291 {:quick_insert, true, activity} ->
292 {:ok, activity}
293
294 {:fake, true, activity} ->
295 {:ok, activity}
296
297 {:error, message} ->
298 Repo.rollback(message)
299 end
300 end
301
302 @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
303 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
304 additional = params[:additional] || %{}
305 # only accept false as false value
306 local = !(params[:local] == false)
307 published = params[:published]
308
309 with listen_data <-
310 make_listen_data(
311 %{to: to, actor: actor, published: published, context: context, object: object},
312 additional
313 ),
314 {:ok, activity} <- insert(listen_data, local),
315 _ <- notify_and_stream(activity),
316 :ok <- maybe_federate(activity) do
317 {:ok, activity}
318 end
319 end
320
321 @spec accept(map()) :: {:ok, Activity.t()} | {:error, any()}
322 def accept(params) do
323 accept_or_reject("Accept", params)
324 end
325
326 @spec reject(map()) :: {:ok, Activity.t()} | {:error, any()}
327 def reject(params) do
328 accept_or_reject("Reject", params)
329 end
330
331 @spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
332 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
333 local = Map.get(params, :local, true)
334 activity_id = Map.get(params, :activity_id, nil)
335
336 with data <-
337 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
338 |> Utils.maybe_put("id", activity_id),
339 {:ok, activity} <- insert(data, local),
340 _ <- notify_and_stream(activity),
341 :ok <- maybe_federate(activity) do
342 {:ok, activity}
343 end
344 end
345
346 @spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
347 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
348 local = !(params[:local] == false)
349 activity_id = params[:activity_id]
350
351 with data <- %{
352 "to" => to,
353 "cc" => cc,
354 "type" => "Update",
355 "actor" => actor,
356 "object" => object
357 },
358 data <- Utils.maybe_put(data, "id", activity_id),
359 {:ok, activity} <- insert(data, local),
360 _ <- notify_and_stream(activity),
361 :ok <- maybe_federate(activity) do
362 {:ok, activity}
363 end
364 end
365
366 @spec follow(User.t(), User.t(), String.t() | nil, boolean(), keyword()) ::
367 {:ok, Activity.t()} | {:error, any()}
368 def follow(follower, followed, activity_id \\ nil, local \\ true, opts \\ []) do
369 with {:ok, result} <-
370 Repo.transaction(fn -> do_follow(follower, followed, activity_id, local, opts) end) do
371 result
372 end
373 end
374
375 defp do_follow(follower, followed, activity_id, local, opts) do
376 skip_notify_and_stream = Keyword.get(opts, :skip_notify_and_stream, false)
377
378 with data <- make_follow_data(follower, followed, activity_id),
379 {:ok, activity} <- insert(data, local),
380 _ <- skip_notify_and_stream || notify_and_stream(activity),
381 :ok <- maybe_federate(activity) do
382 {:ok, activity}
383 else
384 {:error, error} -> Repo.rollback(error)
385 end
386 end
387
388 @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
389 {:ok, Activity.t()} | nil | {:error, any()}
390 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
391 with {:ok, result} <-
392 Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
393 result
394 end
395 end
396
397 defp do_unfollow(follower, followed, activity_id, local) do
398 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
399 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
400 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
401 {:ok, activity} <- insert(unfollow_data, local),
402 _ <- notify_and_stream(activity),
403 :ok <- maybe_federate(activity) do
404 {:ok, activity}
405 else
406 nil -> nil
407 {:error, error} -> Repo.rollback(error)
408 end
409 end
410
411 @spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
412 {:ok, Activity.t()} | {:error, any()}
413 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
414 with {:ok, result} <-
415 Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
416 result
417 end
418 end
419
420 defp do_block(blocker, blocked, activity_id, local) do
421 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
422
423 if unfollow_blocked do
424 follow_activity = fetch_latest_follow(blocker, blocked)
425 if follow_activity, do: unfollow(blocker, blocked, nil, local)
426 end
427
428 with block_data <- make_block_data(blocker, blocked, activity_id),
429 {:ok, activity} <- insert(block_data, local),
430 _ <- notify_and_stream(activity),
431 :ok <- maybe_federate(activity) do
432 {:ok, activity}
433 else
434 {:error, error} -> Repo.rollback(error)
435 end
436 end
437
438 @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
439 def flag(
440 %{
441 actor: actor,
442 context: _context,
443 account: account,
444 statuses: statuses,
445 content: content
446 } = params
447 ) do
448 # only accept false as false value
449 local = !(params[:local] == false)
450 forward = !(params[:forward] == false)
451
452 additional = params[:additional] || %{}
453
454 additional =
455 if forward do
456 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
457 else
458 Map.merge(additional, %{"to" => [], "cc" => []})
459 end
460
461 with flag_data <- make_flag_data(params, additional),
462 {:ok, activity} <- insert(flag_data, local),
463 {:ok, stripped_activity} <- strip_report_status_data(activity),
464 _ <- notify_and_stream(activity),
465 :ok <- maybe_federate(stripped_activity) do
466 User.all_superusers()
467 |> Enum.filter(fn user -> not is_nil(user.email) end)
468 |> Enum.each(fn superuser ->
469 superuser
470 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
471 |> Pleroma.Emails.Mailer.deliver_async()
472 end)
473
474 {:ok, activity}
475 end
476 end
477
478 @spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
479 def move(%User{} = origin, %User{} = target, local \\ true) do
480 params = %{
481 "type" => "Move",
482 "actor" => origin.ap_id,
483 "object" => origin.ap_id,
484 "target" => target.ap_id
485 }
486
487 with true <- origin.ap_id in target.also_known_as,
488 {:ok, activity} <- insert(params, local),
489 _ <- notify_and_stream(activity) do
490 maybe_federate(activity)
491
492 BackgroundWorker.enqueue("move_following", %{
493 "origin_id" => origin.id,
494 "target_id" => target.id
495 })
496
497 {:ok, activity}
498 else
499 false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
500 err -> err
501 end
502 end
503
504 def fetch_activities_for_context_query(context, opts) do
505 public = [Constants.as_public()]
506
507 recipients =
508 if opts["user"],
509 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
510 else: public
511
512 from(activity in Activity)
513 |> maybe_preload_objects(opts)
514 |> maybe_preload_bookmarks(opts)
515 |> maybe_set_thread_muted_field(opts)
516 |> restrict_blocked(opts)
517 |> restrict_recipients(recipients, opts["user"])
518 |> where(
519 [activity],
520 fragment(
521 "?->>'type' = ? and ?->>'context' = ?",
522 activity.data,
523 "Create",
524 activity.data,
525 ^context
526 )
527 )
528 |> exclude_poll_votes(opts)
529 |> exclude_id(opts)
530 |> order_by([activity], desc: activity.id)
531 end
532
533 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
534 def fetch_activities_for_context(context, opts \\ %{}) do
535 context
536 |> fetch_activities_for_context_query(opts)
537 |> Repo.all()
538 end
539
540 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
541 FlakeId.Ecto.CompatType.t() | nil
542 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
543 context
544 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
545 |> limit(1)
546 |> select([a], a.id)
547 |> Repo.one()
548 end
549
550 @spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
551 def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
552 opts = Map.drop(opts, ["user"])
553
554 query = fetch_activities_query([Constants.as_public()], opts)
555
556 query =
557 if opts["restrict_unlisted"] do
558 restrict_unlisted(query)
559 else
560 query
561 end
562
563 Pagination.fetch_paginated(query, opts, pagination)
564 end
565
566 @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
567 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
568 opts
569 |> Map.put("restrict_unlisted", true)
570 |> fetch_public_or_unlisted_activities(pagination)
571 end
572
573 @valid_visibilities ~w[direct unlisted public private]
574
575 defp restrict_visibility(query, %{visibility: visibility})
576 when is_list(visibility) do
577 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
578 query =
579 from(
580 a in query,
581 where:
582 fragment(
583 "activity_visibility(?, ?, ?) = ANY (?)",
584 a.actor,
585 a.recipients,
586 a.data,
587 ^visibility
588 )
589 )
590
591 query
592 else
593 Logger.error("Could not restrict visibility to #{visibility}")
594 end
595 end
596
597 defp restrict_visibility(query, %{visibility: visibility})
598 when visibility in @valid_visibilities do
599 from(
600 a in query,
601 where:
602 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
603 )
604 end
605
606 defp restrict_visibility(_query, %{visibility: visibility})
607 when visibility not in @valid_visibilities do
608 Logger.error("Could not restrict visibility to #{visibility}")
609 end
610
611 defp restrict_visibility(query, _visibility), do: query
612
613 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
614 when is_list(visibility) do
615 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
616 from(
617 a in query,
618 where:
619 not fragment(
620 "activity_visibility(?, ?, ?) = ANY (?)",
621 a.actor,
622 a.recipients,
623 a.data,
624 ^visibility
625 )
626 )
627 else
628 Logger.error("Could not exclude visibility to #{visibility}")
629 query
630 end
631 end
632
633 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
634 when visibility in @valid_visibilities do
635 from(
636 a in query,
637 where:
638 not fragment(
639 "activity_visibility(?, ?, ?) = ?",
640 a.actor,
641 a.recipients,
642 a.data,
643 ^visibility
644 )
645 )
646 end
647
648 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
649 when visibility not in [nil | @valid_visibilities] do
650 Logger.error("Could not exclude visibility to #{visibility}")
651 query
652 end
653
654 defp exclude_visibility(query, _visibility), do: query
655
656 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
657 do: query
658
659 defp restrict_thread_visibility(
660 query,
661 %{"user" => %User{skip_thread_containment: true}},
662 _
663 ),
664 do: query
665
666 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
667 from(
668 a in query,
669 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
670 )
671 end
672
673 defp restrict_thread_visibility(query, _, _), do: query
674
675 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
676 params =
677 params
678 |> Map.put("user", reading_user)
679 |> Map.put("actor_id", user.ap_id)
680
681 recipients =
682 user_activities_recipients(%{
683 "godmode" => params["godmode"],
684 "reading_user" => reading_user
685 })
686
687 fetch_activities(recipients, params)
688 |> Enum.reverse()
689 end
690
691 def fetch_user_activities(user, reading_user, params \\ %{}) do
692 params =
693 params
694 |> Map.put("type", ["Create", "Announce"])
695 |> Map.put("user", reading_user)
696 |> Map.put("actor_id", user.ap_id)
697 |> Map.put("pinned_activity_ids", user.pinned_activities)
698
699 params =
700 if User.blocks?(reading_user, user) do
701 params
702 else
703 params
704 |> Map.put("blocking_user", reading_user)
705 |> Map.put("muting_user", reading_user)
706 end
707
708 recipients =
709 user_activities_recipients(%{
710 "godmode" => params["godmode"],
711 "reading_user" => reading_user
712 })
713
714 fetch_activities(recipients, params)
715 |> Enum.reverse()
716 end
717
718 def fetch_statuses(reading_user, params) do
719 params =
720 params
721 |> Map.put("type", ["Create", "Announce"])
722
723 recipients =
724 user_activities_recipients(%{
725 "godmode" => params["godmode"],
726 "reading_user" => reading_user
727 })
728
729 fetch_activities(recipients, params, :offset)
730 |> Enum.reverse()
731 end
732
733 defp user_activities_recipients(%{"godmode" => true}) do
734 []
735 end
736
737 defp user_activities_recipients(%{"reading_user" => reading_user}) do
738 if reading_user do
739 [Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
740 else
741 [Constants.as_public()]
742 end
743 end
744
745 defp restrict_since(query, %{"since_id" => ""}), do: query
746
747 defp restrict_since(query, %{"since_id" => since_id}) do
748 from(activity in query, where: activity.id > ^since_id)
749 end
750
751 defp restrict_since(query, _), do: query
752
753 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
754 raise "Can't use the child object without preloading!"
755 end
756
757 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
758 when is_list(tag_reject) and tag_reject != [] do
759 from(
760 [_activity, object] in query,
761 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
762 )
763 end
764
765 defp restrict_tag_reject(query, _), do: query
766
767 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
768 raise "Can't use the child object without preloading!"
769 end
770
771 defp restrict_tag_all(query, %{"tag_all" => tag_all})
772 when is_list(tag_all) and tag_all != [] do
773 from(
774 [_activity, object] in query,
775 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
776 )
777 end
778
779 defp restrict_tag_all(query, _), do: query
780
781 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
782 raise "Can't use the child object without preloading!"
783 end
784
785 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
786 from(
787 [_activity, object] in query,
788 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
789 )
790 end
791
792 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
793 from(
794 [_activity, object] in query,
795 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
796 )
797 end
798
799 defp restrict_tag(query, _), do: query
800
801 defp restrict_recipients(query, [], _user), do: query
802
803 defp restrict_recipients(query, recipients, nil) do
804 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
805 end
806
807 defp restrict_recipients(query, recipients, user) do
808 from(
809 activity in query,
810 where: fragment("? && ?", ^recipients, activity.recipients),
811 or_where: activity.actor == ^user.ap_id
812 )
813 end
814
815 defp restrict_local(query, %{"local_only" => true}) do
816 from(activity in query, where: activity.local == true)
817 end
818
819 defp restrict_local(query, _), do: query
820
821 defp restrict_actor(query, %{"actor_id" => actor_id}) do
822 from(activity in query, where: activity.actor == ^actor_id)
823 end
824
825 defp restrict_actor(query, _), do: query
826
827 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
828 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
829 end
830
831 defp restrict_type(query, %{"type" => type}) do
832 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
833 end
834
835 defp restrict_type(query, _), do: query
836
837 defp restrict_state(query, %{"state" => state}) do
838 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
839 end
840
841 defp restrict_state(query, _), do: query
842
843 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
844 from(
845 [_activity, object] in query,
846 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
847 )
848 end
849
850 defp restrict_favorited_by(query, _), do: query
851
852 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
853 raise "Can't use the child object without preloading!"
854 end
855
856 defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do
857 from(
858 [_activity, object] in query,
859 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
860 )
861 end
862
863 defp restrict_media(query, _), do: query
864
865 defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "true", "1"] do
866 from(
867 [_activity, object] in query,
868 where: fragment("?->>'inReplyTo' is null", object.data)
869 )
870 end
871
872 defp restrict_replies(query, %{
873 "reply_filtering_user" => user,
874 "reply_visibility" => "self"
875 }) do
876 from(
877 [activity, object] in query,
878 where:
879 fragment(
880 "?->>'inReplyTo' is null OR ? = ANY(?)",
881 object.data,
882 ^user.ap_id,
883 activity.recipients
884 )
885 )
886 end
887
888 defp restrict_replies(query, %{
889 "reply_filtering_user" => user,
890 "reply_visibility" => "following"
891 }) do
892 from(
893 [activity, object] in query,
894 where:
895 fragment(
896 "?->>'inReplyTo' is null OR ? && array_remove(?, ?) OR ? = ?",
897 object.data,
898 ^[user.ap_id | User.get_cached_user_friends_ap_ids(user)],
899 activity.recipients,
900 activity.actor,
901 activity.actor,
902 ^user.ap_id
903 )
904 )
905 end
906
907 defp restrict_replies(query, _), do: query
908
909 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val in [true, "true", "1"] do
910 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
911 end
912
913 defp restrict_reblogs(query, _), do: query
914
915 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
916
917 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
918 mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
919
920 query =
921 from([activity] in query,
922 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
923 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
924 )
925
926 unless opts["skip_preload"] do
927 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
928 else
929 query
930 end
931 end
932
933 defp restrict_muted(query, _), do: query
934
935 defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
936 blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
937 domain_blocks = user.domain_blocks || []
938
939 following_ap_ids = User.get_friends_ap_ids(user)
940
941 query =
942 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
943
944 from(
945 [activity, object: o] in query,
946 where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
947 where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
948 where:
949 fragment(
950 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
951 activity.data,
952 activity.data,
953 ^blocked_ap_ids
954 ),
955 where:
956 fragment(
957 "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
958 activity.actor,
959 ^domain_blocks,
960 activity.actor,
961 ^following_ap_ids
962 ),
963 where:
964 fragment(
965 "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
966 o.data,
967 ^domain_blocks,
968 o.data,
969 ^following_ap_ids
970 )
971 )
972 end
973
974 defp restrict_blocked(query, _), do: query
975
976 defp restrict_unlisted(query) do
977 from(
978 activity in query,
979 where:
980 fragment(
981 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
982 activity.data,
983 ^[Constants.as_public()]
984 )
985 )
986 end
987
988 # TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only,
989 # the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2'
990 # and `restrict_muted/2`
991
992 defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids})
993 when pinned in [true, "true", "1"] do
994 from(activity in query, where: activity.id in ^ids)
995 end
996
997 defp restrict_pinned(query, _), do: query
998
999 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
1000 muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
1001
1002 from(
1003 activity in query,
1004 where:
1005 fragment(
1006 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
1007 activity.data,
1008 activity.actor,
1009 ^muted_reblogs
1010 )
1011 )
1012 end
1013
1014 defp restrict_muted_reblogs(query, _), do: query
1015
1016 defp restrict_instance(query, %{"instance" => instance}) do
1017 users =
1018 from(
1019 u in User,
1020 select: u.ap_id,
1021 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
1022 )
1023 |> Repo.all()
1024
1025 from(activity in query, where: activity.actor in ^users)
1026 end
1027
1028 defp restrict_instance(query, _), do: query
1029
1030 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
1031
1032 defp exclude_poll_votes(query, _) do
1033 if has_named_binding?(query, :object) do
1034 from([activity, object: o] in query,
1035 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1036 )
1037 else
1038 query
1039 end
1040 end
1041
1042 defp exclude_chat_messages(query, %{"include_chat_messages" => true}), do: query
1043
1044 defp exclude_chat_messages(query, _) do
1045 if has_named_binding?(query, :object) do
1046 from([activity, object: o] in query,
1047 where: fragment("not(?->>'type' = ?)", o.data, "ChatMessage")
1048 )
1049 else
1050 query
1051 end
1052 end
1053
1054 defp exclude_invisible_actors(query, %{"invisible_actors" => true}), do: query
1055
1056 defp exclude_invisible_actors(query, _opts) do
1057 invisible_ap_ids =
1058 User.Query.build(%{invisible: true, select: [:ap_id]})
1059 |> Repo.all()
1060 |> Enum.map(fn %{ap_id: ap_id} -> ap_id end)
1061
1062 from([activity] in query, where: activity.actor not in ^invisible_ap_ids)
1063 end
1064
1065 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1066 from(activity in query, where: activity.id != ^id)
1067 end
1068
1069 defp exclude_id(query, _), do: query
1070
1071 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1072
1073 defp maybe_preload_objects(query, _) do
1074 query
1075 |> Activity.with_preloaded_object()
1076 end
1077
1078 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1079
1080 defp maybe_preload_bookmarks(query, opts) do
1081 query
1082 |> Activity.with_preloaded_bookmark(opts["user"])
1083 end
1084
1085 defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
1086 query
1087 |> Activity.with_preloaded_report_notes()
1088 end
1089
1090 defp maybe_preload_report_notes(query, _), do: query
1091
1092 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1093
1094 defp maybe_set_thread_muted_field(query, opts) do
1095 query
1096 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1097 end
1098
1099 defp maybe_order(query, %{order: :desc}) do
1100 query
1101 |> order_by(desc: :id)
1102 end
1103
1104 defp maybe_order(query, %{order: :asc}) do
1105 query
1106 |> order_by(asc: :id)
1107 end
1108
1109 defp maybe_order(query, _), do: query
1110
1111 defp fetch_activities_query_ap_ids_ops(opts) do
1112 source_user = opts["muting_user"]
1113 ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: []
1114
1115 ap_id_relationships =
1116 ap_id_relationships ++
1117 if opts["blocking_user"] && opts["blocking_user"] == source_user do
1118 [:block]
1119 else
1120 []
1121 end
1122
1123 preloaded_ap_ids = User.outgoing_relationships_ap_ids(source_user, ap_id_relationships)
1124
1125 restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
1126 restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
1127
1128 restrict_muted_reblogs_opts =
1129 Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
1130
1131 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
1132 end
1133
1134 def fetch_activities_query(recipients, opts \\ %{}) do
1135 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
1136 fetch_activities_query_ap_ids_ops(opts)
1137
1138 config = %{
1139 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1140 }
1141
1142 Activity
1143 |> maybe_preload_objects(opts)
1144 |> maybe_preload_bookmarks(opts)
1145 |> maybe_preload_report_notes(opts)
1146 |> maybe_set_thread_muted_field(opts)
1147 |> maybe_order(opts)
1148 |> restrict_recipients(recipients, opts["user"])
1149 |> restrict_replies(opts)
1150 |> restrict_tag(opts)
1151 |> restrict_tag_reject(opts)
1152 |> restrict_tag_all(opts)
1153 |> restrict_since(opts)
1154 |> restrict_local(opts)
1155 |> restrict_actor(opts)
1156 |> restrict_type(opts)
1157 |> restrict_state(opts)
1158 |> restrict_favorited_by(opts)
1159 |> restrict_blocked(restrict_blocked_opts)
1160 |> restrict_muted(restrict_muted_opts)
1161 |> restrict_media(opts)
1162 |> restrict_visibility(opts)
1163 |> restrict_thread_visibility(opts, config)
1164 |> restrict_reblogs(opts)
1165 |> restrict_pinned(opts)
1166 |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
1167 |> restrict_instance(opts)
1168 |> Activity.restrict_deactivated_users()
1169 |> exclude_poll_votes(opts)
1170 |> exclude_chat_messages(opts)
1171 |> exclude_invisible_actors(opts)
1172 |> exclude_visibility(opts)
1173 end
1174
1175 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1176 list_memberships = Pleroma.List.memberships(opts["user"])
1177
1178 fetch_activities_query(recipients ++ list_memberships, opts)
1179 |> Pagination.fetch_paginated(opts, pagination)
1180 |> Enum.reverse()
1181 |> maybe_update_cc(list_memberships, opts["user"])
1182 end
1183
1184 @doc """
1185 Fetch favorites activities of user with order by sort adds to favorites
1186 """
1187 @spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
1188 def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
1189 user.ap_id
1190 |> Activity.Queries.by_actor()
1191 |> Activity.Queries.by_type("Like")
1192 |> Activity.with_joined_object()
1193 |> Object.with_joined_activity()
1194 |> select([_like, object, activity], %{activity | object: object})
1195 |> order_by([like, _, _], desc_nulls_last: like.id)
1196 |> Pagination.fetch_paginated(
1197 Map.merge(params, %{"skip_order" => true}),
1198 pagination,
1199 :object_activity
1200 )
1201 end
1202
1203 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1204 when is_list(list_memberships) and length(list_memberships) > 0 do
1205 Enum.map(activities, fn
1206 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1207 if Enum.any?(bcc, &(&1 in list_memberships)) do
1208 update_in(activity.data["cc"], &[user_ap_id | &1])
1209 else
1210 activity
1211 end
1212
1213 activity ->
1214 activity
1215 end)
1216 end
1217
1218 defp maybe_update_cc(activities, _, _), do: activities
1219
1220 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1221 from(activity in query,
1222 where:
1223 fragment("? && ?", activity.recipients, ^recipients) or
1224 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1225 ^Constants.as_public() in activity.recipients)
1226 )
1227 end
1228
1229 def fetch_activities_bounded(
1230 recipients,
1231 recipients_with_public,
1232 opts \\ %{},
1233 pagination \\ :keyset
1234 ) do
1235 fetch_activities_query([], opts)
1236 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1237 |> Pagination.fetch_paginated(opts, pagination)
1238 |> Enum.reverse()
1239 end
1240
1241 @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
1242 def upload(file, opts \\ []) do
1243 with {:ok, data} <- Upload.store(file, opts) do
1244 obj_data =
1245 if opts[:actor] do
1246 Map.put(data, "actor", opts[:actor])
1247 else
1248 data
1249 end
1250
1251 Repo.insert(%Object{data: obj_data})
1252 end
1253 end
1254
1255 @spec get_actor_url(any()) :: binary() | nil
1256 defp get_actor_url(url) when is_binary(url), do: url
1257 defp get_actor_url(%{"href" => href}) when is_binary(href), do: href
1258
1259 defp get_actor_url(url) when is_list(url) do
1260 url
1261 |> List.first()
1262 |> get_actor_url()
1263 end
1264
1265 defp get_actor_url(_url), do: nil
1266
1267 defp object_to_user_data(data) do
1268 avatar =
1269 data["icon"]["url"] &&
1270 %{
1271 "type" => "Image",
1272 "url" => [%{"href" => data["icon"]["url"]}]
1273 }
1274
1275 banner =
1276 data["image"]["url"] &&
1277 %{
1278 "type" => "Image",
1279 "url" => [%{"href" => data["image"]["url"]}]
1280 }
1281
1282 fields =
1283 data
1284 |> Map.get("attachment", [])
1285 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1286 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1287
1288 emojis =
1289 data
1290 |> Map.get("tag", [])
1291 |> Enum.filter(fn
1292 %{"type" => "Emoji"} -> true
1293 _ -> false
1294 end)
1295 |> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
1296 Map.put(acc, String.trim(name, ":"), url)
1297 end)
1298
1299 locked = data["manuallyApprovesFollowers"] || false
1300 data = Transmogrifier.maybe_fix_user_object(data)
1301 discoverable = data["discoverable"] || false
1302 invisible = data["invisible"] || false
1303 actor_type = data["type"] || "Person"
1304
1305 public_key =
1306 if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
1307 data["publicKey"]["publicKeyPem"]
1308 else
1309 nil
1310 end
1311
1312 shared_inbox =
1313 if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
1314 data["endpoints"]["sharedInbox"]
1315 else
1316 nil
1317 end
1318
1319 user_data = %{
1320 ap_id: data["id"],
1321 uri: get_actor_url(data["url"]),
1322 ap_enabled: true,
1323 banner: banner,
1324 fields: fields,
1325 emoji: emojis,
1326 locked: locked,
1327 discoverable: discoverable,
1328 invisible: invisible,
1329 avatar: avatar,
1330 name: data["name"],
1331 follower_address: data["followers"],
1332 following_address: data["following"],
1333 bio: data["summary"],
1334 actor_type: actor_type,
1335 also_known_as: Map.get(data, "alsoKnownAs", []),
1336 public_key: public_key,
1337 inbox: data["inbox"],
1338 shared_inbox: shared_inbox
1339 }
1340
1341 # nickname can be nil because of virtual actors
1342 user_data =
1343 if data["preferredUsername"] do
1344 Map.put(
1345 user_data,
1346 :nickname,
1347 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1348 )
1349 else
1350 Map.put(user_data, :nickname, nil)
1351 end
1352
1353 {:ok, user_data}
1354 end
1355
1356 def fetch_follow_information_for_user(user) do
1357 with {:ok, following_data} <-
1358 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1359 {:ok, hide_follows} <- collection_private(following_data),
1360 {:ok, followers_data} <-
1361 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1362 {:ok, hide_followers} <- collection_private(followers_data) do
1363 {:ok,
1364 %{
1365 hide_follows: hide_follows,
1366 follower_count: normalize_counter(followers_data["totalItems"]),
1367 following_count: normalize_counter(following_data["totalItems"]),
1368 hide_followers: hide_followers
1369 }}
1370 else
1371 {:error, _} = e -> e
1372 e -> {:error, e}
1373 end
1374 end
1375
1376 defp normalize_counter(counter) when is_integer(counter), do: counter
1377 defp normalize_counter(_), do: 0
1378
1379 def maybe_update_follow_information(user_data) do
1380 with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
1381 {_, true} <- {:user_type_check, user_data[:type] in ["Person", "Service"]},
1382 {_, true} <-
1383 {:collections_available,
1384 !!(user_data[:following_address] && user_data[:follower_address])},
1385 {:ok, info} <-
1386 fetch_follow_information_for_user(user_data) do
1387 info = Map.merge(user_data[:info] || %{}, info)
1388
1389 user_data
1390 |> Map.put(:info, info)
1391 else
1392 {:user_type_check, false} ->
1393 user_data
1394
1395 {:collections_available, false} ->
1396 user_data
1397
1398 {:enabled, false} ->
1399 user_data
1400
1401 e ->
1402 Logger.error(
1403 "Follower/Following counter update for #{user_data.ap_id} failed.\n" <> inspect(e)
1404 )
1405
1406 user_data
1407 end
1408 end
1409
1410 defp collection_private(%{"first" => %{"type" => type}})
1411 when type in ["CollectionPage", "OrderedCollectionPage"],
1412 do: {:ok, false}
1413
1414 defp collection_private(%{"first" => first}) do
1415 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1416 Fetcher.fetch_and_contain_remote_object_from_id(first) do
1417 {:ok, false}
1418 else
1419 {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
1420 {:error, _} = e -> e
1421 e -> {:error, e}
1422 end
1423 end
1424
1425 defp collection_private(_data), do: {:ok, true}
1426
1427 def user_data_from_user_object(data) do
1428 with {:ok, data} <- MRF.filter(data),
1429 {:ok, data} <- object_to_user_data(data) do
1430 {:ok, data}
1431 else
1432 e -> {:error, e}
1433 end
1434 end
1435
1436 def fetch_and_prepare_user_from_ap_id(ap_id) do
1437 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1438 {:ok, data} <- user_data_from_user_object(data),
1439 data <- maybe_update_follow_information(data) do
1440 {:ok, data}
1441 else
1442 {:error, "Object has been deleted"} = e ->
1443 Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1444 {:error, e}
1445
1446 e ->
1447 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1448 {:error, e}
1449 end
1450 end
1451
1452 def make_user_from_ap_id(ap_id) do
1453 user = User.get_cached_by_ap_id(ap_id)
1454
1455 if user && !User.ap_enabled?(user) do
1456 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1457 else
1458 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1459 if user do
1460 user
1461 |> User.remote_user_changeset(data)
1462 |> User.update_and_set_cache()
1463 else
1464 data
1465 |> User.remote_user_changeset()
1466 |> Repo.insert()
1467 |> User.set_cache()
1468 end
1469 else
1470 e -> {:error, e}
1471 end
1472 end
1473 end
1474
1475 def make_user_from_nickname(nickname) do
1476 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1477 make_user_from_ap_id(ap_id)
1478 else
1479 _e -> {:error, "No AP id in WebFinger"}
1480 end
1481 end
1482
1483 # filter out broken threads
1484 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1485 entire_thread_visible_for_user?(activity, user)
1486 end
1487
1488 # do post-processing on a specific activity
1489 def contain_activity(%Activity{} = activity, %User{} = user) do
1490 contain_broken_threads(activity, user)
1491 end
1492
1493 def fetch_direct_messages_query do
1494 Activity
1495 |> restrict_type(%{"type" => "Create"})
1496 |> restrict_visibility(%{visibility: "direct"})
1497 |> order_by([activity], asc: activity.id)
1498 end
1499 end