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