Merge branch 'notification-fixes' into 'develop'
[akkoma] / lib / pleroma / web / activity_pub / builder.ex
1 defmodule Pleroma.Web.ActivityPub.Builder do
2 @moduledoc """
3 This module builds the objects. Meant to be used for creating local objects.
4
5 This module encodes our addressing policies and general shape of our objects.
6 """
7
8 alias Pleroma.Object
9 alias Pleroma.User
10 alias Pleroma.Web.ActivityPub.Utils
11 alias Pleroma.Web.ActivityPub.Visibility
12
13 require Pleroma.Constants
14
15 @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
16 def emoji_react(actor, object, emoji) do
17 with {:ok, data, meta} <- object_action(actor, object) do
18 data =
19 data
20 |> Map.put("content", emoji)
21 |> Map.put("type", "EmojiReact")
22
23 {:ok, data, meta}
24 end
25 end
26
27 @spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
28 def undo(actor, object) do
29 {:ok,
30 %{
31 "id" => Utils.generate_activity_id(),
32 "actor" => actor.ap_id,
33 "type" => "Undo",
34 "object" => object.data["id"],
35 "to" => object.data["to"] || [],
36 "cc" => object.data["cc"] || []
37 }, []}
38 end
39
40 @spec delete(User.t(), String.t()) :: {:ok, map(), keyword()}
41 def delete(actor, object_id) do
42 object = Object.normalize(object_id, false)
43
44 user = !object && User.get_cached_by_ap_id(object_id)
45
46 to =
47 case {object, user} do
48 {%Object{}, _} ->
49 # We are deleting an object, address everyone who was originally mentioned
50 (object.data["to"] || []) ++ (object.data["cc"] || [])
51
52 {_, %User{follower_address: follower_address}} ->
53 # We are deleting a user, address the followers of that user
54 [follower_address]
55 end
56
57 {:ok,
58 %{
59 "id" => Utils.generate_activity_id(),
60 "actor" => actor.ap_id,
61 "object" => object_id,
62 "to" => to,
63 "type" => "Delete"
64 }, []}
65 end
66
67 @spec tombstone(String.t(), String.t()) :: {:ok, map(), keyword()}
68 def tombstone(actor, id) do
69 {:ok,
70 %{
71 "id" => id,
72 "actor" => actor,
73 "type" => "Tombstone"
74 }, []}
75 end
76
77 @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
78 def like(actor, object) do
79 with {:ok, data, meta} <- object_action(actor, object) do
80 data =
81 data
82 |> Map.put("type", "Like")
83
84 {:ok, data, meta}
85 end
86 end
87
88 def announce(actor, object, options \\ []) do
89 public? = Keyword.get(options, :public, false)
90 to = [actor.follower_address, object.data["actor"]]
91
92 to =
93 if public? do
94 [Pleroma.Constants.as_public() | to]
95 else
96 to
97 end
98
99 {:ok,
100 %{
101 "id" => Utils.generate_activity_id(),
102 "actor" => actor.ap_id,
103 "object" => object.data["id"],
104 "to" => to,
105 "context" => object.data["context"],
106 "type" => "Announce",
107 "published" => Utils.make_date()
108 }, []}
109 end
110
111 @spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
112 defp object_action(actor, object) do
113 object_actor = User.get_cached_by_ap_id(object.data["actor"])
114
115 # Address the actor of the object, and our actor's follower collection if the post is public.
116 to =
117 if Visibility.is_public?(object) do
118 [actor.follower_address, object.data["actor"]]
119 else
120 [object.data["actor"]]
121 end
122
123 # CC everyone who's been addressed in the object, except ourself and the object actor's
124 # follower collection
125 cc =
126 (object.data["to"] ++ (object.data["cc"] || []))
127 |> List.delete(actor.ap_id)
128 |> List.delete(object_actor.follower_address)
129
130 {:ok,
131 %{
132 "id" => Utils.generate_activity_id(),
133 "actor" => actor.ap_id,
134 "object" => object.data["id"],
135 "to" => to,
136 "cc" => cc,
137 "context" => object.data["context"]
138 }, []}
139 end
140 end