deliverable 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/deliverable/result.rb +11 -2
- data/lib/deliverable/verifier.rb +56 -10
- data/lib/deliverable/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 789cda6bdfbcd5cbd6f552775c78227c1901fba7c7f792e97e9d87f1f0f0411e
|
|
4
|
+
data.tar.gz: 5d63aee7ce38ad7b098022909a025d688624eef3bfec1f7418085086eac2eb9b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b1ec53c65f23b78024ef0b4ef4741f8cd55f57f3ab85405ae1376fcb6988220985a1655bcf36ca18996bcfa8e30f63048d6b2a010a9149cc19e8047a0a2a900d
|
|
7
|
+
data.tar.gz: e663054eba78bfe71a4638e1e07bd098e17850d32d2de5cdebe96bad7721f897c5bcbc05976126a54ed46d52e0d7f0e74baa6e6f7be821f15d19483492faeb98
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
- Add `smtp_response` to the result: the SMTP code and message returned by
|
|
6
|
+
the destination MX (e.g. `{ code: "421", message: "Greylisted, try again" }`).
|
|
7
|
+
`nil` when no SMTP probe was made or no response was received.
|
|
8
|
+
- Add `retryable` boolean to the result. `true` when the verifier hit a
|
|
9
|
+
transient signal (4xx response, server-busy, timeout, connection refused, or
|
|
10
|
+
a transient DNS error) and the result is `risky`. Callers can use this to
|
|
11
|
+
decide whether to enqueue a follow-up verification.
|
|
12
|
+
|
|
3
13
|
## 0.1.0
|
|
4
14
|
|
|
5
15
|
- Initial release.
|
data/lib/deliverable/result.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Deliverable
|
|
2
2
|
class Result
|
|
3
|
-
attr_reader :email, :score, :score_details, :errors, :warnings, :checks
|
|
3
|
+
attr_reader :email, :score, :score_details, :errors, :warnings, :checks, :smtp_response
|
|
4
4
|
|
|
5
|
-
def initialize(email:, score:, score_details:, classification:, errors:, warnings:, checks:)
|
|
5
|
+
def initialize(email:, score:, score_details:, classification:, errors:, warnings:, checks:, smtp_response: nil, retryable: false)
|
|
6
6
|
@email = email
|
|
7
7
|
@score = score
|
|
8
8
|
@score_details = score_details
|
|
@@ -10,6 +10,8 @@ module Deliverable
|
|
|
10
10
|
@errors = errors
|
|
11
11
|
@warnings = warnings
|
|
12
12
|
@checks = checks
|
|
13
|
+
@smtp_response = smtp_response
|
|
14
|
+
@retryable = retryable
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def valid?
|
|
@@ -20,6 +22,11 @@ module Deliverable
|
|
|
20
22
|
@classification
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
def retryable?
|
|
26
|
+
@retryable
|
|
27
|
+
end
|
|
28
|
+
alias_method :retryable, :retryable?
|
|
29
|
+
|
|
23
30
|
def to_h
|
|
24
31
|
{
|
|
25
32
|
email: @email,
|
|
@@ -29,6 +36,8 @@ module Deliverable
|
|
|
29
36
|
score_details: @score_details,
|
|
30
37
|
errors: @errors,
|
|
31
38
|
warnings: @warnings,
|
|
39
|
+
retryable: @retryable,
|
|
40
|
+
smtp_response: @smtp_response,
|
|
32
41
|
checks: @checks
|
|
33
42
|
}
|
|
34
43
|
end
|
data/lib/deliverable/verifier.rb
CHANGED
|
@@ -44,18 +44,23 @@ module Deliverable
|
|
|
44
44
|
accepts_any_email: false
|
|
45
45
|
}
|
|
46
46
|
@accepts_any_email = nil
|
|
47
|
+
@smtp_response = nil
|
|
48
|
+
@smtp_retryable = false
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
def call
|
|
50
52
|
ok = run_checks
|
|
51
53
|
score = calculate_score
|
|
54
|
+
cls = classification(ok)
|
|
52
55
|
Result.new(
|
|
53
56
|
email: @email,
|
|
54
57
|
score: score,
|
|
55
58
|
score_details: @details.dup,
|
|
56
|
-
classification:
|
|
59
|
+
classification: cls,
|
|
57
60
|
errors: @errors,
|
|
58
61
|
warnings: @warnings,
|
|
62
|
+
smtp_response: @smtp_response,
|
|
63
|
+
retryable: retryable?(cls),
|
|
59
64
|
checks: {
|
|
60
65
|
syntax: @details[:syntax_valid],
|
|
61
66
|
mx_record: @details[:mx_verified],
|
|
@@ -225,18 +230,32 @@ module Deliverable
|
|
|
225
230
|
smtp.start(@sender_domain) do |conn|
|
|
226
231
|
conn.mailfrom(@sender_email)
|
|
227
232
|
begin
|
|
228
|
-
conn.rcptto(recipient)
|
|
233
|
+
response = conn.rcptto(recipient)
|
|
234
|
+
record_response(response.status, response.message) if response.respond_to?(:status)
|
|
229
235
|
return :verified
|
|
230
236
|
rescue Net::SMTPFatalError => e
|
|
237
|
+
record_error(e)
|
|
231
238
|
classify_fatal(e.message)
|
|
232
|
-
rescue Net::SMTPServerBusy
|
|
239
|
+
rescue Net::SMTPServerBusy => e
|
|
240
|
+
record_error(e, retryable: true)
|
|
241
|
+
return :assumed
|
|
242
|
+
rescue Net::SMTPSyntaxError, Net::SMTPAuthenticationError => e
|
|
243
|
+
record_error(e)
|
|
233
244
|
return :assumed
|
|
234
245
|
end
|
|
235
246
|
end
|
|
236
247
|
end
|
|
237
|
-
rescue Timeout::Error
|
|
248
|
+
rescue Timeout::Error => e
|
|
249
|
+
record_transient(e.message)
|
|
250
|
+
:connection_failed
|
|
251
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH => e
|
|
252
|
+
record_transient(e.message)
|
|
238
253
|
:connection_failed
|
|
239
|
-
rescue Net::SMTPAuthenticationError
|
|
254
|
+
rescue Net::SMTPAuthenticationError => e
|
|
255
|
+
record_error(e)
|
|
256
|
+
:assumed
|
|
257
|
+
rescue Net::SMTPServerBusy => e
|
|
258
|
+
record_error(e, retryable: true)
|
|
240
259
|
:assumed
|
|
241
260
|
rescue StandardError => e
|
|
242
261
|
log "SMTP probe failed for #{recipient} on #{mail_server}:#{port}: #{e.message}"
|
|
@@ -244,18 +263,40 @@ module Deliverable
|
|
|
244
263
|
end
|
|
245
264
|
|
|
246
265
|
def classify_fatal(message)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
msg.include?("mailbox unavailable")
|
|
266
|
+
code = extract_code(message)
|
|
267
|
+
case code&.[](0)
|
|
268
|
+
when "5"
|
|
251
269
|
:invalid
|
|
252
|
-
|
|
270
|
+
when "4"
|
|
271
|
+
@smtp_retryable = true
|
|
253
272
|
:assumed
|
|
254
273
|
else
|
|
274
|
+
if message.to_s.downcase.include?("temporary")
|
|
275
|
+
@smtp_retryable = true
|
|
276
|
+
end
|
|
255
277
|
:assumed
|
|
256
278
|
end
|
|
257
279
|
end
|
|
258
280
|
|
|
281
|
+
def record_response(code, message)
|
|
282
|
+
@smtp_response = { code: code.to_s, message: message.to_s.strip }
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def record_error(err, retryable: false)
|
|
286
|
+
code = extract_code(err.message)
|
|
287
|
+
@smtp_response = { code: code, message: err.message.to_s.strip }
|
|
288
|
+
@smtp_retryable = true if retryable || code&.start_with?("4")
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def record_transient(message)
|
|
292
|
+
@smtp_response = { code: nil, message: message.to_s.strip }
|
|
293
|
+
@smtp_retryable = true
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def extract_code(message)
|
|
297
|
+
message.to_s[/\b([45]\d{2})\b/, 1]
|
|
298
|
+
end
|
|
299
|
+
|
|
259
300
|
def check_catch_all(mail_server)
|
|
260
301
|
domain = extract_domain
|
|
261
302
|
return unless domain
|
|
@@ -367,6 +408,11 @@ module Deliverable
|
|
|
367
408
|
"valid"
|
|
368
409
|
end
|
|
369
410
|
|
|
411
|
+
def retryable?(cls)
|
|
412
|
+
return false unless cls == "risky"
|
|
413
|
+
@smtp_retryable || @details[:dns_failed_assumption]
|
|
414
|
+
end
|
|
415
|
+
|
|
370
416
|
def log(message)
|
|
371
417
|
@logger&.warn(message)
|
|
372
418
|
end
|
data/lib/deliverable/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: deliverable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Strömberg
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-05-11 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: valid_email2
|
|
@@ -83,6 +84,7 @@ metadata:
|
|
|
83
84
|
homepage_uri: https://github.com/peopledb/deliverable
|
|
84
85
|
source_code_uri: https://github.com/peopledb/deliverable
|
|
85
86
|
changelog_uri: https://github.com/peopledb/deliverable/blob/main/CHANGELOG.md
|
|
87
|
+
post_install_message:
|
|
86
88
|
rdoc_options: []
|
|
87
89
|
require_paths:
|
|
88
90
|
- lib
|
|
@@ -97,7 +99,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
97
99
|
- !ruby/object:Gem::Version
|
|
98
100
|
version: '0'
|
|
99
101
|
requirements: []
|
|
100
|
-
rubygems_version: 3.
|
|
102
|
+
rubygems_version: 3.5.11
|
|
103
|
+
signing_key:
|
|
101
104
|
specification_version: 4
|
|
102
105
|
summary: 'Honest email verification: syntax, MX, SMTP RCPT, and catch-all detection.'
|
|
103
106
|
test_files: []
|