about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/settings/keyword_mutes_controller.rb64
-rw-r--r--app/helpers/settings/keyword_mutes_helper.rb2
-rw-r--r--app/lib/feed_manager.rb15
-rw-r--r--app/models/glitch.rb7
-rw-r--r--app/models/glitch/keyword_mute.rb66
-rw-r--r--app/views/settings/keyword_mutes/_fields.html.haml11
-rw-r--r--app/views/settings/keyword_mutes/_keyword_mute.html.haml10
-rw-r--r--app/views/settings/keyword_mutes/edit.html.haml6
-rw-r--r--app/views/settings/keyword_mutes/index.html.haml18
-rw-r--r--app/views/settings/keyword_mutes/new.html.haml6
10 files changed, 205 insertions, 0 deletions
diff --git a/app/controllers/settings/keyword_mutes_controller.rb b/app/controllers/settings/keyword_mutes_controller.rb
new file mode 100644
index 000000000..f79e1b320
--- /dev/null
+++ b/app/controllers/settings/keyword_mutes_controller.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+class Settings::KeywordMutesController < ApplicationController
+  layout 'admin'
+
+  before_action :authenticate_user!
+  before_action :load_keyword_mute, only: [:edit, :update, :destroy]
+
+  def index
+    @keyword_mutes = paginated_keyword_mutes_for_account
+  end
+
+  def new
+    @keyword_mute = keyword_mutes_for_account.build
+  end
+
+  def create
+    @keyword_mute = keyword_mutes_for_account.create(keyword_mute_params)
+
+    if @keyword_mute.persisted?
+      redirect_to settings_keyword_mutes_path, notice: I18n.t('generic.changes_saved_msg')
+    else
+      render :new
+    end
+  end
+
+  def update
+    if @keyword_mute.update(keyword_mute_params)
+      redirect_to settings_keyword_mutes_path, notice: I18n.t('generic.changes_saved_msg')
+    else
+      render :edit
+    end
+  end
+
+  def destroy
+    @keyword_mute.destroy!
+
+    redirect_to settings_keyword_mutes_path, notice: I18n.t('generic.changes_saved_msg')
+  end
+
+  def destroy_all
+    keyword_mutes_for_account.delete_all
+
+    redirect_to settings_keyword_mutes_path, notice: I18n.t('generic.changes_saved_msg')
+  end
+
+  private
+
+  def keyword_mutes_for_account
+    Glitch::KeywordMute.where(account: current_account)
+  end
+
+  def load_keyword_mute
+    @keyword_mute = keyword_mutes_for_account.find(params[:id])
+  end
+
+  def keyword_mute_params
+    params.require(:keyword_mute).permit(:keyword, :whole_word)
+  end
+
+  def paginated_keyword_mutes_for_account
+    keyword_mutes_for_account.order(:keyword).page params[:page]
+  end
+end
diff --git a/app/helpers/settings/keyword_mutes_helper.rb b/app/helpers/settings/keyword_mutes_helper.rb
new file mode 100644
index 000000000..7b98cd59e
--- /dev/null
+++ b/app/helpers/settings/keyword_mutes_helper.rb
@@ -0,0 +1,2 @@
+module Settings::KeywordMutesHelper
+end
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index ca15745cb..2ddfac336 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -141,6 +141,8 @@ class FeedManager
     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))
+
     check_for_mutes = [status.account_id]
     check_for_mutes.concat(status.mentions.pluck(:account_id))
     check_for_mutes.concat([status.reblog.account_id]) if status.reblog?
@@ -166,6 +168,18 @@ class FeedManager
     false
   end
 
+  def keyword_filter?(status, matcher)
+    should_filter   = matcher =~ status.text
+    should_filter ||= matcher =~ status.spoiler_text
+
+    if status.reblog?
+      should_filter ||= matcher =~ status.reblog.text
+      should_filter ||= matcher =~ status.reblog.spoiler_text
+    end
+
+    !!should_filter
+  end
+
   def filter_from_mentions?(status, receiver_id)
     return true if receiver_id == status.account_id
 
@@ -175,6 +189,7 @@ class FeedManager
 
     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
   end
diff --git a/app/models/glitch.rb b/app/models/glitch.rb
new file mode 100644
index 000000000..0e497babc
--- /dev/null
+++ b/app/models/glitch.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Glitch
+  def self.table_name_prefix
+    'glitch_'
+  end
+end
diff --git a/app/models/glitch/keyword_mute.rb b/app/models/glitch/keyword_mute.rb
new file mode 100644
index 000000000..73de4d4b7
--- /dev/null
+++ b/app/models/glitch/keyword_mute.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+# == Schema Information
+#
+# 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
+#
+
+class Glitch::KeywordMute < ApplicationRecord
+  belongs_to :account, required: true
+
+  validates_presence_of :keyword
+
+  after_commit :invalidate_cached_matcher
+
+  def self.matcher_for(account_id)
+    Matcher.new(account_id)
+  end
+
+  private
+
+  def invalidate_cached_matcher
+    Rails.cache.delete("keyword_mutes:regex:#{account_id}")
+  end
+
+  class Matcher
+    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 = /#{regex_text}/i
+    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)
+      end
+
+      Regexp.union(kws).source
+    end
+
+    def boundary_regex_for_keyword(keyword)
+      sb = keyword =~ /\A[[:word:]]/ ? '\b' : ''
+      eb = keyword =~ /[[:word:]]\Z/ ? '\b' : ''
+
+      /#{sb}#{Regexp.escape(keyword)}#{eb}/
+    end
+  end
+end
diff --git a/app/views/settings/keyword_mutes/_fields.html.haml b/app/views/settings/keyword_mutes/_fields.html.haml
new file mode 100644
index 000000000..892676f18
--- /dev/null
+++ b/app/views/settings/keyword_mutes/_fields.html.haml
@@ -0,0 +1,11 @@
+.fields-group
+  = f.input :keyword
+  = f.check_box :whole_word
+  = f.label :whole_word, t('keyword_mutes.match_whole_word')
+
+.actions
+  - if f.object.persisted?
+    = f.button :button, t('generic.save_changes'), type: :submit
+    = link_to t('keyword_mutes.remove'), settings_keyword_mute_path(f.object), class: 'negative button', method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
+  - else
+    = f.button :button, t('keyword_mutes.add_keyword'), type: :submit
diff --git a/app/views/settings/keyword_mutes/_keyword_mute.html.haml b/app/views/settings/keyword_mutes/_keyword_mute.html.haml
new file mode 100644
index 000000000..c45cc64fb
--- /dev/null
+++ b/app/views/settings/keyword_mutes/_keyword_mute.html.haml
@@ -0,0 +1,10 @@
+%tr
+  %td
+    = keyword_mute.keyword
+  %td
+    - if keyword_mute.whole_word
+      %i.fa.fa-check
+  %td
+    = table_link_to 'edit', t('keyword_mutes.edit'), edit_settings_keyword_mute_path(keyword_mute)
+  %td
+    = table_link_to 'times', t('keyword_mutes.remove'), settings_keyword_mute_path(keyword_mute), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
diff --git a/app/views/settings/keyword_mutes/edit.html.haml b/app/views/settings/keyword_mutes/edit.html.haml
new file mode 100644
index 000000000..af3949be2
--- /dev/null
+++ b/app/views/settings/keyword_mutes/edit.html.haml
@@ -0,0 +1,6 @@
+- content_for :page_title do
+  = t('keyword_mutes.edit_keyword')
+
+= simple_form_for @keyword_mute, url: settings_keyword_mute_path(@keyword_mute), as: :keyword_mute do |f|
+  = render 'shared/error_messages', object: @keyword_mute
+  = render 'fields', f: f
diff --git a/app/views/settings/keyword_mutes/index.html.haml b/app/views/settings/keyword_mutes/index.html.haml
new file mode 100644
index 000000000..9ef8d55bc
--- /dev/null
+++ b/app/views/settings/keyword_mutes/index.html.haml
@@ -0,0 +1,18 @@
+- content_for :page_title do
+  = t('settings.keyword_mutes')
+
+.table-wrapper
+  %table.table
+    %thead
+      %tr
+        %th= t('keyword_mutes.keyword')
+        %th= t('keyword_mutes.match_whole_word')
+        %th
+        %th
+      %tbody
+        = render partial: 'keyword_mute', collection: @keyword_mutes, as: :keyword_mute
+
+= paginate @keyword_mutes
+.simple_form
+  = link_to t('keyword_mutes.add_keyword'), new_settings_keyword_mute_path, class: 'button'
+  = link_to t('keyword_mutes.remove_all'), destroy_all_settings_keyword_mutes_path, class: 'button negative', method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
diff --git a/app/views/settings/keyword_mutes/new.html.haml b/app/views/settings/keyword_mutes/new.html.haml
new file mode 100644
index 000000000..5c999c8d2
--- /dev/null
+++ b/app/views/settings/keyword_mutes/new.html.haml
@@ -0,0 +1,6 @@
+- content_for :page_title do
+  = t('keyword_mutes.add_keyword')
+
+= simple_form_for @keyword_mute, url: settings_keyword_mutes_path, as: :keyword_mute do |f|
+  = render 'shared/error_messages', object: @keyword_mute
+  = render 'fields', f: f