1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.Builder do
7 This module builds the objects. Meant to be used for creating local objects.
9 This module encodes our addressing policies and general shape of our objects.
15 alias Pleroma.Web.ActivityPub.Relay
16 alias Pleroma.Web.ActivityPub.Utils
17 alias Pleroma.Web.ActivityPub.Visibility
19 require Pleroma.Constants
21 def accept_or_reject(actor, activity, type) do
23 "id" => Utils.generate_activity_id(),
24 "actor" => actor.ap_id,
26 "object" => activity.data["id"],
27 "to" => [activity.actor]
33 @spec reject(User.t(), Activity.t()) :: {:ok, map(), keyword()}
34 def reject(actor, rejected_activity) do
35 accept_or_reject(actor, rejected_activity, "Reject")
38 @spec accept(User.t(), Activity.t()) :: {:ok, map(), keyword()}
39 def accept(actor, accepted_activity) do
40 accept_or_reject(actor, accepted_activity, "Accept")
43 @spec follow(User.t(), User.t()) :: {:ok, map(), keyword()}
44 def follow(follower, followed) do
46 "id" => Utils.generate_activity_id(),
47 "actor" => follower.ap_id,
49 "object" => followed.ap_id,
50 "to" => [followed.ap_id]
56 @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
57 def emoji_react(actor, object, emoji) do
58 with {:ok, data, meta} <- object_action(actor, object) do
61 |> Map.put("content", emoji)
62 |> Map.put("type", "EmojiReact")
68 @spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
69 def undo(actor, object) do
72 "id" => Utils.generate_activity_id(),
73 "actor" => actor.ap_id,
75 "object" => object.data["id"],
76 "to" => object.data["to"] || [],
77 "cc" => object.data["cc"] || []
81 @spec delete(User.t(), String.t()) :: {:ok, map(), keyword()}
82 def delete(actor, object_id) do
83 object = Object.normalize(object_id, fetch: false)
85 user = !object && User.get_cached_by_ap_id(object_id)
88 case {object, user} do
90 # We are deleting an object, address everyone who was originally mentioned
91 (object.data["to"] || []) ++ (object.data["cc"] || [])
93 {_, %User{follower_address: follower_address}} ->
94 # We are deleting a user, address the followers of that user
100 "id" => Utils.generate_activity_id(),
101 "actor" => actor.ap_id,
102 "object" => object_id,
108 def create(actor, object, recipients) do
118 "id" => Utils.generate_activity_id(),
119 "actor" => actor.ap_id,
123 "published" => DateTime.utc_now() |> DateTime.to_iso8601()
125 |> Pleroma.Maps.put_if_present("context", context), []}
128 def chat_message(actor, recipient, content, opts \\ []) do
130 "id" => Utils.generate_object_id(),
131 "actor" => actor.ap_id,
132 "type" => "ChatMessage",
134 "content" => content,
135 "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
136 "emoji" => Emoji.Formatter.get_emoji_map(content)
139 case opts[:attachment] do
140 %Object{data: attachment_data} ->
143 Map.put(basic, "attachment", attachment_data),
152 def answer(user, object, name) do
156 "actor" => user.ap_id,
157 "attributedTo" => user.ap_id,
158 "cc" => [object.data["actor"]],
161 "inReplyTo" => object.data["id"],
162 "context" => object.data["context"],
163 "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
164 "id" => Utils.generate_object_id()
168 @spec tombstone(String.t(), String.t()) :: {:ok, map(), keyword()}
169 def tombstone(actor, id) do
174 "type" => "Tombstone"
178 @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
179 def like(actor, object) do
180 with {:ok, data, meta} <- object_action(actor, object) do
183 |> Map.put("type", "Like")
189 # Retricted to user updates for now, always public
190 @spec update(User.t(), Object.t()) :: {:ok, map(), keyword()}
191 def update(actor, object) do
192 to = [Pleroma.Constants.as_public(), actor.follower_address]
196 "id" => Utils.generate_activity_id(),
198 "actor" => actor.ap_id,
204 @spec block(User.t(), User.t()) :: {:ok, map(), keyword()}
205 def block(blocker, blocked) do
208 "id" => Utils.generate_activity_id(),
210 "actor" => blocker.ap_id,
211 "object" => blocked.ap_id,
212 "to" => [blocked.ap_id]
216 @spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
217 def announce(actor, object, options \\ []) do
218 public? = Keyword.get(options, :public, false)
222 actor.ap_id == Relay.ap_id() ->
223 [actor.follower_address]
225 public? and Visibility.is_local_public?(object) ->
226 [actor.follower_address, object.data["actor"], Utils.as_local_public()]
229 [actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
232 [actor.follower_address, object.data["actor"]]
237 "id" => Utils.generate_activity_id(),
238 "actor" => actor.ap_id,
239 "object" => object.data["id"],
241 "context" => object.data["context"],
242 "type" => "Announce",
243 "published" => Utils.make_date()
247 @spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
248 defp object_action(actor, object) do
249 object_actor = User.get_cached_by_ap_id(object.data["actor"])
251 # Address the actor of the object, and our actor's follower collection if the post is public.
253 if Visibility.is_public?(object) do
254 [actor.follower_address, object.data["actor"]]
256 [object.data["actor"]]
259 # CC everyone who's been addressed in the object, except ourself and the object actor's
260 # follower collection
262 (object.data["to"] ++ (object.data["cc"] || []))
263 |> List.delete(actor.ap_id)
264 |> List.delete(object_actor.follower_address)
268 "id" => Utils.generate_activity_id(),
269 "actor" => actor.ap_id,
270 "object" => object.data["id"],
273 "context" => object.data["context"]
277 @spec pin(User.t(), Object.t()) :: {:ok, map(), keyword()}
278 def pin(%User{} = user, object) do
281 "id" => Utils.generate_activity_id(),
282 "target" => pinned_url(user.nickname),
283 "object" => object.data["id"],
284 "actor" => user.ap_id,
286 "to" => [Pleroma.Constants.as_public()],
287 "cc" => [user.follower_address]
291 @spec unpin(User.t(), Object.t()) :: {:ok, map, keyword()}
292 def unpin(%User{} = user, object) do
295 "id" => Utils.generate_activity_id(),
296 "target" => pinned_url(user.nickname),
297 "object" => object.data["id"],
298 "actor" => user.ap_id,
300 "to" => [Pleroma.Constants.as_public()],
301 "cc" => [user.follower_address]
305 defp pinned_url(nickname) when is_binary(nickname) do
306 Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname)