Add safe dm mode option.
authorlain <lain@soykaf.club>
Wed, 20 Mar 2019 20:09:36 +0000 (21:09 +0100)
committerlain <lain@soykaf.club>
Wed, 20 Mar 2019 20:09:36 +0000 (21:09 +0100)
config/config.exs
docs/config.md
lib/pleroma/formatter.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/common_api/utils.ex
test/formatter_test.exs
test/web/common_api/common_api_test.exs

index ccdd357771dc0057c0f8723eaa53a60895dbae2c..b01c097c5d014e256dc1eae3553837e564384430 100644 (file)
@@ -174,7 +174,8 @@ config :pleroma, :instance,
   no_attachment_links: false,
   welcome_user_nickname: nil,
   welcome_message: nil,
-  max_report_comment_size: 1000
+  max_report_comment_size: 1000,
+  safe_dm_mentions: false
 
 config :pleroma, :markup,
   # XXX - unfortunately, inline images must be enabled by default right now, because
index 20118037359c7f9a2a00d64b127e25b2fe1a1d05..78967204baa32dcfd715c88d3505a12d3321b789 100644 (file)
@@ -101,7 +101,8 @@ config :pleroma, Pleroma.Mailer,
 * `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses
 * `welcome_message`: A message that will be send to a newly registered users as a direct message.
 * `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
-* `max_report_size`: The maximum size of the report comment (Default: `1000`)
+* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
+* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
 
 ## :logger
 * `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
index 1e4ede3f2c8fea4ba484a62ca2194208161cbef8..e3625383b010c7ba555bb1cffde1aea74dd32235 100644 (file)
@@ -8,6 +8,7 @@ defmodule Pleroma.Formatter do
   alias Pleroma.User
   alias Pleroma.Web.MediaProxy
 
+  @safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
   @markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
   @link_regex ~r{((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+}ui
   # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
@@ -45,15 +46,28 @@ defmodule Pleroma.Formatter do
 
   @doc """
   Parses a text and replace plain text links with HTML. Returns a tuple with a result text, mentions, and hashtags.
+
+  If the 'safe_mention' option is given, only consecutive mentions at the start the post are actually mentioned.
   """
   @spec linkify(String.t(), keyword()) ::
           {String.t(), [{String.t(), User.t()}], [{String.t(), String.t()}]}
   def linkify(text, options \\ []) do
     options = options ++ @auto_linker_config
-    acc = %{mentions: MapSet.new(), tags: MapSet.new()}
-    {text, %{mentions: mentions, tags: tags}} = AutoLinker.link_map(text, acc, options)
 
-    {text, MapSet.to_list(mentions), MapSet.to_list(tags)}
+    if options[:safe_mention] && Regex.named_captures(@safe_mention_regex, text) do
+      %{"mentions" => mentions, "rest" => rest} = Regex.named_captures(@safe_mention_regex, text)
+      acc = %{mentions: MapSet.new(), tags: MapSet.new()}
+
+      {text_mentions, %{mentions: mentions}} = AutoLinker.link_map(mentions, acc, options)
+      {text_rest, %{tags: tags}} = AutoLinker.link_map(rest, acc, options)
+
+      {text_mentions <> text_rest, MapSet.to_list(mentions), MapSet.to_list(tags)}
+    else
+      acc = %{mentions: MapSet.new(), tags: MapSet.new()}
+      {text, %{mentions: mentions, tags: tags}} = AutoLinker.link_map(text, acc, options)
+
+      {text, MapSet.to_list(mentions), MapSet.to_list(tags)}
+    end
   end
 
   def emojify(text) do
index b5f79c3bf375a05ac00372a4e242753f1623caaa..50d60aade29d110b76cf860fc2d405cf532c5ebd 100644 (file)
@@ -142,7 +142,8 @@ defmodule Pleroma.Web.CommonAPI do
            make_content_html(
              status,
              attachments,
-             data
+             data,
+             visibility
            ),
          {to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
          context <- make_context(in_reply_to),
index b7513ef284991428881ec2297cfb8cc74466623e..3689454182a37680738fd5d8a5c796046c77a085 100644 (file)
@@ -101,7 +101,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   def make_content_html(
         status,
         attachments,
-        data
+        data,
+        visibility
       ) do
     no_attachment_links =
       data
@@ -110,8 +111,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do
 
     content_type = get_content_type(data["content_type"])
 
+    options =
+      if visibility == "direct" && Config.get([:instance, :safe_dm_mentions]) do
+        [safe_mention: true]
+      else
+        []
+      end
+
     status
-    |> format_input(content_type)
+    |> format_input(content_type, options)
     |> maybe_add_attachments(attachments, no_attachment_links)
     |> maybe_add_nsfw_tag(data)
   end
index 7d8864bf44d929e75a28fc401155d4bafc351aa0..fcdf931b7268443d1ef80e22e0aca626a6724191 100644 (file)
@@ -181,6 +181,31 @@ defmodule Pleroma.FormatterTest do
       expected_text = "@a hi"
       assert {^expected_text, [] = _mentions, [] = _tags} = Formatter.linkify(text)
     end
+
+    test "given the 'safe_mention' option, it will only mention people in the beginning" do
+      user = insert(:user)
+      _other_user = insert(:user)
+      third_user = insert(:user)
+      text = " @#{user.nickname} hey dude i hate @#{third_user.nickname}"
+      {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true)
+
+      assert mentions == [{"@#{user.nickname}", user}]
+
+      assert expected_text ==
+               "<span class='h-card'><a data-user='#{user.id}' class='u-url mention' href='#{
+                 user.ap_id
+               }'>@<span>#{user.nickname}</span></a></span> hey dude i hate <span class='h-card'><a data-user='#{
+                 third_user.id
+               }' class='u-url mention' href='#{third_user.ap_id}'>@<span>#{third_user.nickname}</span></a></span>"
+    end
+
+    test "given the 'safe_mention' option, it will still work without any mention" do
+      text = "A post without any mention"
+      {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true)
+
+      assert mentions == []
+      assert expected_text == text
+    end
   end
 
   describe ".parse_tags" do
index f83f80b4037502b9d23cf186b754598c7b356c43..34aa5bf1884f949c9a8642ffba4a5dc2c07cb6f6 100644 (file)
@@ -10,6 +10,24 @@ defmodule Pleroma.Web.CommonAPITest do
 
   import Pleroma.Factory
 
+  test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
+    har = insert(:user)
+    jafnhar = insert(:user)
+    tridi = insert(:user)
+    option = Pleroma.Config.get([:instance, :safe_dm_mentions])
+    Pleroma.Config.put([:instance, :safe_dm_mentions], true)
+
+    {:ok, activity} =
+      CommonAPI.post(har, %{
+        "status" => "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
+        "visibility" => "direct"
+      })
+
+    refute tridi.ap_id in activity.recipients
+    assert jafnhar.ap_id in activity.recipients
+    Pleroma.Config.put([:instance, :safe_dm_mentions], option)
+  end
+
   test "it de-duplicates tags" do
     user = insert(:user)
     {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})