OHO-16(Perfect Regex)
This commit is contained in:
parent
fd80a9be70
commit
030c8bd99d
2 changed files with 97 additions and 47 deletions
|
|
@ -227,7 +227,7 @@ app.post('/api/transactions/voice', async (req, res) => {
|
||||||
.trim();
|
.trim();
|
||||||
const description = cleaned || 'misc';
|
const description = cleaned || 'misc';
|
||||||
// Step 3: Predict category using your Flask API
|
// Step 3: Predict category using your Flask API
|
||||||
const predictRes = await axios.post('http://10.231.41.181:5000/api/predict', {
|
const predictRes = await axios.post('http://192.168.1.101:5000/api/predict', {
|
||||||
description,
|
description,
|
||||||
});
|
});
|
||||||
const category = predictRes.data?.category || 'Other';
|
const category = predictRes.data?.category || 'Other';
|
||||||
|
|
@ -256,51 +256,86 @@ const bankRules = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// ---------- PARSER ----------
|
// ---------- PARSER ----------
|
||||||
function parseBankMessage(snippet) {
|
function parseBankMessage(email) {
|
||||||
const result = {
|
const result = {
|
||||||
amount: null,
|
amount: null,
|
||||||
vendor: null,
|
vendor: null,
|
||||||
|
rawVendor: null,
|
||||||
type: null,
|
type: null,
|
||||||
date: null,
|
date: null,
|
||||||
referenceNumber: null,
|
referenceNumber: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Amount
|
// ---------- Amount ----------
|
||||||
const amtMatch = snippet.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
|
const amtMatch = email.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
|
||||||
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
|
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
|
||||||
|
|
||||||
// Debit / Credit
|
// ---------- Debit / Credit ----------
|
||||||
if (/debited/i.test(snippet)) result.type = "debit";
|
if (/debited/i.test(email)) result.type = "debit";
|
||||||
else if (/credited/i.test(snippet)) result.type = "credit";
|
else if (/credited/i.test(email)) result.type = "credit";
|
||||||
|
|
||||||
// Date
|
// ---------- Date ----------
|
||||||
const dateMatch = snippet.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
|
const dateMatch = email.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
|
||||||
if (dateMatch) result.date = dateMatch[1];
|
if (dateMatch) result.date = dateMatch[1];
|
||||||
|
|
||||||
// Reference Number (flexible regex)
|
// ---------- Reference Number ----------
|
||||||
const refMatch = snippet.match(/(?:reference number is|UPI reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
|
const refMatch = email.match(/(?:reference number is|UPI transaction reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
|
||||||
if (refMatch) result.referenceNumber = refMatch[1].trim();
|
if (refMatch) result.referenceNumber = refMatch[1].trim();
|
||||||
|
|
||||||
// Vendor extraction - remove emails, pick last uppercase phrase
|
// ---------- Vendor Extraction ----------
|
||||||
let vendorCandidate = snippet.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim();
|
let vendorCandidate = null;
|
||||||
|
|
||||||
const vendorWords = vendorCandidate.match(/(?:\b[A-Z]{2,}\b(?:\s)?)+/g);
|
// 1. Prefer "to VPA ..." pattern, stop at 'on|your|Ref|Txn' etc.
|
||||||
if (vendorWords && vendorWords.length > 0) {
|
const toMatch = email.match(/to\s+(?:VPA\s+)?(?:[^\s@]+\@[^\s]+\s+)?([A-Za-z0-9\s&.-]+)/i);
|
||||||
result.vendor = vendorWords[vendorWords.length - 1].trim();
|
if (toMatch) {
|
||||||
} else {
|
vendorCandidate = toMatch[1]
|
||||||
// Fallback: extract after "to " or "at "
|
.split(/ on | your | ref | txn | transaction | number /i)[0] // cut off junk
|
||||||
const toMatch = snippet.match(/to\s+([A-Za-z0-9\s&.-]+)/i);
|
.trim();
|
||||||
if (toMatch) {
|
}
|
||||||
let v = toMatch[1].trim();
|
|
||||||
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim();
|
// 2. Fallback: fully uppercase phrases
|
||||||
v = v.replace(/\s{2,}/g, ' ');
|
if (!vendorCandidate) {
|
||||||
result.vendor = v;
|
const upperMatch = email.match(/(?:\b[A-Z][A-Z0-9&.-]+\b(?:\s)?)+/g);
|
||||||
|
if (upperMatch) {
|
||||||
|
vendorCandidate = upperMatch[upperMatch.length - 1].trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign rawVendor
|
||||||
|
if (vendorCandidate) {
|
||||||
|
result.rawVendor = vendorCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Cleaning Pipeline ----------
|
||||||
|
if (result.rawVendor) {
|
||||||
|
let v = result.rawVendor;
|
||||||
|
|
||||||
|
// Remove noise keywords
|
||||||
|
v = v.replace(/\b(?:UPI|VPA|REF|Txn|Order|Payment|NEFT|IMPS|Transaction|Number|Your)\b/gi, "");
|
||||||
|
|
||||||
|
// Remove emails
|
||||||
|
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, "");
|
||||||
|
|
||||||
|
// Remove dates & numbers that sneak in
|
||||||
|
v = v.replace(/\d{2}[-/]\d{2}[-/]\d{2,4}/g, "");
|
||||||
|
v = v.replace(/\b\d{6,}\b/g, ""); // strip long numbers like reference ids
|
||||||
|
|
||||||
|
// Replace underscores/dashes with space
|
||||||
|
v = v.replace(/[_\-]+/g, " ");
|
||||||
|
|
||||||
|
// Collapse multiple spaces
|
||||||
|
v = v.replace(/\s{2,}/g, " ").trim();
|
||||||
|
|
||||||
|
// Normalize casing
|
||||||
|
v = v.toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase());
|
||||||
|
|
||||||
|
result.vendor = v;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------- FETCH EMAILS ----------
|
// ---------- FETCH EMAILS ----------
|
||||||
async function fetchBankEmails(accessToken, bankEmails) {
|
async function fetchBankEmails(accessToken, bankEmails) {
|
||||||
const oauth2Client = new google.auth.OAuth2();
|
const oauth2Client = new google.auth.OAuth2();
|
||||||
|
|
|
||||||
|
|
@ -12,54 +12,68 @@ function parseBankMessage(email) {
|
||||||
referenceNumber: null,
|
referenceNumber: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Amount
|
// ---------- Amount ----------
|
||||||
const amtMatch = email.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
|
const amtMatch = email.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
|
||||||
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
|
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
|
||||||
|
|
||||||
// Debit / Credit
|
// ---------- Debit / Credit ----------
|
||||||
if (/debited/i.test(email)) result.type = "debit";
|
if (/debited/i.test(email)) result.type = "debit";
|
||||||
else if (/credited/i.test(email)) result.type = "credit";
|
else if (/credited/i.test(email)) result.type = "credit";
|
||||||
|
|
||||||
// Date
|
// ---------- Date ----------
|
||||||
const dateMatch = email.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
|
const dateMatch = email.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
|
||||||
if (dateMatch) result.date = dateMatch[1];
|
if (dateMatch) result.date = dateMatch[1];
|
||||||
|
|
||||||
// Reference Number (flexible regex)
|
// ---------- Reference Number ----------
|
||||||
const refMatch = email.match(/(?:reference number is|UPI reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
|
const refMatch = email.match(/(?:reference number is|UPI transaction reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
|
||||||
if (refMatch) result.referenceNumber = refMatch[1].trim();
|
if (refMatch) result.referenceNumber = refMatch[1].trim();
|
||||||
|
|
||||||
// Vendor extraction - remove emails, pick last uppercase phrase
|
// ---------- Vendor Extraction ----------
|
||||||
let vendorCandidate = email.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim();
|
let vendorCandidate = null;
|
||||||
|
|
||||||
const vendorWords = vendorCandidate.match(/(?:\b[A-Z]{2,}\b(?:\s)?)+/g);
|
// 1. Prefer "to VPA ..." pattern, stop at 'on|your|Ref|Txn' etc.
|
||||||
if (vendorWords && vendorWords.length > 0) {
|
const toMatch = email.match(/to\s+(?:VPA\s+)?(?:[^\s@]+\@[^\s]+\s+)?([A-Za-z0-9\s&.-]+)/i);
|
||||||
result.rawVendor = vendorWords[vendorWords.length - 1].trim(); // store raw
|
if (toMatch) {
|
||||||
} else {
|
vendorCandidate = toMatch[1]
|
||||||
// Improved vendor fallback
|
.split(/ on | your | ref | txn | transaction | number /i)[0] // cut off junk
|
||||||
const toMatch = email.match(/to\s+(?:VPA\s+)?(?:[^\s@]+\@[^\s]+\s+)?([A-Za-z0-9\s&.-]+)/i);
|
.trim();
|
||||||
if (toMatch) {
|
}
|
||||||
let v = toMatch[1].trim();
|
|
||||||
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim(); // remove emails
|
// 2. Fallback: fully uppercase phrases
|
||||||
v = v.replace(/\s{2,}/g, ' ');
|
if (!vendorCandidate) {
|
||||||
result.rawVendor = v;
|
const upperMatch = email.match(/(?:\b[A-Z][A-Z0-9&.-]+\b(?:\s)?)+/g);
|
||||||
|
if (upperMatch) {
|
||||||
|
vendorCandidate = upperMatch[upperMatch.length - 1].trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign rawVendor
|
||||||
|
if (vendorCandidate) {
|
||||||
|
result.rawVendor = vendorCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- Cleaning Pipeline ----------
|
// ---------- Cleaning Pipeline ----------
|
||||||
if (result.rawVendor) {
|
if (result.rawVendor) {
|
||||||
let v = result.rawVendor;
|
let v = result.rawVendor;
|
||||||
|
|
||||||
// Remove noise keywords
|
// Remove noise keywords
|
||||||
v = v.replace(/\b(?:UPI|VPA|REF|Txn|Order|Payment|NEFT|IMPS)\b/gi, '');
|
v = v.replace(/\b(?:UPI|VPA|REF|Txn|Order|Payment|NEFT|IMPS|Transaction|Number|Your)\b/gi, "");
|
||||||
|
|
||||||
|
// Remove emails
|
||||||
|
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, "");
|
||||||
|
|
||||||
|
// Remove dates & numbers that sneak in
|
||||||
|
v = v.replace(/\d{2}[-/]\d{2}[-/]\d{2,4}/g, "");
|
||||||
|
v = v.replace(/\b\d{6,}\b/g, ""); // strip long numbers like reference ids
|
||||||
|
|
||||||
// Replace underscores/dashes with space
|
// Replace underscores/dashes with space
|
||||||
v = v.replace(/[_\-]+/g, ' ');
|
v = v.replace(/[_\-]+/g, " ");
|
||||||
|
|
||||||
// Collapse multiple spaces
|
// Collapse multiple spaces
|
||||||
v = v.replace(/\s{2,}/g, ' ').trim();
|
v = v.replace(/\s{2,}/g, " ").trim();
|
||||||
|
|
||||||
// Normalize casing (title case)
|
// Normalize casing
|
||||||
v = v.toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
|
v = v.toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase());
|
||||||
|
|
||||||
result.vendor = v;
|
result.vendor = v;
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +81,7 @@ function parseBankMessage(email) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
console.log("=== Bank Email Regex Test ===");
|
console.log("=== Bank Email Regex Test ===");
|
||||||
testEmails.forEach((email, idx) => {
|
testEmails.forEach((email, idx) => {
|
||||||
console.log(`\n-- Email ${idx + 1} --`);
|
console.log(`\n-- Email ${idx + 1} --`);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue