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.
This commit is contained in:
David Yip
2018-06-03 19:41:54 -05:00
parent 37d495eeeb
commit 26573ad7e6
8 changed files with 99 additions and 61 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)