diff --git a/index.js b/index.js index 19d020f11f..2db32461a9 100644 --- a/index.js +++ b/index.js @@ -1,36 +1,241 @@ -const express = require('express') -const path = require('path') - -const port = process.env.PORT || 5006 - -const app = express() - -app.use(express.static(path.join(__dirname, 'public'))) -app.set('views', path.join(__dirname, 'views')) -app.set('view engine', 'ejs') - -app.get('/', (req, res) => { - console.log(`Rendering 'pages/index' for route '/'`) - res.render('pages/index') -}) - -const server = app.listen(port, () => { - console.log(`Listening on ${port}`) -}) - -// The number of seconds an idle Keep-Alive connection is kept open. This should be greater than the Heroku Router's -// Keep-Alive idle timeout of 90 seconds: -// - to ensure that the closing of idle connections is always initiated by the router and not the Node.js server -// - to prevent a race condition if the router sends a request to the app just as Node.js is closing the connection -// https://devcenter.heroku.com/articles/http-routing#keepalives -// https://nodejs.org/api/http.html#serverkeepalivetimeout -server.keepAliveTimeout = 95 * 1000 - -process.on('SIGTERM', async () => { - console.log('SIGTERM signal received: gracefully shutting down') - if (server) { - server.close(() => { - console.log('HTTP server closed') - }) +const axios = require("axios"); + +const SCRIPT_URL = "PASTE_YOUR_NEW_GOOGLE_SCRIPT_URL_HERE"; +const UPI_ID = "8121893882-2@ybl"; + +let sessions = {}; + +module.exports = async (req, res) => { + const msg = req.body.data?.body?.trim(); + const from = req.body.data?.from; + const name = req.body.data?.notifyName || ""; + + if (!sessions[from]) { + sessions[from] = { step: "MENU", phone: from, name }; + } + + const s = sessions[from]; + + // BACK OPTION + if (msg === "0") { + s.step = "MENU"; + } + + let reply = ""; + + switch (s.step) { + + // ---------------- MENU ---------------- + case "MENU": + reply = +`🥛 *Welcome to Bala Milk Dairy* + +Please choose an option: +1️⃣ Buffalo Milk – ₹100/L +2️⃣ Cow Milk – ₹120/L +3️⃣ Paneer – ₹600/Kg +4️⃣ Ghee – ₹1000/Kg +5️⃣ Daily Milk Subscription +6️⃣ Enquiry Only + +Reply with option number.`; + s.step = "PRODUCT"; + break; + + // ---------------- PRODUCT ---------------- + case "PRODUCT": + if (msg === "6") { + s.type = "Enquiry"; + s.step = "ENQUIRY"; + reply = "✍️ Please type your enquiry.\n\n0️⃣ Back"; + break; + } + + const products = { + "1": { name: "Buffalo Milk", price: 100 }, + "2": { name: "Cow Milk", price: 120 }, + "3": { name: "Paneer", price: 600 }, + "4": { name: "Ghee", price: 1000 } + }; + + if (!products[msg]) { + reply = "❌ Invalid option.\n0️⃣ Back"; + break; + } + + s.product = products[msg]; + reply = +`🧾 *${s.product.name}* + +Choose quantity: +1️⃣ 500ml – ₹${s.product.price / 2} +2️⃣ 1 L – ₹${s.product.price} +3️⃣ 2 L – ₹${s.product.price * 2} + +0️⃣ Back`; + s.step = "QUANTITY"; + break; + + // ---------------- QUANTITY ---------------- + case "QUANTITY": + const qtyMap = { + "1": { q: "500ml", m: 0.5 }, + "2": { q: "1L", m: 1 }, + "3": { q: "2L", m: 2 } + }; + + if (!qtyMap[msg]) { + reply = "❌ Choose valid quantity.\n0️⃣ Back"; + break; + } + + s.quantity = qtyMap[msg].q; + s.price = s.product.price * qtyMap[msg].m; + + reply = +`📍 Delivery Address: +1️⃣ Send live location +2️⃣ Type address manually + +0️⃣ Back`; + s.step = "ADDRESS"; + break; + + // ---------------- ADDRESS ---------------- + case "ADDRESS": + if (msg === "1") { + reply = "📌 Please share live location now."; + s.step = "LOCATION"; + } else if (msg === "2") { + reply = "✍️ Please type your full address.\n\n0️⃣ Back"; + s.step = "ADDRESS_TEXT"; + } else { + reply = "❌ Invalid option.\n0️⃣ Back"; + } + break; + + case "ADDRESS_TEXT": + s.address = msg; + s.step = "DELIVERY"; + reply = +`⏰ Delivery Slot: +1️⃣ Morning +2️⃣ Evening + +0️⃣ Back`; + break; + + case "LOCATION": + s.address = "Live Location Shared"; + s.step = "DELIVERY"; + reply = +`⏰ Delivery Slot: +1️⃣ Morning +2️⃣ Evening + +0️⃣ Back`; + break; + + // ---------------- DELIVERY ---------------- + case "DELIVERY": + if (msg === "1") s.delivery = "Morning"; + else if (msg === "2") s.delivery = "Evening"; + else { + reply = "❌ Invalid option.\n0️⃣ Back"; + break; + } + + reply = +`🕒 Enter delivery time (example: 6:30 AM)\n\n0️⃣ Back`; + s.step = "TIME"; + break; + + case "TIME": + s.deliveryTime = `${s.delivery} ${msg}`; + reply = +`💰 Payment Method: +1️⃣ UPI +2️⃣ Cash on Delivery + +0️⃣ Back`; + s.step = "PAYMENT"; + break; + + // ---------------- PAYMENT ---------------- + case "PAYMENT": + if (msg === "1") { + s.payment = "UPI"; + reply = +`💳 Pay using UPI: +👉 ${UPI_ID} + +📸 After payment, send screenshot. + +0️⃣ Back`; + s.step = "SCREENSHOT"; + } else if (msg === "2") { + s.payment = "Cash on Delivery"; + await saveToSheet(s, "COD"); + reply = +`✅ Order Confirmed! + +🙏 Thank you for ordering from *Bala Milk Dairy* 🥛`; + delete sessions[from]; + } else { + reply = "❌ Invalid option.\n0️⃣ Back"; + } + break; + + // ---------------- SCREENSHOT ---------------- + case "SCREENSHOT": + await saveToSheet(s, "UPI Screenshot"); + reply = +`✅ Payment received! + +🙏 Thank you for ordering from *Bala Milk Dairy* 🥛`; + delete sessions[from]; + break; + + // ---------------- ENQUIRY ---------------- + case "ENQUIRY": + await saveToSheet({ + phone: s.phone, + name: s.name, + type: "Enquiry", + product: msg + }, "Enquiry"); + + reply = +`🙏 Thank you for contacting *Bala Milk Dairy*. +We will get back to you soon.`; + delete sessions[from]; + break; } -}) + + await sendMessage(from, reply); + res.sendStatus(200); +}; + +// -------- SEND MESSAGE ---------- +async function sendMessage(to, body) { + await axios.post(process.env.WHATSAPP_API_URL, { + to, + body + }); +} + +// -------- SAVE TO GOOGLE SHEET ---------- +async function saveToSheet(s, method) { + await axios.post(SCRIPT_URL, { + OrderId: "ORD-" + Date.now(), + Phone: s.phone, + ContactName: s.name, + Type: s.type || "Payment", + Product: s.product?.name || "", + Quantity: s.quantity || "", + Price: s.price || "", + Address: s.address || "", + Delivery: s.deliveryTime || "", + Payment: method + }); +} diff --git a/package.json b/package.json index 4893c4ab02..5daa26c6e5 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,16 @@ { - "name": "nodejs-getting-started", - "private": true, - "description": "A sample Node.js app using Express", - "engines": { - "node": "20.x || 22.x || 24.x" - }, + "name": "bala-whatsapp-bot", + "version": "1.0.0", + "description": "WhatsApp Cloud API bot for Bala Milk Store", "main": "index.js", "scripts": { - "start": "node index.js", - "test": "jest" - }, - "dependencies": { - "ejs": "^3.1.10", - "express": "^5.2.1" + "start": "node index.js" }, - "devDependencies": { - "jest": "^30.2.0" - }, - "repository": { - "type": "git", - "url": "https://github.com/heroku/nodejs-getting-started" + "engines": { + "node": ">=18" }, - "keywords": [ - "node", - "heroku", - "express" - ], - "license": "MIT" + "dependencies": { + "axios": "^1.6.2", + "express": "^4.18.2" + } } diff --git a/views/pages/index.ejs b/views/pages/index.ejs index ea7498435d..95a9bfc746 100644 --- a/views/pages/index.ejs +++ b/views/pages/index.ejs @@ -13,7 +13,7 @@ -

Getting Started on Heroku with Node.js

+

Hello from Balaraju 🚀

This is a sample Node application deployed to Heroku. It's a reasonably simple app - but a good foundation for understanding how to get the most out of the Heroku platform.

Getting Started on Heroku with Node.js Getting Started on Heroku Fir with Node.js