note_handler.ex 5.49 KB
Newer Older
1
# Pleroma: A lightweight social networking server
kaniini's avatar
kaniini committed
2
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

lain's avatar
lain committed
5
6
defmodule Pleroma.Web.OStatus.NoteHandler do
  require Logger
7

Haelwenn's avatar
Haelwenn committed
8
9
  alias Pleroma.Activity
  alias Pleroma.Object
lain's avatar
lain committed
10
11
  alias Pleroma.Web.ActivityPub.ActivityPub
  alias Pleroma.Web.ActivityPub.Utils
12
  alias Pleroma.Web.CommonAPI
13
  alias Pleroma.Web.Federator
14
15
  alias Pleroma.Web.OStatus
  alias Pleroma.Web.XML
lain's avatar
lain committed
16
17
18
19
20
21
22

  @doc """
  Get the context for this note. Uses this:
  1. The context of the parent activity
  2. The conversation reference in the ostatus xml
  3. A newly generated context id.
  """
23
  def get_context(entry, in_reply_to) do
lain's avatar
lain committed
24
25
26
27
    context =
      (XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
         XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
      |> String.trim()
lain's avatar
lain committed
28

29
    with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(in_reply_to) do
lain's avatar
lain committed
30
      context
lain's avatar
lain committed
31
32
33
34
35
36
37
    else
      _e ->
        if String.length(context) > 0 do
          context
        else
          Utils.generate_context_id()
        end
lain's avatar
lain committed
38
39
40
    end
  end

41
  def get_people_mentions(entry) do
lain's avatar
lain committed
42
43
44
45
46
    :xmerl_xpath.string(
      '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]',
      entry
    )
    |> Enum.map(fn person -> XML.string_from_xpath("@href", person) end)
lain's avatar
lain committed
47
48
  end

49
50
  def get_collection_mentions(entry) do
    transmogrify = fn
lain's avatar
lain committed
51
      "http://activityschema.org/collection/public" ->
52
        "https://www.w3.org/ns/activitystreams#Public"
lain's avatar
lain committed
53
54

      group ->
55
56
57
        group
    end

lain's avatar
lain committed
58
59
60
61
62
    :xmerl_xpath.string(
      '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]',
      entry
    )
    |> Enum.map(fn collection -> XML.string_from_xpath("@href", collection) |> transmogrify.() end)
63
64
65
  end

  def get_mentions(entry) do
lain's avatar
lain committed
66
67
    (get_people_mentions(entry) ++ get_collection_mentions(entry))
    |> Enum.filter(& &1)
68
69
  end

lain's avatar
lain committed
70
71
72
  def get_emoji(entry) do
    try do
      :xmerl_xpath.string('//link[@rel="emoji"]', entry)
lain's avatar
lain committed
73
      |> Enum.reduce(%{}, fn emoji, acc ->
lain's avatar
lain committed
74
75
76
77
78
79
80
        Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji))
      end)
    rescue
      _e -> nil
    end
  end

lain's avatar
lain committed
81
82
  def make_to_list(actor, mentions) do
    [
lain's avatar
lain committed
83
      actor.follower_address
lain's avatar
lain committed
84
85
86
    ] ++ mentions
  end

lain's avatar
lain committed
87
88
89
90
91
  def add_external_url(note, entry) do
    url = XML.string_from_xpath("//link[@rel='alternate' and @type='text/html']/@href", entry)
    Map.put(note, "external_url", url)
  end

92
  def fetch_replied_to_activity(entry, in_reply_to, options \\ []) do
93
    with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do
lain's avatar
lain committed
94
95
96
      activity
    else
      _e ->
97
98
        with true <- (options[:depth] || 1) <= Federator.max_replies_depth(),
             in_reply_to_href when not is_nil(in_reply_to_href) <-
lain's avatar
lain committed
99
               XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
100
             {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href, options) do
lain's avatar
lain committed
101
102
103
104
105
106
107
          activity
        else
          _e -> nil
        end
    end
  end

108
  # TODO: Clean this up a bit.
109
  def handle_note(entry, doc \\ nil, options \\ []) do
lain's avatar
lain committed
110
    with id <- XML.string_from_xpath("//id", entry),
111
         activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
lain's avatar
lain committed
112
113
114
         [author] <- :xmerl_xpath.string('//author[1]', doc),
         {:ok, actor} <- OStatus.find_make_or_update_user(author),
         content_html <- OStatus.get_content(entry),
lain's avatar
lain committed
115
         cw <- OStatus.get_cw(entry),
116
         in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
117
118
         options <- Keyword.put(options, :depth, (options[:depth] || 0) + 1),
         in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to, options),
119
120
121
         in_reply_to_object <-
           (in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil,
         in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to,
lain's avatar
lain committed
122
         attachments <- OStatus.get_attachments(entry),
123
         context <- get_context(entry, in_reply_to),
lain's avatar
lain committed
124
125
126
127
         tags <- OStatus.get_tags(entry),
         mentions <- get_mentions(entry),
         to <- make_to_list(actor, mentions),
         date <- XML.string_from_xpath("//published", entry),
128
129
         unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted",
         cc <- if(unlisted, do: ["https://www.w3.org/ns/activitystreams#Public"], else: []),
lain's avatar
lain committed
130
131
132
133
134
135
136
         note <-
           CommonAPI.Utils.make_note_data(
             actor.ap_id,
             to,
             context,
             content_html,
             attachments,
137
             in_reply_to_activity,
lain's avatar
lain committed
138
139
140
             [],
             cw
           ),
lain's avatar
lain committed
141
         note <- note |> Map.put("id", id) |> Map.put("tag", tags),
lain's avatar
lain committed
142
         note <- note |> Map.put("published", date),
lain's avatar
lain committed
143
         note <- note |> Map.put("emoji", get_emoji(entry)),
lain's avatar
lain committed
144
         note <- add_external_url(note, entry),
145
         note <- note |> Map.put("cc", cc),
lain's avatar
lain committed
146
         # TODO: Handle this case in make_note_data
lain's avatar
lain committed
147
148
         note <-
           if(
149
150
             in_reply_to && !in_reply_to_activity,
             do: note |> Map.put("inReplyTo", in_reply_to),
lain's avatar
lain committed
151
152
             else: note
           ) do
eal's avatar
eal committed
153
154
155
156
157
158
159
160
161
      ActivityPub.create(%{
        to: to,
        actor: actor,
        context: context,
        object: note,
        published: date,
        local: false,
        additional: %{"cc" => cc}
      })
lain's avatar
lain committed
162
163
164
165
166
167
    else
      %Activity{} = activity -> {:ok, activity}
      e -> {:error, e}
    end
  end
end