From 26573ad7e67e64d6db222877cf2853920c2c7dae Mon Sep 17 00:00:00 2001 From: David Yip Date: Sun, 3 Jun 2018 19:41:54 -0500 Subject: Thread scopes through #matches?. #454. Also add an apply_to_mentions attribute on Glitch::KeywordMute, which is used to calculate scope. Next up: additions to the test suite to demonstrate how scoping works. --- app/lib/feed_manager.rb | 8 ++--- app/models/bookmark.rb | 6 ++-- app/models/glitch/keyword_mute.rb | 53 +++++++++++++++++++++----------- app/models/glitch/keyword_mute_helper.rb | 12 ++++---- 4 files changed, 48 insertions(+), 31 deletions(-) (limited to 'app') diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 3a2dcac68..74794f00c 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -153,7 +153,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 keyword_filter?(status, receiver_id) + return true if keyword_filter?(status, receiver_id, Glitch::KeywordMute::Scopes::HomeFeed) check_for_mutes = [status.account_id] check_for_mutes.concat(status.mentions.pluck(:account_id)) @@ -182,8 +182,8 @@ class FeedManager false end - def keyword_filter?(status, receiver_id) - Glitch::KeywordMuteHelper.new(receiver_id).matches?(status) + def keyword_filter?(status, receiver_id, scope) + Glitch::KeywordMuteHelper.new(receiver_id).matches?(status, scope) end def filter_from_mentions?(status, receiver_id) @@ -197,7 +197,7 @@ class FeedManager should_filter = blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted) should_filter ||= (status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists?) # of if the account is silenced and I'm not following them - should_filter ||= keyword_filter?(status, receiver_id) # or if the mention contains a muted keyword + should_filter ||= keyword_filter?(status, receiver_id, Glitch::KeywordMute::Scopes::Mentions) # or if the mention contains a muted keyword should_filter end diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb index 01dc48ee7..916261a17 100644 --- a/app/models/bookmark.rb +++ b/app/models/bookmark.rb @@ -3,11 +3,11 @@ # # Table name: bookmarks # -# id :integer not null, primary key +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# status_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# status_id :integer not null # class Bookmark < ApplicationRecord diff --git a/app/models/glitch/keyword_mute.rb b/app/models/glitch/keyword_mute.rb index e7cbbe617..11b7958f6 100644 --- a/app/models/glitch/keyword_mute.rb +++ b/app/models/glitch/keyword_mute.rb @@ -3,12 +3,13 @@ # # Table name: glitch_keyword_mutes # -# id :integer not null, primary key -# account_id :integer not null -# keyword :string not null -# whole_word :boolean default(TRUE), not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# keyword :string not null +# whole_word :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null +# apply_to_mentions :boolean default(TRUE), not null # class Glitch::KeywordMute < ApplicationRecord @@ -18,6 +19,12 @@ class Glitch::KeywordMute < ApplicationRecord after_commit :invalidate_cached_matchers + module Scopes + Unscoped = 0b00 + HomeFeed = 0b01 + Mentions = 0b10 + end + def self.text_matcher_for(account_id) TextMatcher.new(account_id) end @@ -26,6 +33,13 @@ class Glitch::KeywordMute < ApplicationRecord TagMatcher.new(account_id) end + def scope + s = Scopes::Unscoped + s |= Scopes::HomeFeed + s |= Scopes::Mentions if apply_to_mentions? + s + end + private def invalidate_cached_matchers @@ -36,10 +50,12 @@ class Glitch::KeywordMute < ApplicationRecord class CachedKeywordMute attr_reader :keyword attr_reader :whole_word + attr_reader :scope - def initialize(keyword, whole_word) + def initialize(keyword, whole_word, scope) @keyword = keyword @whole_word = whole_word + @scope = scope end def boundary_regex_for_keyword @@ -49,26 +65,27 @@ class Glitch::KeywordMute < ApplicationRecord /(?mix:#{sb}#{Regexp.escape(keyword)}#{eb})/ end - def matches?(str) - str =~ (whole_word ? boundary_regex_for_keyword : /#{keyword}/i) + def matches?(str, required_scope) + ((required_scope & scope) == required_scope) && \ + str =~ (whole_word ? boundary_regex_for_keyword : /#{keyword}/i) end end class Matcher attr_reader :account_id - attr_reader :words + attr_reader :keywords def initialize(account_id) @account_id = account_id - @words = Rails.cache.fetch(self.class.cache_key(account_id)) { fetch_keywords } + @keywords = Rails.cache.fetch(self.class.cache_key(account_id)) { fetch_keywords } end protected def fetch_keywords - Glitch::KeywordMute.where(account_id: account_id).pluck(:whole_word, :keyword).map do |whole_word, keyword| - CachedKeywordMute.new(transform_keyword(keyword), whole_word) - end + Glitch::KeywordMute.select(:whole_word, :keyword, :apply_to_mentions) + .where(account_id: account_id) + .map { |kw| CachedKeywordMute.new(transform_keyword(kw.keyword), kw.whole_word, kw.scope) } end def transform_keyword(keyword) @@ -81,8 +98,8 @@ class Glitch::KeywordMute < ApplicationRecord format('keyword_mutes:regex:text:%s', account_id) end - def matches?(str) - words.any? { |w| w.matches?(str) } + def matches?(str, scope) + keywords.any? { |kw| kw.matches?(str, scope) } end end @@ -91,9 +108,9 @@ class Glitch::KeywordMute < ApplicationRecord format('keyword_mutes:regex:tag:%s', account_id) end - def matches?(tags) + def matches?(tags, scope) tags.pluck(:name).any? do |n| - words.any? { |w| w.matches?(n) } + keywords.any? { |kw| kw.matches?(n, scope) } end end diff --git a/app/models/glitch/keyword_mute_helper.rb b/app/models/glitch/keyword_mute_helper.rb index 6d067947f..955c3b1f3 100644 --- a/app/models/glitch/keyword_mute_helper.rb +++ b/app/models/glitch/keyword_mute_helper.rb @@ -9,16 +9,16 @@ class Glitch::KeywordMuteHelper @tag_matcher = Glitch::KeywordMute.tag_matcher_for(receiver_id) end - def matches?(status) - matchers_match?(status) || (status.reblog? && matchers_match?(status.reblog)) + def matches?(status, scope) + matchers_match?(status, scope) || (status.reblog? && matchers_match?(status.reblog, scope)) end private - def matchers_match?(status) - text_matcher.matches?(prepare_text(status.text)) || - text_matcher.matches?(prepare_text(status.spoiler_text)) || - tag_matcher.matches?(status.tags) + def matchers_match?(status, scope) + text_matcher.matches?(prepare_text(status.text), scope) || + text_matcher.matches?(prepare_text(status.spoiler_text), scope) || + tag_matcher.matches?(status.tags, scope) end def prepare_text(text) -- cgit From 97d2df77aae687c983c1294ebcd3962e4f9d985c Mon Sep 17 00:00:00 2001 From: David Yip Date: Sun, 3 Jun 2018 23:10:59 -0500 Subject: Add apply-to-mentions option to keyword mute UI. #454. --- app/controllers/settings/keyword_mutes_controller.rb | 2 +- app/views/settings/keyword_mutes/_fields.html.haml | 3 +++ app/views/settings/keyword_mutes/_keyword_mute.html.haml | 3 +++ app/views/settings/keyword_mutes/index.html.haml | 1 + config/locales/en.yml | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/controllers/settings/keyword_mutes_controller.rb b/app/controllers/settings/keyword_mutes_controller.rb index 699b8a3ef..cf364a903 100644 --- a/app/controllers/settings/keyword_mutes_controller.rb +++ b/app/controllers/settings/keyword_mutes_controller.rb @@ -52,7 +52,7 @@ class Settings::KeywordMutesController < Settings::BaseController end def keyword_mute_params - params.require(:keyword_mute).permit(:keyword, :whole_word) + params.require(:keyword_mute).permit(:keyword, :whole_word, :apply_to_mentions) end def paginated_keyword_mutes_for_account diff --git a/app/views/settings/keyword_mutes/_fields.html.haml b/app/views/settings/keyword_mutes/_fields.html.haml index 892676f18..843e9366a 100644 --- a/app/views/settings/keyword_mutes/_fields.html.haml +++ b/app/views/settings/keyword_mutes/_fields.html.haml @@ -2,6 +2,9 @@ = f.input :keyword = f.check_box :whole_word = f.label :whole_word, t('keyword_mutes.match_whole_word') + %br + = f.check_box :apply_to_mentions + = f.label :apply_to_mentions, t('keyword_mutes.apply_to_mentions') .actions - if f.object.persisted? diff --git a/app/views/settings/keyword_mutes/_keyword_mute.html.haml b/app/views/settings/keyword_mutes/_keyword_mute.html.haml index c45cc64fb..f9f6e3eb6 100644 --- a/app/views/settings/keyword_mutes/_keyword_mute.html.haml +++ b/app/views/settings/keyword_mutes/_keyword_mute.html.haml @@ -4,6 +4,9 @@ %td - if keyword_mute.whole_word %i.fa.fa-check + %td + - if keyword_mute.apply_to_mentions + %i.fa.fa-check %td = table_link_to 'edit', t('keyword_mutes.edit'), edit_settings_keyword_mute_path(keyword_mute) %td diff --git a/app/views/settings/keyword_mutes/index.html.haml b/app/views/settings/keyword_mutes/index.html.haml index 9ef8d55bc..7255f57b3 100644 --- a/app/views/settings/keyword_mutes/index.html.haml +++ b/app/views/settings/keyword_mutes/index.html.haml @@ -7,6 +7,7 @@ %tr %th= t('keyword_mutes.keyword') %th= t('keyword_mutes.match_whole_word') + %th= t('keyword_mutes.apply_to_mentions') %th %th %tbody diff --git a/config/locales/en.yml b/config/locales/en.yml index 3c2a8c3db..4848195aa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -526,6 +526,7 @@ en: title: Invite people keyword_mutes: add_keyword: Add keyword + apply_to_mentions: Apply to mentions edit: Edit edit_keyword: Edit keyword keyword: Keyword -- cgit From 908a770d2b344eba9a519de9087997e97d1b626e Mon Sep 17 00:00:00 2001 From: David Yip Date: Tue, 12 Jun 2018 17:14:35 -0500 Subject: keyword mute: use mentions scope in home feed filtering (#454) If a status shows up in mentions because all keyword mutes that might apply to it are marked as "don't apply to mentions", then it ought to show up in the home feed also. --- app/lib/feed_manager.rb | 18 +++++++++++++++++- spec/lib/feed_manager_spec.rb | 8 ++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 74794f00c..6eb278871 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -153,7 +153,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 keyword_filter?(status, receiver_id, Glitch::KeywordMute::Scopes::HomeFeed) + return true if keyword_filter_from_home?(status, receiver_id) check_for_mutes = [status.account_id] check_for_mutes.concat(status.mentions.pluck(:account_id)) @@ -182,6 +182,22 @@ class FeedManager false end + def keyword_filter_from_home?(status, receiver_id) + # If this status mentions the receiver, use the mentions scope: it's + # possible that the status will show up in the receiver's mentions, which + # means it ought to show up in the home feed as well. + # + # If it doesn't mention the receiver but is still headed for the home feed, + # use the home feed scope. + scope = if status.mentions.pluck(:account_id).include?(receiver_id) + Glitch::KeywordMute::Scopes::Mentions + else + Glitch::KeywordMute::Scopes::HomeFeed + end + + return true if keyword_filter?(status, receiver_id, scope) + end + def keyword_filter?(status, receiver_id, scope) Glitch::KeywordMuteHelper.new(receiver_id).matches?(status, scope) end diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb index a958a9afe..db9bf12d1 100644 --- a/spec/lib/feed_manager_spec.rb +++ b/spec/lib/feed_manager_spec.rb @@ -187,6 +187,14 @@ RSpec.describe FeedManager do expect(FeedManager.instance.filter?(:home, status, alice.id)).to be true end + + it 'returns false if the status is muted by a keyword mute that does not apply to mentions' do + Fabricate('Glitch::KeywordMute', account: alice, keyword: 'take', apply_to_mentions: false) + status = Fabricate(:status, spoiler_text: 'This is a hot take', account: bob) + status.mentions.create!(account_id: alice.id) + + expect(FeedManager.instance.filter?(:home, status, alice.id)).to be false + end end context 'for mentions feed' do -- cgit