Merge tag 'v2.6.0rc1' into instance_only_statuses
This commit is contained in:
@ -34,6 +34,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
after_protocol_change! if protocol_changed?
|
||||
after_key_change! if key_changed? && !@options[:signed_with_known_key]
|
||||
check_featured_collection! if @account.featured_collection_url.present?
|
||||
check_links! unless @account.fields.empty?
|
||||
|
||||
@account
|
||||
rescue Oj::ParseError
|
||||
@ -99,6 +100,10 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id)
|
||||
end
|
||||
|
||||
def check_links!
|
||||
VerifyAccountLinksWorker.perform_async(@account.id)
|
||||
end
|
||||
|
||||
def actor_type
|
||||
if @json['type'].is_a?(Array)
|
||||
@json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) }
|
||||
|
||||
@ -2,16 +2,43 @@
|
||||
|
||||
class AfterBlockService < BaseService
|
||||
def call(account, target_account)
|
||||
FeedManager.instance.clear_from_timeline(account, target_account)
|
||||
clear_home_feed(account, target_account)
|
||||
clear_notifications(account, target_account)
|
||||
clear_conversations(account, target_account)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clear_home_feed(account, target_account)
|
||||
FeedManager.instance.clear_from_timeline(account, target_account)
|
||||
end
|
||||
|
||||
def clear_conversations(account, target_account)
|
||||
AccountConversation.where(account: account)
|
||||
.where('? = ANY(participant_account_ids)', target_account.id)
|
||||
.in_batches
|
||||
.destroy_all
|
||||
end
|
||||
|
||||
def clear_notifications(account, target_account)
|
||||
Notification.where(account: account).joins(:follow).where(activity_type: 'Follow', follows: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account).joins(mention: :status).where(activity_type: 'Mention', statuses: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account).joins(:favourite).where(activity_type: 'Favourite', favourites: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account).joins(:status).where(activity_type: 'Status', statuses: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account)
|
||||
.joins(:follow)
|
||||
.where(activity_type: 'Follow', follows: { account_id: target_account.id })
|
||||
.delete_all
|
||||
|
||||
Notification.where(account: account)
|
||||
.joins(mention: :status)
|
||||
.where(activity_type: 'Mention', statuses: { account_id: target_account.id })
|
||||
.delete_all
|
||||
|
||||
Notification.where(account: account)
|
||||
.joins(:favourite)
|
||||
.where(activity_type: 'Favourite', favourites: { account_id: target_account.id })
|
||||
.delete_all
|
||||
|
||||
Notification.where(account: account)
|
||||
.joins(:status)
|
||||
.where(activity_type: 'Status', statuses: { account_id: target_account.id })
|
||||
.delete_all
|
||||
end
|
||||
end
|
||||
|
||||
@ -12,7 +12,7 @@ class BatchedRemoveStatusService < BaseService
|
||||
def call(statuses)
|
||||
statuses = Status.where(id: statuses.map(&:id)).includes(:account, :stream_entry).flat_map { |status| [status] + status.reblogs.includes(:account, :stream_entry).to_a }
|
||||
|
||||
@mentions = statuses.map { |s| [s.id, s.mentions.includes(:account).to_a] }.to_h
|
||||
@mentions = statuses.map { |s| [s.id, s.active_mentions.includes(:account).to_a] }.to_h
|
||||
@tags = statuses.map { |s| [s.id, s.tags.pluck(:name)] }.to_h
|
||||
|
||||
@stream_entry_batches = []
|
||||
@ -39,7 +39,6 @@ class BatchedRemoveStatusService < BaseService
|
||||
# Cannot be batched
|
||||
statuses.each do |status|
|
||||
unpush_from_public_timelines(status)
|
||||
unpush_from_direct_timelines(status) if status.direct_visibility?
|
||||
batch_salmon_slaps(status) if status.local?
|
||||
end
|
||||
|
||||
@ -96,16 +95,6 @@ class BatchedRemoveStatusService < BaseService
|
||||
end
|
||||
end
|
||||
|
||||
def unpush_from_direct_timelines(status)
|
||||
payload = @json_payloads[status.id]
|
||||
redis.pipelined do
|
||||
@mentions[status.id].each do |mention|
|
||||
redis.publish("timeline:direct:#{mention.account.id}", payload) if mention.account.local?
|
||||
end
|
||||
redis.publish("timeline:direct:#{status.account.id}", payload) if status.account.local?
|
||||
end
|
||||
end
|
||||
|
||||
def batch_salmon_slaps(status)
|
||||
return if @mentions[status.id].empty?
|
||||
|
||||
|
||||
@ -6,14 +6,14 @@ class FanOutOnWriteService < BaseService
|
||||
def call(status)
|
||||
raise Mastodon::RaceConditionError if status.visibility.nil?
|
||||
|
||||
deliver_to_self(status) if status.account.local?
|
||||
|
||||
render_anonymous_payload(status)
|
||||
|
||||
if status.direct_visibility?
|
||||
deliver_to_own_conversation(status)
|
||||
elsif status.limited_visibility?
|
||||
deliver_to_mentioned_followers(status)
|
||||
deliver_to_direct_timelines(status)
|
||||
else
|
||||
deliver_to_self(status) if status.account.local?
|
||||
deliver_to_followers(status)
|
||||
deliver_to_lists(status)
|
||||
end
|
||||
@ -56,7 +56,7 @@ class FanOutOnWriteService < BaseService
|
||||
end
|
||||
|
||||
def deliver_to_mentioned_followers(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to mentioned followers"
|
||||
Rails.logger.debug "Delivering status #{status.id} to limited followers"
|
||||
|
||||
status.mentions.includes(:account).each do |mention|
|
||||
mentioned_account = mention.account
|
||||
@ -93,12 +93,7 @@ class FanOutOnWriteService < BaseService
|
||||
Redis.current.publish('timeline:public:local:media', @payload) if status.local?
|
||||
end
|
||||
|
||||
def deliver_to_direct_timelines(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to direct timelines"
|
||||
|
||||
status.mentions.includes(:account).each do |mention|
|
||||
Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local?
|
||||
end
|
||||
Redis.current.publish("timeline:direct:#{status.account.id}", @payload) if status.account.local?
|
||||
def deliver_to_own_conversation(status)
|
||||
AccountConversation.add_status(status.account, status)
|
||||
end
|
||||
end
|
||||
|
||||
@ -29,7 +29,7 @@ class FetchLinkCardService < BaseService
|
||||
end
|
||||
|
||||
attach_card if @card&.persisted?
|
||||
rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::LengthValidationError => e
|
||||
rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
|
||||
Rails.logger.debug "Error fetching link #{@url}: #{e}"
|
||||
nil
|
||||
end
|
||||
@ -87,34 +87,36 @@ class FetchLinkCardService < BaseService
|
||||
end
|
||||
|
||||
def attempt_oembed
|
||||
embed = FetchOEmbedService.new.call(@url, html: @html)
|
||||
service = FetchOEmbedService.new
|
||||
embed = service.call(@url, html: @html)
|
||||
url = Addressable::URI.parse(service.endpoint_url)
|
||||
|
||||
return false if embed.nil?
|
||||
|
||||
@card.type = embed[:type]
|
||||
@card.title = embed[:title] || ''
|
||||
@card.author_name = embed[:author_name] || ''
|
||||
@card.author_url = embed[:author_url] || ''
|
||||
@card.author_url = embed[:author_url].present? ? (url + embed[:author_url]).to_s : ''
|
||||
@card.provider_name = embed[:provider_name] || ''
|
||||
@card.provider_url = embed[:provider_url] || ''
|
||||
@card.provider_url = embed[:provider_url].present? ? (url + embed[:provider_url]).to_s : ''
|
||||
@card.width = 0
|
||||
@card.height = 0
|
||||
|
||||
case @card.type
|
||||
when 'link'
|
||||
@card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present?
|
||||
@card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
|
||||
when 'photo'
|
||||
return false if embed[:url].blank?
|
||||
|
||||
@card.embed_url = embed[:url]
|
||||
@card.image_remote_url = embed[:url]
|
||||
@card.embed_url = (url + embed[:url]).to_s
|
||||
@card.image_remote_url = (url + embed[:url]).to_s
|
||||
@card.width = embed[:width].presence || 0
|
||||
@card.height = embed[:height].presence || 0
|
||||
when 'video'
|
||||
@card.width = embed[:width].presence || 0
|
||||
@card.height = embed[:height].presence || 0
|
||||
@card.html = Formatter.instance.sanitize(embed[:html], Sanitize::Config::MASTODON_OEMBED)
|
||||
@card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present?
|
||||
@card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
|
||||
when 'rich'
|
||||
# Most providers rely on <script> tags, which is a no-no
|
||||
return false
|
||||
@ -146,7 +148,7 @@ class FetchLinkCardService < BaseService
|
||||
|
||||
@card.title = meta_property(page, 'og:title').presence || page.at_xpath('//title')&.content || ''
|
||||
@card.description = meta_property(page, 'og:description').presence || meta_property(page, 'description') || ''
|
||||
@card.image_remote_url = meta_property(page, 'og:image') if meta_property(page, 'og:image')
|
||||
@card.image_remote_url = (Addressable::URI.parse(@url) + meta_property(page, 'og:image')).to_s if meta_property(page, 'og:image')
|
||||
|
||||
return if @card.title.blank? && @card.html.blank?
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ class FetchOEmbedService
|
||||
|
||||
return if @endpoint_url.blank?
|
||||
|
||||
@endpoint_url = Addressable::URI.parse(@endpoint_url).to_s
|
||||
@endpoint_url = (Addressable::URI.parse(@url) + @endpoint_url).to_s
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
@endpoint_url = nil
|
||||
end
|
||||
|
||||
@ -5,11 +5,13 @@ class MuteService < BaseService
|
||||
return if account.id == target_account.id
|
||||
|
||||
mute = account.mute!(target_account, notifications: notifications)
|
||||
|
||||
if mute.hide_notifications?
|
||||
BlockWorker.perform_async(account.id, target_account.id)
|
||||
else
|
||||
FeedManager.instance.clear_from_timeline(account, target_account)
|
||||
MuteWorker.perform_async(account.id, target_account.id)
|
||||
end
|
||||
|
||||
mute
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,9 +8,10 @@ class NotifyService < BaseService
|
||||
|
||||
return if recipient.user.nil? || blocked?
|
||||
|
||||
create_notification
|
||||
push_notification if @notification.browserable?
|
||||
send_email if email_enabled?
|
||||
create_notification!
|
||||
push_notification! if @notification.browserable?
|
||||
push_to_conversation! if direct_message?
|
||||
send_email! if email_enabled?
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
return
|
||||
end
|
||||
@ -58,9 +59,14 @@ class NotifyService < BaseService
|
||||
@notification.target_status.in_reply_to_account_id == @recipient.id && @notification.target_status.thread&.direct_visibility?
|
||||
end
|
||||
|
||||
def from_staff?
|
||||
@notification.from_account.local? && @notification.from_account.user.present? && @notification.from_account.user.staff?
|
||||
end
|
||||
|
||||
def optional_non_following_and_direct?
|
||||
direct_message? &&
|
||||
@recipient.user.settings.interactions['must_be_following_dm'] &&
|
||||
!from_staff? &&
|
||||
!following_sender? &&
|
||||
!response_to_recipient?
|
||||
end
|
||||
@ -100,18 +106,23 @@ class NotifyService < BaseService
|
||||
end
|
||||
end
|
||||
|
||||
def create_notification
|
||||
def create_notification!
|
||||
@notification.save!
|
||||
end
|
||||
|
||||
def push_notification
|
||||
def push_notification!
|
||||
return if @notification.activity.nil?
|
||||
|
||||
Redis.current.publish("timeline:#{@recipient.id}", Oj.dump(event: :notification, payload: InlineRenderer.render(@notification, @recipient, :notification)))
|
||||
send_push_notifications
|
||||
send_push_notifications!
|
||||
end
|
||||
|
||||
def send_push_notifications
|
||||
def push_to_conversation!
|
||||
return if @notification.activity.nil?
|
||||
AccountConversation.add_status(@recipient, @notification.target_status)
|
||||
end
|
||||
|
||||
def send_push_notifications!
|
||||
subscriptions_ids = ::Web::PushSubscription.where(user_id: @recipient.user.id)
|
||||
.select { |subscription| subscription.pushable?(@notification) }
|
||||
.map(&:id)
|
||||
@ -121,7 +132,7 @@ class NotifyService < BaseService
|
||||
end
|
||||
end
|
||||
|
||||
def send_email
|
||||
def send_email!
|
||||
return if @notification.activity.nil?
|
||||
NotificationMailer.public_send(@notification.type, @recipient, @notification).deliver_later(wait: 2.minutes)
|
||||
end
|
||||
|
||||
@ -8,7 +8,7 @@ class RemoveStatusService < BaseService
|
||||
@status = status
|
||||
@account = status.account
|
||||
@tags = status.tags.pluck(:name).to_a
|
||||
@mentions = status.mentions.includes(:account).to_a
|
||||
@mentions = status.active_mentions.includes(:account).to_a
|
||||
@reblogs = status.reblogs.to_a
|
||||
@stream_entry = status.stream_entry
|
||||
@options = options
|
||||
@ -21,7 +21,6 @@ class RemoveStatusService < BaseService
|
||||
remove_from_hashtags
|
||||
remove_from_public
|
||||
remove_from_media if status.media_attachments.any?
|
||||
remove_from_direct if status.direct_visibility?
|
||||
|
||||
@status.destroy!
|
||||
|
||||
@ -153,13 +152,6 @@ class RemoveStatusService < BaseService
|
||||
Redis.current.publish('timeline:public:local:media', @payload) if @status.local?
|
||||
end
|
||||
|
||||
def remove_from_direct
|
||||
@mentions.each do |mention|
|
||||
Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local?
|
||||
end
|
||||
Redis.current.publish("timeline:direct:#{@account.id}", @payload) if @account.local?
|
||||
end
|
||||
|
||||
def redis
|
||||
Redis.current
|
||||
end
|
||||
|
||||
@ -2,20 +2,26 @@
|
||||
|
||||
class UpdateAccountService < BaseService
|
||||
def call(account, params, raise_error: false)
|
||||
was_locked = account.locked
|
||||
was_locked = account.locked
|
||||
update_method = raise_error ? :update! : :update
|
||||
|
||||
account.send(update_method, params).tap do |ret|
|
||||
next unless ret
|
||||
|
||||
authorize_all_follow_requests(account) if was_locked && !account.locked
|
||||
check_links(account)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authorize_all_follow_requests(account)
|
||||
follow_requests = FollowRequest.where(target_account: account)
|
||||
AuthorizeFollowWorker.push_bulk(follow_requests) do |req|
|
||||
AuthorizeFollowWorker.push_bulk(FollowRequest.where(target_account: account).select(:account_id, :target_account_id)) do |req|
|
||||
[req.account_id, req.target_account_id]
|
||||
end
|
||||
end
|
||||
|
||||
def check_links(account)
|
||||
VerifyAccountLinksWorker.perform_async(account.id)
|
||||
end
|
||||
end
|
||||
|
||||
48
app/services/verify_link_service.rb
Normal file
48
app/services/verify_link_service.rb
Normal file
@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class VerifyLinkService < BaseService
|
||||
def call(field)
|
||||
@link_back = ActivityPub::TagManager.instance.url_for(field.account)
|
||||
@url = field.value_for_verification
|
||||
|
||||
perform_request!
|
||||
|
||||
return unless link_back_present?
|
||||
|
||||
field.mark_verified!
|
||||
field.account.save!
|
||||
rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
|
||||
Rails.logger.debug "Error fetching link #{@url}: #{e}"
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def perform_request!
|
||||
@body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
|
||||
res.code != 200 ? nil : res.body_with_limit
|
||||
end
|
||||
end
|
||||
|
||||
def link_back_present?
|
||||
return false if @body.empty?
|
||||
|
||||
links = Nokogiri::HTML(@body).xpath('//a[contains(concat(" ", normalize-space(@rel), " "), " me ")]|//link[contains(concat(" ", normalize-space(@rel), " "), " me ")]')
|
||||
|
||||
if links.any? { |link| link['href'] == @link_back }
|
||||
true
|
||||
elsif links.empty?
|
||||
false
|
||||
else
|
||||
link_redirects_back?(links.first['href'])
|
||||
end
|
||||
end
|
||||
|
||||
def link_redirects_back?(test_url)
|
||||
redirect_to_url = Request.new(:head, test_url, follow: false).perform do |res|
|
||||
res.headers['Location']
|
||||
end
|
||||
|
||||
redirect_to_url == @link_back
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user