field(:skip_thread_containment, :boolean, default: false)
field(:actor_type, :string, default: "Person")
field(:also_known_as, {:array, :string}, default: [])
+ field(:inbox, :string)
+ field(:shared_inbox, :string)
embeds_one(
:notification_settings,
:bio,
:name,
:ap_id,
+ :inbox,
+ :shared_inbox,
:nickname,
:public_key,
:avatar,
:name,
:avatar,
:public_key,
+ :inbox,
+ :shared_inbox,
:locked,
:no_rich_text,
:default_scope,
:follower_address,
:following_address,
:public_key,
+ :inbox,
+ :shared_inbox,
:avatar,
:last_refreshed_at,
:ap_enabled,
discoverable = data["discoverable"] || false
invisible = data["invisible"] || false
actor_type = data["type"] || "Person"
- public_key = data["publicKey"]["publicKeyPem"]
+
+ public_key =
+ if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
+ data["publicKey"]["publicKeyPem"]
+ else
+ nil
+ end
+
+ shared_inbox =
+ if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
+ data["endpoints"]["sharedInbox"]
+ else
+ nil
+ end
user_data = %{
ap_id: data["id"],
bio: data["summary"],
actor_type: actor_type,
also_known_as: Map.get(data, "alsoKnownAs", []),
- public_key: public_key
+ public_key: public_key,
+ inbox: data["inbox"],
+ shared_inbox: shared_inbox
}
# nickname can be nil because of virtual actors
|> Enum.map(& &1.ap_id)
end
- defp maybe_use_sharedinbox(%User{source_data: data}),
- do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
+ defp maybe_use_sharedinbox(%User{shared_inbox: nil, inbox: inbox}), do: inbox
+ defp maybe_use_sharedinbox(%User{shared_inbox: shared_inbox}), do: shared_inbox
@doc """
Determine a user inbox to use based on heuristics. These heuristics
"""
def determine_inbox(
%Activity{data: activity_data},
- %User{source_data: data} = user
+ %User{inbox: inbox} = user
) do
to = activity_data["to"] || []
cc = activity_data["cc"] || []
maybe_use_sharedinbox(user)
true ->
- data["inbox"]
+ inbox
end
end
inboxes =
recipients
|> Enum.filter(&User.ap_enabled?/1)
- |> Enum.map(fn %{source_data: data} -> data["inbox"] end)
+ |> Enum.map(fn actor -> actor.inbox end)
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|> Instances.filter_reachable()
Repo.checkout(fn ->
Enum.each(inboxes, fn {inbox, unreachable_since} ->
- %User{ap_id: ap_id} =
- Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
+ %User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
# instance would only accept a first message for the first recipient and ignore the rest.
--- /dev/null
+defmodule Pleroma.Repo.Migrations.UsersAddInboxes do
+ use Ecto.Migration
+
+ def up do
+ alter table(:users) do
+ add_if_not_exists(:inbox, :text)
+ add_if_not_exists(:shared_inbox, :text)
+ end
+
+ execute("UPDATE users SET inbox = source_data->>'inbox'")
+ execute("UPDATE users SET shared_inbox = source_data->'endpoints'->>'sharedInbox'")
+ end
+
+ def down do
+ alter table(:users) do
+ remove_if_exists(:inbox, :text)
+ remove_if_exists(:shared_inbox, :text)
+ end
+ end
+end
describe "determine_inbox/2" do
test "it returns sharedInbox for messages involving as:Public in to" do
- user =
- insert(:user, %{
- source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
- })
+ user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
activity = %Activity{
data: %{"to" => [@as_public], "cc" => [user.follower_address]}
end
test "it returns sharedInbox for messages involving as:Public in cc" do
- user =
- insert(:user, %{
- source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
- })
+ user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
activity = %Activity{
data: %{"cc" => [@as_public], "to" => [user.follower_address]}
end
test "it returns sharedInbox for messages involving multiple recipients in to" do
- user =
- insert(:user, %{
- source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
- })
-
+ user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
user_two = insert(:user)
user_three = insert(:user)
end
test "it returns sharedInbox for messages involving multiple recipients in cc" do
- user =
- insert(:user, %{
- source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
- })
-
+ user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
user_two = insert(:user)
user_three = insert(:user)
test "it returns sharedInbox for messages involving multiple recipients in total" do
user =
- insert(:user,
- source_data: %{
- "inbox" => "http://example.com/personal-inbox",
- "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
- }
- )
+ insert(:user, %{
+ shared_inbox: "http://example.com/inbox",
+ inbox: "http://example.com/personal-inbox"
+ })
user_two = insert(:user)
test "it returns inbox for messages involving single recipients in total" do
user =
- insert(:user,
- source_data: %{
- "inbox" => "http://example.com/personal-inbox",
- "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
- }
- )
+ insert(:user, %{
+ shared_inbox: "http://example.com/inbox",
+ inbox: "http://example.com/personal-inbox"
+ })
activity = %Activity{
data: %{"to" => [user.ap_id], "cc" => []}
[:passthrough],
[] do
follower =
- insert(:user,
+ insert(:user, %{
local: false,
- source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"},
+ inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
- )
+ })
actor = insert(:user, follower_address: follower.ap_id)
user = insert(:user)
fetcher =
insert(:user,
local: false,
- source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"},
+ inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
)
another_fetcher =
insert(:user,
local: false,
- source_data: %{"inbox" => "https://domain2.com/users/nick1/inbox"},
+ inbox: "https://domain2.com/users/nick1/inbox",
ap_enabled: true
)
local: false,
nickname: "nick1@domain.com",
ap_id: "https://domain.com/users/nick1",
- source_data: %{"inbox" => inbox1},
+ inbox: inbox1,
ap_enabled: true
})
local: false,
nickname: "nick2@domain2.com",
ap_id: "https://domain2.com/users/nick2",
- source_data: %{"inbox" => inbox2},
+ inbox: inbox2,
ap_enabled: true
})