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