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