260 lines
7.3 KiB
Elixir
260 lines
7.3 KiB
Elixir
defmodule Mobilizon.Service.AntiSpam.Akismet do
|
|
@moduledoc """
|
|
Validate user data
|
|
"""
|
|
|
|
alias Exkismet.Comment, as: AkismetComment
|
|
alias Mobilizon.{Actors, Discussions, Events, Users}
|
|
alias Mobilizon.Actors.Actor
|
|
alias Mobilizon.Discussions.Comment
|
|
alias Mobilizon.Events.Event
|
|
alias Mobilizon.Reports.Report
|
|
alias Mobilizon.Service.AntiSpam.Provider
|
|
alias Mobilizon.Users.User
|
|
alias Mobilizon.Web.Endpoint
|
|
require Logger
|
|
|
|
@behaviour Provider
|
|
|
|
@env Application.compile_env(:mobilizon, :env)
|
|
|
|
@impl Provider
|
|
@spec check_user(String.t(), String.t(), String.t()) ::
|
|
:ham | :spam | :discard | {:error, HTTPoison.Response.t()}
|
|
def check_user(email, ip, user_agent) do
|
|
check_content(%AkismetComment{
|
|
blog: homepage(),
|
|
user_ip: ip,
|
|
comment_author_email: email,
|
|
user_agent: user_agent,
|
|
comment_type: "signup"
|
|
})
|
|
end
|
|
|
|
@impl Provider
|
|
@spec check_profile(String.t(), String.t(), String.t() | nil, String.t(), String.t()) ::
|
|
:ham | :spam | :discard | {:error, HTTPoison.Response.t()}
|
|
def check_profile(username, summary, email \\ nil, ip \\ "127.0.0.1", user_agent \\ nil) do
|
|
check_content(%AkismetComment{
|
|
blog: homepage(),
|
|
user_ip: ip,
|
|
comment_author: username,
|
|
comment_author_email: email,
|
|
comment_content: summary,
|
|
user_agent: user_agent,
|
|
comment_type: "signup"
|
|
})
|
|
end
|
|
|
|
@impl Provider
|
|
@spec check_event(String.t(), String.t(), String.t() | nil, String.t(), String.t()) ::
|
|
:ham | :spam | :discard | {:error, HTTPoison.Response.t()}
|
|
def check_event(event_body, username, email \\ nil, ip \\ "127.0.0.1", user_agent \\ nil) do
|
|
check_content(%AkismetComment{
|
|
blog: homepage(),
|
|
user_ip: ip,
|
|
comment_author: username,
|
|
comment_author_email: email,
|
|
comment_content: event_body,
|
|
user_agent: user_agent,
|
|
comment_type: "blog-post"
|
|
})
|
|
end
|
|
|
|
@impl Provider
|
|
@spec check_comment(String.t(), String.t(), boolean(), String.t() | nil, String.t(), String.t()) ::
|
|
:ham | :spam | :discard | {:error, HTTPoison.Response.t()}
|
|
def check_comment(
|
|
comment_body,
|
|
username,
|
|
is_reply?,
|
|
email \\ nil,
|
|
ip \\ "127.0.0.1",
|
|
user_agent \\ nil
|
|
) do
|
|
check_content(%AkismetComment{
|
|
blog: homepage(),
|
|
user_ip: ip,
|
|
comment_author: username,
|
|
comment_author_email: email,
|
|
comment_content: comment_body,
|
|
user_agent: user_agent,
|
|
comment_type: if(is_reply?, do: "reply", else: "comment")
|
|
})
|
|
end
|
|
|
|
@spec report_ham(Report.t()) :: :ok | {:error, atom()} | {:error, HTTPoison.Response.t()}
|
|
def report_ham(%Report{} = report) do
|
|
report
|
|
|> report_to_akismet_comment()
|
|
|> submit_ham()
|
|
end
|
|
|
|
@spec report_spam(Report.t()) :: :ok | {:error, atom()} | {:error, HTTPoison.Response.t()}
|
|
def report_spam(%Report{} = report) do
|
|
report
|
|
|> report_to_akismet_comment()
|
|
|> submit_spam()
|
|
end
|
|
|
|
@spec homepage() :: String.t()
|
|
defp homepage do
|
|
Endpoint.url()
|
|
end
|
|
|
|
defp check_content(%AkismetComment{} = comment) do
|
|
if @env != :test and ready?() do
|
|
comment
|
|
|> Exkismet.comment_check(key: api_key())
|
|
else
|
|
:ham
|
|
end
|
|
end
|
|
|
|
defp api_key do
|
|
Application.get_env(:mobilizon, __MODULE__) |> get_in([:key])
|
|
end
|
|
|
|
@impl Provider
|
|
def ready?, do: !is_nil(api_key())
|
|
|
|
@spec report_to_akismet_comment(Report.t()) :: AkismetComment.t() | {:error, atom()}
|
|
defp report_to_akismet_comment(%Report{comments: [comment | _]}) do
|
|
with %Comment{text: body, actor: %Actor{} = actor} <-
|
|
Discussions.get_comment_with_preload(comment.id),
|
|
{email, preferred_username, ip} <- actor_details(actor) do
|
|
%AkismetComment{
|
|
blog: homepage(),
|
|
comment_content: body,
|
|
comment_author_email: email,
|
|
comment_author: preferred_username,
|
|
user_ip: ip
|
|
}
|
|
else
|
|
{:error, err} ->
|
|
{:error, err}
|
|
|
|
err ->
|
|
{:error, err}
|
|
end
|
|
end
|
|
|
|
defp report_to_akismet_comment(%Report{events: [%Event{id: event_id} | _]}) do
|
|
with %Event{description: body, organizer_actor: %Actor{} = actor} <-
|
|
Events.get_event_with_preload!(event_id),
|
|
{email, preferred_username, ip} <- actor_details(actor) do
|
|
%AkismetComment{
|
|
blog: homepage(),
|
|
comment_content: body,
|
|
comment_author_email: email,
|
|
comment_author: preferred_username,
|
|
user_ip: ip
|
|
}
|
|
else
|
|
{:error, err} ->
|
|
{:error, err}
|
|
|
|
err ->
|
|
{:error, err}
|
|
end
|
|
end
|
|
|
|
defp report_to_akismet_comment(%Report{reported_id: reported_id}) do
|
|
case reported_id |> Actors.get_actor_with_preload!() |> actor_details() do
|
|
{email, preferred_username, ip} ->
|
|
%AkismetComment{
|
|
blog: homepage(),
|
|
comment_author_email: email,
|
|
comment_author: preferred_username,
|
|
user_ip: ip
|
|
}
|
|
|
|
{:error, err} ->
|
|
{:error, err}
|
|
|
|
err ->
|
|
{:error, err}
|
|
end
|
|
end
|
|
|
|
@spec actor_details(Actor.t()) :: {String.t(), String.t(), any()} | {:error, :invalid_actor}
|
|
defp actor_details(%Actor{
|
|
type: :Person,
|
|
preferred_username: preferred_username,
|
|
user: %User{
|
|
current_sign_in_ip: current_sign_in_ip,
|
|
last_sign_in_ip: last_sign_in_ip,
|
|
email: email
|
|
}
|
|
}) do
|
|
{email, preferred_username, current_sign_in_ip || last_sign_in_ip}
|
|
end
|
|
|
|
defp actor_details(%Actor{
|
|
type: :Person,
|
|
preferred_username: preferred_username,
|
|
user_id: user_id
|
|
})
|
|
when not is_nil(user_id) do
|
|
case user_id |> Users.get_user() |> user_details() do
|
|
{email, ip} ->
|
|
{preferred_username, email, ip}
|
|
|
|
_ ->
|
|
{:error, :invalid_actor}
|
|
end
|
|
end
|
|
|
|
defp actor_details(%Actor{
|
|
type: :Person,
|
|
preferred_username: preferred_username,
|
|
user_id: nil
|
|
}) do
|
|
{nil, preferred_username, "127.0.0.1"}
|
|
end
|
|
|
|
defp actor_details(_) do
|
|
{:error, :invalid_actor}
|
|
end
|
|
|
|
@spec user_details(User.t()) :: {String.t(), any()} | {:error, :user_not_found}
|
|
defp user_details(%User{
|
|
current_sign_in_ip: current_sign_in_ip,
|
|
last_sign_in_ip: last_sign_in_ip,
|
|
email: email
|
|
}) do
|
|
{email, current_sign_in_ip || last_sign_in_ip}
|
|
end
|
|
|
|
defp user_details(_), do: {:error, :user_not_found}
|
|
|
|
@spec submit_spam(AkismetComment.t() | :error) ::
|
|
:ok | {:error, atom()} | {:error, HTTPoison.Response.t()}
|
|
defp submit_spam(%AkismetComment{} = comment) do
|
|
comment
|
|
|> tap(fn comment ->
|
|
Logger.info("Submitting content to Akismet as spam: #{inspect(comment)}")
|
|
end)
|
|
|> Exkismet.submit_spam(key: api_key())
|
|
|> log_response()
|
|
end
|
|
|
|
defp submit_spam({:error, err}), do: {:error, err}
|
|
|
|
@spec submit_ham(AkismetComment.t() | :error) ::
|
|
:ok | {:error, atom()} | {:error, HTTPoison.Response.t()}
|
|
defp submit_ham(%AkismetComment{} = comment) do
|
|
comment
|
|
|> tap(fn comment ->
|
|
Logger.info("Submitting content to Akismet as ham: #{inspect(comment)}")
|
|
end)
|
|
|> Exkismet.submit_ham(key: api_key())
|
|
|> log_response()
|
|
end
|
|
|
|
defp submit_ham({:error, err}), do: {:error, err}
|
|
|
|
defp log_response(res),
|
|
do: tap(res, fn res -> Logger.debug("Return from Akismet is: #{inspect(res)}") end)
|
|
end
|