From 378f5f0fbe21c2533719fed9afe8313586fda5d5 Mon Sep 17 00:00:00 2001
From: Mike Verdone <spiral@arcseconds.net>
Date: Mon, 22 Jul 2019 14:18:58 +0200
Subject: [PATCH] Add activity expiration worker

This is a worker that runs every minute and deletes expired activities.
It's based heavily on the scheduled activities worker.
---
 config/config.exs                         |  3 ++
 docs/config.md                            |  4 ++
 lib/pleroma/activity_expiration_worker.ex | 62 +++++++++++++++++++++++
 lib/pleroma/application.ex                |  4 ++
 test/activity_expiration_worker_test.exs  | 17 +++++++
 5 files changed, 90 insertions(+)
 create mode 100644 lib/pleroma/activity_expiration_worker.ex
 create mode 100644 test/activity_expiration_worker_test.exs

diff --git a/config/config.exs b/config/config.exs
index 569411866..2887353fb 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -447,6 +447,7 @@ config :pleroma, Pleroma.Web.Federator.RetryQueue,
   max_retries: 5
 
 config :pleroma_job_queue, :queues,
+  activity_expiration: 10,
   federator_incoming: 50,
   federator_outgoing: 50,
   web_push: 50,
@@ -536,6 +537,8 @@ config :pleroma, :rate_limit,
   status_id_action: {60_000, 3},
   password_reset: {1_800_000, 5}
 
+config :pleroma, Pleroma.ActivityExpiration, enabled: true
+
 # Import environment specific config. This must remain at the bottom
 # of this file so it overrides the configuration defined above.
 import_config "#{Mix.env()}.exs"
diff --git a/docs/config.md b/docs/config.md
index 02f86dc16..a20ed704f 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -484,6 +484,10 @@ config :auto_linker,
 * `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`)
 * `enabled`: whether scheduled activities are sent to the job queue to be executed
 
+## Pleroma.ActivityExpiration
+
+# `enabled`: whether expired activities will be sent to the job queue to be deleted
+
 ## Pleroma.Web.Auth.Authenticator
 
 * `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator
diff --git a/lib/pleroma/activity_expiration_worker.ex b/lib/pleroma/activity_expiration_worker.ex
new file mode 100644
index 000000000..a341f58df
--- /dev/null
+++ b/lib/pleroma/activity_expiration_worker.ex
@@ -0,0 +1,62 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ActivityExpirationWorker do
+  alias Pleroma.Activity
+  alias Pleroma.ActivityExpiration
+  alias Pleroma.Config
+  alias Pleroma.Repo
+  alias Pleroma.User
+  alias Pleroma.Web.CommonAPI
+  require Logger
+  use GenServer
+  import Ecto.Query
+
+  @schedule_interval :timer.minutes(1)
+
+  def start_link do
+    GenServer.start_link(__MODULE__, nil)
+  end
+
+  @impl true
+  def init(_) do
+    if Config.get([ActivityExpiration, :enabled]) do
+      schedule_next()
+      {:ok, nil}
+    else
+      :ignore
+    end
+  end
+
+  def perform(:execute, expiration_id) do
+    try do
+      expiration =
+        ActivityExpiration
+        |> where([e], e.id == ^expiration_id)
+        |> Repo.one!()
+
+      activity = Activity.get_by_id_with_object(expiration.activity_id)
+      user = User.get_by_ap_id(activity.object.data["actor"])
+      CommonAPI.delete(activity.id, user)
+    rescue
+      error ->
+        Logger.error("#{__MODULE__} Couldn't delete expired activity: #{inspect(error)}")
+    end
+  end
+
+  @impl true
+  def handle_info(:perform, state) do
+    ActivityExpiration.due_expirations(@schedule_interval)
+    |> Enum.each(fn expiration ->
+      PleromaJobQueue.enqueue(:activity_expiration, __MODULE__, [:execute, expiration.id])
+    end)
+
+    schedule_next()
+    {:noreply, state}
+  end
+
+  defp schedule_next do
+    Process.send_after(self(), :perform, @schedule_interval)
+  end
+end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 035331491..42e4a1dfa 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -115,6 +115,10 @@ defmodule Pleroma.Application do
         %{
           id: Pleroma.ScheduledActivityWorker,
           start: {Pleroma.ScheduledActivityWorker, :start_link, []}
+        },
+        %{
+          id: Pleroma.ActivityExpirationWorker,
+          start: {Pleroma.ActivityExpirationWorker, :start_link, []}
         }
       ] ++
         hackney_pool_children() ++
diff --git a/test/activity_expiration_worker_test.exs b/test/activity_expiration_worker_test.exs
new file mode 100644
index 000000000..939d912f1
--- /dev/null
+++ b/test/activity_expiration_worker_test.exs
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ActivityExpirationWorkerTest do
+  use Pleroma.DataCase
+  alias Pleroma.Activity
+  import Pleroma.Factory
+
+  test "deletes an activity" do
+    activity = insert(:note_activity)
+    expiration = insert(:expiration_in_the_past, %{activity_id: activity.id})
+    Pleroma.ActivityExpirationWorker.perform(:execute, expiration.id)
+
+    refute Repo.get(Activity, activity.id)
+  end
+end