Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
[akkoma] / lib / pleroma / conversation.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Conversation do
6 alias Pleroma.Conversation.Participation
7 alias Pleroma.Conversation.Participation.RecipientShip
8 alias Pleroma.Object
9 alias Pleroma.Repo
10 alias Pleroma.User
11 use Ecto.Schema
12 import Ecto.Changeset
13
14 schema "conversations" do
15 # This is the context ap id.
16 field(:ap_id, :string)
17 has_many(:participations, Participation)
18 has_many(:users, through: [:participations, :user])
19
20 timestamps()
21 end
22
23 def creation_cng(struct, params) do
24 struct
25 |> cast(params, [:ap_id])
26 |> validate_required([:ap_id])
27 |> unique_constraint(:ap_id)
28 end
29
30 def create_for_ap_id(ap_id) do
31 %__MODULE__{}
32 |> creation_cng(%{ap_id: ap_id})
33 |> Repo.insert(
34 on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]],
35 returning: true,
36 conflict_target: :ap_id
37 )
38 end
39
40 def get_for_ap_id(ap_id) do
41 Repo.get_by(__MODULE__, ap_id: ap_id)
42 end
43
44 def maybe_create_recipientships(participation, activity) do
45 participation = Repo.preload(participation, :recipients)
46
47 if Enum.empty?(participation.recipients) do
48 recipients = User.get_all_by_ap_id(activity.recipients)
49 RecipientShip.create(recipients, participation)
50 end
51 end
52
53 @doc """
54 This will
55 1. Create a conversation if there isn't one already
56 2. Create a participation for all the people involved who don't have one already
57 3. Bump all relevant participations to 'unread'
58 """
59 def create_or_bump_for(activity, opts \\ []) do
60 with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
61 "Create" <- activity.data["type"],
62 %Object{} = object <- Object.normalize(activity, fetch: false),
63 true <- object.data["type"] in ["Note", "Question"],
64 ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
65 {:ok, conversation} = create_for_ap_id(ap_id)
66
67 users = User.get_users_from_set(activity.recipients, local_only: false)
68
69 participations =
70 Enum.map(users, fn user ->
71 invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
72
73 opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
74
75 {:ok, participation} =
76 Participation.create_for_user_and_conversation(user, conversation, opts)
77
78 maybe_create_recipientships(participation, activity)
79 participation
80 end)
81
82 {:ok,
83 %{
84 conversation
85 | participations: participations
86 }}
87 else
88 e -> {:error, e}
89 end
90 end
91
92 @doc """
93 This is only meant to be run by a mix task. It creates conversations/participations for all direct messages in the database.
94 """
95 def bump_for_all_activities do
96 stream =
97 Pleroma.Web.ActivityPub.ActivityPub.fetch_direct_messages_query()
98 |> Repo.stream()
99
100 Repo.transaction(
101 fn ->
102 stream
103 |> Enum.each(fn a -> create_or_bump_for(a, read: true) end)
104 end,
105 timeout: :infinity
106 )
107 end
108 end