diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/api/v1/filters_controller.rb | 4 | ||||
-rw-r--r-- | app/controllers/api/v1/statuses_controller.rb | 1 | ||||
-rw-r--r-- | app/controllers/filters_controller.rb | 2 | ||||
-rw-r--r-- | app/helpers/filter_helper.rb | 78 | ||||
-rw-r--r-- | app/lib/feed_manager.rb | 6 | ||||
-rw-r--r-- | app/lib/status_filter.rb | 6 | ||||
-rw-r--r-- | app/models/custom_filter.rb | 56 | ||||
-rw-r--r-- | app/serializers/rest/filter_serializer.rb | 7 | ||||
-rw-r--r-- | app/views/filters/_fields.html.haml | 23 |
9 files changed, 23 insertions, 160 deletions
diff --git a/app/controllers/api/v1/filters_controller.rb b/app/controllers/api/v1/filters_controller.rb index 8194480fa..de2c90dba 100644 --- a/app/controllers/api/v1/filters_controller.rb +++ b/app/controllers/api/v1/filters_controller.rb @@ -35,7 +35,7 @@ class Api::V1::FiltersController < Api::BaseController private def set_filters - @filters = params['all'].to_i == 1 ? current_account.custom_filters : current_account.custom_filters.where(custom_cw: nil) + @filters = params['all'].to_i == 1 ? current_account.custom_filters : [] end def set_filter @@ -43,6 +43,6 @@ class Api::V1::FiltersController < Api::BaseController end def resource_params - params.permit(:phrase, :expires_in, :whole_word, :exclude_media, :media_only, :status_text, :spoiler, :tags, :custom_cw, :override_cw, :desc, :no_desc, context: []) + params.permit(:phrase, :expires_in) end end diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 601e400ca..fa3483822 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -2,7 +2,6 @@ class Api::V1::StatusesController < Api::BaseController include Authorization - include FilterHelper before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy] before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy] diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb index 8a4636ba2..9fa653b11 100644 --- a/app/controllers/filters_controller.rb +++ b/app/controllers/filters_controller.rb @@ -58,7 +58,7 @@ class FiltersController < ApplicationController end def resource_params - params.require(:custom_filter).permit(:phrase, :expires_in, :irreversible, :whole_word, :exclude_media, :media_only, :status_text, :spoiler, :tags, :custom_cw, :override_cw, :desc, :no_desc, context: []) + params.require(:custom_filter).permit(:phrase, :expires_in) end def set_body_classes diff --git a/app/helpers/filter_helper.rb b/app/helpers/filter_helper.rb index 77ffa98be..b2f1b00fc 100644 --- a/app/helpers/filter_helper.rb +++ b/app/helpers/filter_helper.rb @@ -1,83 +1,17 @@ module FilterHelper include Redisable - def phrase_filtered?(status, receiver_id, context) - if redis.sismember("filtered_statuses:#{receiver_id}", status.id) - return !(redis.hexists("custom_cw:#{receiver_id}", status.id) || redis.hexists("custom_cw:#{receiver_id}", "c#{status.conversation_id}")) - end - - filters = cached_filters(receiver_id).select { |filter| !filter.expired? } - - unless context.nil? - filters.select! { |filter| filter.context.include?(context.to_s) && !filter.expired? } - end - - if status.media_attachments.any? - filters.delete_if { |filter| filter.exclude_media } - else - filters.delete_if { |filter| filter.media_only } - end - - return false if filters.empty? + def phrase_filtered?(status, receiver_id) + return true if redis.sismember("filtered_statuses:#{receiver_id}", status.id) + return false unless CustomFilter.where(account_id: receiver_id).exists? status = status.reblog if status.reblog? - status_text = status.normalized_text - spoiler_text = status.spoiler_text - tags = status.tags.pluck(:name).join("\n") - descs = status.media_attachments.map { |a| a.description }.join("\n").strip - - filters.each do |filter| - if filter.whole_word - sb = filter.phrase =~ /\A[[:word:]]/ ? '\b' : '' - eb = filter.phrase =~ /[[:word:]]\z/ ? '\b' : '' - - regex = /(?mix:#{sb}#{Regexp.escape(filter.phrase)}#{eb})/ - else - regex = /#{Regexp.escape(filter.phrase)}/i - end - - matched = false - matched ||= regex.match(status_text).present? if filter.status_text - matched ||= regex.match(spoiler_text).present? if filter.spoiler && spoiler_text.present? - matched ||= regex.match(tags).present? if filter.tags && tags.present? - matched ||= regex.match(descs).present? if filter.desc && descs.present? - matched ||= status.media_attachments.all { |a| a.description.blank? } if filter.no_desc && status.media_attachments.any? - if matched - filter_thread(receiver_id, status.conversation_id) if filter.thread && filter.custom_cw.blank? - - unless filter.custom_cw.blank? - cw = if filter.override_cw || status.spoiler_text.blank? - filter.custom_cw - else - "[#{filter.custom_cw}] #{status.spoiler_text}".rstrip - end - - if filter.thread - redis.hset("custom_cw:#{receiver_id}", "c#{status.conversation_id}", cw) - else - redis.hset("custom_cw:#{receiver_id}", status.id, cw) - end - end - - redis.sadd("filtered_statuses:#{receiver_id}", status.id) - return filter.custom_cw.blank? - end + if Status.where(id: status.id).where("statuses.normalized_text ~ ANY(ARRAY(SELECT unaccent(lower(phrase)) FROM custom_filters WHERE account_id = ?))", receiver_id).exists? + redis.sadd("filtered_statuses:#{receiver_id}", status.id) + return true end false end - - def filter_thread(account_id, conversation_id) - return if Status.where(account_id: account_id, conversation_id: conversation_id).exists? - redis.sadd("filtered_threads:#{account_id}", conversation_id) - end - - def filtering_thread?(account_id, conversation_id) - redis.sismember("filtered_threads:#{account_id}", conversation_id) - end - - def cached_filters(account_id) - Rails.cache.fetch("filters:#{account_id}") { CustomFilter.where(account_id: account_id).to_a }.to_a - end end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 6d8f46e6f..b2f040811 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -157,8 +157,7 @@ class FeedManager def filter_from_home?(status, receiver_id) return false if receiver_id == status.account_id return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?) - return true if filtering_thread?(receiver_id, status.conversation_id) - return true if phrase_filtered?(status, receiver_id, :home) + return true if phrase_filtered?(status, receiver_id) check_for_blocks = status.active_mentions.pluck(:account_id) check_for_blocks.concat([status.account_id]) @@ -188,8 +187,7 @@ class FeedManager def filter_from_mentions?(status, receiver_id) return true if receiver_id == status.account_id - return true if filtering_thread?(receiver_id, status.conversation_id) - return true if phrase_filtered?(status, receiver_id, :notifications) + return true if phrase_filtered?(status, receiver_id) # This filter is called from NotifyService, but already after the sender of # the notification has been checked for mute/block. Therefore, it's not diff --git a/app/lib/status_filter.rb b/app/lib/status_filter.rb index d70ed1d21..0cc1ce85c 100644 --- a/app/lib/status_filter.rb +++ b/app/lib/status_filter.rb @@ -23,7 +23,6 @@ class StatusFilter end def filtered_status? - return true if filtering_thread?(account.id, status.conversation_id) blocking_account? || blocking_domain? || muting_account? || filtered_reference? end @@ -34,7 +33,7 @@ class StatusFilter return true if account.user_hides_replies_of_blocker? && reply_to_blocker? # filtered by user? - return true if phrase_filtered?(status, account.id, nil) + return true if phrase_filtered?(status, account.id) # kajiht has no filters if status has no mentions return false if status&.mentions.blank? @@ -46,6 +45,9 @@ class StatusFilter # Don't filter statuses mentioning you. return false if mentioned_account_ids.include?(account.id) + # Filter posts missing media descriptions. + return true if account.filter_undescribed? && status.media_attachments.all? { |attachment| attachment.description.blank? } + return true if account.user_hides_mentions_of_blocked? && mentioned_accounts.where.not(suspended_at: nil).exists? return true if mentioned_account_ids.any? do |mentioned_account_id| diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb index 5feb5fd7d..28d413d9b 100644 --- a/app/models/custom_filter.rb +++ b/app/models/custom_filter.rb @@ -3,60 +3,26 @@ # # Table name: custom_filters # -# id :bigint(8) not null, primary key -# account_id :bigint(8) -# expires_at :datetime -# phrase :text default(""), not null -# context :string default([]), not null, is an Array -# irreversible :boolean default(FALSE), not null -# created_at :datetime not null -# updated_at :datetime not null -# whole_word :boolean default(TRUE), not null -# exclude_media :boolean default(FALSE), not null -# media_only :boolean default(FALSE), not null -# thread :boolean default(FALSE), not null -# spoiler :boolean default(FALSE), not null -# tags :boolean default(FALSE), not null -# status_text :boolean default(FALSE), not null -# custom_cw :text -# override_cw :boolean default(FALSE), not null -# desc :boolean default(FALSE), not null -# no_desc :boolean default(FALSE), not null +# id :bigint(8) not null, primary key +# account_id :bigint(8) +# expires_at :datetime +# phrase :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null # class CustomFilter < ApplicationRecord - VALID_CONTEXTS = %w( - home - notifications - public - thread - ).freeze - include Expireable include Redisable belongs_to :account - validates :phrase, :context, presence: true - validate :context_must_be_valid - validate :irreversible_must_be_within_context - - scope :active_irreversible, -> { where(irreversible: true).where(Arel.sql('expires_at IS NULL OR expires_at > NOW()')) } + validates :phrase, presence: true - before_validation :prepare_custom_cw - before_validation :clean_up_contexts after_commit :remove_cache private - def clean_up_contexts - self.context = Array(context).map(&:strip).map(&:presence).compact - end - - def prepare_custom_cw - custom_cw&.strip! - end - def remove_cache Rails.cache.delete("filters:#{account_id}") redis.del("custom_cw:#{account_id}") @@ -64,12 +30,4 @@ class CustomFilter < ApplicationRecord redis.del("filtered_statuses:#{account_id}") Redis.current.publish("timeline:#{account_id}", Oj.dump(event: :filters_changed)) end - - def context_must_be_valid - errors.add(:context, I18n.t('filters.errors.invalid_context')) if context.empty? || context.any? { |c| !VALID_CONTEXTS.include?(c) } - end - - def irreversible_must_be_within_context - errors.add(:irreversible, I18n.t('filters.errors.invalid_irreversible')) if irreversible? && !context.include?('home') && !context.include?('notifications') - end end diff --git a/app/serializers/rest/filter_serializer.rb b/app/serializers/rest/filter_serializer.rb index b027e9cc1..6d72aa426 100644 --- a/app/serializers/rest/filter_serializer.rb +++ b/app/serializers/rest/filter_serializer.rb @@ -1,14 +1,9 @@ # frozen_string_literal: true class REST::FilterSerializer < ActiveModel::Serializer - attributes :id, :phrase, :context, :whole_word, :expires_at, - :irreversible, :exclude_media, :media_only + attributes :id, :phrase, :expires_at def id object.id.to_s end - - def irreversible - true - end end diff --git a/app/views/filters/_fields.html.haml b/app/views/filters/_fields.html.haml index da0add2ea..afa72ae0c 100644 --- a/app/views/filters/_fields.html.haml +++ b/app/views/filters/_fields.html.haml @@ -6,26 +6,3 @@ .fields-group = f.input :context, wrapper: :with_block_label, collection: CustomFilter::VALID_CONTEXTS, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label_method: lambda { |context| I18n.t("filters.contexts.#{context}") }, include_blank: false - -%hr.spacer/ - -.fields-group - = f.input :whole_word, wrapper: :with_label - -.fields-group - = f.input :status_text, wrapper: :with_label - = f.input :spoiler, wrapper: :with_label - = f.input :tags, wrapper: :with_label - = f.input :thread, wrapper: :with_label - -.fields-group - = f.input :desc, wrapper: :with_label - = f.input :no_desc, wrapper: :with_label - -.fields-group - = f.input :media_only, wrapper: :with_label - = f.input :exclude_media, wrapper: :with_label - -.fields-group - = f.input :custom_cw, as: :string, wrapper: :with_label - = f.input :override_cw, wrapper: :with_label |