Setting Node.js & Express

Using HTML instead of Jade

EJS를 사용하며 app.js 에 다음과 같이 설정을 합니다.

// view engine setup
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.set('views', path.join(__dirname, 'views'));

packages.js 의 dependencies에는 ejs를 추가 시킵니다.

{
    "scripts": {
        "start": "NODE_PATH=./ && supervisor --watch . ./bin/www"
      },
    "dependencies": {
        ...
        "ejs": "*"
      }
}

router에서는 res.render를 통해서 views/index.html 에 있는 html rendering할 수 있습니다.

router.get('/', function (req, res, next) {
    res.render('index', {title: 'Facebook Messenger Bot Tutorial'});
});

npm start를 실행하면, packages.js의 scripts에 있는 start가 실행이 됩니다.

SSL & Nginx

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Facebook Messenger API

Configure Webhooks

FB Messenger Console

현재 Facebook Messenger는 마케팅, 프로모션으로 사용을하면 안됩니다.
따라서 FB에서는 이를 방지하기 위해서 IOS App처럼 Approved를 받아야 합니다.

Faceook Developer에서 Website App을 만든후 콘솔로 들어옵니다.
Messenger -> Setup Webhooks 를 눌러서 설정을 해줍니다.

이때 Callback URL은 HTTPS만 사용가능합니다.

https://domain.co.kr/webhook/ 같은 주소를 사용하고
Token은 “anderson_jo_validation_token” 처럼 맘대로 넣으면 됩니다.
Nginx 설정은 다음과 같이 했습니다.

upstream fb-msg{
    server localhost:3000;
}

server{
    listen 443;
    server_name domain.co.kr;
    charset utf-8;
    client_max_body_size 25M;

    ssl on;
    ssl_certificate     /etc/nginx/ssl/ssl.pem;
    ssl_certificate_key /etc/nginx/ssl/star_ssl.key;

    location / {
        proxy_redirect off;
        proxy_set_header   X-Real-IP            $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Host                   $http_host;
        proxy_set_header   X-NginX-Proxy    true;
        proxy_set_header   Connection "";
        proxy_http_version 1.1;
        proxy_pass         http://fb-msg;
    }
}

서버단에서는 다음과 같이 합니다.

router.get('/webhook/', function(req, res){
  if (req.query['hub.verify_token'] == config.FB_VALICATION_TOKEN){
    return res.send(req.query['hub.challenge']);
  };
  return res.send('Error, wrong validation token');
});

정확하게 webhook을 걸고, Verification까지 완성되면 다음과 같이 됩니다.

Subscribe the app to the page

다음으로 페이스북 페이지를 만들어줍니다. (네.. 못생긴거 압니다.. 죄송합니다.)
FB Create Page

FB 콘솔안의 Token Generation에서 만들어준 Page를 설정해줍니다.

curl을 통해서 해당 Page를 App이 subscribe하도록 설정해줍니다.

curl -ik -X POST "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=<token>"

Receive Messages

router.post('/webhook/', function (req, res) {
  messaging_events = req.body.entry[0].messaging;
  for (i = 0; i < messaging_events.length; i++) {
    event = req.body.entry[0].messaging[i];
    sender = event.sender.id;
    if (event.message && event.message.text) {
      text = event.message.text;
      // Handle a text message from this sender
    }
  }
  res.sendStatus(200);
});

페이스북 페이지에 들어가서 메세지를 열고 메세지를 보냅니다.

req.body는 다음과 같이 생겼습니다.

{ object: 'page',
  entry: 
    [ { id: 1696327967283521,
        time: 1461911522431,
        messaging: [ 
          { sender: { id: 1118282011555900 },
            recipient: { id: 1696327967283521 },
            timestamp: 1461911522401,
            message:  { mid: 'mid.1461911522394:5daed2bac5cfd3cf54',
                        seq: 16,
                        text: '하이! 앤더슨 조!' } }] }] }

Send Messages

function sendTextMessage(sender, text) {

  var messageData = {
    text:text
  }

  request({
    url: 'https://graph.facebook.com/v2.6/me/messages',
    qs: {access_token:token},
    method: 'POST',
    json: {
      recipient: {id:sender},
      message: messageData,
    }
  }, function(error, response, body) {
    if (error) {
      console.log('Error sending message: ', error);
    } else if (response.body.error) {
      console.log('Error: ', response.body.error);
    }
  });
}

function sendGenericMessage(sender) {
  messageData = {
    "attachment": {
      "type": "template",
      "payload": {
        "template_type": "generic",
        "elements": [{
          "title": "First card",
          "subtitle": "Element #1 of an hscroll",
          "image_url": "http://messengerdemo.parseapp.com/img/rift.png",
          "buttons": [{
            "type": "web_url",
            "url": "https://www.messenger.com/",
            "title": "Web url"
          }, {
            "type": "postback",
            "title": "Postback",
            "payload": "Payload for first element in a generic bubble",
          }],
        },{
          "title": "Second card",
          "subtitle": "Element #2 of an hscroll",
          "image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
          "buttons": [{
            "type": "postback",
            "title": "Postback",
            "payload": "Payload for second element in a generic bubble",
          }],
        }]
      }
    }
  };
  request({
    url: 'https://graph.facebook.com/v2.6/me/messages',
    qs: {access_token:token},
    method: 'POST',
    json: {
      recipient: {id:sender},
      message: messageData,
    }
  }, function(error, response, body) {
    if (error) {
      console.log('Error sending message: ', error);
    } else if (response.body.error) {
      console.log('Error: ', response.body.error);
    }
  });
}