@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,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event emitter for BillClaw webhook events
|
|
3
|
+
*
|
|
4
|
+
* This module provides a centralized way to emit and forward webhook events
|
|
5
|
+
* to configured external webhook endpoints.
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic: Uses Logger interface instead of framework-specific APIs.
|
|
8
|
+
*/
|
|
9
|
+
import * as crypto from "node:crypto";
|
|
10
|
+
/**
|
|
11
|
+
* Emit a BillClaw event
|
|
12
|
+
*
|
|
13
|
+
* This function creates a standardized event and forwards it to all
|
|
14
|
+
* configured external webhooks that are subscribed to this event type.
|
|
15
|
+
*
|
|
16
|
+
* @param logger - Logger instance for logging
|
|
17
|
+
* @param webhooks - Array of webhook configurations
|
|
18
|
+
* @param eventType - Type of event to emit
|
|
19
|
+
* @param data - Event data
|
|
20
|
+
*/
|
|
21
|
+
export async function emitEvent(logger, webhooks, eventType, data) {
|
|
22
|
+
const enabledWebhooks = webhooks.filter((w) => w.enabled && w.url);
|
|
23
|
+
if (enabledWebhooks.length === 0) {
|
|
24
|
+
logger.debug?.(`No webhooks configured, skipping event: ${eventType}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Create standardized event
|
|
28
|
+
const event = {
|
|
29
|
+
id: generateEventId(),
|
|
30
|
+
event: eventType,
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
version: "1.0",
|
|
33
|
+
data,
|
|
34
|
+
};
|
|
35
|
+
logger.info?.(`Emitting event: ${eventType} (${event.id})`);
|
|
36
|
+
// Forward to configured webhooks (fire-and-forget)
|
|
37
|
+
const promises = enabledWebhooks.map(async (webhook) => {
|
|
38
|
+
// Check if this webhook is subscribed to this event
|
|
39
|
+
if (webhook.events &&
|
|
40
|
+
webhook.events.length > 0 &&
|
|
41
|
+
!webhook.events.includes(eventType)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Skip webhooks without URL (shouldn't happen due to filter, but TypeScript needs guard)
|
|
45
|
+
if (!webhook.url) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Send webhook with retry logic (don't await)
|
|
49
|
+
sendWebhook(logger, webhook, event).catch((error) => {
|
|
50
|
+
logger.debug?.(`Webhook emission failed:`, error);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
// Fire-and-forget: don't await webhook delivery
|
|
54
|
+
Promise.all(promises).catch(() => {
|
|
55
|
+
// All promises handled individually
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Emit transaction.new event
|
|
60
|
+
*/
|
|
61
|
+
export async function emitTransactionNew(logger, webhooks, transaction) {
|
|
62
|
+
await emitEvent(logger, webhooks, "transaction.new", transaction);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Emit transaction.updated event
|
|
66
|
+
*/
|
|
67
|
+
export async function emitTransactionUpdated(logger, webhooks, transaction) {
|
|
68
|
+
await emitEvent(logger, webhooks, "transaction.updated", transaction);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Emit transaction.deleted event
|
|
72
|
+
*/
|
|
73
|
+
export async function emitTransactionDeleted(logger, webhooks, transactionId, accountId) {
|
|
74
|
+
await emitEvent(logger, webhooks, "transaction.deleted", {
|
|
75
|
+
transactionId,
|
|
76
|
+
accountId,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Emit sync.started event
|
|
81
|
+
*/
|
|
82
|
+
export async function emitSyncStarted(logger, webhooks, accountId, syncId) {
|
|
83
|
+
await emitEvent(logger, webhooks, "sync.started", {
|
|
84
|
+
accountId,
|
|
85
|
+
syncId,
|
|
86
|
+
status: "started",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Emit sync.completed event
|
|
91
|
+
*/
|
|
92
|
+
export async function emitSyncCompleted(logger, webhooks, syncData) {
|
|
93
|
+
await emitEvent(logger, webhooks, "sync.completed", syncData);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Emit sync.failed event
|
|
97
|
+
*/
|
|
98
|
+
export async function emitSyncFailed(logger, webhooks, accountId, syncId, error) {
|
|
99
|
+
await emitEvent(logger, webhooks, "sync.failed", {
|
|
100
|
+
accountId,
|
|
101
|
+
syncId,
|
|
102
|
+
status: "failed",
|
|
103
|
+
error,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Emit account.connected event
|
|
108
|
+
*/
|
|
109
|
+
export async function emitAccountConnected(logger, webhooks, accountId, accountType) {
|
|
110
|
+
await emitEvent(logger, webhooks, "account.connected", {
|
|
111
|
+
accountId,
|
|
112
|
+
accountType,
|
|
113
|
+
status: "connected",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Emit account.disconnected event
|
|
118
|
+
*/
|
|
119
|
+
export async function emitAccountDisconnected(logger, webhooks, accountId, accountType) {
|
|
120
|
+
await emitEvent(logger, webhooks, "account.disconnected", {
|
|
121
|
+
accountId,
|
|
122
|
+
accountType,
|
|
123
|
+
status: "disconnected",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Emit account.error event
|
|
128
|
+
*/
|
|
129
|
+
export async function emitAccountError(logger, webhooks, accountId, accountType, error) {
|
|
130
|
+
await emitEvent(logger, webhooks, "account.error", {
|
|
131
|
+
accountId,
|
|
132
|
+
accountType,
|
|
133
|
+
status: "error",
|
|
134
|
+
error,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Emit webhook.test event
|
|
139
|
+
*/
|
|
140
|
+
export async function emitWebhookTest(logger, webhooks, message = "Test webhook from BillClaw") {
|
|
141
|
+
await emitEvent(logger, webhooks, "webhook.test", {
|
|
142
|
+
message,
|
|
143
|
+
triggeredBy: "user",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Send webhook to external endpoint with signature and retry logic
|
|
148
|
+
*
|
|
149
|
+
* @param logger - Logger instance
|
|
150
|
+
* @param webhook - Webhook configuration with url, secret, retryPolicy
|
|
151
|
+
* @param event - Event to send
|
|
152
|
+
*/
|
|
153
|
+
async function sendWebhook(logger, webhook, event) {
|
|
154
|
+
const maxRetries = webhook.retryPolicy?.maxRetries || 3;
|
|
155
|
+
const initialDelay = webhook.retryPolicy?.initialDelay || 1000;
|
|
156
|
+
const maxDelay = webhook.retryPolicy?.maxDelay || 30000;
|
|
157
|
+
// Create payload
|
|
158
|
+
const payload = { ...event };
|
|
159
|
+
// Sign payload if secret is configured
|
|
160
|
+
if (webhook.secret) {
|
|
161
|
+
payload.signature = generateSignature(event, webhook.secret);
|
|
162
|
+
}
|
|
163
|
+
let attempt = 0;
|
|
164
|
+
let delay = initialDelay;
|
|
165
|
+
while (attempt < maxRetries) {
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetch(webhook.url, {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": "application/json",
|
|
171
|
+
"User-Agent": "BillClaw/1.0",
|
|
172
|
+
"X-Billclaw-Event-Id": event.id,
|
|
173
|
+
"X-Billclaw-Event-Type": event.event,
|
|
174
|
+
"X-Billclaw-Timestamp": event.timestamp,
|
|
175
|
+
...(payload.signature && {
|
|
176
|
+
"X-Billclaw-Signature": payload.signature,
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
body: JSON.stringify(payload),
|
|
180
|
+
});
|
|
181
|
+
if (response.ok) {
|
|
182
|
+
logger.info?.(`Webhook sent successfully to ${webhook.url}`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Don't retry client errors (4xx)
|
|
186
|
+
if (response.status >= 400 && response.status < 500) {
|
|
187
|
+
logger.warn?.(`Webhook rejected by ${webhook.url}: ${response.status}`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`Webhook failed: ${response.status}`);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
attempt++;
|
|
194
|
+
if (attempt >= maxRetries) {
|
|
195
|
+
logger.error?.(`Webhook failed after ${maxRetries} retries to ${webhook.url}:`, error);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Exponential backoff with jitter (30%)
|
|
199
|
+
const jitter = Math.random() * 0.3 * delay;
|
|
200
|
+
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
201
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Generate HMAC-SHA256 signature for webhook payload
|
|
207
|
+
*
|
|
208
|
+
* @param event - Event to sign
|
|
209
|
+
* @param secret - Secret key for HMAC
|
|
210
|
+
* @returns Signature in format "sha256=<hex>"
|
|
211
|
+
*/
|
|
212
|
+
export function generateSignature(event, secret) {
|
|
213
|
+
// Create payload to sign (all fields except signature)
|
|
214
|
+
const payload = {
|
|
215
|
+
id: event.id,
|
|
216
|
+
event: event.event,
|
|
217
|
+
timestamp: event.timestamp,
|
|
218
|
+
version: event.version,
|
|
219
|
+
data: event.data,
|
|
220
|
+
};
|
|
221
|
+
const payloadString = JSON.stringify(payload);
|
|
222
|
+
// Compute HMAC-SHA256
|
|
223
|
+
const hmac = crypto.createHmac("sha256", secret);
|
|
224
|
+
hmac.update(payloadString);
|
|
225
|
+
return `sha256=${hmac.digest("hex")}`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Verify webhook signature
|
|
229
|
+
*
|
|
230
|
+
* Returns true if the signature matches the computed signature.
|
|
231
|
+
* Uses timing-safe comparison to prevent timing attacks.
|
|
232
|
+
*
|
|
233
|
+
* @param payload - JSON string payload
|
|
234
|
+
* @param signature - Signature to verify (format: "sha256=<hex>")
|
|
235
|
+
* @param secret - Secret key for HMAC
|
|
236
|
+
* @returns True if signature is valid
|
|
237
|
+
*/
|
|
238
|
+
export function verifySignature(payload, signature, secret) {
|
|
239
|
+
try {
|
|
240
|
+
const expectedSignature = crypto
|
|
241
|
+
.createHmac("sha256", secret)
|
|
242
|
+
.update(payload)
|
|
243
|
+
.digest("hex");
|
|
244
|
+
const providedSignature = signature.replace("sha256=", "");
|
|
245
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
246
|
+
return crypto.timingSafeEqual(Buffer.from(expectedSignature), Buffer.from(providedSignature));
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Generate unique event ID
|
|
254
|
+
*
|
|
255
|
+
* Format: evt_<timestamp>_<random>
|
|
256
|
+
* Example: evt_lz1h2x3a4b5c6d_abc123def4567
|
|
257
|
+
*/
|
|
258
|
+
function generateEventId() {
|
|
259
|
+
const timestamp = Date.now().toString(36);
|
|
260
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
261
|
+
return `evt_${timestamp}_${random}`;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Type guard to check if an event is a transaction event
|
|
265
|
+
*/
|
|
266
|
+
export function isTransactionEvent(event) {
|
|
267
|
+
return (event.event === "transaction.new" ||
|
|
268
|
+
event.event === "transaction.updated" ||
|
|
269
|
+
event.event === "transaction.deleted");
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Type guard to check if an event is a sync event
|
|
273
|
+
*/
|
|
274
|
+
export function isSyncEvent(event) {
|
|
275
|
+
return (event.event === "sync.started" ||
|
|
276
|
+
event.event === "sync.completed" ||
|
|
277
|
+
event.event === "sync.failed");
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Type guard to check if an event is an account event
|
|
281
|
+
*/
|
|
282
|
+
export function isAccountEvent(event) {
|
|
283
|
+
return (event.event === "account.connected" ||
|
|
284
|
+
event.event === "account.disconnected" ||
|
|
285
|
+
event.event === "account.error");
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=event-emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-emitter.js","sourceRoot":"","sources":["../../src/services/event-emitter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,oBAAoB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
export interface EmailContent {
|
|
8
|
+
id: string;
|
|
9
|
+
subject: string;
|
|
10
|
+
from: string;
|
|
11
|
+
to?: string;
|
|
12
|
+
date: string;
|
|
13
|
+
body: string;
|
|
14
|
+
attachments?: Array<{
|
|
15
|
+
filename: string;
|
|
16
|
+
contentType: string;
|
|
17
|
+
data?: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export interface GmailConfig {
|
|
21
|
+
clientId?: string;
|
|
22
|
+
clientSecret?: string;
|
|
23
|
+
historyId?: string;
|
|
24
|
+
pubsubTopic?: string;
|
|
25
|
+
senderWhitelist: string[];
|
|
26
|
+
keywords: string[];
|
|
27
|
+
confidenceThreshold: number;
|
|
28
|
+
requireAmount: boolean;
|
|
29
|
+
requireDate: boolean;
|
|
30
|
+
billTypePatterns?: Record<string, string[]>;
|
|
31
|
+
}
|
|
32
|
+
export interface BillRecognition {
|
|
33
|
+
isBill: boolean;
|
|
34
|
+
confidence: number;
|
|
35
|
+
amount?: number;
|
|
36
|
+
currency?: string;
|
|
37
|
+
dueDate?: string;
|
|
38
|
+
billType?: string;
|
|
39
|
+
merchant?: string;
|
|
40
|
+
accountNumber?: string;
|
|
41
|
+
reasons: string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Default bill detection keywords
|
|
45
|
+
* Includes both phrases and individual words for better matching
|
|
46
|
+
*/
|
|
47
|
+
export declare const DEFAULT_KEYWORDS: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Known billing domains (extracted from common service providers)
|
|
50
|
+
*/
|
|
51
|
+
export declare const KNOWN_BILLING_DOMAINS: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Extract domain from email address
|
|
54
|
+
*/
|
|
55
|
+
export declare function extractDomain(email: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Recognize if an email is a bill and extract relevant information
|
|
58
|
+
*/
|
|
59
|
+
export declare function recognizeBill(email: EmailContent, config: GmailConfig): BillRecognition;
|
|
60
|
+
/**
|
|
61
|
+
* Recognize bills from multiple emails
|
|
62
|
+
*/
|
|
63
|
+
export declare function recognizeBills(emails: EmailContent[], config: GmailConfig): BillRecognition[];
|
|
64
|
+
/**
|
|
65
|
+
* Filter emails to only bills based on recognition
|
|
66
|
+
*/
|
|
67
|
+
export declare function filterToBills(emails: EmailContent[], config: GmailConfig): Array<{
|
|
68
|
+
email: EmailContent;
|
|
69
|
+
recognition: BillRecognition;
|
|
70
|
+
}>;
|
|
71
|
+
//# sourceMappingURL=bill-recognizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bill-recognizer.d.ts","sourceRoot":"","sources":["../../../src/sources/gmail/bill-recognizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAC,CAAA;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,UAgC5B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,UAmBjC,CAAA;AAyCD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAkKD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,WAAW,GAClB,eAAe,CAkGjB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,WAAW,GAClB,eAAe,EAAE,CAEnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,WAAW,GAClB,KAAK,CAAC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,eAAe,CAAA;CAAE,CAAC,CAO9D"}
|