diff options
author | multiple creatures <dev@multiple-creature.party> | 2020-01-13 21:57:24 -0600 |
---|---|---|
committer | multiple creatures <dev@multiple-creature.party> | 2020-01-13 21:57:24 -0600 |
commit | 67516a07db44196cc4bb94bf94abe5c6f611cf07 (patch) | |
tree | f65b7c79f859329a0438d806e777fa1d7aa7f400 /app | |
parent | 1fbe7c340201ddff5232c7931a265136943ffd59 (diff) |
add new privacy option to auto-defederate after a given timespan + add options to defederate and/or delete past posts + add `defed_in`/`parent:defed_in`/`thread:defed_in` bangtags + ui indicator for posts marked for auto-defederation
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/api/v1/statuses_controller.rb | 2 | ||||
-rw-r--r-- | app/controllers/settings/preferences_controller.rb | 7 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/components/status_icons.js | 3 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/features/status/components/detailed_status.js | 11 | ||||
-rw-r--r-- | app/lib/bangtags.rb | 59 | ||||
-rw-r--r-- | app/lib/user_settings_decorator.rb | 15 | ||||
-rw-r--r-- | app/models/account.rb | 3 | ||||
-rw-r--r-- | app/models/defederating_status.rb | 21 | ||||
-rw-r--r-- | app/models/destructing_status.rb | 1 | ||||
-rw-r--r-- | app/models/status.rb | 19 | ||||
-rw-r--r-- | app/models/user.rb | 15 | ||||
-rw-r--r-- | app/serializers/rest/status_serializer.rb | 5 | ||||
-rw-r--r-- | app/services/post_status_service.rb | 15 | ||||
-rw-r--r-- | app/services/remove_status_service.rb | 37 | ||||
-rw-r--r-- | app/views/settings/preferences/show.html.haml | 18 | ||||
-rw-r--r-- | app/workers/defederate_status_worker.rb | 17 | ||||
-rw-r--r-- | app/workers/mark_expired_statuses_worker.rb | 44 | ||||
-rw-r--r-- | app/workers/scheduler/defederating_statuses_scheduler.rb | 19 |
18 files changed, 291 insertions, 20 deletions
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index c3f2422ce..255c48e62 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -53,6 +53,7 @@ class Api::V1::StatusesController < Api::BaseController visibility: status_params[:visibility], scheduled_at: status_params[:scheduled_at], delete_after: status_params[:delete_after], + defederate_after: status_params[:defederate_after], sharekey: status_params[:sharekey], application: doorkeeper_token.application, poll: status_params[:poll], @@ -100,6 +101,7 @@ class Api::V1::StatusesController < Api::BaseController :sharekey, :scheduled_at, :delete_after, + :defederate_after, :content_type, media_ids: [], poll: [ diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 8573624f2..295fc3129 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -12,6 +12,12 @@ class Settings::PreferencesController < Settings::BaseController def update user_settings.update(user_settings_params.to_h) + MarkExpiredStatusesWorker.perform_async( + current_account.id, + truthy_param?(:setting_defederate_old), + truthy_param?(:setting_lifespan_old) + ) + if current_user.update(user_params) I18n.locale = current_user.locale toggle_filters @@ -85,6 +91,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_max_public_history, :setting_max_public_access, :setting_roar_lifespan, + :setting_roar_defederate, :setting_delayed_roars, :setting_delayed_for, :setting_boost_interval, diff --git a/app/javascript/flavours/glitch/components/status_icons.js b/app/javascript/flavours/glitch/components/status_icons.js index f439afbe6..bba2fac87 100644 --- a/app/javascript/flavours/glitch/components/status_icons.js +++ b/app/javascript/flavours/glitch/components/status_icons.js @@ -64,6 +64,9 @@ export default class StatusIcons extends React.PureComponent { {status.get('delete_after') ? ( <i className='fa fa-clock-o' title={new Date(status.get('delete_after'))} aria-hidden='true' /> ) : null} + {status.get('defederate_after') ? ( + <i className='fa fa-calendar-times-o' title={new Date(status.get('defederate_after'))} aria-hidden='true' /> + ) : null} {status.get('reject_replies') ? ( <i className='fa fa-microphone-slash' title='Rejecting replies' aria-hidden='true' /> ) : null} diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index cde6e4348..bda5e7152 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -131,6 +131,7 @@ export default class DetailedStatus extends ImmutablePureComponent { let favouriteLink = ''; let sharekeyLinks = ''; let destructIcon = ''; + let defederateIcon = ''; let rejectIcon = ''; if (this.props.measureHeight) { @@ -256,6 +257,14 @@ export default class DetailedStatus extends ImmutablePureComponent { ) } + if (status.get('defederate_after')) { + defederateIcon = ( + <span> + <i className='fa fa-calendar-times-o' title={new Date(status.get('defederate_after'))} /> · + </span> + ) + } + if (status.get('reject_replies')) { rejectIcon = ( <span> @@ -285,7 +294,7 @@ export default class DetailedStatus extends ImmutablePureComponent { /> <div className='detailed-status__meta'> - {sharekeyLinks} {reblogLink} · {favouriteLink} · {destructIcon} {rejectIcon} <VisibilityIcon visibility={status.get('visibility')} /> + {sharekeyLinks} {reblogLink} · {favouriteLink} · {defederateIcon} {destructIcon} {rejectIcon} <VisibilityIcon visibility={status.get('visibility')} /> <a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'> <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' /> </a> diff --git a/app/lib/bangtags.rb b/app/lib/bangtags.rb index 9fec00d9d..42dd661e6 100644 --- a/app/lib/bangtags.rb +++ b/app/lib/bangtags.rb @@ -59,6 +59,21 @@ class Bangtags ['all', 'live'] => ['live', 'all'], ['all', 'lifespan'] => ['lifespan', 'all'], ['all', 'delete_in'] => ['delete_in', 'all'], + + ['parent', 'd'] => ['defederate_in', 'parent'], + ['parent', 'defed'] => ['defederate_in', 'parent'], + ['parent', 'defed_in'] => ['defederate_in', 'parent'], + ['parent', 'defederate'] => ['defederate_in', 'parent'], + + ['thread', 'd'] => ['defederate_in', 'thread'], + ['thread', 'defed'] => ['defederate_in', 'thread'], + ['thread', 'defed_in'] => ['defederate_in', 'thread'], + ['thread', 'defederate'] => ['defederate_in', 'thread'], + + ['all', 'd'] => ['defederate_in', 'all'], + ['all', 'defed'] => ['defederate_in', 'all'], + ['all', 'defed_in'] => ['defederate_in', 'all'], + ['all', 'defederate'] => ['defederate_in', 'all'], } # sections of the final status text @@ -730,6 +745,50 @@ class Bangtags s.delete_after = delete_after Rails.cache.delete("statuses/#{s.id}") end + when 'd', 'defed', 'defed_in', 'defederate', 'defederate_in' + chunk = nil + next if cmd[1].nil? + case cmd[1].downcase + when 'parent', 'thread', 'all' + s = cmd[1].downcase.to_sym + s = @parent_status if s == :parent + next unless s == :all || @parent_status.present? + next unless s == :thread || s == :all || @parent_status.account_id == @account.id + i = cmd[2].to_i + unit = cmd[3].present? ? cmd[3].downcase : 'minutes' + else + s = @status + i = cmd[1].to_i + unit = cmd[2].present? ? cmd[2].downcase : 'minutes' + end + defederate_after = case unit + when 'min', 'mins', 'minute', 'minutes' + i.minutes + when 'h', 'hr', 'hrs', 'hour', 'hours' + i.hours + when 'd', 'dy', 'dys', 'day', 'days' + i.days + when 'w', 'wk', 'wks', 'week', 'weeks' + i.weeks + when 'm', 'mn', 'mns', 'month', 'months' + i.months + when 'y', 'yr', 'yrs', 'year', 'years' + i.years + end + if s == :thread + @parent_status.conversation.statuses.where(account_id: @account.id).find_each do |s| + s.defederate_after = defederate_after + Rails.cache.delete("statuses/#{s.id}") + end + elsif s == :all + @account.statuses.find_each do |s| + s.defederate_after = defederate_after + Rails.cache.delete("statuses/#{s.id}") + end + else + s.defederate_after = defederate_after + Rails.cache.delete("statuses/#{s.id}") + end when 'keysmash' keyboard = [ 'asdf', 'jkl;', diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 5f800adc6..56a35a8af 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -42,6 +42,9 @@ class UserSettingsDecorator user.settings['max_public_history'] = max_public_history_preference if change?('setting_max_public_history') user.settings['max_public_access'] = max_public_access_preference if change?('setting_max_public_access') user.settings['roar_lifespan'] = roar_lifespan_preference if change?('setting_roar_lifespan') + user.settings['roar_lifespan_old'] = roar_lifespan_old_preference if change?('setting_roar_lifespan_old') + user.settings['roar_defederate'] = roar_defederate_preference if change?('setting_roar_defederate') + user.settings['roar_defederate_old'] = roar_defederate_old_preference if change?('setting_roar_defederate_old') user.settings['delayed_roars'] = delayed_roars_preference if change?('setting_delayed_roars') user.settings['delayed_for'] = delayed_for_preference if change?('setting_delayed_for') user.settings['boost_interval'] = boost_interval_preference if change?('setting_boost_interval') @@ -160,6 +163,18 @@ class UserSettingsDecorator settings['setting_roar_lifespan'] end + def roar_lifespan_old_preference + settings['setting_roar_lifespan_old'] + end + + def roar_defederate_preference + settings['setting_roar_defederate'] + end + + def roar_defederate_old_preference + settings['setting_roar_defederate_old'] + end + def delayed_for_preference settings['setting_delayed_for'] end diff --git a/app/models/account.rb b/app/models/account.rb index 59685a13b..9cfacc1c0 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -135,6 +135,9 @@ class Account < ApplicationRecord :max_public_history, :max_public_access, :roar_lifespan, + :roar_lifespan_old, + :roar_defederate, + :roar_defederate_old, :delayed_roars?, :hides_public_profile?, diff --git a/app/models/defederating_status.rb b/app/models/defederating_status.rb new file mode 100644 index 000000000..e6af6c1b6 --- /dev/null +++ b/app/models/defederating_status.rb @@ -0,0 +1,21 @@ +# == Schema Information +# +# Table name: defederating_statuses +# +# id :bigint(8) not null, primary key +# status_id :bigint(8) +# defederate_after :datetime +# + +class DefederatingStatus < ApplicationRecord + belongs_to :status, inverse_of: :defederating_status + + validate :validate_future_date + validates :status_id, uniqueness: true + + private + + def validate_future_date + errors.add(:defederate_after, I18n.t('defederating_statuses.too_soon')) if defederate_after.present? && defederate_after < Time.now.utc + PostStatusService::MIN_DESTRUCT_OFFSET + end +end diff --git a/app/models/destructing_status.rb b/app/models/destructing_status.rb index 349e276cb..e22fa402c 100644 --- a/app/models/destructing_status.rb +++ b/app/models/destructing_status.rb @@ -11,6 +11,7 @@ class DestructingStatus < ApplicationRecord belongs_to :status, inverse_of: :destructing_status validate :validate_future_date + validates :status_id, uniqueness: true private diff --git a/app/models/status.rb b/app/models/status.rb index 001437795..f21a2087e 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -82,6 +82,7 @@ class Status < ApplicationRecord has_one :status_stat, inverse_of: :status has_one :poll, inverse_of: :status, dependent: :destroy has_one :destructing_status, inverse_of: :status, dependent: :destroy + has_one :defederating_status, inverse_of: :status, dependent: :destroy has_one :imported_status, inverse_of: :status, dependent: :destroy has_one :sharekey, inverse_of: :status, dependent: :destroy @@ -284,6 +285,10 @@ class Status < ApplicationRecord end def delete_after=(value) + if defederate_after && defederate_after < (Time.now.utc + 5.minutes + value) + value = 5.minutes + value + end + if destructing_status.nil? DestructingStatus.create!(status_id: id, delete_after: Time.now.utc + value) else @@ -291,6 +296,20 @@ class Status < ApplicationRecord end end + def defederate_after + defederating_status&.defederate_after + end + + def defederate_after=(value) + return unless delete_after.nil? || delete_after >= (Time.now.utc + 5.minutes + value) + + if defederating_status.nil? + DefederatingStatus.create!(status_id: id, defederate_after: Time.now.utc + value) + else + defederating_status.defederate_after = Time.now.utc + value + end + end + def mark_for_mass_destruction! @marked_for_mass_destruction = true end diff --git a/app/models/user.rb b/app/models/user.rb index 6c18898e9..b28e3229f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -148,6 +148,9 @@ class User < ApplicationRecord :max_public_history, :max_public_access, :roar_lifespan, + :roar_lifespan_old, + :roar_defederate, + :roar_defederate_old, :delayed_roars, :delayed_for, :boost_interval, @@ -340,6 +343,18 @@ class User < ApplicationRecord @_roar_lifespan ||= [0, (settings.roar_lifespan || 0).to_i].max end + def roar_lifespan_old + @_roar_lifespan_old ||= (settings.roar_lifespan_old || false) + end + + def roar_defederate + @_roar_defederate ||= [0, (settings.roar_defederate || 0).to_i].max + end + + def roar_defederate_old + @_roar_defederate_old ||= (settings.roar_defederate_old || false) + end + def delayed_roars? @delayed_roars ||= (settings.delayed_roars || false) end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 32f75caa3..0a98b2ecd 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -16,6 +16,7 @@ class REST::StatusSerializer < ActiveModel::Serializer attribute :local_only if :local? attribute :sharekey, if: :has_sharekey? attribute :delete_after, if: :current_user? + attribute :defederate_after, if: :current_user? attribute :content, unless: :source_requested? attribute :text, if: :source_requested? @@ -154,6 +155,10 @@ class REST::StatusSerializer < ActiveModel::Serializer object.delete_after end + def defederate_after + object.defederate_after + end + def reject_replies object.reject_replies == true end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index dc1a3bcdc..8a1ca3eca 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -30,6 +30,7 @@ class PostStatusService < BaseService # @option [String] :language # @option [String] :scheduled_at # @option [String] :delete_after + # @option [String] :defederate_after # @option [Account] :mentions Optional accounts to mention out-of-body # @option [Boolean] :noreplies Author does not accept replies # @option [Boolean] :nocrawl Optional skip link card generation @@ -76,6 +77,7 @@ class PostStatusService < BaseService distribute: @options[:distribute], nocrawl: @options[:nocrawl], delete_after: @delete_after.nil? ? nil : @delete_after + 1.minute, + defederate_after: @defederate_after.nil? ? nil : @defederate_after + 1.minute, reject_replies: @options[:noreplies] || false, }.compact @@ -201,6 +203,18 @@ class PostStatusService < BaseService end @delete_after = nil if @delete_after.present? && (@delete_after < MIN_DESTRUCT_OFFSET) + case @options[:defederate_after].class + when NilClass + @defederate_after = @account.user.setting_roar_defederate.to_i.days + when ActiveSupport::Duration + @defederate_after = @options[:defederate_after] + when Integer + @defederate_after = @options[:defederate_after].minutes + when Float + @defederate_after = @options[:defederate_after].minutes + end + @defederate_after = nil if @defederate_after.present? && (@defederate_after < MIN_DESTRUCT_OFFSET) + rescue ArgumentError raise ActiveRecord::RecordInvalid end @@ -310,6 +324,7 @@ class PostStatusService < BaseService visibility: @visibility, local_only: @local_only, delete_after: @delete_after, + defederate_after: @defederate_after, reject_replies: @options[:noreplies] || false, sharekey: @options[:sharekey], language: language_from_option(@options[:language]) || @account.user_default_language&.presence || 'en', diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 20648217d..797794a19 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -16,22 +16,24 @@ class RemoveStatusService < BaseService @stream_entry = status.stream_entry @options = options - RedisLock.acquire(lock_options) do |lock| - if lock.acquired? - remove_from_queued - remove_from_self if status.account.local? - remove_from_followers - remove_from_lists - remove_from_affected - remove_reblogs - remove_from_hashtags - remove_from_public - remove_from_media if status.media_attachments.any? - remove_from_direct if status.direct_visibility? - - @status.destroy! - else - raise Mastodon::RaceConditionError + unless options[:defederate_only] + RedisLock.acquire(lock_options) do |lock| + if lock.acquired? + remove_from_queued + remove_from_self if status.account.local? + remove_from_followers + remove_from_lists + remove_from_affected + remove_reblogs + remove_from_hashtags + remove_from_public + remove_from_media if status.media_attachments.any? + remove_from_direct if status.direct_visibility? + + @status.destroy! + else + raise Mastodon::RaceConditionError + end end end @@ -44,6 +46,9 @@ class RemoveStatusService < BaseService remove_from_remote_followers remove_from_remote_affected + + @status.update(local_only: true) if options[:defederate_only] + Rails.cache.delete("statuses/#{@status.id}") end private diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index cf9529ac3..3d371c14a 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -17,9 +17,21 @@ = f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_floating_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' .fields-group - = f.input :setting_max_public_history, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_history - = f.input :setting_max_public_access, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_access - = f.input :setting_roar_lifespan, collection: [0, 1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.roar_lifespan + .fields-row + .fields-group.fields-row__column.fields-row__column-6 + = f.input :setting_max_public_history, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_history + .fields-group.fields-row__column.fields-row__column-6 + = f.input :setting_max_public_access, collection: [1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.max_public_access + + .fields-group + .fields-row + .fields-group.fields-row__column.fields-row__column-6 + = f.input :setting_roar_defederate, collection: [0, 1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.roar_defederate + = f.input :setting_roar_defederate_old, as: :boolean, wrapper: :with_label + + .fields-group.fields-row__column.fields-row__column-6 + = f.input :setting_roar_lifespan, collection: [0, 1, 3, 6, 7, 14, 30, 60, 90, 180, 365, 730, 1095, 2190], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.lifespan.#{item}")]) }, selected: current_user.roar_lifespan + = f.input :setting_roar_lifespan_old, as: :boolean, wrapper: :with_label .fields-group = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label diff --git a/app/workers/defederate_status_worker.rb b/app/workers/defederate_status_worker.rb new file mode 100644 index 000000000..bae4be9b8 --- /dev/null +++ b/app/workers/defederate_status_worker.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class DefederateStatusWorker + include Sidekiq::Worker + + sidekiq_options unique: :until_executed + + def perform(defederating_status_id) + defederating_status = DefederatingStatus.find(defederating_status_id) + defederating_status.destroy! + + RemoveStatusService.new.call(defederating_status.status, defederate_only: true) + true + rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid + true + end +end diff --git a/app/workers/mark_expired_statuses_worker.rb b/app/workers/mark_expired_statuses_worker.rb new file mode 100644 index 000000000..9eac58357 --- /dev/null +++ b/app/workers/mark_expired_statuses_worker.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class MarkExpiredStatusesWorker + include Sidekiq::Worker + + sidekiq_options queue: 'bulk' + + def perform(account_id, defederate = false, lifespan = false) + @account = Account.find(account_id) + return if @account&.user.nil? + @user = @account.user + + @roar_defederate = @user.roar_defederate.to_i + @roar_lifespan = @user.roar_lifespan.to_i + + defederate = false if @roar_defederate == 0 + lifespan = false if @roar_lifespan == 0 + + return unless defederate || lifespan + + offset = 30.minutes + + @account.statuses.find_each do |status| + modified = false + + if defederate && !status.local_only? && status.updated_at < @roar_defederate.days.ago + status.defederate_after = offset + modified = true + end + + if lifespan && status.updated_at < @roar_lifespan.days.ago + status.delete_after = offset + 30.minutes + modified = true + end + + if modified + Rails.cache.delete("statuses/#{status.id}") + offset += 1.second + end + end + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/scheduler/defederating_statuses_scheduler.rb b/app/workers/scheduler/defederating_statuses_scheduler.rb new file mode 100644 index 000000000..91a644e02 --- /dev/null +++ b/app/workers/scheduler/defederating_statuses_scheduler.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class Scheduler::DefederatingStatusesScheduler + include Sidekiq::Worker + + sidekiq_options unique: :until_executed, retry: 0 + + def perform + due_statuses.find_each do |defederating_status| + DefederateStatusWorker.perform_async(defederating_status.id) + end + end + + private + + def due_statuses + DefederatingStatus.where('defederate_after <= ?', Time.now.utc) + end +end |