6a03ee3c1d2e687a93ab76cf1b6e72749c43c45f
[akkoma] / lib / pleroma / chat.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.Chat do
6 use Ecto.Schema
7
8 import Ecto.Changeset
9 import Ecto.Query
10
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.User
14
15 @moduledoc """
16 Chat keeps a reference to ChatMessage conversations between a user and an recipient. The recipient can be a user (for now) or a group (not implemented yet).
17
18 It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages.
19 """
20
21 schema "chats" do
22 belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
23 field(:recipient, :string)
24 field(:unread, :integer, default: 0, read_after_writes: true)
25
26 timestamps()
27 end
28
29 def last_message_for_chat(chat) do
30 messages_for_chat_query(chat)
31 |> order_by(desc: :id)
32 |> Repo.one()
33 end
34
35 def messages_for_chat_query(chat) do
36 chat =
37 chat
38 |> Repo.preload(:user)
39
40 from(o in Object,
41 where: fragment("?->>'type' = ?", o.data, "ChatMessage"),
42 where:
43 fragment(
44 """
45 (?->>'actor' = ? and ?->'to' = ?)
46 OR (?->>'actor' = ? and ?->'to' = ?)
47 """,
48 o.data,
49 ^chat.user.ap_id,
50 o.data,
51 ^[chat.recipient],
52 o.data,
53 ^chat.recipient,
54 o.data,
55 ^[chat.user.ap_id]
56 )
57 )
58 end
59
60 def creation_cng(struct, params) do
61 struct
62 |> cast(params, [:user_id, :recipient, :unread])
63 |> validate_change(:recipient, fn
64 :recipient, recipient ->
65 case User.get_cached_by_ap_id(recipient) do
66 nil -> [recipient: "must be an existing user"]
67 _ -> []
68 end
69 end)
70 |> validate_required([:user_id, :recipient])
71 |> unique_constraint(:user_id, name: :chats_user_id_recipient_index)
72 end
73
74 def get(user_id, recipient) do
75 __MODULE__
76 |> Repo.get_by(user_id: user_id, recipient: recipient)
77 end
78
79 def get_or_create(user_id, recipient) do
80 %__MODULE__{}
81 |> creation_cng(%{user_id: user_id, recipient: recipient})
82 |> Repo.insert(
83 # Need to set something, otherwise we get nothing back at all
84 on_conflict: [set: [recipient: recipient]],
85 returning: true,
86 conflict_target: [:user_id, :recipient]
87 )
88 end
89
90 def bump_or_create(user_id, recipient) do
91 %__MODULE__{}
92 |> creation_cng(%{user_id: user_id, recipient: recipient, unread: 1})
93 |> Repo.insert(
94 on_conflict: [set: [updated_at: NaiveDateTime.utc_now()], inc: [unread: 1]],
95 conflict_target: [:user_id, :recipient]
96 )
97 end
98
99 def mark_as_read(chat) do
100 chat
101 |> change(%{unread: 0})
102 |> Repo.update()
103 end
104 end