bdd7e78d230d057a7b9677243e702c37e1295b04
[akkoma] / lib / pleroma / web / activity_pub / activity_pub.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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.Conversation
8 alias Pleroma.Notification
9 alias Pleroma.Object
10 alias Pleroma.Object.Fetcher
11 alias Pleroma.Pagination
12 alias Pleroma.Repo
13 alias Pleroma.Upload
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.MRF
16 alias Pleroma.Web.ActivityPub.Transmogrifier
17 alias Pleroma.Web.WebFinger
18
19 import Ecto.Query
20 import Pleroma.Web.ActivityPub.Utils
21 import Pleroma.Web.ActivityPub.Visibility
22
23 require Logger
24
25 # For Announce activities, we filter the recipients based on following status for any actors
26 # that match actual users. See issue #164 for more information about why this is necessary.
27 defp get_recipients(%{"type" => "Announce"} = data) do
28 to = data["to"] || []
29 cc = data["cc"] || []
30 actor = User.get_cached_by_ap_id(data["actor"])
31
32 recipients =
33 (to ++ cc)
34 |> Enum.filter(fn recipient ->
35 case User.get_cached_by_ap_id(recipient) do
36 nil ->
37 true
38
39 user ->
40 User.following?(user, actor)
41 end
42 end)
43
44 {recipients, to, cc}
45 end
46
47 defp get_recipients(%{"type" => "Create"} = data) do
48 to = data["to"] || []
49 cc = data["cc"] || []
50 actor = data["actor"] || []
51 recipients = (to ++ cc ++ [actor]) |> Enum.uniq()
52 {recipients, to, cc}
53 end
54
55 defp get_recipients(data) do
56 to = data["to"] || []
57 cc = data["cc"] || []
58 recipients = to ++ cc
59 {recipients, to, cc}
60 end
61
62 defp check_actor_is_active(actor) do
63 if not is_nil(actor) do
64 with user <- User.get_cached_by_ap_id(actor),
65 false <- user.info.deactivated do
66 :ok
67 else
68 _e -> :reject
69 end
70 else
71 :ok
72 end
73 end
74
75 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
76 limit = Pleroma.Config.get([:instance, :remote_limit])
77 String.length(content) <= limit
78 end
79
80 defp check_remote_limit(_), do: true
81
82 def increase_note_count_if_public(actor, object) do
83 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
84 end
85
86 def decrease_note_count_if_public(actor, object) do
87 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
88 end
89
90 def increase_replies_count_if_reply(%{
91 "object" => %{"inReplyTo" => reply_ap_id} = object,
92 "type" => "Create"
93 }) do
94 if is_public?(object) do
95 Object.increase_replies_count(reply_ap_id)
96 end
97 end
98
99 def increase_replies_count_if_reply(_create_data), do: :noop
100
101 def decrease_replies_count_if_reply(%Object{
102 data: %{"inReplyTo" => reply_ap_id} = object
103 }) do
104 if is_public?(object) do
105 Object.decrease_replies_count(reply_ap_id)
106 end
107 end
108
109 def decrease_replies_count_if_reply(_object), do: :noop
110
111 def insert(map, local \\ true, fake \\ false) when is_map(map) do
112 with nil <- Activity.normalize(map),
113 map <- lazy_put_activity_defaults(map, fake),
114 :ok <- check_actor_is_active(map["actor"]),
115 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
116 {:ok, map} <- MRF.filter(map),
117 {recipients, _, _} = get_recipients(map),
118 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
119 {:ok, map, object} <- insert_full_object(map) do
120 {:ok, activity} =
121 Repo.insert(%Activity{
122 data: map,
123 local: local,
124 actor: map["actor"],
125 recipients: recipients
126 })
127
128 # Splice in the child object if we have one.
129 activity =
130 if !is_nil(object) do
131 Map.put(activity, :object, object)
132 else
133 activity
134 end
135
136 PleromaJobQueue.enqueue(:background, Pleroma.Web.RichMedia.Helpers, [:fetch, activity])
137
138 Notification.create_notifications(activity)
139
140 participations =
141 activity
142 |> Conversation.create_or_bump_for()
143 |> get_participations()
144
145 stream_out(activity)
146 stream_out_participations(participations)
147 {:ok, activity}
148 else
149 %Activity{} = activity ->
150 {:ok, activity}
151
152 {:fake, true, map, recipients} ->
153 activity = %Activity{
154 data: map,
155 local: local,
156 actor: map["actor"],
157 recipients: recipients,
158 id: "pleroma:fakeid"
159 }
160
161 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
162 {:ok, activity}
163
164 error ->
165 {:error, error}
166 end
167 end
168
169 defp get_participations({:ok, %{participations: participations}}), do: participations
170 defp get_participations(_), do: []
171
172 def stream_out_participations(participations) do
173 participations =
174 participations
175 |> Repo.preload(:user)
176
177 Enum.each(participations, fn participation ->
178 Pleroma.Web.Streamer.stream("participation", participation)
179 end)
180 end
181
182 def stream_out(activity) do
183 public = "https://www.w3.org/ns/activitystreams#Public"
184
185 if activity.data["type"] in ["Create", "Announce", "Delete"] do
186 Pleroma.Web.Streamer.stream("user", activity)
187 Pleroma.Web.Streamer.stream("list", activity)
188
189 if Enum.member?(activity.data["to"], public) do
190 Pleroma.Web.Streamer.stream("public", activity)
191
192 if activity.local do
193 Pleroma.Web.Streamer.stream("public:local", activity)
194 end
195
196 if activity.data["type"] in ["Create"] do
197 object = Object.normalize(activity)
198
199 object.data
200 |> Map.get("tag", [])
201 |> Enum.filter(fn tag -> is_bitstring(tag) end)
202 |> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
203
204 if object.data["attachment"] != [] do
205 Pleroma.Web.Streamer.stream("public:media", activity)
206
207 if activity.local do
208 Pleroma.Web.Streamer.stream("public:local:media", activity)
209 end
210 end
211 end
212 else
213 # TODO: Write test, replace with visibility test
214 if !Enum.member?(activity.data["cc"] || [], public) &&
215 !Enum.member?(
216 activity.data["to"],
217 User.get_cached_by_ap_id(activity.data["actor"]).follower_address
218 ),
219 do: Pleroma.Web.Streamer.stream("direct", activity)
220 end
221 end
222 end
223
224 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
225 additional = params[:additional] || %{}
226 # only accept false as false value
227 local = !(params[:local] == false)
228 published = params[:published]
229
230 with create_data <-
231 make_create_data(
232 %{to: to, actor: actor, published: published, context: context, object: object},
233 additional
234 ),
235 {:ok, activity} <- insert(create_data, local, fake),
236 {:fake, false, activity} <- {:fake, fake, activity},
237 _ <- increase_replies_count_if_reply(create_data),
238 # Changing note count prior to enqueuing federation task in order to avoid
239 # race conditions on updating user.info
240 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
241 :ok <- maybe_federate(activity) do
242 {:ok, activity}
243 else
244 {:fake, true, activity} ->
245 {:ok, activity}
246 end
247 end
248
249 def accept(%{to: to, actor: actor, object: object} = params) do
250 # only accept false as false value
251 local = !(params[:local] == false)
252
253 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
254 {:ok, activity} <- insert(data, local),
255 :ok <- maybe_federate(activity) do
256 {:ok, activity}
257 end
258 end
259
260 def reject(%{to: to, actor: actor, object: object} = params) do
261 # only accept false as false value
262 local = !(params[:local] == false)
263
264 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
265 {:ok, activity} <- insert(data, local),
266 :ok <- maybe_federate(activity) do
267 {:ok, activity}
268 end
269 end
270
271 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
272 # only accept false as false value
273 local = !(params[:local] == false)
274
275 with data <- %{
276 "to" => to,
277 "cc" => cc,
278 "type" => "Update",
279 "actor" => actor,
280 "object" => object
281 },
282 {:ok, activity} <- insert(data, local),
283 :ok <- maybe_federate(activity) do
284 {:ok, activity}
285 end
286 end
287
288 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
289 def like(
290 %User{ap_id: ap_id} = user,
291 %Object{data: %{"id" => _}} = object,
292 activity_id \\ nil,
293 local \\ true
294 ) do
295 with nil <- get_existing_like(ap_id, object),
296 like_data <- make_like_data(user, object, activity_id),
297 {:ok, activity} <- insert(like_data, local),
298 {:ok, object} <- add_like_to_object(activity, object),
299 :ok <- maybe_federate(activity) do
300 {:ok, activity, object}
301 else
302 %Activity{} = activity -> {:ok, activity, object}
303 error -> {:error, error}
304 end
305 end
306
307 def unlike(
308 %User{} = actor,
309 %Object{} = object,
310 activity_id \\ nil,
311 local \\ true
312 ) do
313 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
314 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
315 {:ok, unlike_activity} <- insert(unlike_data, local),
316 {:ok, _activity} <- Repo.delete(like_activity),
317 {:ok, object} <- remove_like_from_object(like_activity, object),
318 :ok <- maybe_federate(unlike_activity) do
319 {:ok, unlike_activity, like_activity, object}
320 else
321 _e -> {:ok, object}
322 end
323 end
324
325 def announce(
326 %User{ap_id: _} = user,
327 %Object{data: %{"id" => _}} = object,
328 activity_id \\ nil,
329 local \\ true,
330 public \\ true
331 ) do
332 with true <- is_public?(object),
333 announce_data <- make_announce_data(user, object, activity_id, public),
334 {:ok, activity} <- insert(announce_data, local),
335 {:ok, object} <- add_announce_to_object(activity, object),
336 :ok <- maybe_federate(activity) do
337 {:ok, activity, object}
338 else
339 error -> {:error, error}
340 end
341 end
342
343 def unannounce(
344 %User{} = actor,
345 %Object{} = object,
346 activity_id \\ nil,
347 local \\ true
348 ) do
349 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
350 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
351 {:ok, unannounce_activity} <- insert(unannounce_data, local),
352 :ok <- maybe_federate(unannounce_activity),
353 {:ok, _activity} <- Repo.delete(announce_activity),
354 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
355 {:ok, unannounce_activity, object}
356 else
357 _e -> {:ok, object}
358 end
359 end
360
361 def follow(follower, followed, activity_id \\ nil, local \\ true) do
362 with data <- make_follow_data(follower, followed, activity_id),
363 {:ok, activity} <- insert(data, local),
364 :ok <- maybe_federate(activity) do
365 {:ok, activity}
366 end
367 end
368
369 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
370 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
371 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
372 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
373 {:ok, activity} <- insert(unfollow_data, local),
374 :ok <- maybe_federate(activity) do
375 {:ok, activity}
376 end
377 end
378
379 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
380 user = User.get_cached_by_ap_id(actor)
381 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
382
383 with {:ok, object, activity} <- Object.delete(object),
384 data <- %{
385 "type" => "Delete",
386 "actor" => actor,
387 "object" => id,
388 "to" => to,
389 "deleted_activity_id" => activity && activity.id
390 },
391 {:ok, activity} <- insert(data, local),
392 _ <- decrease_replies_count_if_reply(object),
393 # Changing note count prior to enqueuing federation task in order to avoid
394 # race conditions on updating user.info
395 {:ok, _actor} <- decrease_note_count_if_public(user, object),
396 :ok <- maybe_federate(activity) do
397 {:ok, activity}
398 end
399 end
400
401 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
402 outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
403 unfollow_blocked = Pleroma.Config.get([:activitypub, :unfollow_blocked])
404
405 if unfollow_blocked do
406 follow_activity = fetch_latest_follow(blocker, blocked)
407 if follow_activity, do: unfollow(blocker, blocked, nil, local)
408 end
409
410 with true <- outgoing_blocks,
411 block_data <- make_block_data(blocker, blocked, activity_id),
412 {:ok, activity} <- insert(block_data, local),
413 :ok <- maybe_federate(activity) do
414 {:ok, activity}
415 else
416 _e -> {:ok, nil}
417 end
418 end
419
420 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
421 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
422 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
423 {:ok, activity} <- insert(unblock_data, local),
424 :ok <- maybe_federate(activity) do
425 {:ok, activity}
426 end
427 end
428
429 def flag(
430 %{
431 actor: actor,
432 context: context,
433 account: account,
434 statuses: statuses,
435 content: content
436 } = params
437 ) do
438 # only accept false as false value
439 local = !(params[:local] == false)
440 forward = !(params[:forward] == false)
441
442 additional = params[:additional] || %{}
443
444 params = %{
445 actor: actor,
446 context: context,
447 account: account,
448 statuses: statuses,
449 content: content
450 }
451
452 additional =
453 if forward do
454 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
455 else
456 Map.merge(additional, %{"to" => [], "cc" => []})
457 end
458
459 with flag_data <- make_flag_data(params, additional),
460 {:ok, activity} <- insert(flag_data, local),
461 :ok <- maybe_federate(activity) do
462 Enum.each(User.all_superusers(), fn superuser ->
463 superuser
464 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
465 |> Pleroma.Emails.Mailer.deliver_async()
466 end)
467
468 {:ok, activity}
469 end
470 end
471
472 defp fetch_activities_for_context_query(context, opts) do
473 public = ["https://www.w3.org/ns/activitystreams#Public"]
474
475 recipients =
476 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
477
478 from(activity in Activity)
479 |> restrict_blocked(opts)
480 |> restrict_recipients(recipients, opts["user"])
481 |> where(
482 [activity],
483 fragment(
484 "?->>'type' = ? and ?->>'context' = ?",
485 activity.data,
486 "Create",
487 activity.data,
488 ^context
489 )
490 )
491 |> order_by([activity], desc: activity.id)
492 end
493
494 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
495 def fetch_activities_for_context(context, opts \\ %{}) do
496 context
497 |> fetch_activities_for_context_query(opts)
498 |> Activity.with_preloaded_object()
499 |> Repo.all()
500 end
501
502 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
503 Pleroma.FlakeId.t() | nil
504 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
505 context
506 |> fetch_activities_for_context_query(opts)
507 |> limit(1)
508 |> select([a], a.id)
509 |> Repo.one()
510 end
511
512 def fetch_public_activities(opts \\ %{}) do
513 q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
514
515 q
516 |> restrict_unlisted()
517 |> Pagination.fetch_paginated(opts)
518 |> Enum.reverse()
519 end
520
521 @valid_visibilities ~w[direct unlisted public private]
522
523 defp restrict_visibility(query, %{visibility: visibility})
524 when is_list(visibility) do
525 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
526 query =
527 from(
528 a in query,
529 where:
530 fragment(
531 "activity_visibility(?, ?, ?) = ANY (?)",
532 a.actor,
533 a.recipients,
534 a.data,
535 ^visibility
536 )
537 )
538
539 query
540 else
541 Logger.error("Could not restrict visibility to #{visibility}")
542 end
543 end
544
545 defp restrict_visibility(query, %{visibility: visibility})
546 when visibility in @valid_visibilities do
547 query =
548 from(
549 a in query,
550 where:
551 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
552 )
553
554 query
555 end
556
557 defp restrict_visibility(_query, %{visibility: visibility})
558 when visibility not in @valid_visibilities do
559 Logger.error("Could not restrict visibility to #{visibility}")
560 end
561
562 defp restrict_visibility(query, _visibility), do: query
563
564 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}) do
565 query =
566 from(
567 a in query,
568 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
569 )
570
571 query
572 end
573
574 defp restrict_thread_visibility(query, _), do: query
575
576 def fetch_user_activities(user, reading_user, params \\ %{}) do
577 params =
578 params
579 |> Map.put("type", ["Create", "Announce"])
580 |> Map.put("actor_id", user.ap_id)
581 |> Map.put("whole_db", true)
582 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
583
584 recipients =
585 if reading_user do
586 ["https://www.w3.org/ns/activitystreams#Public"] ++
587 [reading_user.ap_id | reading_user.following]
588 else
589 ["https://www.w3.org/ns/activitystreams#Public"]
590 end
591
592 fetch_activities(recipients, params)
593 |> Enum.reverse()
594 end
595
596 defp restrict_since(query, %{"since_id" => ""}), do: query
597
598 defp restrict_since(query, %{"since_id" => since_id}) do
599 from(activity in query, where: activity.id > ^since_id)
600 end
601
602 defp restrict_since(query, _), do: query
603
604 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
605 raise "Can't use the child object without preloading!"
606 end
607
608 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
609 when is_list(tag_reject) and tag_reject != [] do
610 from(
611 [_activity, object] in query,
612 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
613 )
614 end
615
616 defp restrict_tag_reject(query, _), do: query
617
618 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
619 raise "Can't use the child object without preloading!"
620 end
621
622 defp restrict_tag_all(query, %{"tag_all" => tag_all})
623 when is_list(tag_all) and tag_all != [] do
624 from(
625 [_activity, object] in query,
626 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
627 )
628 end
629
630 defp restrict_tag_all(query, _), do: query
631
632 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
633 raise "Can't use the child object without preloading!"
634 end
635
636 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
637 from(
638 [_activity, object] in query,
639 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
640 )
641 end
642
643 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
644 from(
645 [_activity, object] in query,
646 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
647 )
648 end
649
650 defp restrict_tag(query, _), do: query
651
652 defp restrict_to_cc(query, recipients_to, recipients_cc) do
653 from(
654 activity in query,
655 where:
656 fragment(
657 "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
658 activity.data,
659 ^recipients_to,
660 activity.data,
661 ^recipients_cc
662 )
663 )
664 end
665
666 defp restrict_recipients(query, [], _user), do: query
667
668 defp restrict_recipients(query, recipients, nil) do
669 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
670 end
671
672 defp restrict_recipients(query, recipients, user) do
673 from(
674 activity in query,
675 where: fragment("? && ?", ^recipients, activity.recipients),
676 or_where: activity.actor == ^user.ap_id
677 )
678 end
679
680 defp restrict_local(query, %{"local_only" => true}) do
681 from(activity in query, where: activity.local == true)
682 end
683
684 defp restrict_local(query, _), do: query
685
686 defp restrict_actor(query, %{"actor_id" => actor_id}) do
687 from(activity in query, where: activity.actor == ^actor_id)
688 end
689
690 defp restrict_actor(query, _), do: query
691
692 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
693 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
694 end
695
696 defp restrict_type(query, %{"type" => type}) do
697 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
698 end
699
700 defp restrict_type(query, _), do: query
701
702 defp restrict_state(query, %{"state" => state}) do
703 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
704 end
705
706 defp restrict_state(query, _), do: query
707
708 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
709 from(
710 activity in query,
711 where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data)
712 )
713 end
714
715 defp restrict_favorited_by(query, _), do: query
716
717 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
718 raise "Can't use the child object without preloading!"
719 end
720
721 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
722 from(
723 [_activity, object] in query,
724 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
725 )
726 end
727
728 defp restrict_media(query, _), do: query
729
730 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
731 from(
732 activity in query,
733 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
734 )
735 end
736
737 defp restrict_replies(query, _), do: query
738
739 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
740 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
741 end
742
743 defp restrict_reblogs(query, _), do: query
744
745 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
746
747 defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
748 mutes = info.mutes
749
750 from(
751 activity in query,
752 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
753 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
754 )
755 end
756
757 defp restrict_muted(query, _), do: query
758
759 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
760 blocks = info.blocks || []
761 domain_blocks = info.domain_blocks || []
762
763 query =
764 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
765
766 from(
767 [activity, object: o] in query,
768 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
769 where: fragment("not (? && ?)", activity.recipients, ^blocks),
770 where:
771 fragment(
772 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
773 activity.data,
774 activity.data,
775 ^blocks
776 ),
777 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
778 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
779 )
780 end
781
782 defp restrict_blocked(query, _), do: query
783
784 defp restrict_unlisted(query) do
785 from(
786 activity in query,
787 where:
788 fragment(
789 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
790 activity.data,
791 ^["https://www.w3.org/ns/activitystreams#Public"]
792 )
793 )
794 end
795
796 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
797 from(activity in query, where: activity.id in ^ids)
798 end
799
800 defp restrict_pinned(query, _), do: query
801
802 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
803 muted_reblogs = info.muted_reblogs || []
804
805 from(
806 activity in query,
807 where:
808 fragment(
809 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
810 activity.data,
811 activity.actor,
812 ^muted_reblogs
813 )
814 )
815 end
816
817 defp restrict_muted_reblogs(query, _), do: query
818
819 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
820
821 defp maybe_preload_objects(query, _) do
822 query
823 |> Activity.with_preloaded_object()
824 end
825
826 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
827
828 defp maybe_preload_bookmarks(query, opts) do
829 query
830 |> Activity.with_preloaded_bookmark(opts["user"])
831 end
832
833 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
834
835 defp maybe_set_thread_muted_field(query, opts) do
836 query
837 |> Activity.with_set_thread_muted_field(opts["user"])
838 end
839
840 defp maybe_order(query, %{order: :desc}) do
841 query
842 |> order_by(desc: :id)
843 end
844
845 defp maybe_order(query, %{order: :asc}) do
846 query
847 |> order_by(asc: :id)
848 end
849
850 defp maybe_order(query, _), do: query
851
852 def fetch_activities_query(recipients, opts \\ %{}) do
853 base_query = from(activity in Activity)
854
855 base_query
856 |> maybe_preload_objects(opts)
857 |> maybe_preload_bookmarks(opts)
858 |> maybe_set_thread_muted_field(opts)
859 |> maybe_order(opts)
860 |> restrict_recipients(recipients, opts["user"])
861 |> restrict_tag(opts)
862 |> restrict_tag_reject(opts)
863 |> restrict_tag_all(opts)
864 |> restrict_since(opts)
865 |> restrict_local(opts)
866 |> restrict_actor(opts)
867 |> restrict_type(opts)
868 |> restrict_state(opts)
869 |> restrict_favorited_by(opts)
870 |> restrict_blocked(opts)
871 |> restrict_muted(opts)
872 |> restrict_media(opts)
873 |> restrict_visibility(opts)
874 |> restrict_thread_visibility(opts)
875 |> restrict_replies(opts)
876 |> restrict_reblogs(opts)
877 |> restrict_pinned(opts)
878 |> restrict_muted_reblogs(opts)
879 |> Activity.restrict_deactivated_users()
880 end
881
882 def fetch_activities(recipients, opts \\ %{}) do
883 fetch_activities_query(recipients, opts)
884 |> Pagination.fetch_paginated(opts)
885 |> Enum.reverse()
886 end
887
888 def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
889 fetch_activities_query([], opts)
890 |> restrict_to_cc(recipients_to, recipients_cc)
891 |> Pagination.fetch_paginated(opts)
892 |> Enum.reverse()
893 end
894
895 def upload(file, opts \\ []) do
896 with {:ok, data} <- Upload.store(file, opts) do
897 obj_data =
898 if opts[:actor] do
899 Map.put(data, "actor", opts[:actor])
900 else
901 data
902 end
903
904 Repo.insert(%Object{data: obj_data})
905 end
906 end
907
908 defp object_to_user_data(data) do
909 avatar =
910 data["icon"]["url"] &&
911 %{
912 "type" => "Image",
913 "url" => [%{"href" => data["icon"]["url"]}]
914 }
915
916 banner =
917 data["image"]["url"] &&
918 %{
919 "type" => "Image",
920 "url" => [%{"href" => data["image"]["url"]}]
921 }
922
923 locked = data["manuallyApprovesFollowers"] || false
924 data = Transmogrifier.maybe_fix_user_object(data)
925
926 user_data = %{
927 ap_id: data["id"],
928 info: %{
929 "ap_enabled" => true,
930 "source_data" => data,
931 "banner" => banner,
932 "locked" => locked
933 },
934 avatar: avatar,
935 name: data["name"],
936 follower_address: data["followers"],
937 bio: data["summary"]
938 }
939
940 # nickname can be nil because of virtual actors
941 user_data =
942 if data["preferredUsername"] do
943 Map.put(
944 user_data,
945 :nickname,
946 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
947 )
948 else
949 Map.put(user_data, :nickname, nil)
950 end
951
952 {:ok, user_data}
953 end
954
955 def user_data_from_user_object(data) do
956 with {:ok, data} <- MRF.filter(data),
957 {:ok, data} <- object_to_user_data(data) do
958 {:ok, data}
959 else
960 e -> {:error, e}
961 end
962 end
963
964 def fetch_and_prepare_user_from_ap_id(ap_id) do
965 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
966 {:ok, data} <- user_data_from_user_object(data) do
967 {:ok, data}
968 else
969 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
970 end
971 end
972
973 def make_user_from_ap_id(ap_id) do
974 if _user = User.get_cached_by_ap_id(ap_id) do
975 Transmogrifier.upgrade_user_from_ap_id(ap_id)
976 else
977 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
978 User.insert_or_update_user(data)
979 else
980 e -> {:error, e}
981 end
982 end
983 end
984
985 def make_user_from_nickname(nickname) do
986 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
987 make_user_from_ap_id(ap_id)
988 else
989 _e -> {:error, "No AP id in WebFinger"}
990 end
991 end
992
993 # filter out broken threads
994 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
995 entire_thread_visible_for_user?(activity, user)
996 end
997
998 # do post-processing on a specific activity
999 def contain_activity(%Activity{} = activity, %User{} = user) do
1000 contain_broken_threads(activity, user)
1001 end
1002
1003 def fetch_direct_messages_query do
1004 Activity
1005 |> restrict_type(%{"type" => "Create"})
1006 |> restrict_visibility(%{visibility: "direct"})
1007 |> order_by([activity], asc: activity.id)
1008 end
1009 end