@firela/billclaw-core 0.1.3
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/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/billclaw.d.ts +76 -0
- package/dist/billclaw.d.ts.map +1 -0
- package/dist/billclaw.js +205 -0
- package/dist/billclaw.js.map +1 -0
- package/dist/credentials/index.d.ts +8 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +8 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/keychain.d.ts +92 -0
- package/dist/credentials/keychain.d.ts.map +1 -0
- package/dist/credentials/keychain.js +172 -0
- package/dist/credentials/keychain.js.map +1 -0
- package/dist/credentials/store.d.ts +76 -0
- package/dist/credentials/store.d.ts.map +1 -0
- package/dist/credentials/store.js +144 -0
- package/dist/credentials/store.js.map +1 -0
- package/dist/errors/errors.d.ts +92 -0
- package/dist/errors/errors.d.ts.map +1 -0
- package/dist/errors/errors.js +315 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/errors/index.d.ts +7 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +7 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/exporters/beancount.d.ts +42 -0
- package/dist/exporters/beancount.d.ts.map +1 -0
- package/dist/exporters/beancount.js +141 -0
- package/dist/exporters/beancount.js.map +1 -0
- package/dist/exporters/index.d.ts +8 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +8 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/ledger.d.ts +42 -0
- package/dist/exporters/ledger.d.ts.map +1 -0
- package/dist/exporters/ledger.js +139 -0
- package/dist/exporters/ledger.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/models/config.d.ts +552 -0
- package/dist/models/config.d.ts.map +1 -0
- package/dist/models/config.js +168 -0
- package/dist/models/config.js.map +1 -0
- package/dist/models/index.d.ts +7 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +8 -0
- package/dist/models/index.js.map +1 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/types.d.ts +110 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +85 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/security/audit.d.ts +148 -0
- package/dist/security/audit.d.ts.map +1 -0
- package/dist/security/audit.js +286 -0
- package/dist/security/audit.js.map +1 -0
- package/dist/security/index.d.ts +7 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +7 -0
- package/dist/security/index.js.map +1 -0
- package/dist/services/event-emitter.d.ts +171 -0
- package/dist/services/event-emitter.d.ts.map +1 -0
- package/dist/services/event-emitter.js +287 -0
- package/dist/services/event-emitter.js.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +8 -0
- package/dist/services/index.js.map +1 -0
- package/dist/sources/gmail/bill-recognizer.d.ts +71 -0
- package/dist/sources/gmail/bill-recognizer.d.ts.map +1 -0
- package/dist/sources/gmail/bill-recognizer.js +341 -0
- package/dist/sources/gmail/bill-recognizer.js.map +1 -0
- package/dist/sources/gmail/email-parser.d.ts +68 -0
- package/dist/sources/gmail/email-parser.d.ts.map +1 -0
- package/dist/sources/gmail/email-parser.js +238 -0
- package/dist/sources/gmail/email-parser.js.map +1 -0
- package/dist/sources/gmail/gmail-fetch.d.ts +54 -0
- package/dist/sources/gmail/gmail-fetch.d.ts.map +1 -0
- package/dist/sources/gmail/gmail-fetch.js +300 -0
- package/dist/sources/gmail/gmail-fetch.js.map +1 -0
- package/dist/sources/gmail/index.d.ts +7 -0
- package/dist/sources/gmail/index.d.ts.map +1 -0
- package/dist/sources/gmail/index.js +7 -0
- package/dist/sources/gmail/index.js.map +1 -0
- package/dist/sources/index.d.ts +8 -0
- package/dist/sources/index.d.ts.map +1 -0
- package/dist/sources/index.js +8 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/sources/plaid/index.d.ts +7 -0
- package/dist/sources/plaid/index.d.ts.map +1 -0
- package/dist/sources/plaid/index.js +7 -0
- package/dist/sources/plaid/index.js.map +1 -0
- package/dist/sources/plaid/plaid-sync.d.ts +42 -0
- package/dist/sources/plaid/plaid-sync.d.ts.map +1 -0
- package/dist/sources/plaid/plaid-sync.js +182 -0
- package/dist/sources/plaid/plaid-sync.js.map +1 -0
- package/dist/storage/cache.d.ts +134 -0
- package/dist/storage/cache.d.ts.map +1 -0
- package/dist/storage/cache.js +239 -0
- package/dist/storage/cache.js.map +1 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/indexes.d.ts +136 -0
- package/dist/storage/indexes.d.ts.map +1 -0
- package/dist/storage/indexes.js +294 -0
- package/dist/storage/indexes.js.map +1 -0
- package/dist/storage/locking.d.ts +103 -0
- package/dist/storage/locking.d.ts.map +1 -0
- package/dist/storage/locking.js +158 -0
- package/dist/storage/locking.js.map +1 -0
- package/dist/storage/streaming.d.ts +102 -0
- package/dist/storage/streaming.d.ts.map +1 -0
- package/dist/storage/streaming.js +245 -0
- package/dist/storage/streaming.js.map +1 -0
- package/dist/storage/transaction-storage.d.ts +101 -0
- package/dist/storage/transaction-storage.d.ts.map +1 -0
- package/dist/storage/transaction-storage.js +193 -0
- package/dist/storage/transaction-storage.js.map +1 -0
- package/dist/sync/index.d.ts +7 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +7 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/sync-service.d.ts +42 -0
- package/dist/sync/sync-service.d.ts.map +1 -0
- package/dist/sync/sync-service.js +112 -0
- package/dist/sync/sync-service.js.map +1 -0
- package/dist/test-fixtures.d.ts +38 -0
- package/dist/test-fixtures.d.ts.map +1 -0
- package/dist/test-fixtures.js +137 -0
- package/dist/test-fixtures.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bill Recognition Service - Framework-agnostic bill detection from email content
|
|
3
|
+
*
|
|
4
|
+
* Analyzes email content to determine if it's a bill and extracts relevant information.
|
|
5
|
+
* Uses keyword matching, sender detection, and confidence scoring.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Default bill detection keywords
|
|
9
|
+
* Includes both phrases and individual words for better matching
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_KEYWORDS = [
|
|
12
|
+
// Phrases
|
|
13
|
+
"invoice",
|
|
14
|
+
"statement",
|
|
15
|
+
"bill due",
|
|
16
|
+
"receipt",
|
|
17
|
+
"payment due",
|
|
18
|
+
"amount due",
|
|
19
|
+
"past due",
|
|
20
|
+
"payment received",
|
|
21
|
+
"your bill",
|
|
22
|
+
"account statement",
|
|
23
|
+
"credit card statement",
|
|
24
|
+
"utility bill",
|
|
25
|
+
"electric bill",
|
|
26
|
+
"water bill",
|
|
27
|
+
"internet bill",
|
|
28
|
+
"phone bill",
|
|
29
|
+
"insurance premium",
|
|
30
|
+
"subscription",
|
|
31
|
+
"membership",
|
|
32
|
+
// Individual words for better matching
|
|
33
|
+
"premium",
|
|
34
|
+
"due",
|
|
35
|
+
"notice",
|
|
36
|
+
"invoice",
|
|
37
|
+
"statement",
|
|
38
|
+
"bill",
|
|
39
|
+
"receipt",
|
|
40
|
+
"payment",
|
|
41
|
+
"subscription",
|
|
42
|
+
"membership",
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Known billing domains (extracted from common service providers)
|
|
46
|
+
*/
|
|
47
|
+
export const KNOWN_BILLING_DOMAINS = [
|
|
48
|
+
"@netflix.com",
|
|
49
|
+
"@paypal.com",
|
|
50
|
+
"@amazon.com",
|
|
51
|
+
"@apple.com",
|
|
52
|
+
"@google.com",
|
|
53
|
+
"@spotify.com",
|
|
54
|
+
"@att.com",
|
|
55
|
+
"@verizon.com",
|
|
56
|
+
"@comcast.com",
|
|
57
|
+
"@pge.com",
|
|
58
|
+
"@chase.com",
|
|
59
|
+
"@citibank.com",
|
|
60
|
+
"@amex.com",
|
|
61
|
+
"@discover.com",
|
|
62
|
+
"@capitalone.com",
|
|
63
|
+
"@geico.com",
|
|
64
|
+
"@progressive.com",
|
|
65
|
+
"@statefarm.com",
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Default bill type patterns for classification
|
|
69
|
+
* Ordered by specificity (more specific patterns first)
|
|
70
|
+
*/
|
|
71
|
+
const DEFAULT_BILL_TYPE_PATTERNS = {
|
|
72
|
+
Utility: ["electric", "gas", "water", "utility", "power", "energy", "pge"],
|
|
73
|
+
Internet: ["internet", "broadband", "wifi", "fiber", "xfinity", "comcast"],
|
|
74
|
+
Phone: [
|
|
75
|
+
"phone",
|
|
76
|
+
"wireless",
|
|
77
|
+
"mobile",
|
|
78
|
+
"cellular",
|
|
79
|
+
"att",
|
|
80
|
+
"verizon",
|
|
81
|
+
"t-mobile",
|
|
82
|
+
],
|
|
83
|
+
Insurance: [
|
|
84
|
+
"insurance",
|
|
85
|
+
"premium",
|
|
86
|
+
"coverage",
|
|
87
|
+
"geico",
|
|
88
|
+
"progressive",
|
|
89
|
+
"state farm",
|
|
90
|
+
],
|
|
91
|
+
Subscription: [
|
|
92
|
+
"subscription",
|
|
93
|
+
"membership",
|
|
94
|
+
"netflix",
|
|
95
|
+
"spotify",
|
|
96
|
+
"amazon prime",
|
|
97
|
+
],
|
|
98
|
+
"Credit Card": ["credit card", "visa", "mastercard", "amex", "discover"],
|
|
99
|
+
Housing: ["rent", "lease", "mortgage", "housing", "apartment"],
|
|
100
|
+
Loan: ["loan", "financing", "student loan"],
|
|
101
|
+
Invoice: ["invoice", "billing", "amount due", "payment due"],
|
|
102
|
+
Receipt: ["receipt", "purchase", "order confirmation"],
|
|
103
|
+
Generic: ["statement"], // Most generic, should be last
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Extract domain from email address
|
|
107
|
+
*/
|
|
108
|
+
export function extractDomain(email) {
|
|
109
|
+
const match = email.toLowerCase().match(/@([^@\s]+)$/);
|
|
110
|
+
return match ? match[1] : "";
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if sender is in whitelist
|
|
114
|
+
*/
|
|
115
|
+
function isWhitelistedSender(from, whitelist) {
|
|
116
|
+
const fromLower = from.toLowerCase();
|
|
117
|
+
for (const entry of whitelist) {
|
|
118
|
+
const entryLower = entry.toLowerCase();
|
|
119
|
+
// Exact match (e.g., "billing@paypal.com")
|
|
120
|
+
if (fromLower === entryLower) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
// Domain match (e.g., "@paypal.com" matches "billing@paypal.com")
|
|
124
|
+
if (entryLower.startsWith("@") && fromLower.endsWith(entryLower)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
// Wildcard pattern (e.g., "*@paypal.com")
|
|
128
|
+
if (entryLower.startsWith("*@") &&
|
|
129
|
+
fromLower.endsWith(entryLower.slice(1))) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if text contains any keywords (case-insensitive)
|
|
137
|
+
*/
|
|
138
|
+
function containsKeywords(text, keywords) {
|
|
139
|
+
const textLower = text.toLowerCase();
|
|
140
|
+
return keywords.some((keyword) => textLower.includes(keyword.toLowerCase()));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract amount from text using regex patterns
|
|
144
|
+
*/
|
|
145
|
+
function extractAmount(text) {
|
|
146
|
+
// Pattern 1: $123.45 or $1,234.56
|
|
147
|
+
const usdPattern = /\$\s*([0-9,]+\.?\d*)/g;
|
|
148
|
+
const usdMatch = usdPattern.exec(text);
|
|
149
|
+
if (usdMatch) {
|
|
150
|
+
const amount = parseFloat(usdMatch[1].replace(/,/g, ""));
|
|
151
|
+
if (!isNaN(amount) && amount > 0) {
|
|
152
|
+
return { amount, currency: "USD" };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Pattern 2: €123,45 or €123.45
|
|
156
|
+
const eurPattern = /€\s*([0-9.,]+(?:[,.]\d{2})?)/g;
|
|
157
|
+
const eurMatch = eurPattern.exec(text);
|
|
158
|
+
if (eurMatch) {
|
|
159
|
+
const amountStr = eurMatch[1].replace(/\./g, "").replace(/,/, ".");
|
|
160
|
+
const amount = parseFloat(amountStr);
|
|
161
|
+
if (!isNaN(amount) && amount > 0) {
|
|
162
|
+
return { amount, currency: "EUR" };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Pattern 3: £123.45
|
|
166
|
+
const gbpPattern = /£\s*([0-9,]+\.?\d*)/g;
|
|
167
|
+
const gbpMatch = gbpPattern.exec(text);
|
|
168
|
+
if (gbpMatch) {
|
|
169
|
+
const amount = parseFloat(gbpMatch[1].replace(/,/g, ""));
|
|
170
|
+
if (!isNaN(amount) && amount > 0) {
|
|
171
|
+
return { amount, currency: "GBP" };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Pattern 4: Amount: 123.45 USD
|
|
175
|
+
const amountPattern = /amount[:\s]+([0-9,]+\.?\d*)\s*(USD|EUR|GBP|CAD|AUD)?/i;
|
|
176
|
+
const amountMatch = amountPattern.exec(text);
|
|
177
|
+
if (amountMatch) {
|
|
178
|
+
const amount = parseFloat(amountMatch[1].replace(/,/g, ""));
|
|
179
|
+
const currency = amountMatch[2]?.toUpperCase() || "USD";
|
|
180
|
+
if (!isNaN(amount) && amount > 0) {
|
|
181
|
+
return { amount, currency };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Extract date from text using regex patterns
|
|
188
|
+
*/
|
|
189
|
+
function extractDate(text) {
|
|
190
|
+
// Pattern 1: MM/DD/YYYY or DD/MM/YYYY
|
|
191
|
+
const datePattern1 = /\b(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])\/(20\d{2})\b/g;
|
|
192
|
+
const match1 = datePattern1.exec(text);
|
|
193
|
+
if (match1) {
|
|
194
|
+
return match1[0];
|
|
195
|
+
}
|
|
196
|
+
// Pattern 2: YYYY-MM-DD
|
|
197
|
+
const datePattern2 = /\b(20\d{2})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\b/g;
|
|
198
|
+
const match2 = datePattern2.exec(text);
|
|
199
|
+
if (match2) {
|
|
200
|
+
return match2[0];
|
|
201
|
+
}
|
|
202
|
+
// Pattern 3: "Due Date: January 15, 2024" or "Due: Jan 15"
|
|
203
|
+
const dueDatePattern = /(?:due|payment|expire)[s:\s]+([A-Za-z]+)\s+(\d{1,2}),?\s*(20\d{2})?/i;
|
|
204
|
+
const match3 = dueDatePattern.exec(text);
|
|
205
|
+
if (match3) {
|
|
206
|
+
const month = match3[1];
|
|
207
|
+
const day = match3[2];
|
|
208
|
+
const year = match3[3] || new Date().getFullYear();
|
|
209
|
+
return `${month} ${day}, ${year}`;
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Classify bill type based on content
|
|
215
|
+
* Returns the type with the most matching keywords
|
|
216
|
+
*/
|
|
217
|
+
function classifyBillType(subject, body, patterns) {
|
|
218
|
+
const content = `${subject} ${body}`.toLowerCase();
|
|
219
|
+
// Score each bill type by number of matching keywords
|
|
220
|
+
const scores = [];
|
|
221
|
+
for (const [billType, keywords] of Object.entries(patterns)) {
|
|
222
|
+
const matchCount = keywords.filter((keyword) => content.includes(keyword.toLowerCase())).length;
|
|
223
|
+
if (matchCount > 0) {
|
|
224
|
+
scores.push({ type: billType, score: matchCount });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Return type with highest score, or undefined if no matches
|
|
228
|
+
if (scores.length === 0) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
// Sort by score descending, then by specificity (longer keyword matches first)
|
|
232
|
+
scores.sort((a, b) => {
|
|
233
|
+
if (b.score !== a.score)
|
|
234
|
+
return b.score - a.score;
|
|
235
|
+
// Tie-breaker: prefer types that come first (more specific patterns defined first)
|
|
236
|
+
return 0;
|
|
237
|
+
});
|
|
238
|
+
return scores[0].type;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Recognize if an email is a bill and extract relevant information
|
|
242
|
+
*/
|
|
243
|
+
export function recognizeBill(email, config) {
|
|
244
|
+
const reasons = [];
|
|
245
|
+
let confidence = 0;
|
|
246
|
+
// Use custom keywords or defaults
|
|
247
|
+
const keywords = config.keywords.length > 0 ? config.keywords : DEFAULT_KEYWORDS;
|
|
248
|
+
// Use custom bill type patterns or defaults
|
|
249
|
+
const billTypePatterns = config.billTypePatterns || DEFAULT_BILL_TYPE_PATTERNS;
|
|
250
|
+
// Check 1: Sender whitelist (strongest signal)
|
|
251
|
+
const isWhitelisted = isWhitelistedSender(email.from, config.senderWhitelist);
|
|
252
|
+
if (isWhitelisted) {
|
|
253
|
+
confidence += 0.4;
|
|
254
|
+
reasons.push("Sender is in whitelist");
|
|
255
|
+
}
|
|
256
|
+
// Check 2: Known billing domain
|
|
257
|
+
const isKnownBillingDomain = KNOWN_BILLING_DOMAINS.some((d) => {
|
|
258
|
+
const billingDomain = d.startsWith("@") ? d : `@${d}`;
|
|
259
|
+
return email.from.toLowerCase().endsWith(billingDomain);
|
|
260
|
+
});
|
|
261
|
+
if (isKnownBillingDomain) {
|
|
262
|
+
confidence += 0.25;
|
|
263
|
+
reasons.push("Known billing domain");
|
|
264
|
+
}
|
|
265
|
+
// Check 2.5: Sender contains "billing" - strong signal
|
|
266
|
+
if (email.from.toLowerCase().includes("billing")) {
|
|
267
|
+
confidence += 0.3;
|
|
268
|
+
reasons.push("Sender is billing address");
|
|
269
|
+
}
|
|
270
|
+
// Check 3: Keyword matching in subject and body
|
|
271
|
+
const subjectHasKeywords = containsKeywords(email.subject, keywords);
|
|
272
|
+
const bodyHasKeywords = containsKeywords(email.body, keywords);
|
|
273
|
+
if (subjectHasKeywords) {
|
|
274
|
+
confidence += 0.2;
|
|
275
|
+
reasons.push("Subject contains bill keywords");
|
|
276
|
+
}
|
|
277
|
+
if (bodyHasKeywords) {
|
|
278
|
+
confidence += 0.1;
|
|
279
|
+
reasons.push("Body contains bill keywords");
|
|
280
|
+
}
|
|
281
|
+
// Extract amount and date
|
|
282
|
+
const amountInfo = extractAmount(email.subject + " " + email.body);
|
|
283
|
+
const dueDate = extractDate(email.subject + " " + email.body);
|
|
284
|
+
// Bonus: Has amount
|
|
285
|
+
if (amountInfo) {
|
|
286
|
+
confidence += 0.05;
|
|
287
|
+
reasons.push("Contains amount");
|
|
288
|
+
}
|
|
289
|
+
// Check required fields
|
|
290
|
+
if (config.requireAmount && !amountInfo) {
|
|
291
|
+
return {
|
|
292
|
+
isBill: false,
|
|
293
|
+
confidence: 0,
|
|
294
|
+
reasons: ["Missing required amount"],
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (config.requireDate && !dueDate) {
|
|
298
|
+
return {
|
|
299
|
+
isBill: false,
|
|
300
|
+
confidence: 0,
|
|
301
|
+
reasons: ["Missing required date"],
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
// Classify bill type
|
|
305
|
+
const billType = classifyBillType(email.subject, email.body, billTypePatterns);
|
|
306
|
+
// Extract merchant name (from sender name before email)
|
|
307
|
+
const merchantMatch = email.from.match(/^"?([^"<>@]+)"?\s*</);
|
|
308
|
+
const merchant = merchantMatch
|
|
309
|
+
? merchantMatch[1].trim()
|
|
310
|
+
: email.from.split("@")[0];
|
|
311
|
+
// Determine if it's a bill based on confidence threshold
|
|
312
|
+
const threshold = config.confidenceThreshold || 0.5;
|
|
313
|
+
const isBill = confidence >= threshold;
|
|
314
|
+
return {
|
|
315
|
+
isBill,
|
|
316
|
+
confidence: Math.min(confidence, 1),
|
|
317
|
+
amount: amountInfo?.amount,
|
|
318
|
+
currency: amountInfo?.currency,
|
|
319
|
+
dueDate: dueDate || undefined,
|
|
320
|
+
billType,
|
|
321
|
+
merchant,
|
|
322
|
+
reasons,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Recognize bills from multiple emails
|
|
327
|
+
*/
|
|
328
|
+
export function recognizeBills(emails, config) {
|
|
329
|
+
return emails.map((email) => recognizeBill(email, config));
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Filter emails to only bills based on recognition
|
|
333
|
+
*/
|
|
334
|
+
export function filterToBills(emails, config) {
|
|
335
|
+
const results = emails.map((email) => ({
|
|
336
|
+
email,
|
|
337
|
+
recognition: recognizeBill(email, config),
|
|
338
|
+
}));
|
|
339
|
+
return results.filter(({ recognition }) => recognition.isBill);
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=bill-recognizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bill-recognizer.js","sourceRoot":"","sources":["../../../src/sources/gmail/bill-recognizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Parser Service - Extract structured transaction data from bill emails
|
|
3
|
+
*
|
|
4
|
+
* Parses email content to extract amounts, dates, merchants, and line items.
|
|
5
|
+
* Handles HTML tables, plain text lists, and various email formats.
|
|
6
|
+
*/
|
|
7
|
+
import type { BillRecognition } from "./bill-recognizer.js";
|
|
8
|
+
export interface ParsedTransaction {
|
|
9
|
+
transactionId: string;
|
|
10
|
+
accountId: string;
|
|
11
|
+
date: string;
|
|
12
|
+
amount: number;
|
|
13
|
+
currency: string;
|
|
14
|
+
category: string[];
|
|
15
|
+
merchantName: string;
|
|
16
|
+
paymentChannel: string;
|
|
17
|
+
pending: boolean;
|
|
18
|
+
description?: string;
|
|
19
|
+
lineItems?: LineItem[];
|
|
20
|
+
}
|
|
21
|
+
export interface LineItem {
|
|
22
|
+
description: string;
|
|
23
|
+
amount: number;
|
|
24
|
+
quantity?: number;
|
|
25
|
+
date?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse HTML table from email body
|
|
29
|
+
* Many billing emails contain transaction data in HTML tables
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseHTMLTable(html: string): LineItem[] | null;
|
|
32
|
+
/**
|
|
33
|
+
* Parse plain text list from email body
|
|
34
|
+
* Some bills use plain text with amounts on each line
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseTextList(text: string): LineItem[] | null;
|
|
37
|
+
/**
|
|
38
|
+
* Extract multiple amounts from text and return the largest
|
|
39
|
+
* Useful for finding the total amount in a bill
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractTotalAmount(text: string): number | null;
|
|
42
|
+
/**
|
|
43
|
+
* Extract currency from text
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractCurrency(text: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Parse bill email into structured transaction data
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseBillToTransaction(accountId: string, emailId: string, emailSubject: string, emailBody: string, emailFrom: string, emailDate: string, recognition: BillRecognition): ParsedTransaction;
|
|
50
|
+
/**
|
|
51
|
+
* Parse multiple bill emails into transactions
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseBillsToTransactions(accountId: string, emails: Array<{
|
|
54
|
+
id: string;
|
|
55
|
+
subject: string;
|
|
56
|
+
body: string;
|
|
57
|
+
from: string;
|
|
58
|
+
date: string;
|
|
59
|
+
}>, recognitions: BillRecognition[]): ParsedTransaction[];
|
|
60
|
+
/**
|
|
61
|
+
* Extract account number from email content
|
|
62
|
+
*/
|
|
63
|
+
export declare function extractAccountNumber(text: string): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Extract due date from email content
|
|
66
|
+
*/
|
|
67
|
+
export declare function extractDueDate(text: string): string | undefined;
|
|
68
|
+
//# sourceMappingURL=email-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-parser.d.ts","sourceRoot":"","sources":["../../../src/sources/gmail/email-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAE3D,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAA;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAgD9D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAmB7D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4B9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBpD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,GAC3B,iBAAiB,CAgDnB;AA0BD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,CAAC;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAC,EACF,YAAY,EAAE,eAAe,EAAE,GAC9B,iBAAiB,EAAE,CAYrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiBrE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoB/D"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Parser Service - Extract structured transaction data from bill emails
|
|
3
|
+
*
|
|
4
|
+
* Parses email content to extract amounts, dates, merchants, and line items.
|
|
5
|
+
* Handles HTML tables, plain text lists, and various email formats.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse HTML table from email body
|
|
9
|
+
* Many billing emails contain transaction data in HTML tables
|
|
10
|
+
*/
|
|
11
|
+
export function parseHTMLTable(html) {
|
|
12
|
+
// Look for <table> elements
|
|
13
|
+
const tableMatch = html.match(/<table[^>]*>([\s\S]*?)<\/table>/gi);
|
|
14
|
+
if (!tableMatch) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const lineItems = [];
|
|
18
|
+
for (const table of tableMatch) {
|
|
19
|
+
// Find all <tr> elements
|
|
20
|
+
const rowMatches = table.match(/<tr[^>]*>([\s\S]*?)<\/tr>/gi);
|
|
21
|
+
if (!rowMatches) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
for (const row of rowMatches) {
|
|
25
|
+
// Extract <td> or <th> content
|
|
26
|
+
const cellMatches = row.match(/<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi);
|
|
27
|
+
if (!cellMatches || cellMatches.length < 2) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// Clean cell content (remove HTML tags)
|
|
31
|
+
const cleanCells = cellMatches.map((cell) => cell.replace(/<[^>]+>/g, "").trim());
|
|
32
|
+
// Look for amount in cells (contains $ or number with decimals)
|
|
33
|
+
let amount = 0;
|
|
34
|
+
let description = "";
|
|
35
|
+
for (const cell of cleanCells) {
|
|
36
|
+
const amountMatch = cell.match(/\$?([0-9,]+\.\d{2})/);
|
|
37
|
+
if (amountMatch && !amount) {
|
|
38
|
+
amount = parseFloat(amountMatch[1].replace(/,/g, ""));
|
|
39
|
+
}
|
|
40
|
+
else if (cell && cell.length > 2 && !description) {
|
|
41
|
+
description = cell;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (amount > 0 && description) {
|
|
45
|
+
lineItems.push({ description, amount });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return lineItems.length > 0 ? lineItems : null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse plain text list from email body
|
|
53
|
+
* Some bills use plain text with amounts on each line
|
|
54
|
+
*/
|
|
55
|
+
export function parseTextList(text) {
|
|
56
|
+
const lines = text.split("\n");
|
|
57
|
+
const lineItems = [];
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
// Look for lines with amounts (format: Description $XX.XX or $XX)
|
|
60
|
+
// Allow alphanumeric descriptions (e.g., "Item 1")
|
|
61
|
+
// Cents are optional (e.g., $20 or $20.00)
|
|
62
|
+
const match = line.match(/^([A-Za-z0-9\s&]+)\s+\$?([0-9,]+(?:\.\d{2})?)/);
|
|
63
|
+
if (match) {
|
|
64
|
+
const description = match[1].trim();
|
|
65
|
+
const amount = parseFloat(match[2].replace(/,/g, ""));
|
|
66
|
+
if (amount > 0) {
|
|
67
|
+
lineItems.push({ description, amount });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return lineItems.length > 0 ? lineItems : null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Extract multiple amounts from text and return the largest
|
|
75
|
+
* Useful for finding the total amount in a bill
|
|
76
|
+
*/
|
|
77
|
+
export function extractTotalAmount(text) {
|
|
78
|
+
const amounts = [];
|
|
79
|
+
// Match various amount formats: $123.45, 123.45 USD, etc.
|
|
80
|
+
const patterns = [
|
|
81
|
+
/\$\s*([0-9,]+\.\d{2})/g, // $123.45
|
|
82
|
+
/([0-9,]+\.\d{2})\s*USD/gi, // 123.45 USD
|
|
83
|
+
/total[:\s]+\$?\s*([0-9,]+\.\d{2})/gi, // Total: $123.45
|
|
84
|
+
/amount[:\s]+\$?\s*([0-9,]+\.\d{2})/gi, // Amount: $123.45
|
|
85
|
+
/balance due[:\s]+\$?\s*([0-9,]+\.\d{2})/gi, // Balance Due: $123.45
|
|
86
|
+
];
|
|
87
|
+
for (const pattern of patterns) {
|
|
88
|
+
let match;
|
|
89
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
90
|
+
const amount = parseFloat(match[1].replace(/,/g, ""));
|
|
91
|
+
if (!isNaN(amount) && amount > 0) {
|
|
92
|
+
amounts.push(amount);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (amounts.length === 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
// Return the largest amount (likely the total)
|
|
100
|
+
return Math.max(...amounts);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Extract currency from text
|
|
104
|
+
*/
|
|
105
|
+
export function extractCurrency(text) {
|
|
106
|
+
// Check more specific currency symbols first (before generic $)
|
|
107
|
+
if (text.includes("C$") || /cad/i.test(text)) {
|
|
108
|
+
return "CAD";
|
|
109
|
+
}
|
|
110
|
+
if (text.includes("A$") || /aud/i.test(text)) {
|
|
111
|
+
return "AUD";
|
|
112
|
+
}
|
|
113
|
+
if (text.includes("€") || /eur/i.test(text)) {
|
|
114
|
+
return "EUR";
|
|
115
|
+
}
|
|
116
|
+
if (text.includes("£") || /gbp/i.test(text)) {
|
|
117
|
+
return "GBP";
|
|
118
|
+
}
|
|
119
|
+
if (text.includes("$") || /usd/i.test(text)) {
|
|
120
|
+
return "USD";
|
|
121
|
+
}
|
|
122
|
+
return "USD"; // Default
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse bill email into structured transaction data
|
|
126
|
+
*/
|
|
127
|
+
export function parseBillToTransaction(accountId, emailId, emailSubject, emailBody, emailFrom, emailDate, recognition) {
|
|
128
|
+
// Use recognition data if available
|
|
129
|
+
const amount = recognition.amount
|
|
130
|
+
? Math.round(recognition.amount * 100) // Convert to cents
|
|
131
|
+
: (() => {
|
|
132
|
+
const totalAmount = extractTotalAmount(emailSubject + " " + emailBody);
|
|
133
|
+
return totalAmount ? Math.round(totalAmount * 100) : 0;
|
|
134
|
+
})();
|
|
135
|
+
const currency = recognition.currency || extractCurrency(emailBody);
|
|
136
|
+
// Extract merchant name (prioritize recognition, then email extraction)
|
|
137
|
+
const merchantMatch = emailFrom.match(/^"?([^"<>@]+)"?\s*</);
|
|
138
|
+
const merchantName = recognition.merchant ||
|
|
139
|
+
(merchantMatch ? merchantMatch[1].trim() : emailFrom.split("@")[0]);
|
|
140
|
+
// Extract date (prefer recognition due date, fall back to email date)
|
|
141
|
+
const date = recognition.dueDate || emailDate;
|
|
142
|
+
// Determine category based on bill type
|
|
143
|
+
const category = getCategoryForBillType(recognition.billType);
|
|
144
|
+
// Parse line items if available
|
|
145
|
+
let lineItems;
|
|
146
|
+
const htmlTable = parseHTMLTable(emailBody);
|
|
147
|
+
if (htmlTable) {
|
|
148
|
+
lineItems = htmlTable;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const textList = parseTextList(emailBody);
|
|
152
|
+
if (textList) {
|
|
153
|
+
lineItems = textList;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
transactionId: `${accountId}_email_${emailId}`,
|
|
158
|
+
accountId,
|
|
159
|
+
date,
|
|
160
|
+
amount,
|
|
161
|
+
currency,
|
|
162
|
+
category,
|
|
163
|
+
merchantName,
|
|
164
|
+
paymentChannel: "email",
|
|
165
|
+
pending: false,
|
|
166
|
+
description: emailSubject,
|
|
167
|
+
lineItems,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Map bill type to transaction category
|
|
172
|
+
*/
|
|
173
|
+
function getCategoryForBillType(billType) {
|
|
174
|
+
if (!billType) {
|
|
175
|
+
return ["bills"];
|
|
176
|
+
}
|
|
177
|
+
const categoryMap = {
|
|
178
|
+
"Credit Card": ["credit-card", "bills"],
|
|
179
|
+
Utility: ["utilities", "bills"],
|
|
180
|
+
Internet: ["internet", "utilities"],
|
|
181
|
+
Phone: ["phone", "utilities"],
|
|
182
|
+
Insurance: ["insurance", "bills"],
|
|
183
|
+
Subscription: ["subscriptions", "entertainment"],
|
|
184
|
+
Housing: ["housing", "rent"],
|
|
185
|
+
Loan: ["loan", "debt"],
|
|
186
|
+
Invoice: ["bills"],
|
|
187
|
+
Receipt: ["purchases"],
|
|
188
|
+
};
|
|
189
|
+
return categoryMap[billType] || ["bills"];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Parse multiple bill emails into transactions
|
|
193
|
+
*/
|
|
194
|
+
export function parseBillsToTransactions(accountId, emails, recognitions) {
|
|
195
|
+
return emails.map((email, index) => parseBillToTransaction(accountId, email.id, email.subject, email.body, email.from, email.date, recognitions[index]));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Extract account number from email content
|
|
199
|
+
*/
|
|
200
|
+
export function extractAccountNumber(text) {
|
|
201
|
+
// Common patterns for account numbers
|
|
202
|
+
const patterns = [
|
|
203
|
+
/account\s*(?:number|#|no\.?)[:\s]+([A-Z0-9-]{4,})/i,
|
|
204
|
+
/ending\s+in\s+(\d{4})/i,
|
|
205
|
+
/\*{3,}\s*(\d{4})/i, // *** 1234 or ***1234
|
|
206
|
+
/card\s*(?:number|#|no\.?)[:\s]+([A-Z0-9-]{4,})/i,
|
|
207
|
+
];
|
|
208
|
+
for (const pattern of patterns) {
|
|
209
|
+
const match = pattern.exec(text);
|
|
210
|
+
if (match) {
|
|
211
|
+
return match[1];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Extract due date from email content
|
|
218
|
+
*/
|
|
219
|
+
export function extractDueDate(text) {
|
|
220
|
+
// Pattern: "Due: MM/DD/YYYY" or "Due Date: January 15, 2024"
|
|
221
|
+
const patterns = [
|
|
222
|
+
/(?:due|payment)\s*(?:date|by)?[:\s]+([A-Za-z]+)\s+(\d{1,2}),?\s*(\d{4})?/i,
|
|
223
|
+
/(?:due|payment)\s*(?:date|by)?[:\s]+(\d{1,2})\/(\d{1,2})\/(\d{4})/i,
|
|
224
|
+
/(?:due|payment)\s*(?:date|by)?[:\s]+(\d{4})-(\d{1,2})-(\d{1,2})/i,
|
|
225
|
+
];
|
|
226
|
+
for (const pattern of patterns) {
|
|
227
|
+
const match = pattern.exec(text);
|
|
228
|
+
if (match) {
|
|
229
|
+
// Reconstruct the date string
|
|
230
|
+
if (match[0].includes("/")) {
|
|
231
|
+
return match[0]; // Already in MM/DD/YYYY format
|
|
232
|
+
}
|
|
233
|
+
return match.slice(1).join(" "); // "January 15 2024" format
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=email-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-parser.js","sourceRoot":"","sources":["../../../src/sources/gmail/email-parser.ts"],"names":[],"mappings":"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"}
|