Merge branch 'develop' into fix/ldap-auth-issues
[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.Emoji
9 alias Pleroma.Object
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.Relay
12 alias Pleroma.Web.ActivityPub.Utils
13 alias Pleroma.Web.ActivityPub.Visibility
14
15 require Pleroma.Constants
16
17 @spec follow(User.t(), User.t()) :: {:ok, map(), keyword()}
18 def follow(follower, followed) do
19 data = %{
20 "id" => Utils.generate_activity_id(),
21 "actor" => follower.ap_id,
22 "type" => "Follow",
23 "object" => followed.ap_id,
24 "to" => [followed.ap_id]
25 }
26
27 {:ok, data, []}
28 end
29
30 @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
31 def emoji_react(actor, object, emoji) do
32 with {:ok, data, meta} <- object_action(actor, object) do
33 data =
34 data
35 |> Map.put("content", emoji)
36 |> Map.put("type", "EmojiReact")
37
38 {:ok, data, meta}
39 end
40 end
41
42 @spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
43 def undo(actor, object) do
44 {:ok,
45 %{
46 "id" => Utils.generate_activity_id(),
47 "actor" => actor.ap_id,
48 "type" => "Undo",
49 "object" => object.data["id"],
50 "to" => object.data["to"] || [],
51 "cc" => object.data["cc"] || []
52 }, []}
53 end
54
55 @spec delete(User.t(), String.t()) :: {:ok, map(), keyword()}
56 def delete(actor, object_id) do
57 object = Object.normalize(object_id, false)
58
59 user = !object && User.get_cached_by_ap_id(object_id)
60
61 to =
62 case {object, user} do
63 {%Object{}, _} ->
64 # We are deleting an object, address everyone who was originally mentioned
65 (object.data["to"] || []) ++ (object.data["cc"] || [])
66
67 {_, %User{follower_address: follower_address}} ->
68 # We are deleting a user, address the followers of that user
69 [follower_address]
70 end
71
72 {:ok,
73 %{
74 "id" => Utils.generate_activity_id(),
75 "actor" => actor.ap_id,
76 "object" => object_id,
77 "to" => to,
78 "type" => "Delete"
79 }, []}
80 end
81
82 def create(actor, object, recipients) do
83 context =
84 if is_map(object) do
85 object["context"]
86 else
87 nil
88 end
89
90 {:ok,
91 %{
92 "id" => Utils.generate_activity_id(),
93 "actor" => actor.ap_id,
94 "to" => recipients,
95 "object" => object,
96 "type" => "Create",
97 "published" => DateTime.utc_now() |> DateTime.to_iso8601()
98 }
99 |> Pleroma.Maps.put_if_present("context", context), []}
100 end
101
102 def chat_message(actor, recipient, content, opts \\ []) do
103 basic = %{
104 "id" => Utils.generate_object_id(),
105 "actor" => actor.ap_id,
106 "type" => "ChatMessage",
107 "to" => [recipient],
108 "content" => content,
109 "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
110 "emoji" => Emoji.Formatter.get_emoji_map(content)
111 }
112
113 case opts[:attachment] do
114 %Object{data: attachment_data} ->
115 {
116 :ok,
117 Map.put(basic, "attachment", attachment_data),
118 []
119 }
120
121 _ ->
122 {:ok, basic, []}
123 end
124 end
125
126 def answer(user, object, name) do
127 {:ok,
128 %{
129 "type" => "Answer",
130 "actor" => user.ap_id,
131 "attributedTo" => user.ap_id,
132 "cc" => [object.data["actor"]],
133 "to" => [],
134 "name" => name,
135 "inReplyTo" => object.data["id"],
136 "context" => object.data["context"],
137 "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
138 "id" => Utils.generate_object_id()
139 }, []}
140 end
141
142 @spec tombstone(String.t(), String.t()) :: {:ok, map(), keyword()}
143 def tombstone(actor, id) do
144 {:ok,
145 %{
146 "id" => id,
147 "actor" => actor,
148 "type" => "Tombstone"
149 }, []}
150 end
151
152 @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
153 def like(actor, object) do
154 with {:ok, data, meta} <- object_action(actor, object) do
155 data =
156 data
157 |> Map.put("type", "Like")
158
159 {:ok, data, meta}
160 end
161 end
162
163 # Retricted to user updates for now, always public
164 @spec update(User.t(), Object.t()) :: {:ok, map(), keyword()}
165 def update(actor, object) do
166 to = [Pleroma.Constants.as_public(), actor.follower_address]
167
168 {:ok,
169 %{
170 "id" => Utils.generate_activity_id(),
171 "type" => "Update",
172 "actor" => actor.ap_id,
173 "object" => object,
174 "to" => to
175 }, []}
176 end
177
178 @spec block(User.t(), User.t()) :: {:ok, map(), keyword()}
179 def block(blocker, blocked) do
180 {:ok,
181 %{
182 "id" => Utils.generate_activity_id(),
183 "type" => "Block",
184 "actor" => blocker.ap_id,
185 "object" => blocked.ap_id,
186 "to" => [blocked.ap_id]
187 }, []}
188 end
189
190 @spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
191 def announce(actor, object, options \\ []) do
192 public? = Keyword.get(options, :public, false)
193
194 to =
195 cond do
196 actor.ap_id == Relay.relay_ap_id() ->
197 [actor.follower_address]
198
199 public? ->
200 [actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
201
202 true ->
203 [actor.follower_address, object.data["actor"]]
204 end
205
206 {:ok,
207 %{
208 "id" => Utils.generate_activity_id(),
209 "actor" => actor.ap_id,
210 "object" => object.data["id"],
211 "to" => to,
212 "context" => object.data["context"],
213 "type" => "Announce",
214 "published" => Utils.make_date()
215 }, []}
216 end
217
218 @spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
219 defp object_action(actor, object) do
220 object_actor = User.get_cached_by_ap_id(object.data["actor"])
221
222 # Address the actor of the object, and our actor's follower collection if the post is public.
223 to =
224 if Visibility.is_public?(object) do
225 [actor.follower_address, object.data["actor"]]
226 else
227 [object.data["actor"]]
228 end
229
230 # CC everyone who's been addressed in the object, except ourself and the object actor's
231 # follower collection
232 cc =
233 (object.data["to"] ++ (object.data["cc"] || []))
234 |> List.delete(actor.ap_id)
235 |> List.delete(object_actor.follower_address)
236
237 {:ok,
238 %{
239 "id" => Utils.generate_activity_id(),
240 "actor" => actor.ap_id,
241 "object" => object.data["id"],
242 "to" => to,
243 "cc" => cc,
244 "context" => object.data["context"]
245 }, []}
246 end
247 end