const {text, json, createError} = require('micro') const {get, post, withNamespace} = require('microrouter') const FB = require('fb') FB.setAccessToken(process.env.FB_ACCESS_TOKEN) const fbParser = require('claudia-bot-builder/lib/facebook/parse') const fbValidate = require('claudia-bot-builder/lib/facebook/validate-integrity') const {run} = require('./intents') /** * In-Memory cache for FB graph results */ const recipients = [] /** * GET webhook * * Gets executed when you add the Webhook URL to the Facebook and it tries * to validate it. */ const verifyToken = req => { if (req.query['hub.verify_token'] === process.env.FB_VERIFY_TOKEN) { return req.query['hub.challenge'] } console.info( `Your Facebook Access Token (that Facebook will echo back to you as part of callback URL verification): ${ process.env.FB_VERIFY_TOKEN }` ) return 'INVALID TOKEN' } /** * Validate and convert request to a common message format */ const fbMessage = async req => { req.env = {facebookAppSecret: process.env.FB_APP_SECRET} req.rawBody = await text(req) const data = await json(req) const [[message]] = data.entry.map(entry => entry.messaging.map(message => fbParser(message)) ) const isValid = fbValidate(req) && message.type === 'facebook' return isValid ? message : false } /** * POST webhook * * Gets executed on any message to the Bot. */ const respond = async (req, res) => { const message = await fbMessage(req) if (message) { const recipient = recipients[message.sender] || (await FB.api(message.sender)) // Cache recipients recipients[message.sender] = recipient try { await run(message, recipient) } catch (err) { console.error(err) res.end(err.message) } res.end() } else { throw createError(400, 'Invalid message signature.') } } const fbApi = withNamespace('/api/v1/facebook') module.exports = fbApi(get('/', verifyToken), post('/', respond))