email-bounce-parser 1.0.18 → 1.0.20
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.
- package/lib/parser.js +67 -72
- package/package.json +2 -2
package/lib/parser.js
CHANGED
|
@@ -110,12 +110,13 @@ var REGEXES = {
|
|
|
110
110
|
|
|
111
111
|
recipient : [
|
|
112
112
|
/the following addresses had permanent fatal errors (?:.*?)\s\<(.+?)\>/i,
|
|
113
|
+
/there was a temporary problem delivering your message to (.+?). Gmail will retry/i,
|
|
113
114
|
/the following message to \<(.+?)\> was undeliverable/i,
|
|
114
115
|
/to the following addresses:\s*(.+)/i,
|
|
115
|
-
/^\<(.+?)\>:/m,
|
|
116
|
-
/^(.+?)\s?(?:\s\(.*?\))?\n?\s*[\[|<]mailto\:(.+?)[\]|>]/mi, // "john@acme.com<mailto:john@acme.com>" or "john@acme.com[mailto:john@acme.com]" or "john@acme.com (john@acme.com)<mailto:john@acme.com>"
|
|
117
116
|
/^your message wasn't delivered to (.+?)\s/mi, // "Your message wasn't delivered to john@acme.com"
|
|
118
117
|
/^your message to (.+?) couldn\'t/mi, // "Your message to john@acme.com couldn't"
|
|
118
|
+
/^(.+?)\s?(?:\s\(.*?\))?\n?\s*[\[|<]mailto\:(.+?)[\]|>]/mi, // "john@acme.com<mailto:john@acme.com>" or "john@acme.com[mailto:john@acme.com]" or "john@acme.com (john@acme.com)<mailto:john@acme.com>"
|
|
119
|
+
/^\<(.+?)\>:/m,
|
|
119
120
|
/\<(.+?)\> \(expanded from \<.+?\>\)\:/, // "<john@acme.com> (expanded from <john@digiflex.com>): "
|
|
120
121
|
/\<(.+?)\>:/ // "<john@acme.com>:"
|
|
121
122
|
],
|
|
@@ -124,28 +125,34 @@ var REGEXES = {
|
|
|
124
125
|
blocked : /((?:is listed)|blocked|banned|denied|dnsbl)/i,
|
|
125
126
|
spam : /(spam)/i,
|
|
126
127
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
128
|
+
enhanced_types : {
|
|
129
|
+
no_such_recipient : [
|
|
130
|
+
/(recipient address rejected: access denied)/i, // Outlook DBEB for invalid addresses
|
|
131
|
+
/(no such (?:account|user|mailbox|recipient))/i,
|
|
132
|
+
/((?:terminated|disabled|inactive|not found|unavailable|invalid) (?:account|user|mailbox|recipient))/i,
|
|
133
|
+
/(do(e)?sn\'t exist)/i, // Some Chinese SMTP return "dosn't" instead of "doesn't"
|
|
134
|
+
/(user (?:.+?) does not exist)/i,
|
|
135
|
+
/((?:account|user|mailbox|recipient) (?:.+@.+ )?(?:is )?(?:terminated|disabled|inactive|not found|unavailable|invalid|rejected))/i
|
|
136
|
+
],
|
|
137
|
+
|
|
138
|
+
server_unknown : [
|
|
139
|
+
/(does not accept mail)/i,
|
|
140
|
+
/(domain (?:name\s+)?(?:\S+\s)?(?:not found|does not exist))/i, // "domain does not exist" or "domain name does not exist" or "domain acme.com does not exist" or "domain name acme.com does not exist"
|
|
141
|
+
/(no route to host)/i
|
|
142
|
+
],
|
|
143
|
+
|
|
144
|
+
server_timeout : [
|
|
145
|
+
/(timeout|timed[\s\-]out)/i
|
|
146
|
+
],
|
|
147
|
+
|
|
148
|
+
mailbox_full : [
|
|
149
|
+
/(out of storage|(?:mailbox (?:is )?full)|(?:user (?:is )?over quota))/i // "user is over quota" (iCloud)
|
|
150
|
+
],
|
|
151
|
+
|
|
152
|
+
reputation : [
|
|
153
|
+
/(rate limited due to IP reputation)/i
|
|
154
|
+
]
|
|
155
|
+
},
|
|
149
156
|
|
|
150
157
|
error_code : [
|
|
151
158
|
/(\d{3})[\s\-](\d\.\d\.\d{1,3})/, // "550 5.4.1" or "550-5.4.1" or "550-5.4.11" or "550-5.4.110"
|
|
@@ -164,9 +171,7 @@ class Parser {
|
|
|
164
171
|
* Constructor
|
|
165
172
|
*/
|
|
166
173
|
constructor() {
|
|
167
|
-
this.__regexes =
|
|
168
|
-
|
|
169
|
-
this.__initRegexes();
|
|
174
|
+
this.__regexes = this.__initRegexes(REGEXES);
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
|
|
@@ -243,30 +248,31 @@ class Parser {
|
|
|
243
248
|
/**
|
|
244
249
|
* Initializes regexes
|
|
245
250
|
* @private
|
|
246
|
-
* @
|
|
251
|
+
* @param {object} obj
|
|
252
|
+
* @return {object} Regexes
|
|
247
253
|
*/
|
|
248
|
-
__initRegexes() {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
254
|
+
__initRegexes(obj) {
|
|
255
|
+
var _regexes = {};
|
|
256
|
+
|
|
257
|
+
for (var _key in obj) {
|
|
258
|
+
var _entry = obj[_key];
|
|
252
259
|
|
|
253
260
|
if (Array.isArray(_entry)) {
|
|
254
|
-
|
|
255
|
-
this.__regexes[_key_line] = [];
|
|
261
|
+
_regexes[_key] = [];
|
|
256
262
|
|
|
257
263
|
for (var _i = 0; _i < _entry.length; _i++) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
this.__regexes[_key].push(
|
|
261
|
-
new RE2(_regex)
|
|
264
|
+
_regexes[_key].push(
|
|
265
|
+
new RE2(_entry[_i])
|
|
262
266
|
);
|
|
263
267
|
}
|
|
268
|
+
} else if (typeof _entry === "object" && Object.keys(_entry).length > 0) {
|
|
269
|
+
_regexes[_key] = this.__initRegexes(_entry);
|
|
264
270
|
} else {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this.__regexes[_key] = new RE2(_regex);
|
|
271
|
+
_regexes[_key] = new RE2(_entry);
|
|
268
272
|
}
|
|
269
273
|
}
|
|
274
|
+
|
|
275
|
+
return _regexes;
|
|
270
276
|
}
|
|
271
277
|
|
|
272
278
|
|
|
@@ -299,21 +305,22 @@ class Parser {
|
|
|
299
305
|
_permanent = true;
|
|
300
306
|
}
|
|
301
307
|
|
|
302
|
-
// Make sure the bounce is actually permanent
|
|
303
|
-
if (_permanent === true) {
|
|
304
|
-
var _match = this.__regexes.temporary_safe.match(str);
|
|
305
|
-
|
|
306
|
-
if (_match && _match.length > 1) {
|
|
307
|
-
_temporary = true;
|
|
308
|
-
_permanent = false;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
308
|
if (_code.basic) {
|
|
313
309
|
_type = ERROR_CODES_TYPES[_code.basic] || null
|
|
314
310
|
}
|
|
315
311
|
}
|
|
316
312
|
|
|
313
|
+
// In cases where the bounce is detected as permanent, or the \
|
|
314
|
+
// temporary / permanent status is still unclear, run additional detection
|
|
315
|
+
if (_permanent === true || (_temporary === false && _permanent === false)) {
|
|
316
|
+
var _match = this.__regexes.temporary_safe.match(str);
|
|
317
|
+
|
|
318
|
+
if (_match && _match.length > 1) {
|
|
319
|
+
_temporary = true;
|
|
320
|
+
_permanent = false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
317
324
|
// No label detected? Probably because there's no error code
|
|
318
325
|
if (!_label) {
|
|
319
326
|
// Attempt a lax check
|
|
@@ -385,34 +392,22 @@ class Parser {
|
|
|
385
392
|
_spam = false;
|
|
386
393
|
|
|
387
394
|
// Detect enhanced-type from bounce content
|
|
388
|
-
var
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
_type = "no_such_recipient";
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
_match = loopRegexes(this.__regexes.enhanced_types_server_unknown, str);
|
|
395
|
-
|
|
396
|
-
if (!_type && _match && _match.length > 1) {
|
|
397
|
-
_type = "server_unknown";
|
|
398
|
-
}
|
|
395
|
+
for (var _key in this.__regexes.enhanced_types) {
|
|
396
|
+
var _match = loopRegexes(
|
|
397
|
+
this.__regexes.enhanced_types[_key],
|
|
399
398
|
|
|
400
|
-
|
|
399
|
+
str
|
|
400
|
+
);
|
|
401
401
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
_match = loopRegexes(this.__regexes.enhanced_types_mailbox_full, str);
|
|
407
|
-
|
|
408
|
-
if (!_type && _match && _match.length > 1) {
|
|
409
|
-
_type = "mailbox_full";
|
|
402
|
+
if (!_type && _match && _match.length > 1) {
|
|
403
|
+
_type = _key;
|
|
404
|
+
}
|
|
410
405
|
}
|
|
411
406
|
|
|
412
407
|
// Detect enhanced-type from enhanced error code
|
|
413
408
|
// Notice: this is done as a last resort, as sometimes enhanced code \
|
|
414
409
|
// doesn't clearly convey the actual error
|
|
415
|
-
if (!_type &&
|
|
410
|
+
if (!_type && code.enhanced) {
|
|
416
411
|
_type = ERROR_CODES_TYPES[`${code.basic} ${code.enhanced}`] || null;
|
|
417
412
|
}
|
|
418
413
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "email-bounce-parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Parses bounce emails and extract errors",
|
|
5
5
|
"author": "Eliott Vincent <eliott@crisp.chat>",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"nodeunit-x": "0.15.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"re2": "1.20.
|
|
26
|
+
"re2": "1.20.7"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"parser",
|