2017-03-16 102 views
0

我跟着FB的tutorial製作了一個FB messenger bot。 webhook已成功完成,現在我想繼續捕獲Facebook用戶的輸入(發送到我的頁面的消息),並將其用作我的CasperJS腳本的輸入。最後,我想使用CasperJS腳本的輸出並回復Facebook用戶。在Facebook的信使博特(使用FB與NodeJS的Messenger Messenger Bot webhook + CasperJS

app.js基本上它是由實施例中給出的相同,只是改變了sendTextMessage(recipientId, messageText)功能:

/* 
* Copyright 2016-present, Facebook, Inc. 
* All rights reserved. 
* 
* This source code is licensed under the license found in the 
* LICENSE file in the root directory of this source tree. 
* 
*/ 

/* jshint node: true, devel: true */ 
'use strict'; 

const 
    bodyParser = require('body-parser'), 
    config = require('config'), 
    crypto = require('crypto'), 
    express = require('express'), 
    https = require('https'), 
    request = require('request'); 

var app = express(); 
app.set('port', process.env.PORT || 5000); 
app.set('view engine', 'ejs'); 
app.use(bodyParser.json({ verify: verifyRequestSignature })); 
app.use(express.static('public')); 

/* 
* Be sure to setup your config values before running this code. You can 
* set them using environment variables or modifying the config file in /config. 
* 
*/ 

// App Secret can be retrieved from the App Dashboard 
const APP_SECRET = (process.env.MESSENGER_APP_SECRET) ? 
    process.env.MESSENGER_APP_SECRET : 
    config.get('appSecret'); 

// Arbitrary value used to validate a webhook 
const VALIDATION_TOKEN = (process.env.MESSENGER_VALIDATION_TOKEN) ? 
    (process.env.MESSENGER_VALIDATION_TOKEN) : 
    config.get('validationToken'); 

// Generate a page access token for your page from the App Dashboard 
const PAGE_ACCESS_TOKEN = (process.env.MESSENGER_PAGE_ACCESS_TOKEN) ? 
    (process.env.MESSENGER_PAGE_ACCESS_TOKEN) : 
    config.get('pageAccessToken'); 

// URL where the app is running (include protocol). Used to point to scripts and 
// assets located at this address. 
const SERVER_URL = (process.env.SERVER_URL) ? 
    (process.env.SERVER_URL) : 
    config.get('serverURL'); 

if (!(APP_SECRET && VALIDATION_TOKEN && PAGE_ACCESS_TOKEN && SERVER_URL)) { 
    console.error("Missing config values"); 
    process.exit(1); 
} 

/* 
* Use your own validation token. Check that the token used in the Webhook 
* setup is the same token used here. 
* 
*/ 
app.get('/webhook', function(req, res) { 
    if (req.query['hub.mode'] === 'subscribe' && 
     req.query['hub.verify_token'] === VALIDATION_TOKEN) { 
    console.log("Validating webhook"); 
    res.status(200).send(req.query['hub.challenge']); 
    } else { 
    console.error("Failed validation. Make sure the validation tokens match." + VALIDATION_TOKEN + ", " + req.query['hub.verify_token']); 
    res.sendStatus(403);   
    } 
}); 


/* 
* All callbacks for Messenger are POST-ed. They will be sent to the same 
* webhook. Be sure to subscribe your app to your page to receive callbacks 
* for your page. 
* https://developers.facebook.com/docs/messenger-platform/product-overview/setup#subscribe_app 
* 
*/ 
app.post('/webhook', function (req, res) { 
    var data = req.body; 

    // Make sure this is a page subscription 
    if (data.object == 'page') { 
    // Iterate over each entry 
    // There may be multiple if batched 
    data.entry.forEach(function(pageEntry) { 
     var pageID = pageEntry.id; 
     var timeOfEvent = pageEntry.time; 

     // Iterate over each messaging event 
     pageEntry.messaging.forEach(function(messagingEvent) { 
     if (messagingEvent.optin) { 
      receivedAuthentication(messagingEvent); 
     } else if (messagingEvent.message) { 
      receivedMessage(messagingEvent); 
     } else if (messagingEvent.delivery) { 
      receivedDeliveryConfirmation(messagingEvent); 
     } else if (messagingEvent.postback) { 
      receivedPostback(messagingEvent); 
     } else if (messagingEvent.read) { 
      receivedMessageRead(messagingEvent); 
     } else if (messagingEvent.account_linking) { 
      receivedAccountLink(messagingEvent); 
     } else { 
      console.log("Webhook received unknown messagingEvent: ", messagingEvent); 
     } 
     }); 
    }); 

    // Assume all went well. 
    // 
    // You must send back a 200, within 20 seconds, to let us know you've 
    // successfully received the callback. Otherwise, the request will time out. 
    res.sendStatus(200); 
    } 
}); 

/* 
* This path is used for account linking. The account linking call-to-action 
* (sendAccountLinking) is pointed to this URL. 
* 
*/ 
app.get('/authorize', function(req, res) { 
    var accountLinkingToken = req.query.account_linking_token; 
    var redirectURI = req.query.redirect_uri; 

    // Authorization Code should be generated per user by the developer. This will 
    // be passed to the Account Linking callback. 
    var authCode = "1234567890"; 

    // Redirect users to this URI on successful login 
    var redirectURISuccess = redirectURI + "&authorization_code=" + authCode; 

    res.render('authorize', { 
    accountLinkingToken: accountLinkingToken, 
    redirectURI: redirectURI, 
    redirectURISuccess: redirectURISuccess 
    }); 
}); 

/* 
* Verify that the callback came from Facebook. Using the App Secret from 
* the App Dashboard, we can verify the signature that is sent with each 
* callback in the x-hub-signature field, located in the header. 
* 
* https://developers.facebook.com/docs/graph-api/webhooks#setup 
* 
*/ 
function verifyRequestSignature(req, res, buf) { 
    var signature = req.headers["x-hub-signature"]; 

    if (!signature) { 
    // For testing, let's log an error. In production, you should throw an 
    // error. 
    console.error("Couldn't validate the signature."); 
    } else { 
    var elements = signature.split('='); 
    var method = elements[0]; 
    var signatureHash = elements[1]; 

    var expectedHash = crypto.createHmac('sha1', APP_SECRET) 
         .update(buf) 
         .digest('hex'); 

    if (signatureHash != expectedHash) { 
     throw new Error("Couldn't validate the request signature."); 
    } 
    } 
} 

/* 
* Authorization Event 
* 
* The value for 'optin.ref' is defined in the entry point. For the "Send to 
* Messenger" plugin, it is the 'data-ref' field. Read more at 
* https://developers.facebook.com/docs/messenger-platform/webhook-reference/authentication 
* 
*/ 
function receivedAuthentication(event) { 
    var senderID = event.sender.id; 
    var recipientID = event.recipient.id; 
    var timeOfAuth = event.timestamp; 

    // The 'ref' field is set in the 'Send to Messenger' plugin, in the 'data-ref' 
    // The developer can set this to an arbitrary value to associate the 
    // authentication callback with the 'Send to Messenger' click event. This is 
    // a way to do account linking when the user clicks the 'Send to Messenger' 
    // plugin. 
    var passThroughParam = event.optin.ref; 

    console.log("Received authentication for user %d and page %d with pass " + 
    "through param '%s' at %d", senderID, recipientID, passThroughParam, 
    timeOfAuth); 

    // When an authentication is received, we'll send a message back to the sender 
    // to let them know it was successful. 
    sendTextMessage(senderID, "Authentication successful"); 
} 

/* 
* Message Event 
* 
* This event is called when a message is sent to your page. The 'message' 
* object format can vary depending on the kind of message that was received. 
* Read more at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-received 
* 
* For this example, we're going to echo any text that we get. If we get some 
* special keywords ('button', 'generic', 'receipt'), then we'll send back 
* examples of those bubbles to illustrate the special message bubbles we've 
* created. If we receive a message with an attachment (image, video, audio), 
* then we'll simply confirm that we've received the attachment. 
* 
*/ 
function receivedMessage(event) { 
    var senderID = event.sender.id; 
    var recipientID = event.recipient.id; 
    var timeOfMessage = event.timestamp; 
    var message = event.message; 

    console.log("Received message for user %d and page %d at %d with message:", 
    senderID, recipientID, timeOfMessage); 
    console.log(JSON.stringify(message)); 

    var isEcho = message.is_echo; 
    var messageId = message.mid; 
    var appId = message.app_id; 
    var metadata = message.metadata; 

    // You may get a text or attachment but not both 
    var messageText = message.text; 
    var messageAttachments = message.attachments; 
    var quickReply = message.quick_reply; 

    if (isEcho) { 
    // Just logging message echoes to console 
    console.log("Received echo for message %s and app %d with metadata %s", 
     messageId, appId, metadata); 
    return; 
    } else if (quickReply) { 
    var quickReplyPayload = quickReply.payload; 
    console.log("Quick reply for message %s with payload %s", 
     messageId, quickReplyPayload); 

    sendTextMessage(senderID, "Quick reply tapped"); 
    return; 
    } 

    if (messageText) { 

    // If we receive a text message, check to see if it matches any special 
    // keywords and send back the corresponding example. Otherwise, just echo 
    // the text we received. 
    switch (messageText) { 
     case 'image': 
     sendImageMessage(senderID); 
     break; 

     case 'gif': 
     sendGifMessage(senderID); 
     break; 

     case 'audio': 
     sendAudioMessage(senderID); 
     break; 

     case 'video': 
     sendVideoMessage(senderID); 
     break; 

     case 'file': 
     sendFileMessage(senderID); 
     break; 

     case 'button': 
     sendButtonMessage(senderID); 
     break; 

     case 'generic': 
     sendGenericMessage(senderID); 
     break; 

     case 'receipt': 
     sendReceiptMessage(senderID); 
     break; 

     case 'quick reply': 
     sendQuickReply(senderID); 
     break;   

     case 'read receipt': 
     sendReadReceipt(senderID); 
     break;   

     case 'typing on': 
     sendTypingOn(senderID); 
     break;   

     case 'typing off': 
     sendTypingOff(senderID); 
     break;   

     case 'account linking': 
     sendAccountLinking(senderID); 
     break; 

     default: 
     sendTextMessage(senderID, messageText); 
    } 
    } else if (messageAttachments) { 
    sendTextMessage(senderID, "Message with attachment received"); 
    } 
} 


/* 
* Delivery Confirmation Event 
* 
* This event is sent to confirm the delivery of a message. Read more about 
* these fields at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-delivered 
* 
*/ 
function receivedDeliveryConfirmation(event) { 
    var senderID = event.sender.id; 
    var recipientID = event.recipient.id; 
    var delivery = event.delivery; 
    var messageIDs = delivery.mids; 
    var watermark = delivery.watermark; 
    var sequenceNumber = delivery.seq; 

    if (messageIDs) { 
    messageIDs.forEach(function(messageID) { 
     console.log("Received delivery confirmation for message ID: %s", 
     messageID); 
    }); 
    } 

    console.log("All message before %d were delivered.", watermark); 
} 


/* 
* Postback Event 
* 
* This event is called when a postback is tapped on a Structured Message. 
* https://developers.facebook.com/docs/messenger-platform/webhook-reference/postback-received 
* 
*/ 
function receivedPostback(event) { 
    var senderID = event.sender.id; 
    var recipientID = event.recipient.id; 
    var timeOfPostback = event.timestamp; 

    // The 'payload' param is a developer-defined field which is set in a postback 
    // button for Structured Messages. 
    var payload = event.postback.payload; 

    console.log("Received postback for user %d and page %d with payload '%s' " + 
    "at %d", senderID, recipientID, payload, timeOfPostback); 

    // When a postback is called, we'll send a message back to the sender to 
    // let them know it was successful 
    sendTextMessage(senderID, "Postback called"); 
} 

/* 
* Message Read Event 
* 
* This event is called when a previously-sent message has been read. 
* https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-read 
* 
*/ 
function receivedMessageRead(event) { 
    var senderID = event.sender.id; 
    var recipientID = event.recipient.id; 

    // All messages before watermark (a timestamp) or sequence have been seen. 
    var watermark = event.read.watermark; 
    var sequenceNumber = event.read.seq; 

    console.log("Received message read event for watermark %d and sequence " + 
    "number %d", watermark, sequenceNumber); 
} 

/* 
* Account Link Event 
* 
* This event is called when the Link Account or UnLink Account action has been 
* tapped. 
* https://developers.facebook.com/docs/messenger-platform/webhook-reference/account-linking 
* 
*/ 
function receivedAccountLink(event) { 
    var senderID = event.sender.id; 
    var recipientID = event.recipient.id; 

    var status = event.account_linking.status; 
    var authCode = event.account_linking.authorization_code; 

    console.log("Received account link event with for user %d with status %s " + 
    "and auth code %s ", senderID, status, authCode); 
} 

/* 
* Send an image using the Send API. 
* 
*/ 
function sendImageMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "image", 
     payload: { 
      url: SERVER_URL + "/assets/rift.png" 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a Gif using the Send API. 
* 
*/ 
function sendGifMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "image", 
     payload: { 
      url: SERVER_URL + "/assets/instagram_logo.gif" 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send audio using the Send API. 
* 
*/ 
function sendAudioMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "audio", 
     payload: { 
      url: SERVER_URL + "/assets/sample.mp3" 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a video using the Send API. 
* 
*/ 
function sendVideoMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "video", 
     payload: { 
      url: SERVER_URL + "/assets/allofus480.mov" 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a file using the Send API. 
* 
*/ 
function sendFileMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "file", 
     payload: { 
      url: SERVER_URL + "/assets/test.txt" 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a text message using the Send API. 
* 
*/ 
function sendTextMessage(recipientId, messageText) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     text: "{The data fed from CasperJS}", 
     metadata: "DEVELOPER_DEFINED_METADATA" 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a button message using the Send API. 
* 
*/ 
function sendButtonMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "template", 
     payload: { 
      template_type: "button", 
      text: "This is test text", 
      buttons:[{ 
      type: "web_url", 
      url: "https://www.oculus.com/en-us/rift/", 
      title: "Open Web URL" 
      }, { 
      type: "postback", 
      title: "Trigger Postback", 
      payload: "DEVELOPER_DEFINED_PAYLOAD" 
      }, { 
      type: "phone_number", 
      title: "Call Phone Number", 
      payload: "+16505551234" 
      }] 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a Structured Message (Generic Message type) using the Send API. 
* 
*/ 
function sendGenericMessage(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "template", 
     payload: { 
      template_type: "generic", 
      elements: [{ 
      title: "rift", 
      subtitle: "Next-generation virtual reality", 
      item_url: "https://www.oculus.com/en-us/rift/",    
      image_url: SERVER_URL + "/assets/rift.png", 
      buttons: [{ 
       type: "web_url", 
       url: "https://www.oculus.com/en-us/rift/", 
       title: "Open Web URL" 
      }, { 
       type: "postback", 
       title: "Call Postback", 
       payload: "Payload for first bubble", 
      }], 
      }, { 
      title: "touch", 
      subtitle: "Your Hands, Now in VR", 
      item_url: "https://www.oculus.com/en-us/touch/",    
      image_url: SERVER_URL + "/assets/touch.png", 
      buttons: [{ 
       type: "web_url", 
       url: "https://www.oculus.com/en-us/touch/", 
       title: "Open Web URL" 
      }, { 
       type: "postback", 
       title: "Call Postback", 
       payload: "Payload for second bubble", 
      }] 
      }] 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a receipt message using the Send API. 
* 
*/ 
function sendReceiptMessage(recipientId) { 
    // Generate a random receipt ID as the API requires a unique ID 
    var receiptId = "order" + Math.floor(Math.random()*1000); 

    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message:{ 
     attachment: { 
     type: "template", 
     payload: { 
      template_type: "receipt", 
      recipient_name: "Peter Chang", 
      order_number: receiptId, 
      currency: "USD", 
      payment_method: "Visa 1234",   
      timestamp: "1428444852", 
      elements: [{ 
      title: "Oculus Rift", 
      subtitle: "Includes: headset, sensor, remote", 
      quantity: 1, 
      price: 599.00, 
      currency: "USD", 
      image_url: SERVER_URL + "/assets/riftsq.png" 
      }, { 
      title: "Samsung Gear VR", 
      subtitle: "Frost White", 
      quantity: 1, 
      price: 99.99, 
      currency: "USD", 
      image_url: SERVER_URL + "/assets/gearvrsq.png" 
      }], 
      address: { 
      street_1: "1 Hacker Way", 
      street_2: "", 
      city: "Menlo Park", 
      postal_code: "94025", 
      state: "CA", 
      country: "US" 
      }, 
      summary: { 
      subtotal: 698.99, 
      shipping_cost: 20.00, 
      total_tax: 57.67, 
      total_cost: 626.66 
      }, 
      adjustments: [{ 
      name: "New Customer Discount", 
      amount: -50 
      }, { 
      name: "$100 Off Coupon", 
      amount: -100 
      }] 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a message with Quick Reply buttons. 
* 
*/ 
function sendQuickReply(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     text: "What's your favorite movie genre?", 
     quick_replies: [ 
     { 
      "content_type":"text", 
      "title":"Action", 
      "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_ACTION" 
     }, 
     { 
      "content_type":"text", 
      "title":"Comedy", 
      "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_COMEDY" 
     }, 
     { 
      "content_type":"text", 
      "title":"Drama", 
      "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_DRAMA" 
     } 
     ] 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a read receipt to indicate the message has been read 
* 
*/ 
function sendReadReceipt(recipientId) { 
    console.log("Sending a read receipt to mark message as seen"); 

    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    sender_action: "mark_seen" 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Turn typing indicator on 
* 
*/ 
function sendTypingOn(recipientId) { 
    console.log("Turning typing indicator on"); 

    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    sender_action: "typing_on" 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Turn typing indicator off 
* 
*/ 
function sendTypingOff(recipientId) { 
    console.log("Turning typing indicator off"); 

    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    sender_action: "typing_off" 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Send a message with the account linking call-to-action 
* 
*/ 
function sendAccountLinking(recipientId) { 
    var messageData = { 
    recipient: { 
     id: recipientId 
    }, 
    message: { 
     attachment: { 
     type: "template", 
     payload: { 
      template_type: "button", 
      text: "Welcome. Link your account.", 
      buttons:[{ 
      type: "account_link", 
      url: SERVER_URL + "/authorize" 
      }] 
     } 
     } 
    } 
    }; 

    callSendAPI(messageData); 
} 

/* 
* Call the Send API. The message data goes in the body. If successful, we'll 
* get the message id in a response 
* 
*/ 
function callSendAPI(messageData) { 
    request({ 
    uri: 'https://graph.facebook.com/v2.6/me/messages', 
    qs: { access_token: PAGE_ACCESS_TOKEN }, 
    method: 'POST', 
    json: messageData 

    }, function (error, response, body) { 
    if (!error && response.statusCode == 200) { 
     var recipientId = body.recipient_id; 
     var messageId = body.message_id; 

     if (messageId) { 
     console.log("Successfully sent message with id %s to recipient %s", 
      messageId, recipientId); 
     } else { 
     console.log("Successfully called Send API for recipient %s", 
     recipientId); 
     } 
    } else { 
     console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error); 
    } 
    }); 
} 

// Start server 
// Webhooks must be available via SSL with a certificate signed by a valid 
// certificate authority. 
app.listen(app.get('port'), function() { 
    console.log('Node app is running on port', app.get('port')); 
}); 

module.exports = app; 

接受參數和過程的CasperJS腳本(echo.js)它:

var casper = require("casper").create(); 
if(casper.cli.has(0)) { 
    casper.echo("Received " + casper.cli.get(0)); 
} 
casper.exit(); 

我現在用下面的命令來執行它:

casperjs echo.js input 

將回復:

接收到的輸入

我想這個 「接收的輸入」 的事說回到Facebook的用戶。我怎樣才能做到這一點?我怎樣才能連接這些組件?

謝謝。

+0

如何發送消息在文檔中有明確的描述 - 那麼,您在這裏的具體問題是......? – CBroe

+0

你好@CBroe,我的問題是,我怎樣才能連接CasperJS與NodeJS的webhook? CasperJS和NodeJS可以一起工作嗎?該場景將是:通過webhook>發送請求到CasperJS> CasperJS從其他站點中刪除數據>向webhook發送響應>通過webhook向FB用戶發送響應 – Raptor

+0

我發現了一個名爲[SpookyJS]的東西, (https://github.com/SpookyJS/SpookyJS),似乎相關,但不確定。我現在正在檢查它。 – Raptor

回答

1

您可以在某種異步任務(like a promise)或in a background job中運行您的casper工作。

您可以將發件人的ID和文本或您正在處理的任何內容(無法從您的源代碼中指出)發送到後臺作業或任務,並使用處理過的刮取數據在作業中與您的Messenger客戶端進行響應。

在用已處理的數據進行響應以獲得更好的用戶體驗之前,說出「嗨用戶,我正在抓取一些東西,它只是一秒鐘」。
A typing_on message在這裏也可能不錯,但YMMV。

+0

感謝您的建議。 YMMV =你的Milage May Vary,對嗎? – Raptor

+0

正確。不客氣@Raptor – mraaroncruz