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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# frozen_string_literal: true
class OStatus::Activity::Creation < OStatus::Activity::Base
def perform
if redis.exists("delete_upon_arrival:#{@account.id}:#{id}")
Rails.logger.debug "Delete for status #{id} was queued, ignoring"
return [nil, false]
end
return [nil, false] if @account.suspended?
return perform_via_activitypub if activitypub_uri?
Rails.logger.debug "Creating remote status #{id}"
# Return early if status already exists in db
status = find_status(id)
return [status, false] unless status.nil?
cached_reblog = reblog
ApplicationRecord.transaction do
status = Status.create!(
uri: id,
url: url,
account: @account,
reblog: cached_reblog,
text: content,
spoiler_text: content_warning,
created_at: published,
reply: thread?,
language: content_language,
visibility: visibility_scope,
conversation: find_or_create_conversation,
thread: thread? ? find_status(thread.first) : nil
)
save_mentions(status)
save_hashtags(status)
save_media(status)
end
if thread? && status.thread.nil?
Rails.logger.debug "Trying to attach #{status.id} (#{id}) to #{thread.first}"
ThreadResolveWorker.perform_async(status.id, thread.second)
end
Rails.logger.debug "Queuing remote status #{status.id} (#{id}) for distribution"
LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
DistributionWorker.perform_async(status.id)
[status, true]
end
def perform_via_activitypub
[find_status(activitypub_uri) || ActivityPub::FetchRemoteStatusService.new.call(activitypub_uri), false]
end
def content
@xml.at_xpath('./xmlns:content', xmlns: TagManager::XMLNS).content
end
def content_language
@xml.at_xpath('./xmlns:content', xmlns: TagManager::XMLNS)['xml:lang']&.presence || 'en'
end
def content_warning
@xml.at_xpath('./xmlns:summary', xmlns: TagManager::XMLNS)&.content || ''
end
def visibility_scope
@xml.at_xpath('./mastodon:scope', mastodon: TagManager::MTDN_XMLNS)&.content&.to_sym || :public
end
def published
@xml.at_xpath('./xmlns:published', xmlns: TagManager::XMLNS).content
end
def thread?
!@xml.at_xpath('./thr:in-reply-to', thr: TagManager::THR_XMLNS).nil?
end
def thread
thr = @xml.at_xpath('./thr:in-reply-to', thr: TagManager::THR_XMLNS)
[thr['ref'], thr['href']]
end
private
def find_or_create_conversation
uri = @xml.at_xpath('./ostatus:conversation', ostatus: TagManager::OS_XMLNS)&.attribute('ref')&.content
return if uri.nil?
if TagManager.instance.local_id?(uri)
local_id = TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')
return Conversation.find_by(id: local_id)
end
Conversation.find_by(uri: uri) || Conversation.create!(uri: uri)
end
def save_mentions(parent)
processed_account_ids = []
@xml.xpath('./xmlns:link[@rel="mentioned"]', xmlns: TagManager::XMLNS).each do |link|
next if [TagManager::TYPES[:group], TagManager::TYPES[:collection]].include? link['ostatus:object-type']
mentioned_account = account_from_href(link['href'])
next if mentioned_account.nil? || processed_account_ids.include?(mentioned_account.id)
mentioned_account.mentions.where(status: parent).first_or_create(status: parent)
# So we can skip duplicate mentions
processed_account_ids << mentioned_account.id
end
end
def save_hashtags(parent)
tags = @xml.xpath('./xmlns:category', xmlns: TagManager::XMLNS).map { |category| category['term'] }.select(&:present?)
ProcessHashtagsService.new.call(parent, tags)
end
def save_media(parent)
do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media?
@xml.xpath('./xmlns:link[@rel="enclosure"]', xmlns: TagManager::XMLNS).each do |link|
next unless link['href']
media = MediaAttachment.where(status: parent, remote_url: link['href']).first_or_initialize(account: parent.account, status: parent, remote_url: link['href'])
parsed_url = Addressable::URI.parse(link['href']).normalize
next if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty?
media.save
next if do_not_download
begin
media.file_remote_url = link['href']
media.save!
rescue ActiveRecord::RecordInvalid
next
end
end
end
def account_from_href(href)
url = Addressable::URI.parse(href).normalize
if TagManager.instance.web_domain?(url.host)
Account.find_local(url.path.gsub('/users/', ''))
else
Account.where(uri: href).or(Account.where(url: href)).first || FetchRemoteAccountService.new.call(href)
end
end
end
|