Skip to content

Instantly share code, notes, and snippets.

@1337hero
Created November 24, 2025 21:57
Show Gist options
  • Select an option

  • Save 1337hero/da66d2f849ad10d04b8a03e6a2754abc to your computer and use it in GitHub Desktop.

Select an option

Save 1337hero/da66d2f849ad10d04b8a03e6a2754abc to your computer and use it in GitHub Desktop.
agentic-invoice-bot.js
// 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