logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

note_handler.ex (5616B)


      1 # Pleroma: A lightweight social networking server
      2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
      3 # SPDX-License-Identifier: AGPL-3.0-only
      4 
      5 defmodule Pleroma.Web.OStatus.NoteHandler do
      6   require Logger
      7   require Pleroma.Constants
      8 
      9   alias Pleroma.Activity
     10   alias Pleroma.Object
     11   alias Pleroma.Web.ActivityPub.ActivityPub
     12   alias Pleroma.Web.ActivityPub.Utils
     13   alias Pleroma.Web.CommonAPI
     14   alias Pleroma.Web.Federator
     15   alias Pleroma.Web.OStatus
     16   alias Pleroma.Web.XML
     17 
     18   @doc """
     19   Get the context for this note. Uses this:
     20   1. The context of the parent activity
     21   2. The conversation reference in the ostatus xml
     22   3. A newly generated context id.
     23   """
     24   def get_context(entry, in_reply_to) do
     25     context =
     26       (XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
     27          XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
     28       |> String.trim()
     29 
     30     with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(in_reply_to) do
     31       context
     32     else
     33       _e ->
     34         if String.length(context) > 0 do
     35           context
     36         else
     37           Utils.generate_context_id()
     38         end
     39     end
     40   end
     41 
     42   def get_people_mentions(entry) do
     43     :xmerl_xpath.string(
     44       '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]',
     45       entry
     46     )
     47     |> Enum.map(fn person -> XML.string_from_xpath("@href", person) end)
     48   end
     49 
     50   def get_collection_mentions(entry) do
     51     transmogrify = fn
     52       "http://activityschema.org/collection/public" ->
     53         Pleroma.Constants.as_public()
     54 
     55       group ->
     56         group
     57     end
     58 
     59     :xmerl_xpath.string(
     60       '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]',
     61       entry
     62     )
     63     |> Enum.map(fn collection -> XML.string_from_xpath("@href", collection) |> transmogrify.() end)
     64   end
     65 
     66   def get_mentions(entry) do
     67     (get_people_mentions(entry) ++ get_collection_mentions(entry))
     68     |> Enum.filter(& &1)
     69   end
     70 
     71   def get_emoji(entry) do
     72     try do
     73       :xmerl_xpath.string('//link[@rel="emoji"]', entry)
     74       |> Enum.reduce(%{}, fn emoji, acc ->
     75         Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji))
     76       end)
     77     rescue
     78       _e -> nil
     79     end
     80   end
     81 
     82   def make_to_list(actor, mentions) do
     83     [
     84       actor.follower_address
     85     ] ++ mentions
     86   end
     87 
     88   def add_external_url(note, entry) do
     89     url = XML.string_from_xpath("//link[@rel='alternate' and @type='text/html']/@href", entry)
     90     Map.put(note, "external_url", url)
     91   end
     92 
     93   def fetch_replied_to_activity(entry, in_reply_to, options \\ []) do
     94     with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do
     95       activity
     96     else
     97       _e ->
     98         with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
     99              in_reply_to_href when not is_nil(in_reply_to_href) <-
    100                XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
    101              {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href, options) do
    102           activity
    103         else
    104           _e -> nil
    105         end
    106     end
    107   end
    108 
    109   # TODO: Clean this up a bit.
    110   def handle_note(entry, doc \\ nil, options \\ []) do
    111     with id <- XML.string_from_xpath("//id", entry),
    112          activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
    113          [author] <- :xmerl_xpath.string('//author[1]', doc),
    114          {:ok, actor} <- OStatus.find_make_or_update_actor(author),
    115          content_html <- OStatus.get_content(entry),
    116          cw <- OStatus.get_cw(entry),
    117          in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
    118          options <- Keyword.put(options, :depth, (options[:depth] || 0) + 1),
    119          in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to, options),
    120          in_reply_to_object <-
    121            (in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil,
    122          in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to,
    123          attachments <- OStatus.get_attachments(entry),
    124          context <- get_context(entry, in_reply_to),
    125          tags <- OStatus.get_tags(entry),
    126          mentions <- get_mentions(entry),
    127          to <- make_to_list(actor, mentions),
    128          date <- XML.string_from_xpath("//published", entry),
    129          unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted",
    130          cc <- if(unlisted, do: [Pleroma.Constants.as_public()], else: []),
    131          note <-
    132            CommonAPI.Utils.make_note_data(
    133              actor.ap_id,
    134              to,
    135              context,
    136              content_html,
    137              attachments,
    138              in_reply_to_activity,
    139              [],
    140              cw
    141            ),
    142          note <- note |> Map.put("id", id) |> Map.put("tag", tags),
    143          note <- note |> Map.put("published", date),
    144          note <- note |> Map.put("emoji", get_emoji(entry)),
    145          note <- add_external_url(note, entry),
    146          note <- note |> Map.put("cc", cc),
    147          # TODO: Handle this case in make_note_data
    148          note <-
    149            if(
    150              in_reply_to && !in_reply_to_activity,
    151              do: note |> Map.put("inReplyTo", in_reply_to),
    152              else: note
    153            ) do
    154       ActivityPub.create(%{
    155         to: to,
    156         actor: actor,
    157         context: context,
    158         object: note,
    159         published: date,
    160         local: false,
    161         additional: %{"cc" => cc}
    162       })
    163     else
    164       %Activity{} = activity -> {:ok, activity}
    165       e -> {:error, e}
    166     end
    167   end
    168 end