Created
November 24, 2025 21:57
-
-
Save 1337hero/da66d2f849ad10d04b8a03e6a2754abc to your computer and use it in GitHub Desktop.
agentic-invoice-bot.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // agentic-invoice-bot.js – Opus 4.5 Generated MVP | |
| // Setup: npm init -y; npm i nodemailer imap-simple pdfkit axios dotenv | |
| // Env: Add .env with GMAIL_USER, GMAIL_PASS, PRESTLO_API_KEY | |
| // Run: node agentic-invoice-bot.js (polls every 5 min) | |
| require('dotenv').config(); | |
| const nodemailer = require('nodemailer'); | |
| const ImapSimple = require('imap-simple'); | |
| const PDFDocument = require('pdfkit'); | |
| const fs = require('fs'); | |
| const axios = require('axios'); | |
| class InvoiceAgent { | |
| constructor() { | |
| this.transporter = nodemailer.createTransport({ | |
| service: 'gmail', | |
| auth: { user: process.env.GMAIL_USER, pass: process.env.GMAIL_PASS } | |
| }); | |
| this.apiKey = process.env.PRESTLO_API_KEY; | |
| } | |
| // Agent Step 1: Poll Inbox for Triggers (Agentic: Autonomous scan) | |
| async scanForRequests() { | |
| const config = { | |
| imap: { user: process.env.GMAIL_USER, password: process.env.GMAIL_PASS, host: 'imap.gmail.com', port: 993, tls: true }, | |
| onMail: () => {}, onError: console.error | |
| }; | |
| return new Promise((resolve, reject) => { | |
| ImapSimple.open(config).then(connection => { | |
| connection.search(['UNSEEN', ['SUBJECT', 'invoice request']], (err, results) => { | |
| if (err) reject(err); | |
| resolve(results.map(email => this.parseRequest(email))); // Parse each | |
| connection.end(); | |
| }); | |
| }); | |
| }); | |
| } | |
| // Agent Step 2: Parse & Reason (Opus 4.5 Strength: Deep Extraction) | |
| parseRequest(email) { | |
| const body = email.text || ''; // Fallback to HTML if needed | |
| const keywords = ['invoice', 'bill', 'amount:', 'client:']; // Expandable | |
| if (!keywords.some(k => body.toLowerCase().includes(k))) return null; | |
| // Simple regex extract (scalable to Opus 4.5 vision if attachments) | |
| const amountMatch = body.match(/amount[:\s]*\$?(\d+(?:\.\d{2})?)/i); | |
| const clientMatch = body.match(/client[:\s]*([A-Za-z\s]+)/i); | |
| const descMatch = body.match(/description[:\s]*(.+)/i); | |
| return { | |
| client: clientMatch?.[1]?.trim() || 'Unknown Client', | |
| amount: parseFloat(amountMatch?.[1]) || 0, | |
| desc: descMatch?.[1]?.trim() || 'Standard Services' | |
| }; | |
| } | |
| // Agent Step 3: Generate & Call API (Agentic: Multi-Tool Chain) | |
| async generateInvoice(data) { | |
| try { | |
| // Mock Prestlo API Call (replace with real /api/invoice) | |
| const response = await axios.post('https://prestlo.com/api/invoice', data, { | |
| headers: { Authorization: `Bearer ${this.apiKey}` } | |
| }); | |
| const invoiceId = response.data.id; | |
| await this.createPDF(invoiceId, data); // Side-effect: PDF gen | |
| return invoiceId; | |
| } catch (error) { | |
| console.error('API Fail:', error); // Log for audit | |
| throw error; | |
| } | |
| } | |
| // Helper: PDF Creation (Opus 4.5 "Computer Use" – File I/O) | |
| async createPDF(id, data) { | |
| return new Promise((resolve) => { | |
| const doc = new PDFDocument(); | |
| const path = `./invoice-${id}.pdf`; | |
| doc.pipe(fs.createWriteStream(path)); | |
| doc.fontSize(25).text(`Invoice for ${data.client}`); | |
| doc.text(`Amount: $${data.amount}`); | |
| doc.text(`Description: ${data.desc}`); | |
| doc.end(); | |
| doc.on('end', () => resolve(path)); | |
| }); | |
| } | |
| // Agent Step 4: Send & Close Loop (Autonomous Dispatch) | |
| async sendInvoice(invoiceId, pdfPath, originalEmail) { | |
| const mailOptions = { | |
| from: process.env.GMAIL_USER, | |
| to: originalEmail.from[0].address, | |
| subject: `Invoice ${invoiceId} - Prestlo Generated`, | |
| text: `Attached: Your invoice for ${data.amount}. Questions? Reply.`, | |
| attachments: [{ filename: 'invoice.pdf', path: pdfPath }] | |
| }; | |
| await this.transporter.sendMail(mailOptions); | |
| console.log(`Sent: ${invoiceId}`); // Audit log | |
| } | |
| // Main Agent Loop (Run on cron or webhook for prod) | |
| async run() { | |
| try { | |
| const requests = await this.scanForRequests(); | |
| for (const req of requests.filter(Boolean)) { | |
| const id = await this.generateInvoice(req); | |
| const pdf = await this.createPDF(id, req); | |
| await this.sendInvoice(id, pdf, req.originalEmail); // Assume email obj passed | |
| } | |
| } catch (error) { | |
| console.error('Agent Error:', error); // Retry logic next | |
| } | |
| } | |
| } | |
| // Bootstrap (Finn's "Just Do It") | |
| const agent = new InvoiceAgent(); | |
| setInterval(() => agent.run(), 5 * 60 * 1000); // Poll every 5 min | |
| agent.run(); // Initial run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment