A module to handle coding from internal to wire ActivityPub and back.
"""
alias Pleroma.Activity
+ alias Pleroma.FollowingRelationship
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.Repo
def handle_incoming(%{"id" => nil}, _options), do: :error
def handle_incoming(%{"id" => ""}, _options), do: :error
# length of https:// = 8, should validate better, but good enough for now.
- def handle_incoming(%{"id" => id}, _options) when not (is_binary(id) and length(id) > 8),
+ def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8,
do: :error
# TODO: validate those with a Ecto scheme
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
options
)
- when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do
+ when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer"] do
actor = Containment.get_actor(data)
data =
{_, false} <- {:user_locked, User.locked?(followed)},
{_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
{_, {:ok, _}} <-
- {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")} do
+ {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")},
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do
ActivityPub.accept(%{
to: [follower.ap_id],
actor: followed,
else
{:user_blocked, true} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
+ {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject")
ActivityPub.reject(%{
to: [follower.ap_id],
{:follow, {:error, _}} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
+ {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject")
ActivityPub.reject(%{
to: [follower.ap_id],
})
{:user_locked, true} ->
+ {:ok, _relationship} = FollowingRelationship.update(follower, followed, "pending")
:noop
end
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
- {:ok, _follower} = User.follow(follower, followed) do
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do
ActivityPub.accept(%{
to: follow_activity.data["to"],
type: "Accept",
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"),
{:ok, activity} <-
ActivityPub.reject(%{
to: follow_activity.data["to"],
local: false,
activity_id: id
}) do
- User.unfollow(follower, followed)
-
{:ok, activity}
else
_e -> :error
end
end
+ @misskey_reactions %{
+ "like" => "👍",
+ "love" => "❤️",
+ "laugh" => "😆",
+ "hmm" => "🤔",
+ "surprise" => "😮",
+ "congrats" => "🎉",
+ "angry" => "💢",
+ "confused" => "😥",
+ "rip" => "😇",
+ "pudding" => "🍮",
+ "star" => "⭐"
+ }
+
+ @doc "Rewrite misskey likes into EmojiReactions"
+ def handle_incoming(
+ %{
+ "type" => "Like",
+ "_misskey_reaction" => reaction
+ } = data,
+ options
+ ) do
+ data
+ |> Map.put("type", "EmojiReaction")
+ |> Map.put("content", @misskey_reactions[reaction] || reaction)
+ |> handle_incoming(options)
+ end
+
def handle_incoming(
%{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data,
_options
end
end
+ def handle_incoming(
+ %{
+ "type" => "EmojiReaction",
+ "object" => object_id,
+ "actor" => _actor,
+ "id" => id,
+ "content" => emoji
+ } = data,
+ _options
+ ) do
+ with actor <- Containment.get_actor(data),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, object} <- get_obj_helper(object_id),
+ {:ok, activity, _object} <-
+ ActivityPub.react_with_emoji(actor, object, emoji, activity_id: id, local: false) do
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
def handle_incoming(
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
_options
"Person",
"Application",
"Service",
- "Organization",
- ["Application", "Invisible"]
+ "Organization"
] do
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
- banner = new_user_data[:info][:banner]
- locked = new_user_data[:info][:locked] || false
- attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
- invisible = new_user_data[:info][:invisible] || false
+ locked = new_user_data[:locked] || false
+ attachment = get_in(new_user_data, [:source_data, "attachment"]) || []
+ invisible = new_user_data[:invisible] || false
fields =
attachment
update_data =
new_user_data
- |> Map.take([:name, :bio, :avatar])
- |> Map.put(:info, %{banner: banner, locked: locked, fields: fields, invisible: invisible})
+ |> Map.take([:avatar, :banner, :bio, :name, :also_known_as])
+ |> Map.put(:fields, fields)
+ |> Map.put(:locked, locked)
+ |> Map.put(:invisible, invisible)
actor
|> User.upgrade_changeset(update_data, true)
end
end
+ def handle_incoming(
+ %{
+ "type" => "Undo",
+ "object" => %{"type" => "EmojiReaction", "id" => reaction_activity_id},
+ "actor" => _actor,
+ "id" => id
+ } = data,
+ _options
+ ) do
+ with actor <- Containment.get_actor(data),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, activity, _} <-
+ ActivityPub.unreact_with_emoji(actor, reaction_activity_id,
+ activity_id: id,
+ local: false
+ ) do
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
def handle_incoming(
%{
"type" => "Undo",
end
end
+ def handle_incoming(
+ %{
+ "type" => "Move",
+ "actor" => origin_actor,
+ "object" => origin_actor,
+ "target" => target_actor
+ },
+ _options
+ ) do
+ with %User{} = origin_user <- User.get_cached_by_ap_id(origin_actor),
+ {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_actor),
+ true <- origin_actor in target_user.also_known_as do
+ ActivityPub.move(origin_user, target_user, false)
+ else
+ _e -> :error
+ end
+ end
+
def handle_incoming(_, _), do: :error
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
%{"type" => "Mention", "href" => ap_id, "name" => "@#{nickname}"}
end
- def take_emoji_tags(%User{info: %{emoji: emoji} = _user_info} = _user) do
+ def take_emoji_tags(%User{emoji: emoji}) do
emoji
|> Enum.flat_map(&Map.to_list/1)
|> Enum.map(&build_emoji_tag/1)
Map.put(object, "attachment", attachments)
end
- defp strip_internal_fields(object) do
+ def strip_internal_fields(object) do
object
|> Map.drop(Pleroma.Constants.object_internal_fields())
end
# we pass a fake user so that the followers collection is stripped away
old_follower_address = User.ap_followers(%User{nickname: user.nickname})
- q =
- from(
- u in User,
- where: ^old_follower_address in u.following,
- update: [
- set: [
- following:
- fragment(
- "array_replace(?,?,?)",
- u.following,
- ^old_follower_address,
- ^user.follower_address
- )
- ]
- ]
- )
-
- Repo.update_all(q, [])
-
- q =
- from(
- a in Activity,
- where: ^old_follower_address in a.recipients,
- update: [
- set: [
- recipients:
- fragment(
- "array_replace(?,?,?)",
- a.recipients,
- ^old_follower_address,
- ^user.follower_address
- )
- ]
+ from(
+ a in Activity,
+ where: ^old_follower_address in a.recipients,
+ update: [
+ set: [
+ recipients:
+ fragment(
+ "array_replace(?,?,?)",
+ a.recipients,
+ ^old_follower_address,
+ ^user.follower_address
+ )
]
- )
-
- Repo.update_all(q, [])
+ ]
+ )
+ |> Repo.update_all([])
end
def upgrade_user_from_ap_id(ap_id) do