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