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