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.
Files changed (2) hide show
  1. package/lib/parser.js +67 -72
  2. 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
- enhanced_types_no_such_recipient : [
128
- /(recipient address rejected: access denied)/i, // Outlook DBEB for invalid addresses
129
- /(no such (?:account|user|mailbox|recipient))/i,
130
- /((?:terminated|disabled|inactive|not found|unavailable|invalid) (?:account|user|mailbox|recipient))/i,
131
- /(do(e)?sn\'t exist)/i, // Some Chinese SMTP return "dosn't" instead of "doesn't"
132
- /(user (?:.+?) does not exist)/i,
133
- /((?:account|user|mailbox|recipient) (?:.+@.+ )?(?:is )?(?:terminated|disabled|inactive|not found|unavailable|invalid))/i
134
- ],
135
-
136
- enhanced_types_server_unknown : [
137
- /(does not accept mail)/i,
138
- /(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"
139
- /(no route to host)/i
140
- ],
141
-
142
- enhanced_types_server_timeout : [
143
- /(timeout|timed[\s\-]out)/i
144
- ],
145
-
146
- enhanced_types_mailbox_full : [
147
- /(out of storage|(?:mailbox (?:is )?full)|(?:user (?:is )?over quota))/i // "user is over quota" (iCloud)
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
- * @return {undefined}
251
+ * @param {object} obj
252
+ * @return {object} Regexes
247
253
  */
248
- __initRegexes() {
249
- for (var _key in REGEXES) {
250
- var _key_line = `${_key}_line`;
251
- var _entry = REGEXES[_key];
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
- this.__regexes[_key] = [];
255
- this.__regexes[_key_line] = [];
261
+ _regexes[_key] = [];
256
262
 
257
263
  for (var _i = 0; _i < _entry.length; _i++) {
258
- var _regex = _entry[_i];
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
- var _regex = _entry;
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 _match = loopRegexes(this.__regexes.enhanced_types_no_such_recipient, str);
389
-
390
- if (_match && _match.length > 1) {
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
- _match = loopRegexes(this.__regexes.enhanced_types_server_timeout, str);
399
+ str
400
+ );
401
401
 
402
- if (!_type && _match && _match.length > 1) {
403
- _type = "server_timeout";
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 && _match && code.enhanced) {
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.18",
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.1"
26
+ "re2": "1.20.7"
27
27
  },
28
28
  "keywords": [
29
29
  "parser",