arn-rawmime 0.1.6 → 0.1.8
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/package.json +1 -2
- package/src/utility/mailParser.d.ts +2 -0
- package/src/utility/mailParser.js +72 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arn-rawmime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "A lightweight, dependency-free raw MIME email builder with DKIM support.",
|
|
5
5
|
"author": "ARNDESK",
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"test": "test"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"arn-rawmime": "^0.1.4",
|
|
20
19
|
"he": "^1.2.0",
|
|
21
20
|
"html-to-text": "^10.0.0",
|
|
22
21
|
"mailparser": "^3.9.0",
|
|
@@ -34,6 +34,8 @@ export interface ParsedMailData {
|
|
|
34
34
|
replyToEmail: string | null;
|
|
35
35
|
/** Reply-to display name */
|
|
36
36
|
replyToName: string;
|
|
37
|
+
/** Shared email address extracted from the email body (lowercase) */
|
|
38
|
+
sharedEmail: string | null;
|
|
37
39
|
/** Email subject */
|
|
38
40
|
subject: string;
|
|
39
41
|
/** Email date */
|
|
@@ -18,6 +18,65 @@ export const randomizeDoublelistPostID = (length = 11) => {
|
|
|
18
18
|
return result;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// HELPER: Extract Email Address from Headers
|
|
23
|
+
// ============================================================================
|
|
24
|
+
const extractEmailFromHeader = (raw) => {
|
|
25
|
+
if (!raw) return "";
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(raw)) {
|
|
28
|
+
if (raw.length === 0) return "";
|
|
29
|
+
return extractEmailFromHeader(raw[0]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof raw === "string") {
|
|
33
|
+
return raw.replace(/[<>]/g, "").trim().toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof raw === "object") {
|
|
37
|
+
if (raw.value && Array.isArray(raw.value) && raw.value.length > 0) {
|
|
38
|
+
const firstAddr = raw.value[0];
|
|
39
|
+
if (firstAddr && firstAddr.address) {
|
|
40
|
+
return firstAddr.address.trim().toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (raw.text) {
|
|
44
|
+
return raw.text.replace(/[<>]/g, "").trim().toLowerCase();
|
|
45
|
+
}
|
|
46
|
+
if (raw.address) {
|
|
47
|
+
return raw.address.trim().toLowerCase();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return "";
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// HELPER: Extract Shared Email Address from Body
|
|
56
|
+
// Matches patterns where the sender shared their email:
|
|
57
|
+
// HTML: Sender shared their email: <a href="mailto:fivetrey53@yahoo.com">fivetrey53@yahoo.com</a>
|
|
58
|
+
// Text: Sender shared their email: fivetrey53@yahoo.com
|
|
59
|
+
// Sample HTML fixture: examples/doublelist-shared-email.html
|
|
60
|
+
// ============================================================================
|
|
61
|
+
const extractSharedEmailFromBody = (html, text) => {
|
|
62
|
+
let sharedEmail = null;
|
|
63
|
+
const sharedEmailRegex = /Sender shared their email:\s*(?:<a\s+href="mailto:)?([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i;
|
|
64
|
+
|
|
65
|
+
if (html) {
|
|
66
|
+
const match = html.match(sharedEmailRegex);
|
|
67
|
+
if (match && match[1]) {
|
|
68
|
+
sharedEmail = match[1].trim().toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!sharedEmail && text) {
|
|
72
|
+
const match = text.match(sharedEmailRegex);
|
|
73
|
+
if (match && match[1]) {
|
|
74
|
+
sharedEmail = match[1].trim().toLowerCase();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return sharedEmail;
|
|
78
|
+
};
|
|
79
|
+
|
|
21
80
|
// ============================================================================
|
|
22
81
|
// HELPER: Detect Bounce Emails
|
|
23
82
|
// ============================================================================
|
|
@@ -89,34 +148,9 @@ export const parseMail = async ({ rawData }) => {
|
|
|
89
148
|
});
|
|
90
149
|
|
|
91
150
|
// --- 3. Extract Headers ---
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
let envelopeTo = "";
|
|
97
|
-
if (envelopeToRaw) {
|
|
98
|
-
if (typeof envelopeToRaw === "string") {
|
|
99
|
-
envelopeTo = envelopeToRaw.replace(/[<>]/g, "").trim().toLowerCase();
|
|
100
|
-
} else if (typeof envelopeToRaw === "object") {
|
|
101
|
-
const val = envelopeToRaw.text || envelopeToRaw.value || "";
|
|
102
|
-
envelopeTo = val.toString().replace(/[<>]/g, "").trim().toLowerCase();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
let envelopeToDomain = envelopeTo && envelopeTo.includes("@") ? envelopeTo.split("@")[1].toLowerCase() : null;
|
|
106
|
-
|
|
107
|
-
let returnPathRaw = mail.headers.get("return-path");
|
|
108
|
-
if (Array.isArray(returnPathRaw)) {
|
|
109
|
-
returnPathRaw = returnPathRaw[0];
|
|
110
|
-
}
|
|
111
|
-
let returnPath = null;
|
|
112
|
-
if (returnPathRaw) {
|
|
113
|
-
if (typeof returnPathRaw === "string") {
|
|
114
|
-
returnPath = returnPathRaw.replace(/[<>]/g, "").trim().toLowerCase();
|
|
115
|
-
} else if (typeof returnPathRaw === "object") {
|
|
116
|
-
const val = returnPathRaw.text || returnPathRaw.value || "";
|
|
117
|
-
returnPath = val.toString().replace(/[<>]/g, "").trim().toLowerCase();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
151
|
+
const envelopeTo = extractEmailFromHeader(mail.headers.get("envelope-to"));
|
|
152
|
+
const envelopeToDomain = envelopeTo && envelopeTo.includes("@") ? envelopeTo.split("@")[1].toLowerCase() : null;
|
|
153
|
+
const returnPath = extractEmailFromHeader(mail.headers.get("return-path")) || null;
|
|
120
154
|
|
|
121
155
|
let fromEmail = mail.from?.value?.[0]?.address ? mail.from.value[0].address.toLowerCase() : null;
|
|
122
156
|
let toEmail = mail.to?.value?.[0]?.address ? mail.to.value[0].address.toLowerCase() : null;
|
|
@@ -156,6 +190,15 @@ export const parseMail = async ({ rawData }) => {
|
|
|
156
190
|
let subject = mail.subject || "";
|
|
157
191
|
const attachmentCount = mail.attachments ? mail.attachments.length : 0;
|
|
158
192
|
|
|
193
|
+
// --- 5b. Extract Shared Email from Body ---
|
|
194
|
+
const sharedEmail = extractSharedEmailFromBody(html, text);
|
|
195
|
+
if (sharedEmail) {
|
|
196
|
+
replyToEmail = sharedEmail;
|
|
197
|
+
if (!replyToName || replyToName === "") {
|
|
198
|
+
replyToName = cleanName(null, sharedEmail);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
159
202
|
// --- 6. Handle Bounces ---
|
|
160
203
|
let status;
|
|
161
204
|
const detectedBounceEmail = detectBounce(subject, fromEmail, text, html, mail.dsn);
|
|
@@ -229,6 +272,7 @@ export const parseMail = async ({ rawData }) => {
|
|
|
229
272
|
toName,
|
|
230
273
|
replyToEmail,
|
|
231
274
|
replyToName,
|
|
275
|
+
sharedEmail: sharedEmail || null,
|
|
232
276
|
subject,
|
|
233
277
|
date: mail.date || null,
|
|
234
278
|
messageId: mail.messageId || null,
|