ソースを参照

Merge branch 'feature/digest-email' into 'develop'

Feature/digest email

See merge request pleroma/pleroma!1078
tags/v1.0.90
lain 6ヶ月前
コミット
29807ef6a5
33個のファイルの変更658行の追加17行の削除
  1. +2
    -0
      CHANGELOG.md
  2. +8
    -0
      config/config.exs
  3. +2
    -0
      config/test.exs
  4. +12
    -0
      docs/config.md
  5. +33
    -0
      lib/mix/tasks/pleroma/digest.ex
  6. +2
    -0
      lib/mix/tasks/pleroma/instance.ex
  7. +16
    -1
      lib/pleroma/application.ex
  8. +35
    -0
      lib/pleroma/digest_email_worker.ex
  9. +70
    -1
      lib/pleroma/emails/user_email.ex
  10. +9
    -0
      lib/pleroma/jwt.ex
  11. +26
    -2
      lib/pleroma/notification.ex
  12. +75
    -0
      lib/pleroma/user.ex
  13. +25
    -0
      lib/pleroma/user/info.ex
  14. +20
    -0
      lib/pleroma/web/mailer/subscription_controller.ex
  15. +2
    -0
      lib/pleroma/web/router.ex
  16. +20
    -0
      lib/pleroma/web/templates/email/digest.html.eex
  17. +10
    -0
      lib/pleroma/web/templates/layout/email.html.eex
  18. +1
    -0
      lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
  19. +1
    -0
      lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
  20. +5
    -0
      lib/pleroma/web/views/email_view.ex
  21. +3
    -0
      lib/pleroma/web/views/mailer/subscription_view.ex
  22. +3
    -1
      mix.exs
  23. +9
    -8
      mix.lock
  24. +20
    -0
      priv/repo/migrations/20190412052952_add_user_info_fields.exs
  25. +9
    -0
      priv/repo/migrations/20190413085040_add_signin_and_last_digest_dates_to_user.exs
  26. +2
    -0
      priv/templates/sample_config.eex
  27. +51
    -0
      test/mix/tasks/pleroma.digest_test.exs
  28. +48
    -1
      test/notification_test.exs
  29. +2
    -1
      test/support/builders/user_builder.ex
  30. +2
    -1
      test/support/factory.ex
  31. +24
    -0
      test/user_info_test.exs
  32. +8
    -1
      test/user_search_test.exs
  33. +103
    -0
      test/user_test.exs

+ 2
- 0
CHANGELOG.md ファイルの表示

@@ -93,6 +93,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rich media: Do not crawl private IP ranges

### Added
- Digest email for inactive users
- Add a generic settings store for frontends / clients to use.
- Explicit addressing option for posting.
- Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).
@@ -119,6 +120,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `notify_email` option
- Configuration: Media proxy `whitelist` option
- Configuration: `report_uri` option
- Configuration: `email_notifications` option
- Configuration: `limit_to_local_content` option
- Pleroma API: User subscriptions
- Pleroma API: Healthcheck endpoint


+ 8
- 0
config/config.exs ファイルの表示

@@ -514,6 +514,14 @@ config :pleroma, Pleroma.ScheduledActivity,
total_user_limit: 300,
enabled: true

config :pleroma, :email_notifications,
digest: %{
active: false,
schedule: "0 0 * * 0",
interval: 7,
inactivity_threshold: 7
}

config :pleroma, :oauth2,
token_expires_in: 600,
issue_new_refresh_token: true,


+ 2
- 0
config/test.exs ファイルの表示

@@ -81,6 +81,8 @@ rum_enabled = System.get_env("RUM_ENABLED") == "true"
config :pleroma, :database, rum_enabled: rum_enabled
IO.puts("RUM enabled: #{rum_enabled}")

config :joken, default_signer: "yU8uHKq+yyAkZ11Hx//jcdacWc8yQ1bxAAGrplzB0Zwwjkp35v0RK9SO8WTPr6QZ"

config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock

try do


+ 12
- 0
docs/config.md ファイルの表示

@@ -536,6 +536,18 @@ Authentication / authorization settings.
* `oauth_consumer_template`: OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex`.
* `oauth_consumer_strategies`: the list of enabled OAuth consumer strategies; by default it's set by `OAUTH_CONSUMER_STRATEGIES` environment variable. Each entry in this space-delimited string should be of format `<strategy>` or `<strategy>:<dependency>` (e.g. `twitter` or `keycloak:ueberauth_keycloak_strategy` in case dependency is named differently than `ueberauth_<strategy>`).

## :email_notifications

Email notifications settings.

- digest - emails of "what you've missed" for users who have been
inactive for a while.
- active: globally enable or disable digest emails
- schedule: When to send digest email, in [crontab format](https://en.wikipedia.org/wiki/Cron).
"0 0 * * 0" is the default, meaning "once a week at midnight on Sunday morning"
- interval: Minimum interval between digest emails to one user
- inactivity_threshold: Minimum user inactivity threshold

## OAuth consumer mode

OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.).


+ 33
- 0
lib/mix/tasks/pleroma/digest.ex ファイルの表示

@@ -0,0 +1,33 @@
defmodule Mix.Tasks.Pleroma.Digest do
use Mix.Task

@shortdoc "Manages digest emails"
@moduledoc """
Manages digest emails

## Send digest email since given date (user registration date by default)
ignoring user activity status.

``mix pleroma.digest test <nickname> <since_date>``

Example: ``mix pleroma.digest test donaldtheduck 2019-05-20``
"""
def run(["test", nickname | opts]) do
Mix.Pleroma.start_pleroma()

user = Pleroma.User.get_by_nickname(nickname)

last_digest_emailed_at =
with [date] <- opts,
{:ok, datetime} <- Timex.parse(date, "{YYYY}-{0M}-{0D}") do
datetime
else
_ -> user.inserted_at
end

patched_user = %{user | last_digest_emailed_at: last_digest_emailed_at}

_user = Pleroma.DigestEmailWorker.perform(patched_user)
Mix.shell().info("Digest email have been sent to #{nickname} (#{user.email})")
end
end

+ 2
- 0
lib/mix/tasks/pleroma/instance.ex ファイルの表示

@@ -183,6 +183,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
)

secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
@@ -200,6 +201,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
dbuser: dbuser,
dbpass: dbpass,
secret: secret,
jwt_secret: jwt_secret,
signing_salt: signing_salt,
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),


+ 16
- 1
lib/pleroma/application.ex ファイルの表示

@@ -162,7 +162,9 @@ defmodule Pleroma.Application do
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Pleroma.Supervisor]
Supervisor.start_link(children, opts)
result = Supervisor.start_link(children, opts)
:ok = after_supervisor_start()
result
end

defp setup_instrumenters do
@@ -227,4 +229,17 @@ defmodule Pleroma.Application do
:hackney_pool.child_spec(pool, options)
end
end

defp after_supervisor_start do
with digest_config <- Application.get_env(:pleroma, :email_notifications)[:digest],
true <- digest_config[:active] do
PleromaJobQueue.schedule(
digest_config[:schedule],
:digest_emails,
Pleroma.DigestEmailWorker
)
end

:ok
end
end

+ 35
- 0
lib/pleroma/digest_email_worker.ex ファイルの表示

@@ -0,0 +1,35 @@
defmodule Pleroma.DigestEmailWorker do
import Ecto.Query

@queue_name :digest_emails

def perform do
config = Pleroma.Config.get([:email_notifications, :digest])
negative_interval = -Map.fetch!(config, :interval)
inactivity_threshold = Map.fetch!(config, :inactivity_threshold)
inactive_users_query = Pleroma.User.list_inactive_users_query(inactivity_threshold)

now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)

from(u in inactive_users_query,
where: fragment(~s(? #> '{"email_notifications","digest"}' @> 'true'), u.info),
where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"),
select: u
)
|> Pleroma.Repo.all()
|> Enum.each(&PleromaJobQueue.enqueue(@queue_name, __MODULE__, [&1]))
end

@doc """
Send digest email to the given user.
Updates `last_digest_emailed_at` field for the user and returns the updated user.
"""
@spec perform(Pleroma.User.t()) :: Pleroma.User.t()
def perform(user) do
with %Swoosh.Email{} = email <- Pleroma.Emails.UserEmail.digest_email(user) do
Pleroma.Emails.Mailer.deliver_async(email)
end

Pleroma.User.touch_last_digest_emailed_at(user)
end
end

+ 70
- 1
lib/pleroma/emails/user_email.ex ファイルの表示

@@ -5,7 +5,7 @@
defmodule Pleroma.Emails.UserEmail do
@moduledoc "User emails"

import Swoosh.Email
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}

alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router
@@ -87,4 +87,73 @@ defmodule Pleroma.Emails.UserEmail do
|> subject("#{instance_name()} account confirmation")
|> html_body(html_body)
end

@doc """
Email used in digest email notifications
Includes Mentions and New Followers data
If there are no mentions (even when new followers exist), the function will return nil
"""
@spec digest_email(Pleroma.User.t()) :: Swoosh.Email.t() | nil
def digest_email(user) do
new_notifications =
Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
|> Enum.reduce(%{followers: [], mentions: []}, fn
%{activity: %{data: %{"type" => "Create"}, actor: actor} = activity} = notification,
acc ->
new_mention = %{
data: notification,
object: Pleroma.Object.normalize(activity),
from: Pleroma.User.get_by_ap_id(actor)
}

%{acc | mentions: [new_mention | acc.mentions]}

%{activity: %{data: %{"type" => "Follow"}, actor: actor} = activity} = notification,
acc ->
new_follower = %{
data: notification,
object: Pleroma.Object.normalize(activity),
from: Pleroma.User.get_by_ap_id(actor)
}

%{acc | followers: [new_follower | acc.followers]}

_, acc ->
acc
end)

with [_ | _] = mentions <- new_notifications.mentions do
html_data = %{
instance: instance_name(),
user: user,
mentions: mentions,
followers: new_notifications.followers,
unsubscribe_link: unsubscribe_url(user, "digest")
}

new()
|> to(recipient(user))
|> from(sender())
|> subject("Your digest from #{instance_name()}")
|> render_body("digest.html", html_data)
else
_ ->
nil
end
end

@doc """
Generate unsubscribe link for given user and notifications type.
The link contains JWT token with the data, and subscription can be modified without
authorization.
"""
@spec unsubscribe_url(Pleroma.User.t(), String.t()) :: String.t()
def unsubscribe_url(user, notifications_type) do
token =
%{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
|> Pleroma.JWT.generate_and_sign!()
|> Base.encode64()

Router.Helpers.subscription_url(Pleroma.Web.Endpoint, :unsubscribe, token)
end
end

+ 9
- 0
lib/pleroma/jwt.ex ファイルの表示

@@ -0,0 +1,9 @@
defmodule Pleroma.JWT do
use Joken.Config

@impl true
def token_config do
default_claims(skip: [:aud])
|> add_claim("aud", &Pleroma.Web.Endpoint.url/0, &(&1 == Pleroma.Web.Endpoint.url()))
end
end

+ 26
- 2
lib/pleroma/notification.ex ファイルの表示

@@ -18,6 +18,8 @@ defmodule Pleroma.Notification do
import Ecto.Query
import Ecto.Changeset

@type t :: %__MODULE__{}

schema "notifications" do
field(:seen, :boolean, default: false)
belongs_to(:user, User, type: Pleroma.FlakeId)
@@ -31,7 +33,7 @@ defmodule Pleroma.Notification do
|> cast(attrs, [:seen])
end

def for_user_query(user, opts) do
def for_user_query(user, opts \\ []) do
query =
Notification
|> where(user_id: ^user.id)
@@ -75,6 +77,25 @@ defmodule Pleroma.Notification do
|> Pagination.fetch_paginated(opts)
end

@doc """
Returns notifications for user received since given date.

## Examples

iex> Pleroma.Notification.for_user_since(%Pleroma.User{}, ~N[2019-04-13 11:22:33])
[%Pleroma.Notification{}, %Pleroma.Notification{}]

iex> Pleroma.Notification.for_user_since(%Pleroma.User{}, ~N[2019-04-15 11:22:33])
[]
"""
@spec for_user_since(Pleroma.User.t(), NaiveDateTime.t()) :: [t()]
def for_user_since(user, date) do
from(n in for_user_query(user),
where: n.updated_at > ^date
)
|> Repo.all()
end

def set_read_up_to(%{id: user_id} = _user, id) do
query =
from(
@@ -82,7 +103,10 @@ defmodule Pleroma.Notification do
where: n.user_id == ^user_id,
where: n.id <= ^id,
update: [
set: [seen: true]
set: [
seen: true,
updated_at: ^NaiveDateTime.utc_now()
]
]
)



+ 75
- 0
lib/pleroma/user.ex ファイルの表示

@@ -57,6 +57,7 @@ defmodule Pleroma.User do
field(:search_type, :integer, virtual: true)
field(:tags, {:array, :string}, default: [])
field(:last_refreshed_at, :naive_datetime_usec)
field(:last_digest_emailed_at, :naive_datetime)
has_many(:notifications, Notification)
has_many(:registrations, Registration)
embeds_one(:info, User.Info)
@@ -1419,6 +1420,80 @@ defmodule Pleroma.User do
target.ap_id not in user.info.muted_reblogs
end

@doc """
The function returns a query to get users with no activity for given interval of days.
Inactive users are those who didn't read any notification, or had any activity where
the user is the activity's actor, during `inactivity_threshold` days.
Deactivated users will not appear in this list.

## Examples

iex> Pleroma.User.list_inactive_users()
%Ecto.Query{}
"""
@spec list_inactive_users_query(integer()) :: Ecto.Query.t()
def list_inactive_users_query(inactivity_threshold \\ 7) do
negative_inactivity_threshold = -inactivity_threshold
now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
# Subqueries are not supported in `where` clauses, join gets too complicated.
has_read_notifications =
from(n in Pleroma.Notification,
where: n.seen == true,
group_by: n.id,
having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
select: n.user_id
)
|> Pleroma.Repo.all()

from(u in Pleroma.User,
left_join: a in Pleroma.Activity,
on: u.ap_id == a.actor,
where: not is_nil(u.nickname),
where: fragment("not (?->'deactivated' @> 'true')", u.info),
where: u.id not in ^has_read_notifications,
group_by: u.id,
having:
max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
is_nil(max(a.inserted_at))
)
end

@doc """
Enable or disable email notifications for user

## Examples

iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => false}}}, "digest", true)
Pleroma.User{info: %{email_notifications: %{"digest" => true}}}

iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => true}}}, "digest", false)
Pleroma.User{info: %{email_notifications: %{"digest" => false}}}
"""
@spec switch_email_notifications(t(), String.t(), boolean()) ::
{:ok, t()} | {:error, Ecto.Changeset.t()}
def switch_email_notifications(user, type, status) do
info = Pleroma.User.Info.update_email_notifications(user.info, %{type => status})

change(user)
|> put_embed(:info, info)
|> update_and_set_cache()
end

@doc """
Set `last_digest_emailed_at` value for the user to current time
"""
@spec touch_last_digest_emailed_at(t()) :: t()
def touch_last_digest_emailed_at(user) do
now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)

{:ok, updated_user} =
user
|> change(%{last_digest_emailed_at: now})
|> update_and_set_cache()

updated_user
end

@spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
def toggle_confirmation(%User{} = user) do
need_confirmation? = !user.info.confirmation_pending


+ 25
- 0
lib/pleroma/user/info.ex ファイルの表示

@@ -45,6 +45,7 @@ defmodule Pleroma.User.Info do
field(:hide_follows, :boolean, default: false)
field(:hide_favorites, :boolean, default: true)
field(:pinned_activities, {:array, :string}, default: [])
field(:email_notifications, :map, default: %{"digest" => false})
field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: [])
field(:pleroma_settings_store, :map, default: %{})
@@ -95,6 +96,30 @@ defmodule Pleroma.User.Info do
|> validate_required([:notification_settings])
end

@doc """
Update email notifications in the given User.Info struct.

Examples:

iex> update_email_notifications(%Pleroma.User.Info{email_notifications: %{"digest" => false}}, %{"digest" => true})
%Pleroma.User.Info{email_notifications: %{"digest" => true}}

"""
@spec update_email_notifications(t(), map()) :: Ecto.Changeset.t()
def update_email_notifications(info, settings) do
email_notifications =
info.email_notifications
|> Map.merge(settings)
|> Map.take(["digest"])

params = %{email_notifications: email_notifications}
fields = [:email_notifications]

info
|> cast(params, fields)
|> validate_required(fields)
end

def add_to_note_count(info, number) do
set_note_count(info, info.note_count + number)
end


+ 20
- 0
lib/pleroma/web/mailer/subscription_controller.ex ファイルの表示

@@ -0,0 +1,20 @@
defmodule Pleroma.Web.Mailer.SubscriptionController do
use Pleroma.Web, :controller

alias Pleroma.JWT
alias Pleroma.Repo
alias Pleroma.User

def unsubscribe(conn, %{"token" => encoded_token}) do
with {:ok, token} <- Base.decode64(encoded_token),
{:ok, claims} <- JWT.verify_and_validate(token),
%{"act" => %{"unsubscribe" => type}, "sub" => uid} <- claims,
%User{} = user <- Repo.get(User, uid),
{:ok, _user} <- User.switch_email_notifications(user, type, false) do
render(conn, "unsubscribe_success.html", email: user.email)
else
_err ->
render(conn, "unsubscribe_failure.html")
end
end
end

+ 2
- 0
lib/pleroma/web/router.ex ファイルの表示

@@ -608,6 +608,8 @@ defmodule Pleroma.Web.Router do
post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation)
post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)

get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
end

pipeline :activitypub do


+ 20
- 0
lib/pleroma/web/templates/email/digest.html.eex ファイルの表示

@@ -0,0 +1,20 @@
<h1>Hey <%= @user.nickname %>, here is what you've missed!</h1>

<h2>New Mentions:</h2>
<ul>
<%= for %{data: mention, object: object, from: from} <- @mentions do %>
<li><%= link from.nickname, to: mention.activity.actor %>: <%= raw object.data["content"] %></li>
<% end %>
</ul>

<%= if @followers != [] do %>
<h2><%= length(@followers) %> New Followers:</h2>
<ul>
<%= for %{data: follow, from: from} <- @followers do %>
<li><%= link from.nickname, to: follow.activity.actor %></li>
<% end %>
</ul>
<% end %>

<p>You have received this email because you have signed up to receive digest emails from <b><%= @instance %></b> Pleroma instance.</p>
<p>The email address you are subscribed as is <%= @user.email %>. To unsubscribe, please go <%= link "here", to: @unsubscribe_link %>.</p>

+ 10
- 0
lib/pleroma/web/templates/layout/email.html.eex ファイルの表示

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><%= @email.subject %></title>
</head>
<body>
<%= render @view_module, @view_template, assigns %>
</body>
</html>

+ 1
- 0
lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex ファイルの表示

@@ -0,0 +1 @@
<h1>UNSUBSCRIBE FAILURE</h1>

+ 1
- 0
lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex ファイルの表示

@@ -0,0 +1 @@
<h1>UNSUBSCRIBE SUCCESSFUL</h1>

+ 5
- 0
lib/pleroma/web/views/email_view.ex ファイルの表示

@@ -0,0 +1,5 @@
defmodule Pleroma.Web.EmailView do
use Pleroma.Web, :view
import Phoenix.HTML
import Phoenix.HTML.Link
end

+ 3
- 0
lib/pleroma/web/views/mailer/subscription_view.ex ファイルの表示

@@ -0,0 +1,3 @@
defmodule Pleroma.Web.Mailer.SubscriptionView do
use Pleroma.Web, :view
end

+ 3
- 1
mix.exs ファイルの表示

@@ -127,6 +127,7 @@ defmodule Pleroma.Mixfile do
{:ex_doc, "~> 0.20.2", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"},
{:swoosh, "~> 0.23.2"},
{:phoenix_swoosh, "~> 0.2"},
{:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},
{:floki, "~> 0.20.0"},
@@ -139,7 +140,7 @@ defmodule Pleroma.Mixfile do
{:http_signatures,
git: "https://git.pleroma.social/pleroma/http_signatures.git",
ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"},
{:pleroma_job_queue, "~> 0.2.0"},
{:pleroma_job_queue, "~> 0.3"},
{:telemetry, "~> 0.3"},
{:prometheus_ex, "~> 3.0"},
{:prometheus_plugs, "~> 1.1"},
@@ -147,6 +148,7 @@ defmodule Pleroma.Mixfile do
{:prometheus_ecto, "~> 1.4"},
{:recon, github: "ferd/recon", tag: "2.4.0"},
{:quack, "~> 0.1.1"},
{:joken, "~> 2.0"},
{:benchee, "~> 1.0"},
{:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},
{:ex_rated, "~> 1.3"},


+ 9
- 8
mix.lock ファイルの表示

@@ -5,16 +5,17 @@
"bbcode": {:hex, :bbcode, "0.1.1", "0023e2c7814119b2e620b7add67182e3f6019f92bfec9a22da7e99821aceba70", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
"cachex": {:hex, :cachex, "3.0.3", "4e2d3e05814a5738f5ff3903151d5c25636d72a3527251b753f501ad9c657967", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
"calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
"comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
"comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"crontab": {:hex, :crontab, "1.1.7", "b9219f0bdc8678b94143655a8f229716c5810c0636a4489f98c0956137e53985", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
@@ -43,14 +44,15 @@
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
"joken": {:hex, :joken, "2.0.1", "ec9ab31bf660f343380da033b3316855197c8d4c6ef597fa3fcb451b326beb14", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"},
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
"mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], [], "hexpm"},
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
"mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"mogrify": {:hex, :mogrify, "0.6.1", "de1b527514f2d95a7bbe9642eb556061afb337e220cf97adbf3a4e6438ed70af", [:mix], [], "hexpm"},
"mox": {:hex, :mox, "0.5.1", "f86bb36026aac1e6f924a4b6d024b05e9adbed5c63e8daa069bd66fb3292165b", [:mix], [], "hexpm"},
@@ -61,20 +63,19 @@
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
"pleroma_job_queue": {:hex, :pleroma_job_queue, "0.2.0", "879e660aa1cebe8dc6f0aaaa6aa48b4875e89cd961d4a585fd128e0773b31a18", [:mix], [], "hexpm"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.2.0", "a7e0b32077cd6d2323ae15198839b05d9caddfa20663fd85787479e81f89520e", [:mix], [{:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 0.1", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm"},
"pleroma_job_queue": {:hex, :pleroma_job_queue, "0.3.0", "b84538d621f0c3d6fcc1cff9d5648d3faaf873b8b21b94e6503428a07a48ec47", [:mix], [{:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}], "hexpm"},
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus": {:hex, :prometheus, "4.4.1", "1e96073b3ed7788053768fea779cbc896ddc3bdd9ba60687f2ad50b252ac87d6", [:mix, :rebar3], [], "hexpm"},
"prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_phoenix": {:hex, :prometheus_phoenix, "1.3.0", "c4b527e0b3a9ef1af26bdcfbfad3998f37795b9185d475ca610fe4388fdd3bb5", [:mix], [{:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.0", "6dbd39e3165b9ef1c94a7a820e9ffe08479f949dcdd431ed4aaea7b250eebfde", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
@@ -88,7 +89,7 @@
"tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"ueberauth": {:hex, :ueberauth, "0.6.1", "9e90d3337dddf38b1ca2753aca9b1e53d8a52b890191cdc55240247c89230412", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"},
"web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
}

+ 20
- 0
priv/repo/migrations/20190412052952_add_user_info_fields.exs ファイルの表示

@@ -0,0 +1,20 @@
defmodule Pleroma.Repo.Migrations.AddEmailNotificationsToUserInfo do
use Ecto.Migration

def up do
execute("
UPDATE users
SET info = info || '{
\"email_notifications\": {
\"digest\": false
}
}'")
end

def down do
execute("
UPDATE users
SET info = info - 'email_notifications'
")
end
end

+ 9
- 0
priv/repo/migrations/20190413085040_add_signin_and_last_digest_dates_to_user.exs ファイルの表示

@@ -0,0 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddSigninAndLastDigestDatesToUser do
use Ecto.Migration

def change do
alter table(:users) do
add(:last_digest_emailed_at, :naive_datetime, default: fragment("now()"))
end
end
end

+ 2
- 0
priv/templates/sample_config.eex ファイルの表示

@@ -68,3 +68,5 @@ config :pleroma, Pleroma.Uploaders.Local, uploads: "<%= uploads_dir %>"
# For using third-party S3 clones like wasabi, also do:
# config :ex_aws, :s3,
# host: "s3.wasabisys.com"

config :joken, default_signer: "<%= jwt_secret %>"

+ 51
- 0
test/mix/tasks/pleroma.digest_test.exs ファイルの表示

@@ -0,0 +1,51 @@
defmodule Mix.Tasks.Pleroma.DigestTest do
use Pleroma.DataCase

import Pleroma.Factory
import Swoosh.TestAssertions

alias Pleroma.Web.CommonAPI

setup_all do
Mix.shell(Mix.Shell.Process)

on_exit(fn ->
Mix.shell(Mix.Shell.IO)
end)

:ok
end

describe "pleroma.digest test" do
test "Sends digest to the given user" do
user1 = insert(:user)
user2 = insert(:user)

Enum.each(0..10, fn i ->
{:ok, _activity} =
CommonAPI.post(user1, %{
"status" => "hey ##{i} @#{user2.nickname}!"
})
end)

yesterday =
NaiveDateTime.add(
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
-60 * 60 * 24,
:second
)

{:ok, yesterday_date} = Timex.format(yesterday, "%F", :strftime)

:ok = Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname, yesterday_date])

assert_receive {:mix_shell, :info, [message]}
assert message =~ "Digest email have been sent"

assert_email_sent(
to: {user2.name, user2.email},
html_body: ~r/new mentions:/i
)
end
end
end

+ 48
- 1
test/notification_test.exs ファイルの表示

@@ -4,13 +4,15 @@

defmodule Pleroma.NotificationTest do
use Pleroma.DataCase

import Pleroma.Factory

alias Pleroma.Notification
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Streamer
alias Pleroma.Web.TwitterAPI.TwitterAPI
import Pleroma.Factory

describe "create_notifications" do
test "notifies someone when they are directly addressed" do
@@ -352,6 +354,51 @@ defmodule Pleroma.NotificationTest do
end
end

describe "for_user_since/2" do
defp days_ago(days) do
NaiveDateTime.add(
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
-days * 60 * 60 * 24,
:second
)
end

test "Returns recent notifications" do
user1 = insert(:user)
user2 = insert(:user)

Enum.each(0..10, fn i ->
{:ok, _activity} =
CommonAPI.post(user1, %{
"status" => "hey ##{i} @#{user2.nickname}!"
})
end)

{old, new} = Enum.split(Notification.for_user(user2), 5)

Enum.each(old, fn notification ->
notification
|> cast(%{updated_at: days_ago(10)}, [:updated_at])
|> Pleroma.Repo.update!()
end)

recent_notifications_ids =
user2
|> Notification.for_user_since(
NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
)
|> Enum.map(& &1.id)

Enum.each(old, fn %{id: id} ->
refute id in recent_notifications_ids
end)

Enum.each(new, fn %{id: id} ->
assert id in recent_notifications_ids
end)
end
end

describe "notification target determination" do
test "it sends notifications to addressed users in new messages" do
user = insert(:user)


+ 2
- 1
test/support/builders/user_builder.ex ファイルの表示

@@ -9,7 +9,8 @@ defmodule Pleroma.Builders.UserBuilder do
nickname: "testname",
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
bio: "A tester.",
ap_id: "some id"
ap_id: "some id",
last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
}

Map.merge(user, data)


+ 2
- 1
test/support/factory.ex ファイルの表示

@@ -31,7 +31,8 @@ defmodule Pleroma.Factory do
nickname: sequence(:nickname, &"nick#{&1}"),
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
bio: sequence(:bio, &"Tester Number #{&1}"),
info: %{}
info: %{},
last_digest_emailed_at: NaiveDateTime.utc_now()
}

%{


+ 24
- 0
test/user_info_test.exs ファイルの表示

@@ -0,0 +1,24 @@
defmodule Pleroma.UserInfoTest do
alias Pleroma.Repo
alias Pleroma.User.Info

use Pleroma.DataCase

import Pleroma.Factory

describe "update_email_notifications/2" do
setup do
user = insert(:user, %{info: %{email_notifications: %{"digest" => true}}})

{:ok, user: user}
end

test "Notifications are updated", %{user: user} do
true = user.info.email_notifications["digest"]
changeset = Info.update_email_notifications(user.info, %{"digest" => false})
assert changeset.valid?
{:ok, result} = Ecto.Changeset.apply_action(changeset, :insert)
assert result.email_notifications["digest"] == false
end
end
end

+ 8
- 1
test/user_search_test.exs ファイルの表示

@@ -193,7 +193,14 @@ defmodule Pleroma.UserSearchTest do
user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")

assert length(results) == 1
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)

expected =
result
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
|> Map.put(:last_digest_emailed_at, nil)

assert user == expected
end

test "excludes a blocked users from search result" do


+ 103
- 0
test/user_test.exs ファイルの表示

@@ -1237,6 +1237,109 @@ defmodule Pleroma.UserTest do
assert Map.get(user_show, "followers_count") == 2
end

describe "list_inactive_users_query/1" do
defp days_ago(days) do
NaiveDateTime.add(
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
-days * 60 * 60 * 24,
:second
)
end

test "Users are inactive by default" do
total = 10

users =
Enum.map(1..total, fn _ ->
insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
end)

inactive_users_ids =
Pleroma.User.list_inactive_users_query()
|> Pleroma.Repo.all()
|> Enum.map(& &1.id)

Enum.each(users, fn user ->
assert user.id in inactive_users_ids
end)
end

test "Only includes users who has no recent activity" do
total = 10

users =
Enum.map(1..total, fn _ ->
insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
end)

{inactive, active} = Enum.split(users, trunc(total / 2))

Enum.map(active, fn user ->
to = Enum.random(users -- [user])

{:ok, _} =
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(user, %{
"status" => "hey @#{to.nickname}"
})
end)

inactive_users_ids =
Pleroma.User.list_inactive_users_query()
|> Pleroma.Repo.all()
|> Enum.map(& &1.id)

Enum.each(active, fn user ->
refute user.id in inactive_users_ids
end)

Enum.each(inactive, fn user ->
assert user.id in inactive_users_ids
end)
end

test "Only includes users with no read notifications" do
total = 10

users =
Enum.map(1..total, fn _ ->
insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
end)

[sender | recipients] = users
{inactive, active} = Enum.split(recipients, trunc(total / 2))

Enum.each(recipients, fn to ->
{:ok, _} =
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{
"status" => "hey @#{to.nickname}"
})

{:ok, _} =
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{
"status" => "hey again @#{to.nickname}"
})
end)

Enum.each(active, fn user ->
[n1, _n2] = Pleroma.Notification.for_user(user)
{:ok, _} = Pleroma.Notification.read_one(user, n1.id)
end)

inactive_users_ids =
Pleroma.User.list_inactive_users_query()
|> Pleroma.Repo.all()
|> Enum.map(& &1.id)

Enum.each(active, fn user ->
refute user.id in inactive_users_ids
end)

Enum.each(inactive, fn user ->
assert user.id in inactive_users_ids
end)
end
end

describe "toggle_confirmation/1" do
test "if user is confirmed" do
user = insert(:user, info: %{confirmation_pending: false})


読み込み中…
キャンセル
保存