Gab Social. All are welcome.
This commit is contained in:
60
app/workers/activitypub/delivery_worker.rb
Normal file
60
app/workers/activitypub/delivery_worker.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::DeliveryWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
STOPLIGHT_FAILURE_THRESHOLD = 10
|
||||
STOPLIGHT_COOLDOWN = 60
|
||||
|
||||
sidekiq_options queue: 'push', retry: 16, dead: false
|
||||
|
||||
HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
|
||||
|
||||
def perform(json, source_account_id, inbox_url, options = {})
|
||||
return if DeliveryFailureTracker.unavailable?(inbox_url)
|
||||
|
||||
@options = options.with_indifferent_access
|
||||
@json = json
|
||||
@source_account = Account.find(source_account_id)
|
||||
@inbox_url = inbox_url
|
||||
|
||||
perform_request
|
||||
|
||||
failure_tracker.track_success!
|
||||
rescue => e
|
||||
failure_tracker.track_failure!
|
||||
raise e.class, "Delivery failed for #{inbox_url}: #{e.message}", e.backtrace[0]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_request
|
||||
request = Request.new(:post, @inbox_url, body: @json)
|
||||
request.on_behalf_of(@source_account, :uri, sign_with: @options[:sign_with])
|
||||
request.add_headers(HEADERS)
|
||||
end
|
||||
|
||||
def perform_request
|
||||
light = Stoplight(@inbox_url) do
|
||||
build_request.perform do |response|
|
||||
raise GabSocial::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response)
|
||||
end
|
||||
end
|
||||
|
||||
light.with_threshold(STOPLIGHT_FAILURE_THRESHOLD)
|
||||
.with_cool_off_time(STOPLIGHT_COOLDOWN)
|
||||
.run
|
||||
end
|
||||
|
||||
def response_successful?(response)
|
||||
(200...300).cover?(response.code)
|
||||
end
|
||||
|
||||
def response_error_unsalvageable?(response)
|
||||
(400...500).cover?(response.code) && ![401, 408, 429].include?(response.code)
|
||||
end
|
||||
|
||||
def failure_tracker
|
||||
@failure_tracker ||= DeliveryFailureTracker.new(@inbox_url)
|
||||
end
|
||||
end
|
||||
65
app/workers/activitypub/distribute_poll_update_worker.rb
Normal file
65
app/workers/activitypub/distribute_poll_update_worker.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::DistributePollUpdateWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push', unique: :until_executed, retry: 0
|
||||
|
||||
def perform(status_id)
|
||||
@status = Status.find(status_id)
|
||||
@account = @status.account
|
||||
|
||||
return unless @status.preloadable_poll
|
||||
|
||||
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
|
||||
[payload, @account.id, inbox_url]
|
||||
end
|
||||
|
||||
relay! if relayable?
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relayable?
|
||||
@status.public_visibility?
|
||||
end
|
||||
|
||||
def inboxes
|
||||
return @inboxes if defined?(@inboxes)
|
||||
|
||||
@inboxes = [@status.mentions, @status.reblogs, @status.preloadable_poll.votes].flat_map do |relation|
|
||||
relation.includes(:account).map do |record|
|
||||
record.account.preferred_inbox_url if !record.account.local? && record.account.activitypub?
|
||||
end
|
||||
end
|
||||
|
||||
@inboxes.concat(@account.followers.inboxes) unless @status.direct_visibility?
|
||||
@inboxes.uniq!
|
||||
@inboxes.compact!
|
||||
@inboxes
|
||||
end
|
||||
|
||||
def signed_payload
|
||||
Oj.dump(ActivityPub::LinkedDataSignature.new(unsigned_payload).sign!(@account))
|
||||
end
|
||||
|
||||
def unsigned_payload
|
||||
ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::UpdatePollSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
end
|
||||
|
||||
def payload
|
||||
@payload ||= @status.distributable? ? signed_payload : Oj.dump(unsigned_payload)
|
||||
end
|
||||
|
||||
def relay!
|
||||
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
|
||||
[payload, @account.id, inbox_url]
|
||||
end
|
||||
end
|
||||
end
|
||||
65
app/workers/activitypub/distribution_worker.rb
Normal file
65
app/workers/activitypub/distribution_worker.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::DistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(status_id)
|
||||
@status = Status.find(status_id)
|
||||
@account = @status.account
|
||||
|
||||
return if skip_distribution?
|
||||
|
||||
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
|
||||
[payload, @account.id, inbox_url]
|
||||
end
|
||||
|
||||
relay! if relayable?
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def skip_distribution?
|
||||
@status.direct_visibility? || @status.limited_visibility?
|
||||
end
|
||||
|
||||
def relayable?
|
||||
@status.public_visibility?
|
||||
end
|
||||
|
||||
def inboxes
|
||||
# Deliver the status to all followers.
|
||||
# If the status is a reply to another local status, also forward it to that
|
||||
# status' authors' followers.
|
||||
@inboxes ||= if @status.reply? && @status.thread.account.local? && @status.distributable?
|
||||
@account.followers.or(@status.thread.account.followers).inboxes
|
||||
else
|
||||
@account.followers.inboxes
|
||||
end
|
||||
end
|
||||
|
||||
def signed_payload
|
||||
Oj.dump(ActivityPub::LinkedDataSignature.new(unsigned_payload).sign!(@account))
|
||||
end
|
||||
|
||||
def unsigned_payload
|
||||
ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::ActivitySerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
end
|
||||
|
||||
def payload
|
||||
@payload ||= @status.distributable? ? signed_payload : Oj.dump(unsigned_payload)
|
||||
end
|
||||
|
||||
def relay!
|
||||
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
|
||||
[payload, @account.id, inbox_url]
|
||||
end
|
||||
end
|
||||
end
|
||||
14
app/workers/activitypub/fetch_replies_worker.rb
Normal file
14
app/workers/activitypub/fetch_replies_worker.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FetchRepliesWorker
|
||||
include Sidekiq::Worker
|
||||
include ExponentialBackoff
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 3
|
||||
|
||||
def perform(parent_status_id, replies_uri)
|
||||
ActivityPub::FetchRepliesService.new.call(Status.find(parent_status_id), replies_uri)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
5
app/workers/activitypub/low_priority_delivery_worker.rb
Normal file
5
app/workers/activitypub/low_priority_delivery_worker.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::LowPriorityDeliveryWorker < ActivityPub::DeliveryWorker
|
||||
sidekiq_options queue: 'pull', retry: 8, dead: false
|
||||
end
|
||||
15
app/workers/activitypub/post_upgrade_worker.rb
Normal file
15
app/workers/activitypub/post_upgrade_worker.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::PostUpgradeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(domain)
|
||||
Account.where(domain: domain)
|
||||
.where(protocol: :ostatus)
|
||||
.where.not(last_webfingered_at: nil)
|
||||
.in_batches
|
||||
.update_all(last_webfingered_at: nil)
|
||||
end
|
||||
end
|
||||
13
app/workers/activitypub/processing_worker.rb
Normal file
13
app/workers/activitypub/processing_worker.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::ProcessingWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options backtrace: true
|
||||
|
||||
def perform(account_id, body, delivered_to_account_id = nil)
|
||||
ActivityPub::ProcessCollectionService.new.call(body, Account.find(account_id), override_timestamps: true, delivered_to_account_id: delivered_to_account_id, delivery: true)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
Rails.logger.debug "Error processing incoming ActivityPub object: #{e}"
|
||||
end
|
||||
end
|
||||
23
app/workers/activitypub/raw_distribution_worker.rb
Normal file
23
app/workers/activitypub/raw_distribution_worker.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::RawDistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(json, source_account_id, exclude_inboxes = [])
|
||||
@account = Account.find(source_account_id)
|
||||
|
||||
ActivityPub::DeliveryWorker.push_bulk(inboxes - exclude_inboxes) do |inbox_url|
|
||||
[json, @account.id, inbox_url]
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inboxes
|
||||
@inboxes ||= @account.followers.inboxes
|
||||
end
|
||||
end
|
||||
45
app/workers/activitypub/reply_distribution_worker.rb
Normal file
45
app/workers/activitypub/reply_distribution_worker.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Obsolete but kept around to make sure existing jobs do not fail after upgrade.
|
||||
# Should be removed in a subsequent release.
|
||||
|
||||
class ActivityPub::ReplyDistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(status_id)
|
||||
@status = Status.find(status_id)
|
||||
@account = @status.thread&.account
|
||||
|
||||
return unless @account.present? && @status.distributable?
|
||||
|
||||
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
|
||||
[payload, @status.account_id, inbox_url]
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inboxes
|
||||
@inboxes ||= @account.followers.inboxes
|
||||
end
|
||||
|
||||
def signed_payload
|
||||
Oj.dump(ActivityPub::LinkedDataSignature.new(unsigned_payload).sign!(@status.account))
|
||||
end
|
||||
|
||||
def unsigned_payload
|
||||
ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::ActivitySerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
end
|
||||
|
||||
def payload
|
||||
@payload ||= @status.distributable? ? signed_payload : Oj.dump(unsigned_payload)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::SynchronizeFeaturedCollectionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', unique: :until_executed
|
||||
|
||||
def perform(account_id)
|
||||
ActivityPub::FetchFeaturedCollectionService.new.call(Account.find(account_id))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
40
app/workers/activitypub/update_distribution_worker.rb
Normal file
40
app/workers/activitypub/update_distribution_worker.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::UpdateDistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(account_id, options = {})
|
||||
@options = options.with_indifferent_access
|
||||
@account = Account.find(account_id)
|
||||
|
||||
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
|
||||
[signed_payload, @account.id, inbox_url]
|
||||
end
|
||||
|
||||
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
|
||||
[signed_payload, @account.id, inbox_url]
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inboxes
|
||||
@inboxes ||= @account.followers.inboxes
|
||||
end
|
||||
|
||||
def signed_payload
|
||||
@signed_payload ||= Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account, sign_with: @options[:sign_with]))
|
||||
end
|
||||
|
||||
def payload
|
||||
@payload ||= ActiveModelSerializers::SerializableResource.new(
|
||||
@account,
|
||||
serializer: ActivityPub::UpdateSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
end
|
||||
end
|
||||
11
app/workers/admin/suspension_worker.rb
Normal file
11
app/workers/admin/suspension_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SuspensionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(account_id, remove_user = false)
|
||||
SuspendAccountService.new.call(Account.find(account_id), including_user: remove_user)
|
||||
end
|
||||
end
|
||||
11
app/workers/after_account_domain_block_worker.rb
Normal file
11
app/workers/after_account_domain_block_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AfterAccountDomainBlockWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, domain)
|
||||
AfterBlockDomainFromAccountService.new.call(Account.find(account_id), domain)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
31
app/workers/after_remote_follow_request_worker.rb
Normal file
31
app/workers/after_remote_follow_request_worker.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AfterRemoteFollowRequestWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 5
|
||||
|
||||
attr_reader :follow_request
|
||||
|
||||
def perform(follow_request_id)
|
||||
@follow_request = FollowRequest.find(follow_request_id)
|
||||
process_follow_service if processing_required?
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_follow_service
|
||||
follow_request.destroy
|
||||
FollowService.new.call(follow_request.account, updated_account.acct)
|
||||
end
|
||||
|
||||
def processing_required?
|
||||
!updated_account.nil? && !updated_account.locked?
|
||||
end
|
||||
|
||||
def updated_account
|
||||
@_updated_account ||= FetchRemoteAccountService.new.call(follow_request.target_account.remote_url)
|
||||
end
|
||||
end
|
||||
31
app/workers/after_remote_follow_worker.rb
Normal file
31
app/workers/after_remote_follow_worker.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AfterRemoteFollowWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 5
|
||||
|
||||
attr_reader :follow
|
||||
|
||||
def perform(follow_id)
|
||||
@follow = Follow.find(follow_id)
|
||||
process_follow_service if processing_required?
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_follow_service
|
||||
follow.destroy
|
||||
FollowService.new.call(follow.account, updated_account.acct)
|
||||
end
|
||||
|
||||
def updated_account
|
||||
@_updated_account ||= FetchRemoteAccountService.new.call(follow.target_account.remote_url)
|
||||
end
|
||||
|
||||
def processing_required?
|
||||
!updated_account.nil? && updated_account.locked?
|
||||
end
|
||||
end
|
||||
14
app/workers/authorize_follow_worker.rb
Normal file
14
app/workers/authorize_follow_worker.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AuthorizeFollowWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(source_account_id, target_account_id)
|
||||
source_account = Account.find(source_account_id)
|
||||
target_account = Account.find(target_account_id)
|
||||
|
||||
AuthorizeFollowService.new.call(source_account, target_account)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
26
app/workers/backup_worker.rb
Normal file
26
app/workers/backup_worker.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BackupWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', backtrace: true, retry: 5, dead: false
|
||||
|
||||
sidekiq_retries_exhausted do |msg|
|
||||
backup_id = msg['args'].first
|
||||
|
||||
ActiveRecord::Base.connection_pool.with_connection do
|
||||
backup = Backup.find(backup_id)
|
||||
backup&.destroy
|
||||
end
|
||||
end
|
||||
|
||||
def perform(backup_id)
|
||||
backup = Backup.find(backup_id)
|
||||
user = backup.user
|
||||
|
||||
BackupService.new.call(backup)
|
||||
|
||||
user.backups.where.not(id: backup.id).destroy_all
|
||||
UserMailer.backup_ready(user, backup).deliver_later
|
||||
end
|
||||
end
|
||||
12
app/workers/block_worker.rb
Normal file
12
app/workers/block_worker.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BlockWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, target_account_id)
|
||||
AfterBlockService.new.call(
|
||||
Account.find(account_id),
|
||||
Account.find(target_account_id)
|
||||
)
|
||||
end
|
||||
end
|
||||
9
app/workers/bootstrap_timeline_worker.rb
Normal file
9
app/workers/bootstrap_timeline_worker.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BootstrapTimelineWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id)
|
||||
BootstrapTimelineService.new.call(Account.find(account_id))
|
||||
end
|
||||
end
|
||||
11
app/workers/concerns/exponential_backoff.rb
Normal file
11
app/workers/concerns/exponential_backoff.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ExponentialBackoff
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
sidekiq_retry_in do |count|
|
||||
15 + 10 * (count**4) + rand(10 * (count**4))
|
||||
end
|
||||
end
|
||||
end
|
||||
21
app/workers/digest_mailer_worker.rb
Normal file
21
app/workers/digest_mailer_worker.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DigestMailerWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'mailers'
|
||||
|
||||
attr_reader :user
|
||||
|
||||
def perform(user_id)
|
||||
@user = User.find(user_id)
|
||||
deliver_digest if @user.allows_digest_emails?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def deliver_digest
|
||||
NotificationMailer.digest(user.account).deliver_now!
|
||||
user.touch(:last_emailed_at)
|
||||
end
|
||||
end
|
||||
17
app/workers/distribution_worker.rb
Normal file
17
app/workers/distribution_worker.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(status_id)
|
||||
RedisLock.acquire(redis: Redis.current, key: "distribute:#{status_id}") do |lock|
|
||||
if lock.acquired?
|
||||
FanOutOnWriteService.new.call(Status.find(status_id))
|
||||
else
|
||||
raise GabSocial::RaceConditionError
|
||||
end
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/workers/domain_block_worker.rb
Normal file
11
app/workers/domain_block_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DomainBlockWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(domain_block_id)
|
||||
BlockDomainService.new.call(DomainBlock.find(domain_block_id))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
43
app/workers/feed_insert_worker.rb
Normal file
43
app/workers/feed_insert_worker.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FeedInsertWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(status_id, id, type = :home)
|
||||
@type = type.to_sym
|
||||
@status = Status.find(status_id)
|
||||
|
||||
case @type
|
||||
when :home
|
||||
@follower = Account.find(id)
|
||||
when :list
|
||||
@list = List.find(id)
|
||||
@follower = @list.account
|
||||
end
|
||||
|
||||
check_and_insert
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_and_insert
|
||||
perform_push unless feed_filtered?
|
||||
end
|
||||
|
||||
def feed_filtered?
|
||||
# Note: Lists are a variation of home, so the filtering rules
|
||||
# of home apply to both
|
||||
FeedManager.instance.filter?(:home, @status, @follower.id)
|
||||
end
|
||||
|
||||
def perform_push
|
||||
case @type
|
||||
when :home
|
||||
FeedManager.instance.push_to_home(@follower, @status)
|
||||
when :list
|
||||
FeedManager.instance.push_to_list(@list, @status)
|
||||
end
|
||||
end
|
||||
end
|
||||
12
app/workers/fetch_reply_worker.rb
Normal file
12
app/workers/fetch_reply_worker.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FetchReplyWorker
|
||||
include Sidekiq::Worker
|
||||
include ExponentialBackoff
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 3
|
||||
|
||||
def perform(child_url)
|
||||
FetchRemoteStatusService.new.call(child_url)
|
||||
end
|
||||
end
|
||||
32
app/workers/import/relationship_worker.rb
Normal file
32
app/workers/import/relationship_worker.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Import::RelationshipWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 8, dead: false
|
||||
|
||||
def perform(account_id, target_account_uri, relationship, options = {})
|
||||
from_account = Account.find(account_id)
|
||||
target_account = ResolveAccountService.new.call(target_account_uri)
|
||||
options.symbolize_keys!
|
||||
|
||||
return if target_account.nil?
|
||||
|
||||
case relationship
|
||||
when 'follow'
|
||||
FollowService.new.call(from_account, target_account, options)
|
||||
when 'unfollow'
|
||||
UnfollowService.new.call(from_account, target_account)
|
||||
when 'block'
|
||||
BlockService.new.call(from_account, target_account)
|
||||
when 'unblock'
|
||||
UnblockService.new.call(from_account, target_account)
|
||||
when 'mute'
|
||||
MuteService.new.call(from_account, target_account, options)
|
||||
when 'unmute'
|
||||
UnmuteService.new.call(from_account, target_account)
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
14
app/workers/import_worker.rb
Normal file
14
app/workers/import_worker.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ImportWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(import_id)
|
||||
import = Import.find(import_id)
|
||||
ImportService.new.call(import)
|
||||
ensure
|
||||
import&.destroy
|
||||
end
|
||||
end
|
||||
13
app/workers/link_crawl_worker.rb
Normal file
13
app/workers/link_crawl_worker.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class LinkCrawlWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 0
|
||||
|
||||
def perform(status_id)
|
||||
FetchLinkCardService.new.call(Status.find(status_id))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
19
app/workers/local_notification_worker.rb
Normal file
19
app/workers/local_notification_worker.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class LocalNotificationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(receiver_account_id, activity_id = nil, activity_class_name = nil)
|
||||
if activity_id.nil? && activity_class_name.nil?
|
||||
activity = Mention.find(receiver_account_id)
|
||||
receiver = activity.account
|
||||
else
|
||||
receiver = Account.find(receiver_account_id)
|
||||
activity = activity_class_name.constantize.find(activity_id)
|
||||
end
|
||||
|
||||
NotifyService.new.call(receiver, activity)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
14
app/workers/maintenance/destroy_media_worker.rb
Normal file
14
app/workers/maintenance/destroy_media_worker.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Maintenance::DestroyMediaWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(media_attachment_id)
|
||||
media = media_attachment_id.is_a?(MediaAttachment) ? media_attachment_id : MediaAttachment.find(media_attachment_id)
|
||||
media.destroy
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
16
app/workers/maintenance/redownload_account_media_worker.rb
Normal file
16
app/workers/maintenance/redownload_account_media_worker.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Maintenance::RedownloadAccountMediaWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(account_id)
|
||||
account = account_id.is_a?(Account) ? account_id : Account.find(account_id)
|
||||
account.reset_avatar!
|
||||
account.reset_header!
|
||||
account.save
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
18
app/workers/maintenance/uncache_media_worker.rb
Normal file
18
app/workers/maintenance/uncache_media_worker.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Maintenance::UncacheMediaWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(media_attachment_id)
|
||||
media = media_attachment_id.is_a?(MediaAttachment) ? media_attachment_id : MediaAttachment.find(media_attachment_id)
|
||||
|
||||
return if media.file.blank?
|
||||
|
||||
media.file.destroy
|
||||
media.save
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/workers/merge_worker.rb
Normal file
11
app/workers/merge_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(from_account_id, into_account_id)
|
||||
FeedManager.instance.merge_into_timeline(Account.find(from_account_id), Account.find(into_account_id))
|
||||
end
|
||||
end
|
||||
12
app/workers/mute_worker.rb
Normal file
12
app/workers/mute_worker.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MuteWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, target_account_id)
|
||||
FeedManager.instance.clear_from_timeline(
|
||||
Account.find(account_id),
|
||||
Account.find(target_account_id)
|
||||
)
|
||||
end
|
||||
end
|
||||
11
app/workers/notification_worker.rb
Normal file
11
app/workers/notification_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class NotificationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push', retry: 5
|
||||
|
||||
def perform(xml, source_account_id, target_account_id)
|
||||
SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id))
|
||||
end
|
||||
end
|
||||
24
app/workers/poll_expiration_notify_worker.rb
Normal file
24
app/workers/poll_expiration_notify_worker.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PollExpirationNotifyWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed
|
||||
|
||||
def perform(poll_id)
|
||||
poll = Poll.find(poll_id)
|
||||
|
||||
# Notify poll owner and remote voters
|
||||
if poll.local?
|
||||
ActivityPub::DistributePollUpdateWorker.perform_async(poll.status.id)
|
||||
NotifyService.new.call(poll.account, poll)
|
||||
end
|
||||
|
||||
# Notify local voters
|
||||
poll.votes.includes(:account).map(&:account).select(&:local?).each do |account|
|
||||
NotifyService.new.call(account, poll)
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/workers/processing_worker.rb
Normal file
11
app/workers/processing_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProcessingWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options backtrace: true
|
||||
|
||||
def perform(account_id, body)
|
||||
ProcessFeedService.new.call(body, Account.find(account_id), override_timestamps: true)
|
||||
end
|
||||
end
|
||||
26
app/workers/publish_scheduled_status_worker.rb
Normal file
26
app/workers/publish_scheduled_status_worker.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PublishScheduledStatusWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed
|
||||
|
||||
def perform(scheduled_status_id)
|
||||
scheduled_status = ScheduledStatus.find(scheduled_status_id)
|
||||
scheduled_status.destroy!
|
||||
|
||||
PostStatusService.new.call(
|
||||
scheduled_status.account,
|
||||
options_with_objects(scheduled_status.params.with_indifferent_access)
|
||||
)
|
||||
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
|
||||
true
|
||||
end
|
||||
|
||||
def options_with_objects(options)
|
||||
options.tap do |options_hash|
|
||||
options_hash[:application] = Doorkeeper::Application.find(options_hash.delete(:application_id)) if options[:application_id]
|
||||
options_hash[:thread] = Status.find(options_hash.delete(:in_reply_to_id)) if options_hash[:in_reply_to_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
82
app/workers/pubsubhubbub/confirmation_worker.rb
Normal file
82
app/workers/pubsubhubbub/confirmation_worker.rb
Normal file
@@ -0,0 +1,82 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pubsubhubbub::ConfirmationWorker
|
||||
include Sidekiq::Worker
|
||||
include RoutingHelper
|
||||
|
||||
sidekiq_options queue: 'push', retry: false
|
||||
|
||||
attr_reader :subscription, :mode, :secret, :lease_seconds
|
||||
|
||||
def perform(subscription_id, mode, secret = nil, lease_seconds = nil)
|
||||
@subscription = Subscription.find(subscription_id)
|
||||
@mode = mode
|
||||
@secret = secret
|
||||
@lease_seconds = lease_seconds
|
||||
process_confirmation
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_confirmation
|
||||
prepare_subscription
|
||||
|
||||
callback_get_with_params
|
||||
logger.debug "Confirming PuSH subscription for #{subscription.callback_url} with challenge #{challenge}: #{@callback_response_body}"
|
||||
|
||||
update_subscription
|
||||
end
|
||||
|
||||
def update_subscription
|
||||
if successful_subscribe?
|
||||
subscription.save!
|
||||
elsif successful_unsubscribe?
|
||||
subscription.destroy!
|
||||
end
|
||||
end
|
||||
|
||||
def successful_subscribe?
|
||||
subscribing? && response_matches_challenge?
|
||||
end
|
||||
|
||||
def successful_unsubscribe?
|
||||
(unsubscribing? && response_matches_challenge?) || !subscription.confirmed?
|
||||
end
|
||||
|
||||
def response_matches_challenge?
|
||||
@callback_response_body == challenge
|
||||
end
|
||||
|
||||
def subscribing?
|
||||
mode == 'subscribe'
|
||||
end
|
||||
|
||||
def unsubscribing?
|
||||
mode == 'unsubscribe'
|
||||
end
|
||||
|
||||
def callback_get_with_params
|
||||
Request.new(:get, subscription.callback_url, params: callback_params).perform do |response|
|
||||
@callback_response_body = response.body_with_limit
|
||||
end
|
||||
end
|
||||
|
||||
def callback_params
|
||||
{
|
||||
'hub.topic': account_url(subscription.account, format: :atom),
|
||||
'hub.mode': mode,
|
||||
'hub.challenge': challenge,
|
||||
'hub.lease_seconds': subscription.lease_seconds,
|
||||
}
|
||||
end
|
||||
|
||||
def prepare_subscription
|
||||
subscription.secret = secret
|
||||
subscription.lease_seconds = lease_seconds
|
||||
subscription.confirmed = true
|
||||
end
|
||||
|
||||
def challenge
|
||||
@_challenge ||= SecureRandom.hex
|
||||
end
|
||||
end
|
||||
81
app/workers/pubsubhubbub/delivery_worker.rb
Normal file
81
app/workers/pubsubhubbub/delivery_worker.rb
Normal file
@@ -0,0 +1,81 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pubsubhubbub::DeliveryWorker
|
||||
include Sidekiq::Worker
|
||||
include RoutingHelper
|
||||
|
||||
sidekiq_options queue: 'push', retry: 3, dead: false
|
||||
|
||||
sidekiq_retry_in do |count|
|
||||
5 * (count + 1)
|
||||
end
|
||||
|
||||
attr_reader :subscription, :payload
|
||||
|
||||
def perform(subscription_id, payload)
|
||||
@subscription = Subscription.find(subscription_id)
|
||||
@payload = payload
|
||||
process_delivery unless blocked_domain?
|
||||
rescue => e
|
||||
raise e.class, "Delivery failed for #{subscription&.callback_url}: #{e.message}", e.backtrace[0]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_delivery
|
||||
callback_post_payload do |payload_delivery|
|
||||
raise GabSocial::UnexpectedResponseError, payload_delivery unless response_successful? payload_delivery
|
||||
end
|
||||
|
||||
subscription.touch(:last_successful_delivery_at)
|
||||
end
|
||||
|
||||
def callback_post_payload(&block)
|
||||
request = Request.new(:post, subscription.callback_url, body: payload)
|
||||
request.add_headers(headers)
|
||||
request.perform(&block)
|
||||
end
|
||||
|
||||
def blocked_domain?
|
||||
DomainBlock.blocked?(host)
|
||||
end
|
||||
|
||||
def host
|
||||
Addressable::URI.parse(subscription.callback_url).normalized_host
|
||||
end
|
||||
|
||||
def headers
|
||||
{
|
||||
'Content-Type' => 'application/atom+xml',
|
||||
'Link' => link_header,
|
||||
}.merge(signature_headers.to_h)
|
||||
end
|
||||
|
||||
def link_header
|
||||
LinkHeader.new([hub_link_header, self_link_header]).to_s
|
||||
end
|
||||
|
||||
def hub_link_header
|
||||
[api_push_url, [%w(rel hub)]]
|
||||
end
|
||||
|
||||
def self_link_header
|
||||
[account_url(subscription.account, format: :atom), [%w(rel self)]]
|
||||
end
|
||||
|
||||
def signature_headers
|
||||
{ 'X-Hub-Signature' => payload_signature } if subscription.secret?
|
||||
end
|
||||
|
||||
def payload_signature
|
||||
"sha1=#{hmac_payload_digest}"
|
||||
end
|
||||
|
||||
def hmac_payload_digest
|
||||
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret, payload)
|
||||
end
|
||||
|
||||
def response_successful?(payload_delivery)
|
||||
payload_delivery.code > 199 && payload_delivery.code < 300
|
||||
end
|
||||
end
|
||||
32
app/workers/pubsubhubbub/distribution_worker.rb
Normal file
32
app/workers/pubsubhubbub/distribution_worker.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pubsubhubbub::DistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(stream_entry_ids)
|
||||
stream_entries = StreamEntry.where(id: stream_entry_ids).includes(:status).reject { |e| e.status.nil? || e.status.hidden? }
|
||||
|
||||
return if stream_entries.empty?
|
||||
|
||||
@account = stream_entries.first.account
|
||||
@subscriptions = active_subscriptions.to_a
|
||||
|
||||
distribute_public!(stream_entries)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def distribute_public!(stream_entries)
|
||||
@payload = OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, stream_entries))
|
||||
|
||||
Pubsubhubbub::DeliveryWorker.push_bulk(@subscriptions) do |subscription_id|
|
||||
[subscription_id, @payload]
|
||||
end
|
||||
end
|
||||
|
||||
def active_subscriptions
|
||||
Subscription.where(account: @account).active.pluck(:id)
|
||||
end
|
||||
end
|
||||
22
app/workers/pubsubhubbub/raw_distribution_worker.rb
Normal file
22
app/workers/pubsubhubbub/raw_distribution_worker.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pubsubhubbub::RawDistributionWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(xml, source_account_id)
|
||||
@account = Account.find(source_account_id)
|
||||
@subscriptions = active_subscriptions.to_a
|
||||
|
||||
Pubsubhubbub::DeliveryWorker.push_bulk(@subscriptions) do |subscription|
|
||||
[subscription.id, xml]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active_subscriptions
|
||||
Subscription.where(account: @account).active.select('id, callback_url, domain')
|
||||
end
|
||||
end
|
||||
34
app/workers/pubsubhubbub/subscribe_worker.rb
Normal file
34
app/workers/pubsubhubbub/subscribe_worker.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pubsubhubbub::SubscribeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push', retry: 10, unique: :until_executed, dead: false
|
||||
|
||||
sidekiq_retry_in do |count|
|
||||
case count
|
||||
when 0
|
||||
30.minutes.seconds
|
||||
when 1
|
||||
2.hours.seconds
|
||||
when 2
|
||||
12.hours.seconds
|
||||
else
|
||||
24.hours.seconds * (count - 2)
|
||||
end
|
||||
end
|
||||
|
||||
sidekiq_retries_exhausted do |msg, _e|
|
||||
account = Account.find(msg['args'].first)
|
||||
Sidekiq.logger.error "PuSH subscription attempts for #{account.acct} exhausted. Unsubscribing"
|
||||
::UnsubscribeService.new.call(account)
|
||||
end
|
||||
|
||||
def perform(account_id)
|
||||
account = Account.find(account_id)
|
||||
logger.debug "PuSH re-subscribing to #{account.acct}"
|
||||
::SubscribeService.new.call(account)
|
||||
rescue => e
|
||||
raise e.class, "Subscribe failed for #{account&.acct}: #{e.message}", e.backtrace[0]
|
||||
end
|
||||
end
|
||||
15
app/workers/pubsubhubbub/unsubscribe_worker.rb
Normal file
15
app/workers/pubsubhubbub/unsubscribe_worker.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pubsubhubbub::UnsubscribeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push', retry: false, unique: :until_executed, dead: false
|
||||
|
||||
def perform(account_id)
|
||||
account = Account.find(account_id)
|
||||
logger.debug "PuSH unsubscribing from #{account.acct}"
|
||||
::UnsubscribeService.new.call(account)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
15
app/workers/push_conversation_worker.rb
Normal file
15
app/workers/push_conversation_worker.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PushConversationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(conversation_account_id)
|
||||
conversation = AccountConversation.find(conversation_account_id)
|
||||
message = InlineRenderer.render(conversation, conversation.account, :conversation)
|
||||
timeline_id = "timeline:direct:#{conversation.account_id}"
|
||||
|
||||
Redis.current.publish(timeline_id, Oj.dump(event: :conversation, payload: message, queued_at: (Time.now.to_f * 1000.0).to_i))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
16
app/workers/push_update_worker.rb
Normal file
16
app/workers/push_update_worker.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PushUpdateWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, status_id, timeline_id = nil)
|
||||
account = Account.find(account_id)
|
||||
status = Status.find(status_id)
|
||||
message = InlineRenderer.render(status, account, :status)
|
||||
timeline_id = "timeline:#{account.id}" if timeline_id.nil?
|
||||
|
||||
Redis.current.publish(timeline_id, Oj.dump(event: :update, payload: message, queued_at: (Time.now.to_f * 1000.0).to_i))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
24
app/workers/refollow_worker.rb
Normal file
24
app/workers/refollow_worker.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RefollowWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(target_account_id)
|
||||
target_account = Account.find(target_account_id)
|
||||
return unless target_account.protocol == :activitypub
|
||||
|
||||
target_account.followers.where(domain: nil).reorder(nil).find_each do |follower|
|
||||
# Locally unfollow remote account
|
||||
follower.unfollow!(target_account)
|
||||
|
||||
# Schedule re-follow
|
||||
begin
|
||||
FollowService.new.call(follower, target_account)
|
||||
rescue GabSocial::NotPermittedError, ActiveRecord::RecordNotFound, GabSocial::UnexpectedResponseError, HTTP::Error, OpenSSL::SSL::SSLError
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
14
app/workers/regeneration_worker.rb
Normal file
14
app/workers/regeneration_worker.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RegenerationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed
|
||||
|
||||
def perform(account_id, _ = :home)
|
||||
account = Account.find(account_id)
|
||||
PrecomputeFeedService.new.call(account)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
13
app/workers/remote_profile_update_worker.rb
Normal file
13
app/workers/remote_profile_update_worker.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RemoteProfileUpdateWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(account_id, body, resubscribe)
|
||||
UpdateRemoteProfileService.new.call(body, Account.find(account_id), resubscribe)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/workers/removal_worker.rb
Normal file
11
app/workers/removal_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RemovalWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(status_id)
|
||||
RemoveStatusService.new.call(Status.find(status_id))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/workers/resolve_account_worker.rb
Normal file
11
app/workers/resolve_account_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ResolveAccountWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', unique: :until_executed
|
||||
|
||||
def perform(uri)
|
||||
ResolveAccountService.new.call(uri)
|
||||
end
|
||||
end
|
||||
13
app/workers/salmon_worker.rb
Normal file
13
app/workers/salmon_worker.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SalmonWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options backtrace: true
|
||||
|
||||
def perform(account_id, body)
|
||||
ProcessInteractionService.new.call(body, Account.find(account_id))
|
||||
rescue Nokogiri::XML::XPath::SyntaxError, ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
17
app/workers/scheduler/backup_cleanup_scheduler.rb
Normal file
17
app/workers/scheduler/backup_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::BackupCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
old_backups.reorder(nil).find_each(&:destroy!)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def old_backups
|
||||
Backup.where('created_at < ?', 7.days.ago)
|
||||
end
|
||||
end
|
||||
12
app/workers/scheduler/doorkeeper_cleanup_scheduler.rb
Normal file
12
app/workers/scheduler/doorkeeper_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::DoorkeeperCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
|
||||
Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
|
||||
end
|
||||
end
|
||||
25
app/workers/scheduler/email_scheduler.rb
Normal file
25
app/workers/scheduler/email_scheduler.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::EmailScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
FREQUENCY = 7.days.freeze
|
||||
SIGN_IN_OFFSET = 1.day.freeze
|
||||
|
||||
def perform
|
||||
eligible_users.reorder(nil).find_each do |user|
|
||||
next unless user.allows_digest_emails?
|
||||
DigestMailerWorker.perform_async(user.id)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def eligible_users
|
||||
User.emailable
|
||||
.where('current_sign_in_at < ?', (FREQUENCY + SIGN_IN_OFFSET).ago)
|
||||
.where('last_emailed_at IS NULL OR last_emailed_at < ?', FREQUENCY.ago)
|
||||
end
|
||||
end
|
||||
61
app/workers/scheduler/feed_cleanup_scheduler.rb
Normal file
61
app/workers/scheduler/feed_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::FeedCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
include Redisable
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
clean_home_feeds!
|
||||
clean_list_feeds!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clean_home_feeds!
|
||||
clean_feeds!(inactive_account_ids, :home)
|
||||
end
|
||||
|
||||
def clean_list_feeds!
|
||||
clean_feeds!(inactive_list_ids, :list)
|
||||
end
|
||||
|
||||
def clean_feeds!(ids, type)
|
||||
reblogged_id_sets = {}
|
||||
|
||||
redis.pipelined do
|
||||
ids.each do |feed_id|
|
||||
redis.del(feed_manager.key(type, feed_id))
|
||||
reblog_key = feed_manager.key(type, feed_id, 'reblogs')
|
||||
# We collect a future for this: we don't block while getting
|
||||
# it, but we can iterate over it later.
|
||||
reblogged_id_sets[feed_id] = redis.zrange(reblog_key, 0, -1)
|
||||
redis.del(reblog_key)
|
||||
end
|
||||
end
|
||||
|
||||
# Remove all of the reblog tracking keys we just removed the
|
||||
# references to.
|
||||
redis.pipelined do
|
||||
reblogged_id_sets.each do |feed_id, future|
|
||||
future.value.each do |reblogged_id|
|
||||
reblog_set_key = feed_manager.key(type, feed_id, "reblogs:#{reblogged_id}")
|
||||
redis.del(reblog_set_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inactive_account_ids
|
||||
@inactive_account_ids ||= User.confirmed.inactive.pluck(:account_id)
|
||||
end
|
||||
|
||||
def inactive_list_ids
|
||||
List.where(account_id: inactive_account_ids).pluck(:id)
|
||||
end
|
||||
|
||||
def feed_manager
|
||||
FeedManager.instance
|
||||
end
|
||||
end
|
||||
15
app/workers/scheduler/ip_cleanup_scheduler.rb
Normal file
15
app/workers/scheduler/ip_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::IpCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
RETENTION_PERIOD = 1.year
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
time_ago = RETENTION_PERIOD.ago
|
||||
SessionActivation.where('updated_at < ?', time_ago).destroy_all
|
||||
User.where('last_sign_in_at < ?', time_ago).update_all(last_sign_in_ip: nil)
|
||||
end
|
||||
end
|
||||
17
app/workers/scheduler/media_cleanup_scheduler.rb
Normal file
17
app/workers/scheduler/media_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::MediaCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
unattached_media.find_each(&:destroy)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unattached_media
|
||||
MediaAttachment.reorder(nil).unattached.where('created_at < ?', 1.day.ago)
|
||||
end
|
||||
end
|
||||
11
app/workers/scheduler/pghero_scheduler.rb
Normal file
11
app/workers/scheduler/pghero_scheduler.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::PgheroScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
PgHero.capture_space_stats
|
||||
end
|
||||
end
|
||||
19
app/workers/scheduler/scheduled_statuses_scheduler.rb
Normal file
19
app/workers/scheduler/scheduled_statuses_scheduler.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::ScheduledStatusesScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
due_statuses.find_each do |scheduled_status|
|
||||
PublishScheduledStatusWorker.perform_at(scheduled_status.scheduled_at, scheduled_status.id)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def due_statuses
|
||||
ScheduledStatus.where('scheduled_at <= ?', Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET)
|
||||
end
|
||||
end
|
||||
11
app/workers/scheduler/subscriptions_cleanup_scheduler.rb
Normal file
11
app/workers/scheduler/subscriptions_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::SubscriptionsCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
Subscription.expired.in_batches.delete_all
|
||||
end
|
||||
end
|
||||
17
app/workers/scheduler/subscriptions_scheduler.rb
Normal file
17
app/workers/scheduler/subscriptions_scheduler.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::SubscriptionsScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
Pubsubhubbub::SubscribeWorker.push_bulk(expiring_accounts.pluck(:id))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expiring_accounts
|
||||
Account.expiring(1.day.from_now).partitioned
|
||||
end
|
||||
end
|
||||
14
app/workers/scheduler/user_cleanup_scheduler.rb
Normal file
14
app/workers/scheduler/user_cleanup_scheduler.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Scheduler::UserCleanupScheduler
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options unique: :until_executed, retry: 0
|
||||
|
||||
def perform
|
||||
User.where('confirmed_at is NULL AND confirmation_sent_at <= ?', 2.days.ago).reorder(nil).find_in_batches do |batch|
|
||||
Account.where(id: batch.map(&:account_id)).delete_all
|
||||
User.where(id: batch.map(&:id)).delete_all
|
||||
end
|
||||
end
|
||||
end
|
||||
18
app/workers/thread_resolve_worker.rb
Normal file
18
app/workers/thread_resolve_worker.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ThreadResolveWorker
|
||||
include Sidekiq::Worker
|
||||
include ExponentialBackoff
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 3
|
||||
|
||||
def perform(child_status_id, parent_url)
|
||||
child_status = Status.find(child_status_id)
|
||||
parent_status = FetchRemoteStatusService.new.call(parent_url)
|
||||
|
||||
return if parent_status.nil?
|
||||
|
||||
child_status.thread = parent_status
|
||||
child_status.save!
|
||||
end
|
||||
end
|
||||
11
app/workers/unfavourite_worker.rb
Normal file
11
app/workers/unfavourite_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UnfavouriteWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, status_id)
|
||||
UnfavouriteService.new.call(Account.find(account_id), Status.find(status_id))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
18
app/workers/unfollow_follow_worker.rb
Normal file
18
app/workers/unfollow_follow_worker.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UnfollowFollowWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(follower_account_id, old_target_account_id, new_target_account_id)
|
||||
follower_account = Account.find(follower_account_id)
|
||||
old_target_account = Account.find(old_target_account_id)
|
||||
new_target_account = Account.find(new_target_account_id)
|
||||
|
||||
FollowService.new.call(follower_account, new_target_account)
|
||||
UnfollowService.new.call(follower_account, old_target_account)
|
||||
rescue ActiveRecord::RecordNotFound, GabSocial::NotPermittedError
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/workers/unmerge_worker.rb
Normal file
11
app/workers/unmerge_worker.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UnmergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull'
|
||||
|
||||
def perform(from_account_id, into_account_id)
|
||||
FeedManager.instance.unmerge_from_timeline(Account.find(from_account_id), Account.find(into_account_id))
|
||||
end
|
||||
end
|
||||
20
app/workers/verify_account_links_worker.rb
Normal file
20
app/workers/verify_account_links_worker.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class VerifyAccountLinksWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: false, unique: :until_executed
|
||||
|
||||
def perform(account_id)
|
||||
account = Account.find(account_id)
|
||||
|
||||
account.fields.each do |field|
|
||||
next unless !field.verified? && field.verifiable?
|
||||
VerifyLinkService.new.call(field)
|
||||
end
|
||||
|
||||
account.save! if account.changed?
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
18
app/workers/web/push_notification_worker.rb
Normal file
18
app/workers/web/push_notification_worker.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Web::PushNotificationWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options backtrace: true
|
||||
|
||||
def perform(subscription_id, notification_id)
|
||||
subscription = ::Web::PushSubscription.find(subscription_id)
|
||||
notification = Notification.find(notification_id)
|
||||
|
||||
subscription.push(notification) unless notification.activity.nil?
|
||||
rescue Webpush::ResponseError => e
|
||||
subscription.destroy! if (400..499).cover?(e.response.code.to_i)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user