forked from AkkomaGang/akkoma
Merge branch 'safe-mentions' into 'develop'
Add safe dm mode option. See merge request pleroma/pleroma!958
This commit is contained in:
commit
1c3d3d0c2b
9 changed files with 102 additions and 9 deletions
|
@ -172,7 +172,8 @@
|
||||||
no_attachment_links: false,
|
no_attachment_links: false,
|
||||||
welcome_user_nickname: nil,
|
welcome_user_nickname: nil,
|
||||||
welcome_message: nil,
|
welcome_message: nil,
|
||||||
max_report_comment_size: 1000
|
max_report_comment_size: 1000,
|
||||||
|
safe_dm_mentions: false
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
|
|
@ -101,7 +101,8 @@ config :pleroma, Pleroma.Mailer,
|
||||||
* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses
|
* `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_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.
|
* `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
|
## :logger
|
||||||
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
|
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Formatter do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
|
||||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
@link_regex ~r{((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+}ui
|
@link_regex ~r{((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+}ui
|
||||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||||
|
@ -45,15 +46,28 @@ def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Parses a text and replace plain text links with HTML. Returns a tuple with a result text, mentions, and hashtags.
|
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()) ::
|
@spec linkify(String.t(), keyword()) ::
|
||||||
{String.t(), [{String.t(), User.t()}], [{String.t(), String.t()}]}
|
{String.t(), [{String.t(), User.t()}], [{String.t(), String.t()}]}
|
||||||
def linkify(text, options \\ []) do
|
def linkify(text, options \\ []) do
|
||||||
options = options ++ @auto_linker_config
|
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
|
end
|
||||||
|
|
||||||
def emojify(text) do
|
def emojify(text) do
|
||||||
|
|
|
@ -142,7 +142,8 @@ def post(user, %{"status" => status} = data) do
|
||||||
make_content_html(
|
make_content_html(
|
||||||
status,
|
status,
|
||||||
attachments,
|
attachments,
|
||||||
data
|
data,
|
||||||
|
visibility
|
||||||
),
|
),
|
||||||
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
||||||
context <- make_context(in_reply_to),
|
context <- make_context(in_reply_to),
|
||||||
|
|
|
@ -101,7 +101,8 @@ def to_for_user_and_mentions(_user, mentions, inReplyTo, "direct") do
|
||||||
def make_content_html(
|
def make_content_html(
|
||||||
status,
|
status,
|
||||||
attachments,
|
attachments,
|
||||||
data
|
data,
|
||||||
|
visibility
|
||||||
) do
|
) do
|
||||||
no_attachment_links =
|
no_attachment_links =
|
||||||
data
|
data
|
||||||
|
@ -110,8 +111,15 @@ def make_content_html(
|
||||||
|
|
||||||
content_type = get_content_type(data["content_type"])
|
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
|
status
|
||||||
|> format_input(content_type)
|
|> format_input(content_type, options)
|
||||||
|> maybe_add_attachments(attachments, no_attachment_links)
|
|> maybe_add_attachments(attachments, no_attachment_links)
|
||||||
|> maybe_add_nsfw_tag(data)
|
|> maybe_add_nsfw_tag(data)
|
||||||
end
|
end
|
||||||
|
|
|
@ -197,7 +197,9 @@ def config(conn, _params) do
|
||||||
vapidPublicKey: vapid_public_key,
|
vapidPublicKey: vapid_public_key,
|
||||||
accountActivationRequired:
|
accountActivationRequired:
|
||||||
if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
|
if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
|
||||||
invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0")
|
invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
|
||||||
|
safeDMMentionsEnabled:
|
||||||
|
if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
|
||||||
}
|
}
|
||||||
|
|
||||||
pleroma_fe =
|
pleroma_fe =
|
||||||
|
|
|
@ -181,6 +181,31 @@ test "does not give a replacement for single-character local nicknames who don't
|
||||||
expected_text = "@a hi"
|
expected_text = "@a hi"
|
||||||
assert {^expected_text, [] = _mentions, [] = _tags} = Formatter.linkify(text)
|
assert {^expected_text, [] = _mentions, [] = _tags} = Formatter.linkify(text)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe ".parse_tags" do
|
describe ".parse_tags" do
|
||||||
|
|
|
@ -10,6 +10,24 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
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
|
test "it de-duplicates tags" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
|
||||||
|
|
|
@ -75,6 +75,29 @@ test "it marks a single notification as read", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/statusnet/config.json" do
|
describe "GET /api/statusnet/config.json" do
|
||||||
|
test "returns the state of safe_dm_mentions flag", %{conn: conn} do
|
||||||
|
option = Pleroma.Config.get([:instance, :safe_dm_mentions])
|
||||||
|
Pleroma.Config.put([:instance, :safe_dm_mentions], true)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/statusnet/config.json")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert response["site"]["safeDMMentionsEnabled"] == "1"
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :safe_dm_mentions], false)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/statusnet/config.json")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert response["site"]["safeDMMentionsEnabled"] == "0"
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :safe_dm_mentions], option)
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns the managed config", %{conn: conn} do
|
test "it returns the managed config", %{conn: conn} do
|
||||||
Pleroma.Config.put([:instance, :managed_config], false)
|
Pleroma.Config.put([:instance, :managed_config], false)
|
||||||
Pleroma.Config.put([:fe], theme: "rei-ayanami-towel")
|
Pleroma.Config.put([:fe], theme: "rei-ayanami-towel")
|
||||||
|
|
Loading…
Reference in a new issue