diff options
Diffstat (limited to 'app/services')
-rw-r--r-- | app/services/fetch_remote_status_service.rb | 71 | ||||
-rw-r--r-- | app/services/follow_remote_account_service.rb | 2 | ||||
-rw-r--r-- | app/services/process_feed_service.rb | 22 |
3 files changed, 93 insertions, 2 deletions
diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb new file mode 100644 index 000000000..c872cb385 --- /dev/null +++ b/app/services/fetch_remote_status_service.rb @@ -0,0 +1,71 @@ +class FetchRemoteStatusService < BaseService + def call(url) + response = http_client.head(url) + + Rails.logger.debug "Remote status HEAD request returned code #{response.code}" + return nil if response.code != 200 + + if response.mime_type == 'application/atom+xml' + return process_atom(url, fetch(url)) + elsif !response['Link'].blank? + return process_headers(response) + else + return process_html(fetch(url)) + end + end + + private + + def process_atom(url, body) + Rails.logger.debug "Processing Atom for remote status" + + xml = Nokogiri::XML(body) + account = extract_author(url, xml) + + return nil if account.nil? + + statuses = ProcessFeedService.new.(body, account) + + return statuses.first + end + + def process_html(body) + Rails.logger.debug "Processing HTML for remote status" + + page = Nokogiri::HTML(body) + alternate_link = page.xpath('//link[@rel="alternate"]').find { |link| link['type'] == 'application/atom+xml' } + + return nil if alternate_link.nil? + return process_atom(alternate_link['href'], fetch(alternate_link['href'])) + end + + def process_headers(response) + Rails.logger.debug "Processing link header for remote status" + + link_header = LinkHeader.parse(response['Link']) + alternate_link = link_header.find_link(['rel', 'alternate'], ['type', 'application/atom+xml']) + + return nil if alternate_link.nil? + return process_atom(alternate_link.href, fetch(alternate_link.href)) + end + + def extract_author(url, xml) + url_parts = Addressable::URI.parse(url) + username = xml.at_xpath('//xmlns:author/xmlns:name').try(:content) + domain = url_parts.host + + return nil if username.nil? + + Rails.logger.debug "Going to webfinger #{username}@#{domain}" + + return FollowRemoteAccountService.new.("#{username}@#{domain}") + end + + def fetch(url) + http_client.get(url).to_s + end + + def http_client + HTTP.timeout(:per_operation, write: 20, connect: 20, read: 50) + end +end diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb index f3a384ecc..ef3949e36 100644 --- a/app/services/follow_remote_account_service.rb +++ b/app/services/follow_remote_account_service.rb @@ -72,7 +72,7 @@ class FollowRemoteAccountService < BaseService end def http_client - HTTP + HTTP.timeout(:per_operation, write: 20, connect: 20, read: 50) end end diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb index a69205494..49834f5c1 100644 --- a/app/services/process_feed_service.rb +++ b/app/services/process_feed_service.rb @@ -2,10 +2,11 @@ class ProcessFeedService < BaseService # Create local statuses from an Atom feed # @param [String] body Atom feed # @param [Account] account Account this feed belongs to + # @return [Enumerable] created statuses def call(body, account) xml = Nokogiri::XML(body) update_remote_profile_service.(xml.at_xpath('/xmlns:feed/xmlns:author'), account) unless xml.at_xpath('/xmlns:feed').nil? - xml.xpath('//xmlns:entry').each { |entry| process_entry(account, entry) } + xml.xpath('//xmlns:entry').reverse_each.map { |entry| process_entry(account, entry) }.compact end private @@ -45,6 +46,8 @@ class ProcessFeedService < BaseService DistributionWorker.perform_async(status.id) end + + return status end def record_remote_mentions(status, links) @@ -103,6 +106,10 @@ class ProcessFeedService < BaseService def add_reply!(entry, status) status.thread = find_original_status(entry, thread_id(entry)) status.save! + + if status.thread.nil? && !thread_href(entry).nil? + ThreadResolveWorker.perform_async(status.id, thread_href(entry)) + end end def delete_post!(status) @@ -131,6 +138,13 @@ class ProcessFeedService < BaseService status = Status.new(account: account, uri: target_id(xml), text: target_content(xml), url: target_url(xml), created_at: published(xml), updated_at: updated(xml)) status.thread = find_original_status(xml, thread_id(xml)) + status.save + + if status.saved? && status.thread.nil? && !thread_href(xml).nil? + ThreadResolveWorker.perform_async(status.id, thread_href(xml)) + end + + status rescue Goldfinger::Error, HTTP::Error nil end @@ -153,6 +167,12 @@ class ProcessFeedService < BaseService nil end + def thread_href(xml) + xml.at_xpath('./thr:in-reply-to').attribute('href').value + rescue + nil + end + def target_id(xml) xml.at_xpath('.//activity:object/xmlns:id').content rescue |