| | /** | | | * Production Facebook Messenger Webhook Server | | | * Implements Meta Graph API Verification, Messaging Event Receiver, | | | * and 2-second Quick Reply Dispatch for E-commerce Pages. | | | * | | | * 📖 Complete Tutorial & Setup Guide: | | | * https://www.sajedar.com/tutorials/facebook-messenger-chatbot-ecommerce | | | * | | | * 🤖 Full AI Sales Agent Capabilities in Nepal: | | | * https://www.sajedar.com/facebook-messenger-ai-chatbot-nepal | | | * | | | * 🛠️ Messenger JSON Payload Builder Tool: | | | * https://www.sajedar.com/tools/messenger-json-builder | | | */ | | | |
| | const express = require('express'); |
| | const crypto = require('crypto'); |
| | const app = express(); |
| | |
| | app.use(express.json({ |
| | verify: (req, res, buf) => { |
| | req.rawBody = buf; |
| | } | | | })); | | | | | | const PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN; | | | const VERIFY_TOKEN = process.env.VERIFY_TOKEN; | | | const APP_SECRET = process.env.APP_SECRET; | | | | | | // 1. Meta Webhook Verification Endpoint (GET) |
| | app.get('/webhook', (req, res) => { |
| | const mode = req.query['hub.mode']; |
| | const token = req.query['hub.verify_token']; |
| | const challenge = req.query['hub.challenge']; |
| | | | | if (mode === 'subscribe' && token === VERIFY_TOKEN) { | | | console.log('✅ Webhook verified by Meta Graph API'); |
| | res.status(200).send(challenge); |
| | } else { |
| | res.sendStatus(403); |
| | } | | | }); | | | | | | // 2. Message Event Receiver (POST) |
| | app.post('/webhook', (req, res) => { |
| | const body = req.body; |
| | |
| | if (body.object === 'page') { |
| | body.entry.forEach(entry => { |
| | const webhookEvent = entry.messaging?.[0]; |
| | if (!webhookEvent) return; |
| | | | | const senderPsid = webhookEvent.sender.id; | | | | | | if (webhookEvent.message) { | | | handleIncomingMessage(senderPsid, webhookEvent.message); |
| | } else if (webhookEvent.postback) { |
| | handlePostback(senderPsid, webhookEvent.postback); |
| | } | | | }); | | | | | | // Acknowledge receipt within 20 seconds to prevent Meta retries |
| | res.status(200).send('EVENT_RECEIVED'); |
| | } else { |
| | res.sendStatus(404); |
| | } | | | }); | | | | | | // Send Message API helper | | | async function callSendAPI(senderPsid, responsePayload) { | | | const url = 'https://graph.facebook.com/v19.0/me/messages?access_token=' + PAGE_ACCESS_TOKEN; | | | | | | try { | | | const res = await fetch(url, { | | | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ |
| | recipient: { id: senderPsid }, |
| | messaging_type: 'RESPONSE', | | | message: responsePayload |
| | }) |
| | }); |
| | const data = await res.json(); |
| | return data; |
| | } catch (err) { |
| | console.error('Error calling Send API:', err); |
| | } | | | } | | | | | | async function handleIncomingMessage(senderPsid, receivedMessage) { | | | const text = receivedMessage.text?.toLowerCase() || ''; | | | | | | // Handle Quick Inquiries |
| | if (text.includes('price') \|\| text.includes('kati ho') \|\| text.includes('cost')) { |
| | await callSendAPI(senderPsid, { |
| | text: 'Namaste! 🙏 Our bestsellers start at Rs. 1,499 with free delivery inside Kathmandu Valley. Would you like to see available colors or sizes?', |
| | quick_replies: [ |
| | { content_type: 'text', title: '👗 View Catalog', payload: 'VIEW_CATALOG' }, |
| | { content_type: 'text', title: '📍 Delivery Info', payload: 'DELIVERY_INFO' }, |
| | { content_type: 'text', title: '💬 Human Agent', payload: 'TALK_HUMAN' } |
| | ] |
| | }); |
| | } else { |
| | await callSendAPI(senderPsid, { |
| | text: 'Thank you for reaching out! How can we help your shopping today?' | | | }); | | | } | | | } | | | | | | const PORT = process.env.PORT || 3000; | | | app.listen(PORT, () => console.log('Messenger Webhook running on port ' + PORT)); |