defp extract_list(_), do: []
def maybe_splice_recipient(ap_id, params) do
- need_splice =
+ need_splice? =
!recipient_in_collection(ap_id, params["to"]) &&
!recipient_in_collection(ap_id, params["cc"])
- cc_list = extract_list(params["cc"])
-
- if need_splice do
- params
- |> Map.put("cc", [ap_id | cc_list])
+ if need_splice? do
+ cc_list = extract_list(params["cc"])
+ Map.put(params, "cc", [ap_id | cc_list])
else
params
end
"object" => object
}
- Notification.get_notified_from_activity(%Activity{data: fake_create_activity}, false)
+ get_notified_from_object(fake_create_activity)
end
def get_notified_from_object(object) do
@doc """
Enqueues an activity for federation if it's local
"""
+ @spec maybe_federate(any()) :: :ok
def maybe_federate(%Activity{local: true} = activity) do
if Pleroma.Config.get!([:instance, :federating]) do
- priority =
- case activity.data["type"] do
- "Delete" -> 10
- "Create" -> 1
- _ -> 5
- end
-
- Pleroma.Web.Federator.publish(activity, priority)
+ Pleroma.Web.Federator.publish(activity)
end
:ok
Adds an id and a published data if they aren't there,
also adds it to an included object
"""
- def lazy_put_activity_defaults(map, fake \\ false) do
+ def lazy_put_activity_defaults(map, fake? \\ false) do
map =
- unless fake do
+ if not fake? do
%{data: %{"id" => context}, id: context_id} = create_context(map["context"])
map
end
if is_map(map["object"]) do
- object = lazy_put_object_defaults(map["object"], map, fake)
+ object = lazy_put_object_defaults(map["object"], map, fake?)
%{map | "object" => object}
else
map
@doc """
Adds an id and published date if they aren't there.
"""
- def lazy_put_object_defaults(map, activity \\ %{}, fake)
+ def lazy_put_object_defaults(map, activity \\ %{}, fake?)
- def lazy_put_object_defaults(map, activity, true = _fake) do
+ def lazy_put_object_defaults(map, activity, true = _fake?) do
map
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("id", "pleroma:fake_object_id")
|> Map.put_new("context_id", activity["context_id"])
end
- def lazy_put_object_defaults(map, activity, _fake) do
+ def lazy_put_object_defaults(map, activity, _fake?) do
map
|> Map.put_new_lazy("id", &generate_object_id/0)
|> Map.put_new_lazy("published", &make_date/0)
def insert_full_object(%{"object" => %{"type" => type} = object_data} = map)
when is_map(object_data) and type in @supported_object_types do
with {:ok, object} <- Object.create(object_data) do
- map =
- map
- |> Map.put("object", object.data["id"])
+ map = Map.put(map, "object", object.data["id"])
{:ok, map, object}
end
def insert_full_object(map), do: {:ok, map, nil}
- def update_object_in_activities(%{data: %{"id" => id}} = object) do
- # TODO
- # Update activities that already had this. Could be done in a seperate process.
- # Alternatively, just don't do this and fetch the current object each time. Most
- # could probably be taken from cache.
- relevant_activities = Activity.get_all_create_by_object_ap_id(id)
-
- Enum.map(relevant_activities, fn activity ->
- new_activity_data = activity.data |> Map.put("object", object.data)
- changeset = Changeset.change(activity, data: new_activity_data)
- Repo.update(changeset)
- end)
- end
-
#### Like-related helpers
@doc """
Returns an existing like if a user already liked an object
"""
+ @spec get_existing_like(String.t(), map()) :: Activity.t() | nil
def get_existing_like(actor, %{data: %{"id" => id}}) do
- query =
- from(
- activity in Activity,
- where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
- # this is to use the index
- where:
- fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
- activity.data,
- activity.data,
- ^id
- ),
- where: fragment("(?)->>'type' = 'Like'", activity.data)
- )
-
- Repo.one(query)
+ actor
+ |> Activity.Queries.by_actor()
+ |> Activity.Queries.by_object_id(id)
+ |> Activity.Queries.by_type("Like")
+ |> limit(1)
+ |> Repo.one()
end
@doc """
Returns like activities targeting an object
"""
def get_object_likes(%{data: %{"id" => id}}) do
- query =
- from(
- activity in Activity,
- # this is to use the index
- where:
- fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
- activity.data,
- activity.data,
- ^id
- ),
- where: fragment("(?)->>'type' = 'Like'", activity.data)
- )
-
- Repo.all(query)
+ id
+ |> Activity.Queries.by_object_id()
+ |> Activity.Queries.by_type("Like")
+ |> Repo.all()
end
+ @spec make_like_data(User.t(), map(), String.t()) :: map()
def make_like_data(
%User{ap_id: ap_id} = actor,
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|> List.delete(actor.ap_id)
|> List.delete(object_actor.follower_address)
- data = %{
+ %{
"type" => "Like",
"actor" => ap_id,
"object" => id,
"cc" => cc,
"context" => object.data["context"]
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
+ @spec update_element_in_object(String.t(), list(any), Object.t()) ::
+ {:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def update_element_in_object(property, element, object) do
- with new_data <-
- object.data
- |> Map.put("#{property}_count", length(element))
- |> Map.put("#{property}s", element),
- changeset <- Changeset.change(object, data: new_data),
- {:ok, object} <- Object.update_and_set_cache(changeset),
- _ <- update_object_in_activities(object) do
- {:ok, object}
- end
- end
+ data =
+ Map.merge(
+ object.data,
+ %{"#{property}_count" => length(element), "#{property}s" => element}
+ )
- def update_likes_in_object(likes, object) do
- update_element_in_object("like", likes, object)
+ object
+ |> Changeset.change(data: data)
+ |> Object.update_and_set_cache()
end
+ @spec add_like_to_object(Activity.t(), Object.t()) ::
+ {:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
- likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
-
- with likes <- [actor | likes] |> Enum.uniq() do
- update_likes_in_object(likes, object)
- end
+ [actor | fetch_likes(object)]
+ |> Enum.uniq()
+ |> update_likes_in_object(object)
end
+ @spec remove_like_from_object(Activity.t(), Object.t()) ::
+ {:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do
- likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
+ object
+ |> fetch_likes()
+ |> List.delete(actor)
+ |> update_likes_in_object(object)
+ end
+
+ defp update_likes_in_object(likes, object) do
+ update_element_in_object("like", likes, object)
+ end
- with likes <- likes |> List.delete(actor) do
- update_likes_in_object(likes, object)
+ defp fetch_likes(object) do
+ if is_list(object.data["likes"]) do
+ object.data["likes"]
+ else
+ []
end
end
[state, actor, object]
)
+ User.set_follow_state_cache(actor, object, state)
activity = Activity.get_by_id(activity.id)
{:ok, activity}
rescue
end
end
- def update_follow_state(%Activity{} = activity, state) do
- with new_data <-
- activity.data
- |> Map.put("state", state),
- changeset <- Changeset.change(activity, data: new_data),
- {:ok, activity} <- Repo.update(changeset) do
+ def update_follow_state(
+ %Activity{data: %{"actor" => actor, "object" => object}} = activity,
+ state
+ ) do
+ new_data = Map.put(activity.data, "state", state)
+ changeset = Changeset.change(activity, data: new_data)
+
+ with {:ok, activity} <- Repo.update(changeset) do
+ User.set_follow_state_cache(actor, object, state)
{:ok, activity}
end
end
%User{ap_id: followed_id} = _followed,
activity_id
) do
- data = %{
+ %{
"type" => "Follow",
"actor" => follower_id,
"to" => [followed_id],
"object" => followed_id,
"state" => "pending"
}
-
- data = if activity_id, do: Map.put(data, "id", activity_id), else: data
-
- data
+ |> maybe_put("id", activity_id)
end
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
- query =
- from(
- activity in Activity,
- where:
- fragment(
- "? ->> 'type' = 'Follow'",
- activity.data
- ),
- where: activity.actor == ^follower_id,
- # this is to use the index
- where:
- fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
- activity.data,
- activity.data,
- ^followed_id
- ),
- order_by: [fragment("? desc nulls last", activity.id)],
- limit: 1
- )
-
- Repo.one(query)
+ "Follow"
+ |> Activity.Queries.by_type()
+ |> where(actor: ^follower_id)
+ # this is to use the index
+ |> Activity.Queries.by_object_id(followed_id)
+ |> order_by([activity], fragment("? desc nulls last", activity.id))
+ |> limit(1)
+ |> Repo.one()
end
#### Announce-related helpers
@doc """
Retruns an existing announce activity if the notice has already been announced
"""
- def get_existing_announce(actor, %{data: %{"id" => id}}) do
- query =
- from(
- activity in Activity,
- where: activity.actor == ^actor,
- # this is to use the index
- where:
- fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
- activity.data,
- activity.data,
- ^id
- ),
- where: fragment("(?)->>'type' = 'Announce'", activity.data)
- )
-
- Repo.one(query)
+ def get_existing_announce(actor, %{data: %{"id" => ap_id}}) do
+ "Announce"
+ |> Activity.Queries.by_type()
+ |> where(actor: ^actor)
+ # this is to use the index
+ |> Activity.Queries.by_object_id(ap_id)
+ |> Repo.one()
end
@doc """
activity_id,
false
) do
- data = %{
+ %{
"type" => "Announce",
"actor" => ap_id,
"object" => id,
"cc" => [],
"context" => object.data["context"]
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
def make_announce_data(
activity_id,
true
) do
- data = %{
+ %{
"type" => "Announce",
"actor" => ap_id,
"object" => id,
"cc" => [Pleroma.Constants.as_public()],
"context" => object.data["context"]
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
@doc """
%Activity{data: %{"context" => context}} = activity,
activity_id
) do
- data = %{
+ %{
"type" => "Undo",
"actor" => ap_id,
"object" => activity.data,
"cc" => [Pleroma.Constants.as_public()],
"context" => context
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
def make_unlike_data(
%Activity{data: %{"context" => context}} = activity,
activity_id
) do
- data = %{
+ %{
"type" => "Undo",
"actor" => ap_id,
"object" => activity.data,
"cc" => [Pleroma.Constants.as_public()],
"context" => context
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
def add_announce_to_object(
object
) do
announcements =
- if is_list(object.data["announcements"]), do: object.data["announcements"], else: []
+ if is_list(object.data["announcements"]) do
+ Enum.uniq([actor | object.data["announcements"]])
+ else
+ [actor]
+ end
- with announcements <- [actor | announcements] |> Enum.uniq() do
- update_element_in_object("announcement", announcements, object)
- end
+ update_element_in_object("announcement", announcements, object)
end
def add_announce_to_object(_, object), do: {:ok, object}
#### Unfollow-related helpers
def make_unfollow_data(follower, followed, follow_activity, activity_id) do
- data = %{
+ %{
"type" => "Undo",
"actor" => follower.ap_id,
"to" => [followed.ap_id],
"object" => follow_activity.data
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
#### Block-related helpers
def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do
- query =
- from(
- activity in Activity,
- where:
- fragment(
- "? ->> 'type' = 'Block'",
- activity.data
- ),
- where: activity.actor == ^blocker_id,
- # this is to use the index
- where:
- fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
- activity.data,
- activity.data,
- ^blocked_id
- ),
- order_by: [fragment("? desc nulls last", activity.id)],
- limit: 1
- )
-
- Repo.one(query)
+ "Block"
+ |> Activity.Queries.by_type()
+ |> where(actor: ^blocker_id)
+ # this is to use the index
+ |> Activity.Queries.by_object_id(blocked_id)
+ |> order_by([activity], fragment("? desc nulls last", activity.id))
+ |> limit(1)
+ |> Repo.one()
end
def make_block_data(blocker, blocked, activity_id) do
- data = %{
+ %{
"type" => "Block",
"actor" => blocker.ap_id,
"to" => [blocked.ap_id],
"object" => blocked.ap_id
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
def make_unblock_data(blocker, blocked, block_activity, activity_id) do
- data = %{
+ %{
"type" => "Undo",
"actor" => blocker.ap_id,
"to" => [blocked.ap_id],
"object" => block_activity.data
}
-
- if activity_id, do: Map.put(data, "id", activity_id), else: data
+ |> maybe_put("id", activity_id)
end
#### Create-related helpers
#### Report-related helpers
def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
- with new_data <- Map.put(activity.data, "state", state),
- changeset <- Changeset.change(activity, data: new_data),
- {:ok, activity} <- Repo.update(changeset) do
- {:ok, activity}
- end
+ new_data = Map.put(activity.data, "state", state)
+
+ activity
+ |> Changeset.change(data: new_data)
+ |> Repo.update()
end
def update_report_state(_, _), do: {:error, "Unsupported state"}
end
def get_existing_votes(actor, %{data: %{"id" => id}}) do
- query =
- from(
- [activity, object: object] in Activity.with_preloaded_object(Activity),
- where: fragment("(?)->>'type' = 'Create'", activity.data),
- where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
- where:
- fragment(
- "(?)->>'inReplyTo' = ?",
- object.data,
- ^to_string(id)
- ),
- where: fragment("(?)->>'type' = 'Answer'", object.data)
- )
-
- Repo.all(query)
+ actor
+ |> Activity.Queries.by_actor()
+ |> Activity.Queries.by_type("Create")
+ |> Activity.with_preloaded_object()
+ |> where([a, object: o], fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(id)))
+ |> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
+ |> Repo.all()
end
+
+ defp maybe_put(map, _key, nil), do: map
+ defp maybe_put(map, key, value), do: Map.put(map, key, value)
end