diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 24b4f045a..8485a8009 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -192,12 +192,11 @@ def follow(follower, followed, activity_id \\ nil, local \\ true) do end end - def unfollow(follower, followed, local \\ true) do + def unfollow(follower, followed, activity_id \\ nil, local \\ true) do with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed), - unfollow_data <- make_unfollow_data(follower, followed, follow_activity), + unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id), {:ok, activity} <- insert(unfollow_data, local), - :ok, - maybe_federate(activity) do + :ok <- maybe_federate(activity) do {:ok, activity} end end @@ -221,6 +220,29 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ tru end end + def block(blocker, blocked, activity_id \\ nil, local \\ true) do + follow_activity = fetch_latest_follow(blocker, blocked) + + if follow_activity do + unfollow(blocker, blocked, nil, local) + end + + with block_data <- make_block_data(blocker, blocked, activity_id), + {:ok, activity} <- insert(block_data, local), + :ok <- maybe_federate(activity) do + {:ok, activity} + end + end + + def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do + with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked), + unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id), + {:ok, activity} <- insert(unblock_data, local), + :ok <- maybe_federate(activity) do + {:ok, activity} + end + end + def fetch_activities_for_context(context, opts \\ %{}) do public = ["https://www.w3.org/ns/activitystreams#Public"] diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 7d6fd8632..803445011 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -241,6 +241,56 @@ def handle_incoming( end end + def handle_incoming( + %{ + "type" => "Undo", + "object" => %{"type" => "Follow", "object" => followed}, + "actor" => follower, + "id" => id + } = _data + ) do + with %User{local: true} = followed <- User.get_cached_by_ap_id(followed), + %User{} = follower <- User.get_or_fetch_by_ap_id(follower), + {:ok, activity} <- ActivityPub.unfollow(follower, followed, id, false) do + User.unfollow(follower, followed) + {:ok, activity} + else + e -> :error + end + end + + def handle_incoming( + %{ + "type" => "Undo", + "object" => %{"type" => "Block", "object" => blocked}, + "actor" => blocker, + "id" => id + } = _data + ) do + with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked), + %User{} = blocker <- User.get_or_fetch_by_ap_id(blocker), + {:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do + User.unblock(blocker, blocked) + {:ok, activity} + else + e -> :error + end + end + + def handle_incoming( + %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = data + ) do + with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), + %User{} = blocker = User.get_or_fetch_by_ap_id(blocker), + {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do + User.unfollow(blocker, blocked) + User.block(blocker, blocked) + {:ok, activity} + else + e -> :error + end + end + def handle_incoming( %{ "type" => "Undo", @@ -261,7 +311,6 @@ def handle_incoming( # TODO # Accept - # Undo for non-Announce def handle_incoming(_), do: :error diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index a3feca480..831e13b7e 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -347,13 +347,56 @@ def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do #### Unfollow-related helpers - def make_unfollow_data(follower, followed, follow_activity) do - %{ + 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["id"] + "object" => follow_activity.data } + + if activity_id, do: Map.put(data, "id", activity_id), else: data + 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( + "? @> ?", + activity.data, + ^%{type: "Block", object: blocked_id} + ), + where: activity.actor == ^blocker_id, + order_by: [desc: :id], + limit: 1 + ) + + Repo.one(query) + 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 + 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 end #### Create-related helpers diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index e6365620e..2c0277124 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -453,24 +453,18 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do end end - # TODO: Clean up and unify def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), - {:ok, follower, follow_activity} <- User.unfollow(follower, followed), - {:ok, _activity} <- - ActivityPub.insert(%{ - "type" => "Undo", - "actor" => follower.ap_id, - # get latest Follow for these users - "object" => follow_activity.data["id"] - }) do + {:ok, _activity} <- ActivityPub.unfollow(follower, followed), + {:ok, follower, _} <- User.unfollow(follower, followed) do render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) end end def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), - {:ok, blocker} <- User.block(blocker, blocked) do + {:ok, blocker} <- User.block(blocker, blocked), + {:ok, _activity} <- ActivityPub.block(blocker, blocked) do render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) else {:error, message} -> @@ -482,7 +476,8 @@ def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), - {:ok, blocker} <- User.unblock(blocker, blocked) do + {:ok, blocker} <- User.unblock(blocker, blocked), + {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) else {:error, message} -> diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex index 64cadba1b..4179d86c9 100644 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ b/lib/pleroma/web/ostatus/activity_representer.ex @@ -232,7 +232,12 @@ def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) end # Only undos of follow for now. Will need to get redone once there are more - def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) do + def to_simple_form( + %{data: %{"type" => "Undo", "object" => %{"type" => "Follow"} = follow_activity}} = + activity, + user, + with_author + ) do h = fn str -> [to_charlist(str)] end updated_at = activity.data["published"] @@ -240,34 +245,26 @@ def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) d author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] - follow_activity = - if is_map(activity.data["object"]) do - Activity.get_by_ap_id(activity.data["object"]["id"]) - else - Activity.get_by_ap_id(activity.data["object"]) - end - mentions = (activity.recipients || []) |> get_mentions + follow_activity = Activity.get_by_ap_id(follow_activity["id"]) - if follow_activity do - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, - {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']}, - {:id, h.(activity.data["id"])}, - {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, - {:content, [type: 'html'], - ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, - {:published, h.(inserted_at)}, - {:updated, h.(updated_at)}, - {:"activity:object", - [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, - {:id, h.(follow_activity.data["object"])}, - {:uri, h.(follow_activity.data["object"])} - ]}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []} - ] ++ mentions ++ author - end + [ + {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, + {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']}, + {:id, h.(activity.data["id"])}, + {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, + {:content, [type: 'html'], + ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, + {:published, h.(inserted_at)}, + {:updated, h.(updated_at)}, + {:"activity:object", + [ + {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, + {:id, h.(follow_activity.data["object"])}, + {:uri, h.(follow_activity.data["object"])} + ]}, + {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []} + ] ++ mentions ++ author end def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex new file mode 100644 index 000000000..a115bf4c8 --- /dev/null +++ b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex @@ -0,0 +1,17 @@ +defmodule Pleroma.Web.OStatus.UnfollowHandler do + alias Pleroma.Web.{XML, OStatus} + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.User + + def handle(entry, doc) do + with {:ok, actor} <- OStatus.find_make_or_update_user(doc), + id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), + followed_uri when not is_nil(followed_uri) <- + XML.string_from_xpath("/entry/activity:object/id", entry), + {:ok, followed} <- OStatus.find_or_make_user(followed_uri), + {:ok, activity} <- ActivityPub.unfollow(actor, followed, id, false) do + User.unfollow(actor, followed) + {:ok, activity} + end + end +end diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 5c4a1fd69..f0ff0624f 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.OStatus do alias Pleroma.{Repo, User, Web, Object, Activity} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.{WebFinger, Websub} - alias Pleroma.Web.OStatus.{FollowHandler, NoteHandler, DeleteHandler} + alias Pleroma.Web.OStatus.{FollowHandler, UnfollowHandler, NoteHandler, DeleteHandler} alias Pleroma.Web.ActivityPub.Transmogrifier def feed_path(user) do @@ -47,6 +47,9 @@ def handle_incoming(xml_string) do 'http://activitystrea.ms/schema/1.0/follow' -> with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity + 'http://activitystrea.ms/schema/1.0/unfollow' -> + with {:ok, activity} <- UnfollowHandler.handle(entry, doc), do: activity + 'http://activitystrea.ms/schema/1.0/share' -> with {:ok, activity, retweeted_activity} <- handle_share(entry, doc), do: [activity, retweeted_activity] diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index c2e1f07a5..57837205e 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -99,7 +99,7 @@ def to_map( ) do created_at = created_at |> Utils.date_to_asctime() - text = "#{user.nickname} undid the action at #{undid_activity}" + text = "#{user.nickname} undid the action at #{undid_activity["id"]}" %{ "id" => activity.id, diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 722e436e2..3ccdaed6f 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -36,14 +36,7 @@ def follow(%User{} = follower, params) do def unfollow(%User{} = follower, params) do with {:ok, %User{} = unfollowed} <- get_user(params), {:ok, follower, follow_activity} <- User.unfollow(follower, unfollowed), - {:ok, _activity} <- - ActivityPub.insert(%{ - "type" => "Undo", - "actor" => follower.ap_id, - # get latest Follow for these users - "object" => follow_activity.data["id"], - "published" => make_date() - }) do + {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed) do {:ok, follower, unfollowed} else err -> err @@ -52,7 +45,8 @@ def unfollow(%User{} = follower, params) do def block(%User{} = blocker, params) do with {:ok, %User{} = blocked} <- get_user(params), - {:ok, blocker} <- User.block(blocker, blocked) do + {:ok, blocker} <- User.block(blocker, blocked), + {:ok, _activity} <- ActivityPub.block(blocker, blocked) do {:ok, blocker, blocked} else err -> err @@ -61,7 +55,8 @@ def block(%User{} = blocker, params) do def unblock(%User{} = blocker, params) do with {:ok, %User{} = blocked} <- get_user(params), - {:ok, blocker} <- User.unblock(blocker, blocked) do + {:ok, blocker} <- User.unblock(blocker, blocked), + {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do {:ok, blocker, blocked} else err -> err diff --git a/test/fixtures/mastodon-block-activity.json b/test/fixtures/mastodon-block-activity.json new file mode 100644 index 000000000..186719d2c --- /dev/null +++ b/test/fixtures/mastodon-block-activity.json @@ -0,0 +1,29 @@ +{ + "type": "Block", + "signature": { + "type": "RsaSignature2017", + "signatureValue": "Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==", + "creator": "http://mastodon.example.org/users/admin#main-key", + "created": "2018-02-17T13:29:31Z" + }, + "object": "http://localtesting.pleroma.lol/users/lain", + "nickname": "lain", + "id": "http://mastodon.example.org/users/admin#follows/2", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} diff --git a/test/fixtures/mastodon-unblock-activity.json b/test/fixtures/mastodon-unblock-activity.json new file mode 100644 index 000000000..f4a2d30dd --- /dev/null +++ b/test/fixtures/mastodon-unblock-activity.json @@ -0,0 +1,34 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ], + "signature": { + "type": "RsaSignature2017", + "signatureValue": "Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==", + "creator": "http://mastodon.example.org/users/admin#main-key", + "created": "2018-02-17T13:29:31Z" + }, + "type": "Undo", + "object": { + "type": "Block", + "object": "http://localtesting.pleroma.lol/users/lain", + "nickname": "lain", + "id": "http://mastodon.example.org/users/admin#blocks/2", + "actor": "http://mastodon.example.org/users/admin" + }, + "actor": "http://mastodon.example.org/users/admin", + "id": "http://mastodon.example.org/users/admin#blocks/2/undo" +} diff --git a/test/fixtures/mastodon-unfollow-activity.json b/test/fixtures/mastodon-unfollow-activity.json new file mode 100644 index 000000000..ae5ab7c66 --- /dev/null +++ b/test/fixtures/mastodon-unfollow-activity.json @@ -0,0 +1,34 @@ +{ + "@context":[ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot":"http://joinmastodon.org/ns#", + "sensitive":"as:sensitive", + "ostatus":"http://ostatus.org#", + "movedTo":"as:movedTo", + "manuallyApprovesFollowers":"as:manuallyApprovesFollowers", + "inReplyToAtomUri":"ostatus:inReplyToAtomUri", + "conversation":"ostatus:conversation", + "atomUri":"ostatus:atomUri", + "Hashtag":"as:Hashtag", + "Emoji":"toot:Emoji" + } + ], + "signature":{ + "type":"RsaSignature2017", + "signatureValue":"Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==", + "creator":"http://mastodon.example.org/users/admin#main-key", + "created":"2018-02-17T13:29:31Z" + }, + "type":"Undo", + "object":{ + "type":"Follow", + "object":"http://localtesting.pleroma.lol/users/lain", + "nickname":"lain", + "id":"http://mastodon.example.org/users/admin#follows/2", + "actor":"http://mastodon.example.org/users/admin" + }, + "actor":"http://mastodon.example.org/users/admin", + "id": "http://mastodon.example.org/users/admin#follow/2/undo" +} diff --git a/test/fixtures/unfollow.xml b/test/fixtures/unfollow.xml new file mode 100644 index 000000000..7a8f8fd2e --- /dev/null +++ b/test/fixtures/unfollow.xml @@ -0,0 +1,68 @@ + + + GNU social + https://social.heldscal.la/api/statuses/user_timeline/23211.atom + lambadalambda timeline + Updates from lambadalambda on social.heldscal.la! + https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg + 2017-05-07T09:54:49+00:00 + + http://activitystrea.ms/schema/1.0/person + https://social.heldscal.la/user/23211 + lambadalambda + Call me Deacon Blues. + + + + + + lambadalambda + Constance Variable + Call me Deacon Blues. + + Berlin + + + homepage + https://heldscal.la + true + + + + + + + + + + + + + undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00 + Constance Variable (lambadalambda@social.heldscal.la)'s status on Sunday, 07-May-2017 09:54:49 UTC + <a href="https://social.heldscal.la/lambadalambda">Constance Variable</a> stopped following <a href="https://pawoo.net/@pekorino">mono</a>. + + http://activitystrea.ms/schema/1.0/unfollow + 2017-05-07T09:54:49+00:00 + 2017-05-07T09:54:49+00:00 + + http://activitystrea.ms/schema/1.0/person + https://pawoo.net/users/pekorino + mono + http://shitposter.club/mono 孤独のグルメ + + + + + pekorino + mono + http://shitposter.club/mono 孤独のグルメ + + + tag:social.heldscal.la,2017-05-07:objectType=thread:nonce=6e80caf94e03029f + + + + + + diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 9adce84b5..081c202b1 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -425,7 +425,40 @@ test "creates an undo activity for the last follow" do assert activity.data["type"] == "Undo" assert activity.data["actor"] == follower.ap_id - assert activity.data["object"] == follow_activity.data["id"] + + assert is_map(activity.data["object"]) + assert activity.data["object"]["type"] == "Follow" + assert activity.data["object"]["object"] == followed.ap_id + assert activity.data["object"]["id"] == follow_activity.data["id"] + end + end + + describe "blocking / unblocking" do + test "creates a block activity" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, activity} = ActivityPub.block(blocker, blocked) + + assert activity.data["type"] == "Block" + assert activity.data["actor"] == blocker.ap_id + assert activity.data["object"] == blocked.ap_id + end + + test "creates an undo activity for the last block" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, block_activity} = ActivityPub.block(blocker, blocked) + {:ok, activity} = ActivityPub.unblock(blocker, blocked) + + assert activity.data["type"] == "Undo" + assert activity.data["actor"] == blocker.ap_id + + assert is_map(activity.data["object"]) + assert activity.data["object"]["type"] == "Block" + assert activity.data["object"]["object"] == blocked.ap_id + assert activity.data["object"]["id"] == block_activity.data["id"] end end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index a0af75a69..cf6b1d0b5 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -315,6 +315,76 @@ test "it works for incoming unannounces with an existing notice" do assert data["object"]["id"] == "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" end + + test "it works for incomming unfollows with an existing follow" do + user = insert(:user) + + follow_data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) + + data = + File.read!("test/fixtures/mastodon-unfollow-activity.json") + |> Poison.decode!() + |> Map.put("object", follow_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Undo" + assert data["object"]["type"] == "Follow" + assert data["object"]["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + refute User.following?(User.get_by_ap_id(data["actor"]), user) + end + + test "it works for incoming blocks" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + blocker = User.get_by_ap_id(data["actor"]) + + assert User.blocks?(blocker, user) + end + + test "it works for incoming unblocks with an existing block" do + user = insert(:user) + + block_data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) + + data = + File.read!("test/fixtures/mastodon-unblock-activity.json") + |> Poison.decode!() + |> Map.put("object", block_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + assert data["type"] == "Undo" + assert data["object"]["type"] == "Block" + assert data["object"]["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + blocker = User.get_by_ap_id(data["actor"]) + + refute User.blocks?(blocker, user) + end end describe "prepare outgoing" do diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs index 3e7cf0155..f095e41dd 100644 --- a/test/web/ostatus/ostatus_test.exs +++ b/test/web/ostatus/ostatus_test.exs @@ -278,6 +278,30 @@ test "handle incoming follows" do assert User.following?(follower, followed) end + test "handle incoming unfollows with existing follow" do + incoming_follow = File.read!("test/fixtures/follow.xml") + {:ok, [_activity]} = OStatus.handle_incoming(incoming_follow) + + incoming = File.read!("test/fixtures/unfollow.xml") + {:ok, [activity]} = OStatus.handle_incoming(incoming) + + assert activity.data["type"] == "Undo" + + assert activity.data["id"] == + "undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00" + + assert activity.data["actor"] == "https://social.heldscal.la/user/23211" + assert is_map(activity.data["object"]) + assert activity.data["object"]["type"] == "Follow" + assert activity.data["object"]["object"] == "https://pawoo.net/users/pekorino" + refute activity.local + + follower = User.get_by_ap_id(activity.data["actor"]) + followed = User.get_by_ap_id(activity.data["object"]["object"]) + + refute User.following?(follower, followed) + end + describe "new remote user creation" do test "returns local users" do local_user = insert(:user) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 73443e053..c2ea41aa3 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -444,7 +444,7 @@ test "without valid credentials", %{conn: conn} do test "with credentials", %{conn: conn, user: current_user} do blocked = insert(:user) - {:ok, current_user} = User.block(current_user, blocked) + {:ok, current_user, blocked} = TwitterAPI.block(current_user, %{"user_id" => blocked.id}) assert User.blocks?(current_user, blocked) conn = diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index af565c0e5..4716abb84 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -166,7 +166,7 @@ test "Block another user using screen_name" do test "Unblock another user using user_id" do unblocked = insert(:user) user = insert(:user) - User.block(user, unblocked) + {:ok, user, _unblocked} = TwitterAPI.block(user, %{"user_id" => unblocked.id}) {:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"user_id" => unblocked.id}) assert user.info["blocks"] == [] @@ -175,7 +175,7 @@ test "Unblock another user using user_id" do test "Unblock another user using screen_name" do unblocked = insert(:user) user = insert(:user) - User.block(user, unblocked) + {:ok, user, _unblocked} = TwitterAPI.block(user, %{"screen_name" => unblocked.nickname}) {:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"screen_name" => unblocked.nickname}) assert user.info["blocks"] == []