about summary refs log tree commit diff
path: root/app/services/process_feed_service.rb
blob: 1c85942f39e6d6fe52a58d35f9e9e4e9cd5315f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class ProcessFeedService < BaseService
  # Create local statuses from an Atom feed
  # @param [String] body Atom feed
  # @param [Account] account Account this feed belongs to
  def call(body, account)
    xml = Nokogiri::XML(body)

    unless xml.at_xpath('/xmlns:feed').nil?
      update_remote_profile_service.(xml.at_xpath('/xmlns:feed/xmlns:author'), account)
    end

    xml.xpath('//xmlns:entry').each do |entry|
      next unless [:note, :comment, :activity].includes? object_type(entry)

      status = Status.find_by(uri: activity_id(entry))

      next unless status.nil?

      status = Status.new(uri: activity_id(entry), account: account, text: content(entry), created_at: published(entry), updated_at: updated(entry))

      if object_type(entry) == :comment
        add_reply!(entry, status)
      elsif verb(entry) == :share
        add_reblog!(entry, status)
      else
        add_post!(entry, status)
      end

      process_mentions_service.(status) unless status.new_record?
    end
  end

  private

  def add_post!(_entry, status)
    status.save!
  end

  def add_reblog!(entry, status)
    status.reblog = find_original_status(entry, target_id(entry))

    if status.reblog.nil?
      status.reblog = fetch_remote_status(entry)
    end

    status.save! unless status.reblog.nil?
  end

  def add_reply!(entry, status)
    status.thread = find_original_status(entry, thread_id(entry))
    status.save!
  end

  def find_original_status(_xml, id)
    return nil if id.nil?

    if local_id?(id)
      Status.find(unique_tag_to_local_id(id, 'Status'))
    else
      Status.find_by(uri: id)
    end
  end

  def fetch_remote_status(xml)
    username = xml.at_xpath('./activity:object/xmlns:author/xmlns:name').content
    url      = xml.at_xpath('./activity:object/xmlns:author/xmlns:uri').content
    domain   = Addressable::URI.parse(url).host
    account  = Account.find_by(username: username, domain: domain)

    if account.nil?
      account = follow_remote_account_service.("acct:#{username}@#{domain}", false)
      return nil if account.nil?
    end

    Status.new(account: account, uri: target_id(xml), text: target_content(xml), url: target_url(xml))
  end

  def published(xml)
    xml.at_xpath('./xmlns:published').content
  end

  def updated(xml)
    xml.at_xpath('./xmlns:updated').content
  end

  def content(xml)
    xml.at_xpath('./xmlns:content').content
  end

  def thread_id(xml)
    xml.at_xpath('./thr:in-reply-to-id').attribute('ref').value
  rescue
    nil
  end

  def target_id(xml)
    xml.at_xpath('.//activity:object/xmlns:id').content
  rescue
    nil
  end

  def activity_id(xml)
    xml.at_xpath('./xmlns:id').content
  end

  def target_content(xml)
    xml.at_xpath('.//activity:object/xmlns:content').content
  end

  def target_url(xml)
    xml.at_xpath('.//activity:object/xmlns:link[@rel=alternate]').attribute('href').value
  end

  def object_type(xml)
    xml.at_xpath('./activity:object-type').content.gsub('http://activitystrea.ms/schema/1.0/', '').to_sym
  rescue
    :note
  end

  def verb(xml)
    xml.at_xpath('./activity:verb').content.gsub('http://activitystrea.ms/schema/1.0/', '').to_sym
  rescue
    :post
  end

  def follow_remote_account_service
    @follow_remote_account_service ||= FollowRemoteAccountService.new
  end

  def process_mentions_service
    @process_mentions_service ||= ProcessMentionsService.new
  end

  def update_remote_profile_service
    @update_remote_profile_service ||= UpdateRemoteProfileService.new
  end
end