ChatMessageValidator: Allow one message in an array, too.
[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 announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
367 {:ok, Activity.t(), Object.t()} | {:error, any()}
368 def announce(
369 %User{ap_id: _} = user,
370 %Object{data: %{"id" => _}} = object,
371 activity_id \\ nil,
372 local \\ true,
373 public \\ true
374 ) do
375 with {:ok, result} <-
376 Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
377 result
378 end
379 end
380
381 defp do_announce(user, object, activity_id, local, public) do
382 with true <- is_announceable?(object, user, public),
383 object <- Object.get_by_id(object.id),
384 announce_data <- make_announce_data(user, object, activity_id, public),
385 {:ok, activity} <- insert(announce_data, local),
386 {:ok, object} <- add_announce_to_object(activity, object),
387 _ <- notify_and_stream(activity),
388 :ok <- maybe_federate(activity) do
389 {:ok, activity, object}
390 else
391 false -> {:error, false}
392 {:error, error} -> Repo.rollback(error)
393 end
394 end
395
396 @spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
397 {:ok, Activity.t()} | {:error, any()}
398 def follow(follower, followed, activity_id \\ nil, local \\ true) do
399 with {:ok, result} <-
400 Repo.transaction(fn -> do_follow(follower, followed, activity_id, local) end) do
401 result
402 end
403 end
404
405 defp do_follow(follower, followed, activity_id, local) do
406 with data <- make_follow_data(follower, followed, activity_id),
407 {:ok, activity} <- insert(data, local),
408 _ <- notify_and_stream(activity),
409 :ok <- maybe_federate(activity) do
410 {:ok, activity}
411 else
412 {:error, error} -> Repo.rollback(error)
413 end
414 end
415
416 @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
417 {:ok, Activity.t()} | nil | {:error, any()}
418 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
419 with {:ok, result} <-
420 Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
421 result
422 end
423 end
424
425 defp do_unfollow(follower, followed, activity_id, local) do
426 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
427 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
428 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
429 {:ok, activity} <- insert(unfollow_data, local),
430 _ <- notify_and_stream(activity),
431 :ok <- maybe_federate(activity) do
432 {:ok, activity}
433 else
434 nil -> nil
435 {:error, error} -> Repo.rollback(error)
436 end
437 end
438
439 @spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
440 {:ok, Activity.t()} | {:error, any()}
441 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
442 with {:ok, result} <-
443 Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
444 result
445 end
446 end
447
448 defp do_block(blocker, blocked, activity_id, local) do
449 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
450
451 if unfollow_blocked do
452 follow_activity = fetch_latest_follow(blocker, blocked)
453 if follow_activity, do: unfollow(blocker, blocked, nil, local)
454 end
455
456 with block_data <- make_block_data(blocker, blocked, activity_id),
457 {:ok, activity} <- insert(block_data, local),
458 _ <- notify_and_stream(activity),
459 :ok <- maybe_federate(activity) do
460 {:ok, activity}
461 else
462 {:error, error} -> Repo.rollback(error)
463 end
464 end
465
466 @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
467 def flag(
468 %{
469 actor: actor,
470 context: _context,
471 account: account,
472 statuses: statuses,
473 content: content
474 } = params
475 ) do
476 # only accept false as false value
477 local = !(params[:local] == false)
478 forward = !(params[:forward] == false)
479
480 additional = params[:additional] || %{}
481
482 additional =
483 if forward do
484 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
485 else
486 Map.merge(additional, %{"to" => [], "cc" => []})
487 end
488
489 with flag_data <- make_flag_data(params, additional),
490 {:ok, activity} <- insert(flag_data, local),
491 {:ok, stripped_activity} <- strip_report_status_data(activity),
492 _ <- notify_and_stream(activity),
493 :ok <- maybe_federate(stripped_activity) do
494 User.all_superusers()
495 |> Enum.filter(fn user -> not is_nil(user.email) end)
496 |> Enum.each(fn superuser ->
497 superuser
498 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
499 |> Pleroma.Emails.Mailer.deliver_async()
500 end)
501
502 {:ok, activity}
503 end
504 end
505
506 @spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
507 def move(%User{} = origin, %User{} = target, local \\ true) do
508 params = %{
509 "type" => "Move",
510 "actor" => origin.ap_id,
511 "object" => origin.ap_id,
512 "target" => target.ap_id
513 }
514
515 with true <- origin.ap_id in target.also_known_as,
516 {:ok, activity} <- insert(params, local),
517 _ <- notify_and_stream(activity) do
518 maybe_federate(activity)
519
520 BackgroundWorker.enqueue("move_following", %{
521 "origin_id" => origin.id,
522 "target_id" => target.id
523 })
524
525 {:ok, activity}
526 else
527 false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
528 err -> err
529 end
530 end
531
532 def fetch_activities_for_context_query(context, opts) do
533 public = [Constants.as_public()]
534
535 recipients =
536 if opts["user"],
537 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
538 else: public
539
540 from(activity in Activity)
541 |> maybe_preload_objects(opts)
542 |> maybe_preload_bookmarks(opts)
543 |> maybe_set_thread_muted_field(opts)
544 |> restrict_blocked(opts)
545 |> restrict_recipients(recipients, opts["user"])
546 |> where(
547 [activity],
548 fragment(
549 "?->>'type' = ? and ?->>'context' = ?",
550 activity.data,
551 "Create",
552 activity.data,
553 ^context
554 )
555 )
556 |> exclude_poll_votes(opts)
557 |> exclude_id(opts)
558 |> order_by([activity], desc: activity.id)
559 end
560
561 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
562 def fetch_activities_for_context(context, opts \\ %{}) do
563 context
564 |> fetch_activities_for_context_query(opts)
565 |> Repo.all()
566 end
567
568 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
569 FlakeId.Ecto.CompatType.t() | nil
570 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
571 context
572 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
573 |> limit(1)
574 |> select([a], a.id)
575 |> Repo.one()
576 end
577
578 @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
579 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
580 opts = Map.drop(opts, ["user"])
581
582 [Constants.as_public()]
583 |> fetch_activities_query(opts)
584 |> restrict_unlisted()
585 |> Pagination.fetch_paginated(opts, pagination)
586 end
587
588 @valid_visibilities ~w[direct unlisted public private]
589
590 defp restrict_visibility(query, %{visibility: visibility})
591 when is_list(visibility) do
592 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
593 query =
594 from(
595 a in query,
596 where:
597 fragment(
598 "activity_visibility(?, ?, ?) = ANY (?)",
599 a.actor,
600 a.recipients,
601 a.data,
602 ^visibility
603 )
604 )
605
606 query
607 else
608 Logger.error("Could not restrict visibility to #{visibility}")
609 end
610 end
611
612 defp restrict_visibility(query, %{visibility: visibility})
613 when visibility in @valid_visibilities do
614 from(
615 a in query,
616 where:
617 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
618 )
619 end
620
621 defp restrict_visibility(_query, %{visibility: visibility})
622 when visibility not in @valid_visibilities do
623 Logger.error("Could not restrict visibility to #{visibility}")
624 end
625
626 defp restrict_visibility(query, _visibility), do: query
627
628 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
629 when is_list(visibility) do
630 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
631 from(
632 a in query,
633 where:
634 not fragment(
635 "activity_visibility(?, ?, ?) = ANY (?)",
636 a.actor,
637 a.recipients,
638 a.data,
639 ^visibility
640 )
641 )
642 else
643 Logger.error("Could not exclude visibility to #{visibility}")
644 query
645 end
646 end
647
648 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
649 when visibility in @valid_visibilities do
650 from(
651 a in query,
652 where:
653 not fragment(
654 "activity_visibility(?, ?, ?) = ?",
655 a.actor,
656 a.recipients,
657 a.data,
658 ^visibility
659 )
660 )
661 end
662
663 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
664 when visibility not in [nil | @valid_visibilities] do
665 Logger.error("Could not exclude visibility to #{visibility}")
666 query
667 end
668
669 defp exclude_visibility(query, _visibility), do: query
670
671 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
672 do: query
673
674 defp restrict_thread_visibility(
675 query,
676 %{"user" => %User{skip_thread_containment: true}},
677 _
678 ),
679 do: query
680
681 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
682 from(
683 a in query,
684 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
685 )
686 end
687
688 defp restrict_thread_visibility(query, _, _), do: query
689
690 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
691 params =
692 params
693 |> Map.put("user", reading_user)
694 |> Map.put("actor_id", user.ap_id)
695
696 recipients =
697 user_activities_recipients(%{
698 "godmode" => params["godmode"],
699 "reading_user" => reading_user
700 })
701
702 fetch_activities(recipients, params)
703 |> Enum.reverse()
704 end
705
706 def fetch_user_activities(user, reading_user, params \\ %{}) do
707 params =
708 params
709 |> Map.put("type", ["Create", "Announce"])
710 |> Map.put("user", reading_user)
711 |> Map.put("actor_id", user.ap_id)
712 |> Map.put("pinned_activity_ids", user.pinned_activities)
713
714 params =
715 if User.blocks?(reading_user, user) do
716 params
717 else
718 params
719 |> Map.put("blocking_user", reading_user)
720 |> Map.put("muting_user", reading_user)
721 end
722
723 recipients =
724 user_activities_recipients(%{
725 "godmode" => params["godmode"],
726 "reading_user" => reading_user
727 })
728
729 fetch_activities(recipients, params)
730 |> Enum.reverse()
731 end
732
733 def fetch_statuses(reading_user, params) do
734 params =
735 params
736 |> Map.put("type", ["Create", "Announce"])
737
738 recipients =
739 user_activities_recipients(%{
740 "godmode" => params["godmode"],
741 "reading_user" => reading_user
742 })
743
744 fetch_activities(recipients, params, :offset)
745 |> Enum.reverse()
746 end
747
748 defp user_activities_recipients(%{"godmode" => true}) do
749 []
750 end
751
752 defp user_activities_recipients(%{"reading_user" => reading_user}) do
753 if reading_user do
754 [Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
755 else
756 [Constants.as_public()]
757 end
758 end
759
760 defp restrict_since(query, %{"since_id" => ""}), do: query
761
762 defp restrict_since(query, %{"since_id" => since_id}) do
763 from(activity in query, where: activity.id > ^since_id)
764 end
765
766 defp restrict_since(query, _), do: query
767
768 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
769 raise "Can't use the child object without preloading!"
770 end
771
772 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
773 when is_list(tag_reject) and tag_reject != [] do
774 from(
775 [_activity, object] in query,
776 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
777 )
778 end
779
780 defp restrict_tag_reject(query, _), do: query
781
782 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
783 raise "Can't use the child object without preloading!"
784 end
785
786 defp restrict_tag_all(query, %{"tag_all" => tag_all})
787 when is_list(tag_all) and tag_all != [] do
788 from(
789 [_activity, object] in query,
790 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
791 )
792 end
793
794 defp restrict_tag_all(query, _), do: query
795
796 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
797 raise "Can't use the child object without preloading!"
798 end
799
800 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
801 from(
802 [_activity, object] in query,
803 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
804 )
805 end
806
807 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
808 from(
809 [_activity, object] in query,
810 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
811 )
812 end
813
814 defp restrict_tag(query, _), do: query
815
816 defp restrict_recipients(query, [], _user), do: query
817
818 defp restrict_recipients(query, recipients, nil) do
819 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
820 end
821
822 defp restrict_recipients(query, recipients, user) do
823 from(
824 activity in query,
825 where: fragment("? && ?", ^recipients, activity.recipients),
826 or_where: activity.actor == ^user.ap_id
827 )
828 end
829
830 defp restrict_local(query, %{"local_only" => true}) do
831 from(activity in query, where: activity.local == true)
832 end
833
834 defp restrict_local(query, _), do: query
835
836 defp restrict_actor(query, %{"actor_id" => actor_id}) do
837 from(activity in query, where: activity.actor == ^actor_id)
838 end
839
840 defp restrict_actor(query, _), do: query
841
842 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
843 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
844 end
845
846 defp restrict_type(query, %{"type" => type}) do
847 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
848 end
849
850 defp restrict_type(query, _), do: query
851
852 defp restrict_state(query, %{"state" => state}) do
853 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
854 end
855
856 defp restrict_state(query, _), do: query
857
858 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
859 from(
860 [_activity, object] in query,
861 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
862 )
863 end
864
865 defp restrict_favorited_by(query, _), do: query
866
867 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
868 raise "Can't use the child object without preloading!"
869 end
870
871 defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do
872 from(
873 [_activity, object] in query,
874 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
875 )
876 end
877
878 defp restrict_media(query, _), do: query
879
880 defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "true", "1"] do
881 from(
882 [_activity, object] in query,
883 where: fragment("?->>'inReplyTo' is null", object.data)
884 )
885 end
886
887 defp restrict_replies(query, %{
888 "reply_filtering_user" => user,
889 "reply_visibility" => "self"
890 }) do
891 from(
892 [activity, object] in query,
893 where:
894 fragment(
895 "?->>'inReplyTo' is null OR ? = ANY(?)",
896 object.data,
897 ^user.ap_id,
898 activity.recipients
899 )
900 )
901 end
902
903 defp restrict_replies(query, %{
904 "reply_filtering_user" => user,
905 "reply_visibility" => "following"
906 }) do
907 from(
908 [activity, object] in query,
909 where:
910 fragment(
911 "?->>'inReplyTo' is null OR ? && array_remove(?, ?) OR ? = ?",
912 object.data,
913 ^[user.ap_id | User.get_cached_user_friends_ap_ids(user)],
914 activity.recipients,
915 activity.actor,
916 activity.actor,
917 ^user.ap_id
918 )
919 )
920 end
921
922 defp restrict_replies(query, _), do: query
923
924 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val in [true, "true", "1"] do
925 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
926 end
927
928 defp restrict_reblogs(query, _), do: query
929
930 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
931
932 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
933 mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
934
935 query =
936 from([activity] in query,
937 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
938 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
939 )
940
941 unless opts["skip_preload"] do
942 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
943 else
944 query
945 end
946 end
947
948 defp restrict_muted(query, _), do: query
949
950 defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
951 blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
952 domain_blocks = user.domain_blocks || []
953
954 following_ap_ids = User.get_friends_ap_ids(user)
955
956 query =
957 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
958
959 from(
960 [activity, object: o] in query,
961 where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
962 where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
963 where:
964 fragment(
965 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
966 activity.data,
967 activity.data,
968 ^blocked_ap_ids
969 ),
970 where:
971 fragment(
972 "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
973 activity.actor,
974 ^domain_blocks,
975 activity.actor,
976 ^following_ap_ids
977 ),
978 where:
979 fragment(
980 "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
981 o.data,
982 ^domain_blocks,
983 o.data,
984 ^following_ap_ids
985 )
986 )
987 end
988
989 defp restrict_blocked(query, _), do: query
990
991 defp restrict_unlisted(query) do
992 from(
993 activity in query,
994 where:
995 fragment(
996 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
997 activity.data,
998 ^[Constants.as_public()]
999 )
1000 )
1001 end
1002
1003 # TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only,
1004 # the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2'
1005 # and `restrict_muted/2`
1006
1007 defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids})
1008 when pinned in [true, "true", "1"] do
1009 from(activity in query, where: activity.id in ^ids)
1010 end
1011
1012 defp restrict_pinned(query, _), do: query
1013
1014 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
1015 muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
1016
1017 from(
1018 activity in query,
1019 where:
1020 fragment(
1021 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
1022 activity.data,
1023 activity.actor,
1024 ^muted_reblogs
1025 )
1026 )
1027 end
1028
1029 defp restrict_muted_reblogs(query, _), do: query
1030
1031 defp restrict_instance(query, %{"instance" => instance}) do
1032 users =
1033 from(
1034 u in User,
1035 select: u.ap_id,
1036 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
1037 )
1038 |> Repo.all()
1039
1040 from(activity in query, where: activity.actor in ^users)
1041 end
1042
1043 defp restrict_instance(query, _), do: query
1044
1045 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
1046
1047 defp exclude_poll_votes(query, _) do
1048 if has_named_binding?(query, :object) do
1049 from([activity, object: o] in query,
1050 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1051 )
1052 else
1053 query
1054 end
1055 end
1056
1057 defp exclude_chat_messages(query, %{"include_chat_messages" => true}), do: query
1058
1059 defp exclude_chat_messages(query, _) do
1060 if has_named_binding?(query, :object) do
1061 from([activity, object: o] in query,
1062 where: fragment("not(?->>'type' = ?)", o.data, "ChatMessage")
1063 )
1064 else
1065 query
1066 end
1067 end
1068
1069 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1070 from(activity in query, where: activity.id != ^id)
1071 end
1072
1073 defp exclude_id(query, _), do: query
1074
1075 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1076
1077 defp maybe_preload_objects(query, _) do
1078 query
1079 |> Activity.with_preloaded_object()
1080 end
1081
1082 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1083
1084 defp maybe_preload_bookmarks(query, opts) do
1085 query
1086 |> Activity.with_preloaded_bookmark(opts["user"])
1087 end
1088
1089 defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
1090 query
1091 |> Activity.with_preloaded_report_notes()
1092 end
1093
1094 defp maybe_preload_report_notes(query, _), do: query
1095
1096 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1097
1098 defp maybe_set_thread_muted_field(query, opts) do
1099 query
1100 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1101 end
1102
1103 defp maybe_order(query, %{order: :desc}) do
1104 query
1105 |> order_by(desc: :id)
1106 end
1107
1108 defp maybe_order(query, %{order: :asc}) do
1109 query
1110 |> order_by(asc: :id)
1111 end
1112
1113 defp maybe_order(query, _), do: query
1114
1115 defp fetch_activities_query_ap_ids_ops(opts) do
1116 source_user = opts["muting_user"]
1117 ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: []
1118
1119 ap_id_relationships =
1120 ap_id_relationships ++
1121 if opts["blocking_user"] && opts["blocking_user"] == source_user do
1122 [:block]
1123 else
1124 []
1125 end
1126
1127 preloaded_ap_ids = User.outgoing_relationships_ap_ids(source_user, ap_id_relationships)
1128
1129 restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
1130 restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
1131
1132 restrict_muted_reblogs_opts =
1133 Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
1134
1135 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
1136 end
1137
1138 def fetch_activities_query(recipients, opts \\ %{}) do
1139 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
1140 fetch_activities_query_ap_ids_ops(opts)
1141
1142 config = %{
1143 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1144 }
1145
1146 Activity
1147 |> maybe_preload_objects(opts)
1148 |> maybe_preload_bookmarks(opts)
1149 |> maybe_preload_report_notes(opts)
1150 |> maybe_set_thread_muted_field(opts)
1151 |> maybe_order(opts)
1152 |> restrict_recipients(recipients, opts["user"])
1153 |> restrict_replies(opts)
1154 |> restrict_tag(opts)
1155 |> restrict_tag_reject(opts)
1156 |> restrict_tag_all(opts)
1157 |> restrict_since(opts)
1158 |> restrict_local(opts)
1159 |> restrict_actor(opts)
1160 |> restrict_type(opts)
1161 |> restrict_state(opts)
1162 |> restrict_favorited_by(opts)
1163 |> restrict_blocked(restrict_blocked_opts)
1164 |> restrict_muted(restrict_muted_opts)
1165 |> restrict_media(opts)
1166 |> restrict_visibility(opts)
1167 |> restrict_thread_visibility(opts, config)
1168 |> restrict_reblogs(opts)
1169 |> restrict_pinned(opts)
1170 |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
1171 |> restrict_instance(opts)
1172 |> Activity.restrict_deactivated_users()
1173 |> exclude_poll_votes(opts)
1174 |> exclude_chat_messages(opts)
1175 |> exclude_visibility(opts)
1176 end
1177
1178 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1179 list_memberships = Pleroma.List.memberships(opts["user"])
1180
1181 fetch_activities_query(recipients ++ list_memberships, opts)
1182 |> Pagination.fetch_paginated(opts, pagination)
1183 |> Enum.reverse()
1184 |> maybe_update_cc(list_memberships, opts["user"])
1185 end
1186
1187 @doc """
1188 Fetch favorites activities of user with order by sort adds to favorites
1189 """
1190 @spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
1191 def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
1192 user.ap_id
1193 |> Activity.Queries.by_actor()
1194 |> Activity.Queries.by_type("Like")
1195 |> Activity.with_joined_object()
1196 |> Object.with_joined_activity()
1197 |> select([_like, object, activity], %{activity | object: object})
1198 |> order_by([like, _, _], desc: like.id)
1199 |> Pagination.fetch_paginated(
1200 Map.merge(params, %{"skip_order" => true}),
1201 pagination,
1202 :object_activity
1203 )
1204 end
1205
1206 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1207 when is_list(list_memberships) and length(list_memberships) > 0 do
1208 Enum.map(activities, fn
1209 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1210 if Enum.any?(bcc, &(&1 in list_memberships)) do
1211 update_in(activity.data["cc"], &[user_ap_id | &1])
1212 else
1213 activity
1214 end
1215
1216 activity ->
1217 activity
1218 end)
1219 end
1220
1221 defp maybe_update_cc(activities, _, _), do: activities
1222
1223 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1224 from(activity in query,
1225 where:
1226 fragment("? && ?", activity.recipients, ^recipients) or
1227 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1228 ^Constants.as_public() in activity.recipients)
1229 )
1230 end
1231
1232 def fetch_activities_bounded(
1233 recipients,
1234 recipients_with_public,
1235 opts \\ %{},
1236 pagination \\ :keyset
1237 ) do
1238 fetch_activities_query([], opts)
1239 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1240 |> Pagination.fetch_paginated(opts, pagination)
1241 |> Enum.reverse()
1242 end
1243
1244 @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
1245 def upload(file, opts \\ []) do
1246 with {:ok, data} <- Upload.store(file, opts) do
1247 obj_data =
1248 if opts[:actor] do
1249 Map.put(data, "actor", opts[:actor])
1250 else
1251 data
1252 end
1253
1254 Repo.insert(%Object{data: obj_data})
1255 end
1256 end
1257
1258 @spec get_actor_url(any()) :: binary() | nil
1259 defp get_actor_url(url) when is_binary(url), do: url
1260 defp get_actor_url(%{"href" => href}) when is_binary(href), do: href
1261
1262 defp get_actor_url(url) when is_list(url) do
1263 url
1264 |> List.first()
1265 |> get_actor_url()
1266 end
1267
1268 defp get_actor_url(_url), do: nil
1269
1270 defp object_to_user_data(data) do
1271 avatar =
1272 data["icon"]["url"] &&
1273 %{
1274 "type" => "Image",
1275 "url" => [%{"href" => data["icon"]["url"]}]
1276 }
1277
1278 banner =
1279 data["image"]["url"] &&
1280 %{
1281 "type" => "Image",
1282 "url" => [%{"href" => data["image"]["url"]}]
1283 }
1284
1285 fields =
1286 data
1287 |> Map.get("attachment", [])
1288 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1289 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1290
1291 emojis =
1292 data
1293 |> Map.get("tag", [])
1294 |> Enum.filter(fn
1295 %{"type" => "Emoji"} -> true
1296 _ -> false
1297 end)
1298 |> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
1299 Map.put(acc, String.trim(name, ":"), url)
1300 end)
1301
1302 locked = data["manuallyApprovesFollowers"] || false
1303 data = Transmogrifier.maybe_fix_user_object(data)
1304 discoverable = data["discoverable"] || false
1305 invisible = data["invisible"] || false
1306 actor_type = data["type"] || "Person"
1307
1308 public_key =
1309 if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
1310 data["publicKey"]["publicKeyPem"]
1311 else
1312 nil
1313 end
1314
1315 shared_inbox =
1316 if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
1317 data["endpoints"]["sharedInbox"]
1318 else
1319 nil
1320 end
1321
1322 user_data = %{
1323 ap_id: data["id"],
1324 uri: get_actor_url(data["url"]),
1325 ap_enabled: true,
1326 banner: banner,
1327 fields: fields,
1328 emoji: emojis,
1329 locked: locked,
1330 discoverable: discoverable,
1331 invisible: invisible,
1332 avatar: avatar,
1333 name: data["name"],
1334 follower_address: data["followers"],
1335 following_address: data["following"],
1336 bio: data["summary"],
1337 actor_type: actor_type,
1338 also_known_as: Map.get(data, "alsoKnownAs", []),
1339 public_key: public_key,
1340 inbox: data["inbox"],
1341 shared_inbox: shared_inbox
1342 }
1343
1344 # nickname can be nil because of virtual actors
1345 user_data =
1346 if data["preferredUsername"] do
1347 Map.put(
1348 user_data,
1349 :nickname,
1350 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1351 )
1352 else
1353 Map.put(user_data, :nickname, nil)
1354 end
1355
1356 {:ok, user_data}
1357 end
1358
1359 def fetch_follow_information_for_user(user) do
1360 with {:ok, following_data} <-
1361 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1362 {:ok, hide_follows} <- collection_private(following_data),
1363 {:ok, followers_data} <-
1364 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1365 {:ok, hide_followers} <- collection_private(followers_data) do
1366 {:ok,
1367 %{
1368 hide_follows: hide_follows,
1369 follower_count: normalize_counter(followers_data["totalItems"]),
1370 following_count: normalize_counter(following_data["totalItems"]),
1371 hide_followers: hide_followers
1372 }}
1373 else
1374 {:error, _} = e -> e
1375 e -> {:error, e}
1376 end
1377 end
1378
1379 defp normalize_counter(counter) when is_integer(counter), do: counter
1380 defp normalize_counter(_), do: 0
1381
1382 def maybe_update_follow_information(user_data) do
1383 with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
1384 {_, true} <- {:user_type_check, user_data[:type] in ["Person", "Service"]},
1385 {_, true} <-
1386 {:collections_available,
1387 !!(user_data[:following_address] && user_data[:follower_address])},
1388 {:ok, info} <-
1389 fetch_follow_information_for_user(user_data) do
1390 info = Map.merge(user_data[:info] || %{}, info)
1391
1392 user_data
1393 |> Map.put(:info, info)
1394 else
1395 {:user_type_check, false} ->
1396 user_data
1397
1398 {:collections_available, false} ->
1399 user_data
1400
1401 {:enabled, false} ->
1402 user_data
1403
1404 e ->
1405 Logger.error(
1406 "Follower/Following counter update for #{user_data.ap_id} failed.\n" <> inspect(e)
1407 )
1408
1409 user_data
1410 end
1411 end
1412
1413 defp collection_private(%{"first" => %{"type" => type}})
1414 when type in ["CollectionPage", "OrderedCollectionPage"],
1415 do: {:ok, false}
1416
1417 defp collection_private(%{"first" => first}) do
1418 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1419 Fetcher.fetch_and_contain_remote_object_from_id(first) do
1420 {:ok, false}
1421 else
1422 {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
1423 {:error, _} = e -> e
1424 e -> {:error, e}
1425 end
1426 end
1427
1428 defp collection_private(_data), do: {:ok, true}
1429
1430 def user_data_from_user_object(data) do
1431 with {:ok, data} <- MRF.filter(data),
1432 {:ok, data} <- object_to_user_data(data) do
1433 {:ok, data}
1434 else
1435 e -> {:error, e}
1436 end
1437 end
1438
1439 def fetch_and_prepare_user_from_ap_id(ap_id) do
1440 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1441 {:ok, data} <- user_data_from_user_object(data),
1442 data <- maybe_update_follow_information(data) do
1443 {:ok, data}
1444 else
1445 {:error, "Object has been deleted"} = e ->
1446 Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1447 {:error, e}
1448
1449 e ->
1450 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1451 {:error, e}
1452 end
1453 end
1454
1455 def make_user_from_ap_id(ap_id) do
1456 user = User.get_cached_by_ap_id(ap_id)
1457
1458 if user && !User.ap_enabled?(user) do
1459 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1460 else
1461 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1462 if user do
1463 user
1464 |> User.remote_user_changeset(data)
1465 |> User.update_and_set_cache()
1466 else
1467 data
1468 |> User.remote_user_changeset()
1469 |> Repo.insert()
1470 |> User.set_cache()
1471 end
1472 else
1473 e -> {:error, e}
1474 end
1475 end
1476 end
1477
1478 def make_user_from_nickname(nickname) do
1479 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1480 make_user_from_ap_id(ap_id)
1481 else
1482 _e -> {:error, "No AP id in WebFinger"}
1483 end
1484 end
1485
1486 # filter out broken threads
1487 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1488 entire_thread_visible_for_user?(activity, user)
1489 end
1490
1491 # do post-processing on a specific activity
1492 def contain_activity(%Activity{} = activity, %User{} = user) do
1493 contain_broken_threads(activity, user)
1494 end
1495
1496 def fetch_direct_messages_query do
1497 Activity
1498 |> restrict_type(%{"type" => "Create"})
1499 |> restrict_visibility(%{visibility: "direct"})
1500 |> order_by([activity], asc: activity.id)
1501 end
1502 end