Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
[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.Utils
12 alias Pleroma.Web.ActivityPub.Visibility
13
14 @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
15 def emoji_react(actor, object, emoji) do
16 with {:ok, data, meta} <- object_action(actor, object) do
17 data =
18 data
19 |> Map.put("content", emoji)
20 |> Map.put("type", "EmojiReact")
21
22 {:ok, data, meta}
23 end
24 end
25
26 @spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
27 def undo(actor, object) do
28 {:ok,
29 %{
30 "id" => Utils.generate_activity_id(),
31 "actor" => actor.ap_id,
32 "type" => "Undo",
33 "object" => object.data["id"],
34 "to" => object.data["to"] || [],
35 "cc" => object.data["cc"] || []
36 }, []}
37 end
38
39 @spec delete(User.t(), String.t()) :: {:ok, map(), keyword()}
40 def delete(actor, object_id) do
41 object = Object.normalize(object_id, false)
42
43 user = !object && User.get_cached_by_ap_id(object_id)
44
45 to =
46 case {object, user} do
47 {%Object{}, _} ->
48 # We are deleting an object, address everyone who was originally mentioned
49 (object.data["to"] || []) ++ (object.data["cc"] || [])
50
51 {_, %User{follower_address: follower_address}} ->
52 # We are deleting a user, address the followers of that user
53 [follower_address]
54 end
55
56 {:ok,
57 %{
58 "id" => Utils.generate_activity_id(),
59 "actor" => actor.ap_id,
60 "object" => object_id,
61 "to" => to,
62 "type" => "Delete"
63 }, []}
64 end
65
66 def create(actor, object, recipients) do
67 {:ok,
68 %{
69 "id" => Utils.generate_activity_id(),
70 "actor" => actor.ap_id,
71 "to" => recipients,
72 "object" => object,
73 "type" => "Create",
74 "published" => DateTime.utc_now() |> DateTime.to_iso8601()
75 }, []}
76 end
77
78 def chat_message(actor, recipient, content, opts \\ []) do
79 basic = %{
80 "id" => Utils.generate_object_id(),
81 "actor" => actor.ap_id,
82 "type" => "ChatMessage",
83 "to" => [recipient],
84 "content" => content,
85 "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
86 "emoji" => Emoji.Formatter.get_emoji_map(content)
87 }
88
89 case opts[:attachment] do
90 %Object{data: attachment_data} ->
91 {
92 :ok,
93 Map.put(basic, "attachment", attachment_data),
94 []
95 }
96
97 _ ->
98 {:ok, basic, []}
99 end
100 end
101
102 @spec tombstone(String.t(), String.t()) :: {:ok, map(), keyword()}
103 def tombstone(actor, id) do
104 {:ok,
105 %{
106 "id" => id,
107 "actor" => actor,
108 "type" => "Tombstone"
109 }, []}
110 end
111
112 @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
113 def like(actor, object) do
114 with {:ok, data, meta} <- object_action(actor, object) do
115 data =
116 data
117 |> Map.put("type", "Like")
118
119 {:ok, data, meta}
120 end
121 end
122
123 @spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
124 defp object_action(actor, object) do
125 object_actor = User.get_cached_by_ap_id(object.data["actor"])
126
127 # Address the actor of the object, and our actor's follower collection if the post is public.
128 to =
129 if Visibility.is_public?(object) do
130 [actor.follower_address, object.data["actor"]]
131 else
132 [object.data["actor"]]
133 end
134
135 # CC everyone who's been addressed in the object, except ourself and the object actor's
136 # follower collection
137 cc =
138 (object.data["to"] ++ (object.data["cc"] || []))
139 |> List.delete(actor.ap_id)
140 |> List.delete(object_actor.follower_address)
141
142 {:ok,
143 %{
144 "id" => Utils.generate_activity_id(),
145 "actor" => actor.ap_id,
146 "object" => object.data["id"],
147 "to" => to,
148 "cc" => cc,
149 "context" => object.data["context"]
150 }, []}
151 end
152 end