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