You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.3 KiB

  1. # frozen_string_literal: true
  2. # == Schema Information
  3. #
  4. # Table name: glitch_keyword_mutes
  5. #
  6. # id :integer not null, primary key
  7. # account_id :integer not null
  8. # keyword :string not null
  9. # whole_word :boolean default(TRUE), not null
  10. # created_at :datetime not null
  11. # updated_at :datetime not null
  12. #
  13. class Glitch::KeywordMute < ApplicationRecord
  14. belongs_to :account, required: true
  15. validates_presence_of :keyword
  16. after_commit :invalidate_cached_matchers
  17. def self.text_matcher_for(account_id)
  18. TextMatcher.new(account_id)
  19. end
  20. def self.tag_matcher_for(account_id)
  21. TagMatcher.new(account_id)
  22. end
  23. private
  24. def invalidate_cached_matchers
  25. Rails.cache.delete(TextMatcher.cache_key(account_id))
  26. Rails.cache.delete(TagMatcher.cache_key(account_id))
  27. end
  28. class RegexpMatcher
  29. attr_reader :account_id
  30. attr_reader :regex
  31. def initialize(account_id)
  32. @account_id = account_id
  33. regex_text = Rails.cache.fetch(self.class.cache_key(account_id)) { make_regex_text }
  34. @regex = /#{regex_text}/
  35. end
  36. protected
  37. def keywords
  38. Glitch::KeywordMute.where(account_id: account_id).pluck(:whole_word, :keyword)
  39. end
  40. def boundary_regex_for_keyword(keyword)
  41. sb = keyword =~ /\A[[:word:]]/ ? '\b' : ''
  42. eb = keyword =~ /[[:word:]]\Z/ ? '\b' : ''
  43. /(?mix:#{sb}#{Regexp.escape(keyword)}#{eb})/
  44. end
  45. end
  46. class TextMatcher < RegexpMatcher
  47. def self.cache_key(account_id)
  48. format('keyword_mutes:regex:text:%s', account_id)
  49. end
  50. def matches?(str)
  51. !!(regex =~ str)
  52. end
  53. private
  54. def make_regex_text
  55. kws = keywords.map! do |whole_word, keyword|
  56. whole_word ? boundary_regex_for_keyword(keyword) : /(?i:#{keyword})/
  57. end
  58. Regexp.union(kws).source
  59. end
  60. end
  61. class TagMatcher < RegexpMatcher
  62. def self.cache_key(account_id)
  63. format('keyword_mutes:regex:tag:%s', account_id)
  64. end
  65. def matches?(tags)
  66. tags.pluck(:name).any? { |n| regex =~ n }
  67. end
  68. private
  69. def make_regex_text
  70. kws = keywords.map! do |whole_word, keyword|
  71. term = (Tag::HASHTAG_RE =~ keyword) ? $1 : keyword
  72. whole_word ? boundary_regex_for_keyword(term) : term
  73. end
  74. Regexp.union(kws).source
  75. end
  76. end
  77. end