-
Notifications
You must be signed in to change notification settings - Fork 8.3k
/
screened_email.rb
88 lines (72 loc) · 2.49 KB
/
screened_email.rb
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
# frozen_string_literal: true
# A ScreenedEmail record represents an email address that is being watched,
# typically when creating a new User account. If the email of the signup form
# (or some other form) matches a ScreenedEmail record, an action can be
# performed based on the action_type.
class ScreenedEmail < ActiveRecord::Base
include ScreeningModel
default_action :block
validates :email, presence: true, uniqueness: true
before_save :downcase_email
def downcase_email
self.email = email.downcase
end
def self.canonical(email)
name, domain = email.split("@", 2)
name = name.gsub(/\+.*/, "")
name = name.gsub(".", "") if %w[gmail.com googlemail.com].include?(domain.downcase)
"#{name}@#{domain}".downcase
end
def self.block(email, opts = {})
email = canonical(email)
find_by_email(email) || create!(opts.slice(:action_type, :ip_address).merge(email: email))
end
def self.should_block?(email)
email = canonical(email)
screened_emails = ScreenedEmail.order(created_at: :desc).limit(100)
distances = {}
screened_emails.each do |se|
distances[se.email] = levenshtein(se.email.downcase, email.downcase)
end
max_distance = SiteSetting.levenshtein_distance_spammer_emails
screened_email =
screened_emails
.select { |se| distances[se.email] <= max_distance }
.sort { |se| distances[se.email] }
.first
screened_email.record_match! if screened_email
screened_email.try(:action_type) == actions[:block]
end
def self.levenshtein(first, second)
matrix = [(0..first.length).to_a]
(1..second.length).each { |j| matrix << [j] + [0] * (first.length) }
(1..second.length).each do |i|
(1..first.length).each do |j|
if first[j - 1] == second[i - 1]
matrix[i][j] = matrix[i - 1][j - 1]
else
matrix[i][j] = [matrix[i - 1][j], matrix[i][j - 1], matrix[i - 1][j - 1]].min + 1
end
end
end
matrix.last.last
end
end
# == Schema Information
#
# Table name: screened_emails
#
# id :integer not null, primary key
# email :string not null
# action_type :integer not null
# match_count :integer default(0), not null
# last_match_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# ip_address :inet
#
# Indexes
#
# index_screened_emails_on_email (email) UNIQUE
# index_screened_emails_on_last_match_at (last_match_at)
#