From 699224a900d54b6d32e0bd3f2abd9eccc523df11 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Mon, 7 Sep 2020 22:14:40 +0200
Subject: [PATCH 01/11] ForceBotUnlistedPolicy: initial add, tiny clean up from
 my previous version

---
 .../mrf/force_bot_unlisted_policy.ex          | 61 +++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex

diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
new file mode 100644
index 000000000..31fd90586
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
@@ -0,0 +1,61 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
+  alias Pleroma.User
+  @behaviour Pleroma.Web.ActivityPub.MRF
+  @moduledoc "Remove bot posts from federated timeline"
+
+  require Pleroma.Constants
+
+  defp check_by_actor_type(user) do
+    if user.actor_type in ["Application", "Service"], do: 1.0, else: 0.0
+  end
+
+  defp check_by_nickname(user) do
+    if Regex.match?(~r/bot@|ebooks@/i, user.nickname), do: 1.0, else: 0.0
+  end
+
+  defp botness_score(user), do: check_by_actor_type(user) + check_by_nickname(user)
+
+  @impl true
+  def filter(
+         %{
+           "type" => "Create",
+           "to" => to,
+           "cc" => cc,
+           "actor" => actor,
+           "object" => object
+         } = message
+       ) do
+    user = User.get_cached_by_ap_id(actor)
+    isbot = 0.8 < botness_score(user)
+
+    if isbot and Enum.member?(to, Pleroma.Constants.as_public()) do
+      to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
+      cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
+
+      object =
+        object
+        |> Map.put("to", to)
+        |> Map.put("cc", cc)
+
+      message =
+        message
+        |> Map.put("to", to)
+        |> Map.put("cc", cc)
+        |> Map.put("object", object)
+
+      {:ok, message}
+    else
+      {:ok, message}
+    end
+  end
+
+  @impl true
+  def filter(message), do: {:ok, message}
+
+  @impl true
+  def describe, do: {:ok, %{}}
+end

From 57cf0cc3b3029cb0ff017c53e2602ad945b8d9b3 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Mon, 7 Sep 2020 22:50:37 +0200
Subject: [PATCH 02/11] ForceBotUnlistedPolicy: add test

---
 .../mrf/force_bot_unlisted_policy_test.ex     | 58 +++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex

diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex
new file mode 100644
index 000000000..84e2a9024
--- /dev/null
+++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex
@@ -0,0 +1,58 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+
+  import Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy
+
+  defp generate_messages(actor) do
+    {%{
+      "actor" => actor.ap_id,
+      "type" => "Create",
+      "object" => %{},
+      "to" => [@public, "f"],
+      "cc" => [actor.follower_address, "d"]
+    }, %{
+      "actor" => actor.ap_id,
+      "type" => "Create",
+      "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+      "to" => ["f", actor.follower_address],
+      "cc" => ["d", @public]
+    }}
+  end
+
+  test "removes from the federated timeline by nickname heuristics 1" do
+    actor = insert(:user, %{nickname: "annoying_ebooks@example.com"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+
+  test "removes from the federated timeline by nickname heuristics 2" do
+    actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+
+  test "removes from the federated timeline by actor type Application" do
+    actor = insert(:user, %{actor_type: "Application"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+
+  test "removes from the federated timeline by actor type Service" do
+    actor = insert(:user, %{actor_type: "Service"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+end

From 8b695c3eeb6ee7a91fc5a8a4293fb3cb53212818 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Mon, 7 Sep 2020 22:53:45 +0200
Subject: [PATCH 03/11] ForceBotUnlistedPolicy: format

---
 .../mrf/force_bot_unlisted_policy.ex          | 16 ++++++------
 .../mrf/force_bot_unlisted_policy_test.ex     | 25 ++++++++++---------
 2 files changed, 21 insertions(+), 20 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
index 31fd90586..7290f444b 100644
--- a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
@@ -21,14 +21,14 @@ defp botness_score(user), do: check_by_actor_type(user) + check_by_nickname(user
 
   @impl true
   def filter(
-         %{
-           "type" => "Create",
-           "to" => to,
-           "cc" => cc,
-           "actor" => actor,
-           "object" => object
-         } = message
-       ) do
+        %{
+          "type" => "Create",
+          "to" => to,
+          "cc" => cc,
+          "actor" => actor,
+          "object" => object
+        } = message
+      ) do
     user = User.get_cached_by_ap_id(actor)
     isbot = 0.8 < botness_score(user)
 
diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex
index 84e2a9024..6f001c233 100644
--- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex
+++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex
@@ -10,18 +10,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
 
   defp generate_messages(actor) do
     {%{
-      "actor" => actor.ap_id,
-      "type" => "Create",
-      "object" => %{},
-      "to" => [@public, "f"],
-      "cc" => [actor.follower_address, "d"]
-    }, %{
-      "actor" => actor.ap_id,
-      "type" => "Create",
-      "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
-      "to" => ["f", actor.follower_address],
-      "cc" => ["d", @public]
-    }}
+       "actor" => actor.ap_id,
+       "type" => "Create",
+       "object" => %{},
+       "to" => [@public, "f"],
+       "cc" => [actor.follower_address, "d"]
+     },
+     %{
+       "actor" => actor.ap_id,
+       "type" => "Create",
+       "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+       "to" => ["f", actor.follower_address],
+       "cc" => ["d", @public]
+     }}
   end
 
   test "removes from the federated timeline by nickname heuristics 1" do

From d2fd1d348122d8b0c3f4b0262cfc980afed83448 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Mon, 7 Sep 2020 23:04:07 +0200
Subject: [PATCH 04/11] ForceBotUnlistedPolicy: fix test extension

---
 ...unlisted_policy_test.ex => force_bot_unlisted_policy_test.exs} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename test/web/activity_pub/mrf/{force_bot_unlisted_policy_test.ex => force_bot_unlisted_policy_test.exs} (100%)

diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
similarity index 100%
rename from test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex
rename to test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs

From 0a25c92cfafce6af9ff9a40ac278dafac69e0c08 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Mon, 7 Sep 2020 23:18:36 +0200
Subject: [PATCH 05/11] ForceBotUnlistedPolicy: try to fix test

---
 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
index 6f001c233..85ca95b0a 100644
--- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
+++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
@@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
   use Pleroma.DataCase
   import Pleroma.Factory
 
-  import Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy
+  alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy
 
   defp generate_messages(actor) do
     {%{

From d074e54013cb38d308693891e0353c0dccc54b85 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Mon, 7 Sep 2020 23:28:29 +0200
Subject: [PATCH 06/11] ForceBotUnlistedPolicy: try to fix test 2

---
 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
index 85ca95b0a..86dd9ddae 100644
--- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
+++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
   import Pleroma.Factory
 
   alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy
+  @public "https://www.w3.org/ns/activitystreams#Public"
 
   defp generate_messages(actor) do
     {%{

From 95688c90ad9cd6438a764b4ea6e0f2e3b594b5c8 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Tue, 8 Sep 2020 01:13:49 +0200
Subject: [PATCH 07/11] ForceBotUnlistedPolicy: simplify code

---
 .../activity_pub/mrf/force_bot_unlisted_policy.ex   | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
index 7290f444b..ea9c3d3f5 100644
--- a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
@@ -9,15 +9,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
 
   require Pleroma.Constants
 
-  defp check_by_actor_type(user) do
-    if user.actor_type in ["Application", "Service"], do: 1.0, else: 0.0
-  end
+  defp check_by_actor_type(user), do: user.actor_type in ["Application", "Service"]
+  defp check_by_nickname(user), do: Regex.match?(~r/bot@|ebooks@/i, user.nickname)
 
-  defp check_by_nickname(user) do
-    if Regex.match?(~r/bot@|ebooks@/i, user.nickname), do: 1.0, else: 0.0
-  end
-
-  defp botness_score(user), do: check_by_actor_type(user) + check_by_nickname(user)
+  defp check_if_bot(user), do: check_by_actor_type(user) or check_by_nickname(user)
 
   @impl true
   def filter(
@@ -30,7 +25,7 @@ def filter(
         } = message
       ) do
     user = User.get_cached_by_ap_id(actor)
-    isbot = 0.8 < botness_score(user)
+    isbot = check_if_bot(user)
 
     if isbot and Enum.member?(to, Pleroma.Constants.as_public()) do
       to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]

From 7215563641e2a5096293128519d6a454aabc46f2 Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Tue, 8 Sep 2020 11:32:46 +0000
Subject: [PATCH 08/11] description.exs: add ForceBotUnlistedPolicy

---
 config/description.exs | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/config/description.exs b/config/description.exs
index 18c133f02..2191e8d6d 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -1669,6 +1669,15 @@
       }
     ]
   },
+  %{
+    group: :pleroma,
+    key: :mrf_force_bot_unlisted,
+    tab: :mrf,
+    related_policy: "Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy",
+    label: "MRF Force Bot Unlisted Policy",
+    type: :boolean,
+    description: "Makes bot posts to disappear from public timelines"
+  },
   %{
     group: :pleroma,
     key: :mrf_subchain,

From efff2caccccae76dd749df67322c61a70957268b Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Tue, 8 Sep 2020 11:34:04 +0000
Subject: [PATCH 09/11] docs: cheatsheet: add ForceBotUnlistedPolicy

---
 docs/configuration/cheatsheet.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index a9a650fab..2ca4d7351 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -115,6 +115,7 @@ To add configuration to your config file, you can copy it from the base config.
     * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
     * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
     * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.ActivityExpiration` to be enabled for processing the scheduled delections.
+    * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
 * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
 * `transparency_exclusions`: Exclude specific instance names from MRF transparency.  The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
 

From 5d814f739cd2bb55822e0cfdb9ad2526d10482bb Mon Sep 17 00:00:00 2001
From: Alibek Omarov <a1ba.omarov@gmail.com>
Date: Tue, 8 Sep 2020 11:35:34 +0000
Subject: [PATCH 10/11] changelog: add ForceBotUnlistedPolicy

---
 CHANGELOG.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46f1e859b..1b1ea02ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## Unreleased
 
+### Added
+
+- MRF policy to rewrite bot posts scope from public to unlisted
+
 ### Removed
 
 - **Breaking:** Removed `Pleroma.Workers.Cron.StatsWorker` setting from Oban `:crontab`.

From bb0d7b7aaad4f441a14c94c56081ec30c96ea799 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@FreeBSD.org>
Date: Tue, 8 Sep 2020 09:31:47 -0500
Subject: [PATCH 11/11] Revert "description.exs: add ForceBotUnlistedPolicy"

This reverts commit 7215563641e2a5096293128519d6a454aabc46f2.
---
 config/description.exs | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/config/description.exs b/config/description.exs
index d9f9b6b84..eac97ad64 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -1669,15 +1669,6 @@
       }
     ]
   },
-  %{
-    group: :pleroma,
-    key: :mrf_force_bot_unlisted,
-    tab: :mrf,
-    related_policy: "Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy",
-    label: "MRF Force Bot Unlisted Policy",
-    type: :boolean,
-    description: "Makes bot posts to disappear from public timelines"
-  },
   %{
     group: :pleroma,
     key: :mrf_subchain,