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, Glitch::KeywordMute.matcher_for(receiver_id))
+ return true if keyword_filter?(status, receiver_id)
check_for_mutes = [status.account_id]
check_for_mutes.concat(status.mentions.pluck(:account_id))
false
end
- def keyword_filter?(status, matcher)
- should_filter = matcher =~ status.text
- should_filter ||= matcher =~ status.spoiler_text
- should_filter ||= status.tags.find_each.any? { |t| matcher =~ t.name }
+ def keyword_filter?(status, receiver_id)
+ text_matcher = Glitch::KeywordMute.text_matcher_for(receiver_id)
+ tag_matcher = Glitch::KeywordMute.tag_matcher_for(receiver_id)
+
+ should_filter = text_matcher =~ status.text
+ should_filter ||= text_matcher =~ status.spoiler_text
+ should_filter ||= tag_matcher =~ status.tags
if status.reblog?
reblog = status.reblog
- should_filter ||= matcher =~ reblog.text
- should_filter ||= matcher =~ reblog.spoiler_text
- should_filter ||= reblog.tags.find_each.any? { |t| matcher =~ t.name }
+ should_filter ||= text_matcher =~ reblog.text
+ should_filter ||= text_matcher =~ reblog.spoiler_text
+ should_filter ||= tag_matcher =~ status.tags
end
!!should_filter
should_filter = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked
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, Glitch::KeywordMute.matcher_for(receiver_id)) # or if the mention contains a muted keyword
+ should_filter ||= keyword_filter?(status, receiver_id) # or if the mention contains a muted keyword
should_filter
end
validates_presence_of :keyword
- after_commit :invalidate_cached_matcher
+ after_commit :invalidate_cached_matchers
- def self.matcher_for(account_id)
- Matcher.new(account_id)
+ def self.text_matcher_for(account_id)
+ TextMatcher.new(account_id)
+ end
+
+ def self.tag_matcher_for(account_id)
+ TagMatcher.new(account_id)
end
private
- def invalidate_cached_matcher
- Rails.cache.delete("keyword_mutes:regex:#{account_id}")
+ def invalidate_cached_matchers
+ Rails.cache.delete(TextMatcher.cache_key(account_id))
+ Rails.cache.delete(TagMatcher.cache_key(account_id))
end
- class Matcher
+ class RegexpMatcher
attr_reader :account_id
attr_reader :regex
def initialize(account_id)
@account_id = account_id
- regex_text = Rails.cache.fetch("keyword_mutes:regex:#{account_id}") { regex_text_for_account }
+ regex_text = Rails.cache.fetch(self.class.cache_key(account_id)) { make_regex_text }
@regex = /#{regex_text}/
end
+ protected
+
+ def keywords
+ Glitch::KeywordMute.where(account_id: account_id).pluck(:whole_word, :keyword)
+ end
+
+ def boundary_regex_for_keyword(keyword)
+ sb = keyword =~ /\A[[:word:]]/ ? '\b' : ''
+ eb = keyword =~ /[[:word:]]\Z/ ? '\b' : ''
+
+ /(?mix:#{sb}#{Regexp.escape(keyword)}#{eb})/
+ end
+ end
+
+ class TextMatcher < RegexpMatcher
+ def self.cache_key(account_id)
+ format('keyword_mutes:regex:%s', account_id)
+ end
+
def =~(str)
regex =~ str
end
private
- def keywords
- Glitch::KeywordMute.where(account_id: account_id).select(:keyword, :id, :whole_word)
- end
-
- def regex_text_for_account
- kws = keywords.find_each.with_object([]) do |kw, a|
- a << (kw.whole_word ? boundary_regex_for_keyword(kw.keyword) : kw.keyword)
+ def make_regex_text
+ kws = keywords.map! do |whole_word, keyword|
+ whole_word ? boundary_regex_for_keyword(keyword) : keyword
end
Regexp.union(kws).source
end
+ end
- def boundary_regex_for_keyword(keyword)
- sb = keyword =~ /\A[[:word:]]/ ? '\b' : ''
- eb = keyword =~ /[[:word:]]\Z/ ? '\b' : ''
+ class TagMatcher < RegexpMatcher
+ def self.cache_key(account_id)
+ format('keyword_mutes:tag:%s', account_id)
+ end
- /(?mix:#{sb}#{Regexp.escape(keyword)}#{eb})/
+ def =~(tags)
+ tags.pluck(:name).detect { |n| regex =~ n }
+ end
+
+ private
+
+ def make_regex_text
+ kws = keywords.map! do |whole_word, keyword|
+ term = (Tag::HASHTAG_RE =~ keyword) ? $1 : keyword
+ whole_word ? boundary_regex_for_keyword(term) : term
+ end
+
+ Regexp.union(kws).source
end
end
end