diff --git a/lib/pleroma/user/mailing_list.ex b/lib/pleroma/user/mailing_list.ex
new file mode 100644
index 000000000..2cc223cc7
--- /dev/null
+++ b/lib/pleroma/user/mailing_list.ex
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.MailingList do
+ @moduledoc """
+ Functions for generating email lists from local users.
+ """
+ import Ecto.Query
+
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ defp subscribers_query do
+ User.Query.build(%{
+ local: true,
+ is_active: true,
+ is_approved: true,
+ is_confirmed: true,
+ accepts_newsletter: true
+ })
+ |> where([u], not is_nil(u.email))
+ end
+
+ def generate_csv do
+ subscribers_query()
+ |> generate_csv()
+ end
+
+ def generate_csv(query) do
+ query
+ |> Repo.all()
+ |> Enum.map(&build_row/1)
+ |> build_csv()
+ end
+
+ defp build_row(%User{email: email}), do: email
+
+ defp build_csv(lines) do
+ ["Email Address" | lines]
+ |> Enum.join("\n")
+ end
+end
diff --git a/test/pleroma/user/mailing_list_test.exs b/test/pleroma/user/mailing_list_test.exs
new file mode 100644
index 000000000..50b10584c
--- /dev/null
+++ b/test/pleroma/user/mailing_list_test.exs
@@ -0,0 +1,26 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.MailingListTest do
+ alias Pleroma.User.MailingList
+
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+
+ test "generate_csv/0" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+ user3 = insert(:user)
+
+ expected = """
+ Email Address
+ #{user1.email}
+ #{user2.email}
+ #{user3.email}\
+ """
+
+ assert MailingList.generate_csv() == expected
+ end
+end