Merge branch 'conversations-import' into 'develop'

Conversations import

Closes #871

See merge request pleroma/pleroma!1127
This commit is contained in:
rinpatch 2019-05-16 18:32:42 +00:00
commit ad76307a82
8 changed files with 79 additions and 10 deletions

View file

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc. - A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc.
- [Prometheus](https://prometheus.io/) metrics - [Prometheus](https://prometheus.io/) metrics
- Support for Mastodon's remote interaction - Support for Mastodon's remote interaction
- Mix Tasks: `mix pleroma.database bump_all_conversations`
- Mix Tasks: `mix pleroma.database remove_embedded_objects` - Mix Tasks: `mix pleroma.database remove_embedded_objects`
- Mix Tasks: `mix pleroma.user toggle_confirmed` - Mix Tasks: `mix pleroma.user toggle_confirmed`
- Federation: Support for reports - Federation: Support for reports

View file

@ -4,6 +4,7 @@
defmodule Mix.Tasks.Pleroma.Database do defmodule Mix.Tasks.Pleroma.Database do
alias Mix.Tasks.Pleroma.Common alias Mix.Tasks.Pleroma.Common
alias Pleroma.Conversation
require Logger require Logger
use Mix.Task use Mix.Task
@ -19,6 +20,11 @@ defmodule Mix.Tasks.Pleroma.Database do
Options: Options:
- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references - `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
## Create a conversation for all existing DMs. Can be safely re-run.
mix pleroma.database bump_all_conversations
""" """
def run(["remove_embedded_objects" | args]) do def run(["remove_embedded_objects" | args]) do
{options, [], []} = {options, [], []} =
@ -48,4 +54,9 @@ def run(["remove_embedded_objects" | args]) do
) )
end end
end end
def run(["bump_all_conversations"]) do
Common.start_pleroma()
Conversation.bump_for_all_activities()
end
end end

View file

@ -45,7 +45,7 @@ def get_for_ap_id(ap_id) do
2. Create a participation for all the people involved who don't have one already 2. Create a participation for all the people involved who don't have one already
3. Bump all relevant participations to 'unread' 3. Bump all relevant participations to 'unread'
""" """
def create_or_bump_for(activity) do def create_or_bump_for(activity, opts \\ []) do
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
"Create" <- activity.data["type"], "Create" <- activity.data["type"],
object <- Pleroma.Object.normalize(activity), object <- Pleroma.Object.normalize(activity),
@ -58,7 +58,7 @@ def create_or_bump_for(activity) do
participations = participations =
Enum.map(users, fn user -> Enum.map(users, fn user ->
{:ok, participation} = {:ok, participation} =
Participation.create_for_user_and_conversation(user, conversation) Participation.create_for_user_and_conversation(user, conversation, opts)
participation participation
end) end)
@ -72,4 +72,21 @@ def create_or_bump_for(activity) do
e -> {:error, e} e -> {:error, e}
end end
end end
@doc """
This is only meant to be run by a mix task. It creates conversations/participations for all direct messages in the database.
"""
def bump_for_all_activities do
stream =
Pleroma.Web.ActivityPub.ActivityPub.fetch_direct_messages_query()
|> Repo.stream()
Repo.transaction(
fn ->
stream
|> Enum.each(fn a -> create_or_bump_for(a, read: true) end)
end,
timeout: :infinity
)
end
end end

View file

@ -22,15 +22,17 @@ defmodule Pleroma.Conversation.Participation do
def creation_cng(struct, params) do def creation_cng(struct, params) do
struct struct
|> cast(params, [:user_id, :conversation_id]) |> cast(params, [:user_id, :conversation_id, :read])
|> validate_required([:user_id, :conversation_id]) |> validate_required([:user_id, :conversation_id])
end end
def create_for_user_and_conversation(user, conversation) do def create_for_user_and_conversation(user, conversation, opts \\ []) do
read = !!opts[:read]
%__MODULE__{} %__MODULE__{}
|> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) |> creation_cng(%{user_id: user.id, conversation_id: conversation.id, read: read})
|> Repo.insert( |> Repo.insert(
on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], on_conflict: [set: [read: read, updated_at: NaiveDateTime.utc_now()]],
returning: true, returning: true,
conflict_target: [:user_id, :conversation_id] conflict_target: [:user_id, :conversation_id]
) )

View file

@ -974,4 +974,11 @@ def contain_broken_threads(%Activity{} = activity, %User{} = user) do
def contain_activity(%Activity{} = activity, %User{} = user) do def contain_activity(%Activity{} = activity, %User{} = user) do
contain_broken_threads(activity, user) contain_broken_threads(activity, user)
end end
def fetch_direct_messages_query do
Activity
|> restrict_type(%{"type" => "Create"})
|> restrict_visibility(%{visibility: "direct"})
|> order_by([activity], asc: activity.id)
end
end end

View file

@ -14,11 +14,12 @@ def is_public?(data) do
end end
def is_private?(activity) do def is_private?(activity) do
unless is_public?(activity) do with false <- is_public?(activity),
follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address %User{follower_address: follower_address} <-
Enum.any?(activity.data["to"], &(&1 == follower_address)) User.get_cached_by_ap_id(activity.data["actor"]) do
follower_address in activity.data["to"]
else else
false _ -> false
end end
end end

View file

@ -11,6 +11,26 @@ defmodule Pleroma.ConversationTest do
import Pleroma.Factory import Pleroma.Factory
test "it goes through old direct conversations" do
user = insert(:user)
other_user = insert(:user)
{:ok, _activity} =
CommonAPI.post(user, %{"visibility" => "direct", "status" => "hey @#{other_user.nickname}"})
Repo.delete_all(Conversation)
Repo.delete_all(Conversation.Participation)
refute Repo.one(Conversation)
Conversation.bump_for_all_activities()
assert Repo.one(Conversation)
[participation, _p2] = Repo.all(Conversation.Participation)
assert participation.read
end
test "it creates a conversation for given ap_id" do test "it creates a conversation for given ap_id" do
assert {:ok, %Conversation{} = conversation} = assert {:ok, %Conversation{} = conversation} =
Conversation.create_for_ap_id("https://some_ap_id") Conversation.create_for_ap_id("https://some_ap_id")

View file

@ -96,6 +96,16 @@ test "visible_for_user?", %{
refute Visibility.visible_for_user?(direct, unrelated) refute Visibility.visible_for_user?(direct, unrelated)
end end
test "doesn't die when the user doesn't exist",
%{
direct: direct,
user: user
} do
Repo.delete(user)
Cachex.clear(:user_cache)
refute Visibility.is_private?(direct)
end
test "get_visibility", %{ test "get_visibility", %{
public: public, public: public,
private: private, private: private,