about summary refs log tree commit diff
path: root/app/lib/status_filter.rb
blob: 80254ffb4fc4388f6c29ed18793394a312114a40 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# frozen_string_literal: true

class StatusFilter
  include FilterHelper
  include Redisable

  attr_reader :status, :account

  def initialize(status, account, preloaded_relations = {})
    @status              = status
    @account             = account
    @preloaded_relations = preloaded_relations
  end

  def filtered?
    return true if status.nil? || blocked_by_policy?
    return false if account.nil? || account.id == status.account_id
    return true if redis.sismember("filtered_statuses:#{account.id}", status.id)

    if filtered_status? || silenced_account?
      redis.sadd("filtered_statuses:#{account.id}", status.id)
      return true
    end

    false
  end

  private

  def account_present?
    !account.nil?
  end

  def filtered_status?
    blocking_account? || blocking_domain? || muting_account? || filtered_reference?
  end

  def filtered_reference?
    # filter muted/blocked
    return true if account.user_hides_replies_of_blocked? && reply_to_blocked?
    return true if account.user_hides_replies_of_muted? && reply_to_muted?
    return true if account.user_hides_replies_of_blocker? && reply_to_blocker?

    # filtered by user?
    if account.user.filters_enabled && !account.user.filter_timelines_only
      if account.user.invert_filters
        return true unless phrase_filtered?(status, account.id, skip_redis: true)
      else
        return true if phrase_filtered?(status, account.id, skip_redis: true)
      end
    end

    # kajiht has no filters if status has no mentions
    return false if status&.mentions.blank?

    # Grab a list of account IDs mentioned in the status.
    mentioned_account_ids = status.mentions.pluck(:account_id)
    mentioned_accounts = Account.where(id: mentioned_account_ids)

    # Don't filter statuses mentioning you.
    return false if mentioned_account_ids.include?(account.id)

    # Filter posts missing media descriptions.
    return true if account.user.filter_undescribed? && status.media_attachments.present? && status.media_attachments.all? { |attachment| attachment.description.blank? }

    return true if account.user_hides_mentions_of_blocked? && mentioned_accounts.where.not(suspended_at: nil).exists?

    return true if mentioned_account_ids.any? do |mentioned_account_id|
      break true if @preloaded_relations[:muting] && account.user_hides_mentions_of_muted? && @preloaded_relations[:muting][mentioned_account_id]
      break true if @preloaded_relations[:blocking] && account.user_hides_mentions_of_blocked? && @preloaded_relations[:blocking][mentioned_account_id]

      if @preloaded_relations[:blocked_by]
        break true if account.user_hides_mentions_of_blocker? && @preloaded_relations[:blocked_by][mentioned_account_id]
      else
        break true if account.user_hides_mentions_of_blocker? && Block.where(account_id: mentioned_account_id, target_account_id: account.id).exists?
      end

      break false unless status.reply? && status.private_visibility? && account.user_hides_mentions_outside_scope?
      @preloaded_relations[:following] && !@preloaded_relations[:following][mentioned_account_id]
    end

    return true if !@preloaded_relations[:muting] && account.user_hides_mentions_of_muted? && account.muting?(mentioned_account_ids)
    return true if !@preloaded_relations[:blocking] && account.user_hides_mentions_of_blocked? && account.blocking?(mentioned_account_ids)
    return false unless status.reply? && status.private_visibility? && account.user_hides_mentions_outside_scope?
    !@preloaded_relations[:following] && (mentioned_account_ids - account.following_ids).any?
  end

  def reply_to_blocked?
    @preloaded_relations[:blocking] ? @preloaded_relations[:blocking][status.in_reply_to_account_id] : account.blocking?(status.in_reply_to_account_id)
  end

  def reply_to_muted?
    @preloaded_relations[:muting] ? @preloaded_relations[:muting][status.in_reply_to_account_id] : account.muting?(status.in_reply_to_account_id)
  end

  def blocking_account?
    @preloaded_relations[:blocking] ? @preloaded_relations[:blocking][status.account_id] : account.blocking?(status.account_id)
  end

  def blocking_domain?
    @preloaded_relations[:domain_blocking_by_domain] ? @preloaded_relations[:domain_blocking_by_domain][status.account_domain] : account.domain_blocking?(status.account_domain)
  end

  def muting_account?
    @preloaded_relations[:muting] ? @preloaded_relations[:muting][status.account_id] : account.muting?(status.account_id)
  end

  def account_following_status_account?
    @preloaded_relations[:following] ? @preloaded_relations[:following][status.account_id] : account&.following?(status.account_id)
  end

  def reply_to_blocker?
    status.in_reply_to_account.present? && status.in_reply_to_account.blocking?(account)
  end

  def silenced_account?
    !account&.silenced? && status_account_silenced? && !account_following_status_account?
  end

  def status_account_silenced?
    status.account.silenced?
  end

  def blocked_by_policy?
    !policy_allows_show?
  end

  def policy_allows_show?
    StatusPolicy.new(account, status, @preloaded_relations).show?
  end
end