2016年6月20日 星期一

Creating a Line BOT on Microsoft Azure


Pre-conditions

  • Node.js with Express & Jade
  • Microsoft Azure App Service
  • Line BOT trial account

Node.js with Express & Jade

  1. Install Node.js with Express & Jade
  2. Create an Express project (Ref.: Express application generator)
    $ express LineBOT
  3. Install dependencies
    $ cd LineBOT
    $ npm install
  4. Run the app
    $ DEBUG=LineBot:* npm start
    > LineBOT@0.0.0 start /Users/phchu/Documents/Nodejs/LineBOT
    > node ./bin/www
    
  5. Open the url
    GET / 304 400.918 ms - -
    GET /stylesheets/style.css 304 3.950 ms - -

Microsoft Azure App Service

Advantages:
  • Free
  • SSL(https)
  • fixed IP address (it's important to configure Line BOT callback server's white list)




Deploy the "LineBOT" project generated in previous step

  1. Install Azure CLI & login
    $ azure login
    info:    Executing command login
    |info:    To sign in, use a web browser to open the page https://aka.ms/devicelogin. Enter the code ********* to authenticate.
    /info:    Added subscription Enterprise
    info:    Added subscription Azure in Open
    +
    info:    login command OK
  2. Create the site by "azure site create --git {appname}"
    $ azure site create --git phbot
    azure site create --git phbot
    info:    Executing command site create
    + Getting sites                                                                
    + Getting locations                                                            
    help:    Choose a location
      1) South Central US
      2) North Europe
      3) West Europe
      4) Southeast Asia
      5) East Asia
      6) West US
      7) East US
      8) Japan West
      9) Japan East
      10) East US 2
      11) North Central US
      12) Central US
      13) Brazil South
      14) Canada Central
      15) Canada East
      : 5
    info:    Creating a new web site at phbot.azurewebsites.net
    \info:    Created website at phbot.azurewebsites.net                           
    +
    info:    Executing `git init`
    info:    Creating default iisnode.yml file
    info:    Initializing remote Azure repository
    + Updating site information                                                    
    info:    Remote azure repository initialized
    + Getting site information                                                     
    + Getting user information                                                     
    help:    Please provide the username for Git deployment
    Publishing username: phchu
    info:    Executing `git remote add azure https://phchu@phbot.scm.azurewebsites.net/phbot.git`
    info:    A new remote, 'azure', has been added to your local git repository
    info:    Use git locally to make changes to your site, commit, and then use 'git push azure master' to deploy to Azure
    info:    site create command OK
    
  3. Deploy app to Azure
    $ git add .
    $ git commit -m "initial commit"
    [master (root-commit) e2a8aba] initial commit
     12 files changed, 314 insertions(+)
     create mode 100644 .vscode/launch.json
     create mode 100644 app.js
     create mode 100755 bin/www
     create mode 100644 iisnode.yml
     create mode 100644 npm-debug.log
     create mode 100644 package.json
     create mode 100644 public/stylesheets/style.css
     create mode 100644 routes/index.js
     create mode 100644 routes/users.js
     create mode 100644 views/error.jade
     create mode 100644 views/index.jade
     create mode 100644 views/layout.jade
    $ git push azure master
    
  4. Finally, launch the Azure app in the browser:
Tip: Use "azure site log tail" to see the console log
$ azure site log tail
info:    Executing command site log tail
+ Getting site information
Welcome, you are now connected to log-streaming service.             

Line BOT trial account

We have to get the following attributes from the trial account:
  1. Channel ID
  2. Channel Secret (Click "Show" button to see it)
  3. MID

Configure the Line BOT account attributes in project

Creating a file named "config.js" as below:
module.exports = {
 'LINE':{ 
  CHANNEL_ID: '1234567890',
  CHANNEL_SERECT: '1ad5135ufygthrjekadf107d2',
  MID: '0s98d7f65g4h3jki2u3y4t5re678w9',
 } 
};

Setup the "Callback URL" in channel

Callback URL format: http://[app_name].azurewebsites.net:433/[router]
Ref.: Getting started with BOT API Trial-Registering a callback URL
https://phbot.azurewebsites.net:443/callback

Create the callback router in the project

  1. Create "callback.js" in folder routes
    var express = require('express');
    var router = express.Router();
    
    /* GET line bot callback. */
    router.post('/', function(req, res, next) {
      res.send('respond with a resource');
    });
    
    module.exports = router;
    
  2. Configure the routing in app.js
    var callback = require('./routes/callback');
    app.use('/callback', callback);
    
  3. Verify the callback URL
    Click the "VERIFY" button, and it will show "Success." message.

    $ azure site log tail
    info:    Executing command site log tail
    + Getting site information
    Welcome, you are now connected to log-streaming service.
    POST /callback 200 6.439 ms - 23             
    

Line BOT API

Add fixed propreties in config.js

module.exports = {
    'LINE':{ 
 API: 'https://trialbot-api.line.me/v1/',
 CHANNEL_ID: '1234567890',
 CHANNEL_SERECT: '1ad5135ufygthrjekadf107d2',
 MID: '0s98d7f65g4h3jki2u3y4t5re678w9',
 TEXT: 1,
 IMAGE: 2,
    } 
};

Receive message

  1. Add BOT as a friend by scanning the QR-code above Callback URL
  2. Add console.log in callback.js to show message content then commit it
    /* GET line bot callback. */
    router.post('/', function(req, res, next) {
      console.log('callback: ', req.body);
      var result = req.body.result;
      for(var i=0; i < result.length; i++){
          var data = result[i].content;
          console.log('message content: ', data);
        }  
    });
    
  3. Say "Hi, BOT" to the online Line BOT

    Console:
    callback:  { result: 
       [ { content: [Object],
           createdTime: 1466386987962,
           eventType: '138311609000106303',
           from: 'u09y8t76r5e4ws8d7f6g5h4jdfghjkxcvbnmkl',
           fromChannel: 1341301815,
           id: 'WB1520-3548181394',
           to: [Object],
           toChannel: 1234567890 } ] }
    message content:  { toType: 1,
      createdTime: 1466386987943,
      from: 'u09y8t76r5e4ws8d7f6g5h4jdfghjkxcvbnmkl',
      location: null,
      id: '4488676771230',
      to: [ 'sdfghjk8f7gh6j5k4l30lk98j7h6g5f4d3s' ],
      text: 'Hi, BOT',
      contentMetadata: { AT_RECV_MODE: '2', SKIP_BADGE_COUNT: 'true' },
      deliveredTime: 0,
      contentType: 1,
      seq: null }
    

Send text message

  1. Install module dependency: request
    npm install request --save
  2. Add a new function sendMessage to reply(repeat) sender's message
    function sendMessage(sender, content, type) {
      console.log('Send message: ', content);
      var query_fields = 'events';
      var data;
      switch (type) {
        case config.LINE.TEXT:
          data = {
            to: [sender],                   //Array of target user. Max count: 150.
            toChannel: 1383378250,          //Fixed value
            eventType: '138311608800106203',//Fixed value
            content: {
              contentType: type,
              toType: 1,
              text: content
            }
          };
          break;
        default:
          break;
      }
      console.log('Send data: ', data);
    
      request({
        url: config.LINE.API.concat(query_fields),
        headers: {
          'Content-Type': 'application/json; charset=UTF-8',
          'X-Line-ChannelID': config.LINE.CHANNEL_ID,
          'X-Line-ChannelSecret': config.LINE.CHANNEL_SERECT,
          'X-Line-Trusted-User-With-ACL': config.LINE.MID
        },
        method: 'POST',
        body: JSON.stringify(data)
      }, function (error, response, body) {
        if (error) {
          console.log('Error sending message: ', error);
        } else if (response.body.error) {
          console.log('Error: ', response.body.error);
        }
        console.log('Send message response: ', body);
      });
    }
    
  3. Reply sender's message by repeating it
    /* GET line bot callback. */
    router.post('/', function(req, res, next) {
      console.log('callback: ', req.body);  
      var result = req.body.result;
      for(var i = 0; i < result.length; i++){
          var data = result[i].content;
          console.log('message content: ', data);
          sendMessage(data.from, data.text, config.LINE.TEXT);
        }  
    });
    
  4. Commit and Try it

    Ater replying message successfully, the BOT will receive a response with messageId and timestamp.
    Console
    Send message response:  
    {
     "failed": [],
     "messageId": "1466392481962",
     "timestamp": 1466392481962,
     "version": 1
    }
    
    If you received the following response, add the denied IP address in whitelist
    Send message response:  
    {
      "statusCode": "427",
      "statusMessage": "Your ip address [213.73.167.123] is not allowed to access this API. Please add your IP to the IP whitelist in the developer center."
    }
    

Get user profile

  1. Add a new function queryProfile to query sender's name
    function queryProfile(sender, callback) {
      var query_fields = 'profiles?mids=';
      request({
        url: config.LINE.API.concat(query_fields, sender),
        headers: {
          'Content-Type': 'application/json; charset=UTF-8',
          'X-Line-ChannelID': config.LINE.CHANNEL_ID,
          'X-Line-ChannelSecret': config.LINE.CHANNEL_SERECT,
          'X-Line-Trusted-User-With-ACL': config.LINE.MID
        },
        method: 'GET'
      }, function (error, response, body) {
        if (error) {
          console.log('Error sending message: ', error);
        } else if (response.body.error) {
          console.log('Error: ', response.body.error);
        }
        var jsonResult = JSON.parse(body);
        console.log('Profile response: ', jsonResult);  
        for (var i = 0; i < jsonResult.count; i++) {      
          var userName = jsonResult.contacts[i].displayName;
          console.log('User name: ', userName);
          callback(userName);
        }      
      });
    }
    
  2. Reply sender's message by saying "Hi, [user_name]"
    /* GET line bot callback. */
    router.post('/', function(req, res, next) {
      console.log('callback: ', req.body);     
      var result = req.body.result;
      for(var i = 0; i < result.length; i++){
          var data = result[i].content;
          console.log('message content: ', data);
          queryProfile(data.from, function(user) {
            var reply_msg = 'Hi, '.concat(user);
            sendMessage(data.from, reply_msg, config.LINE.TEXT);   
          });      
        }  
    });
    
  3. Commit and Try it

    Console:
    Profile response:  
    { contacts: 
       [ { displayName: 'phchu',
           mid: 'sdfghjk8f7gh6j5k4l30lk98j7h6g5f4d3s',
           pictureUrl: '',
           statusMessage: '' } ],
      count: 1,
      display: 1,
      pagingRequest: { start: 1, display: 1, sortBy: 'MID' },
      start: 1,
      total: 1 }
    User name:  phchu
    Send message:  Hi, phchu
    Send data:  { to: [ 'sdfghjk8f7gh6j5k4l30lk98j7h6g5f4d3s' ],
      toChannel: 1383378250,
      eventType: '138311608800106203',
      content: { contentType: 1, toType: 1, text: 'Hi, phchu' } }
    Send message response:  {"failed":[],"messageId":"1466394625241","timestamp":1466394625241,"version":1}
    

Send image message

  1. Reply sender's message by an image
    If sender's message contains string "Hi,", replying message by saying "Hi, [user_name]". Otherwise, replying the image sticker as below.

  2. Add a new image message type in sendMessage function
    case config.LINE.IMAGE:
          data = {
            to: [sender],
            toChannel: 1383378250,
            eventType: '138311608800106203',
            content: {
              contentType: type,
              toType: 1,
              originalContentUrl: content,
              previewImageUrl: content
            }
          };
          break;
    
  3. Commit and Try it

     Console:
    Send message:  https://sdl-stickershop.line.naver.jp/products/0/0/4/1331/android/stickers/23770.png
    Send data:  { to: [ 'sdfghjk8f7gh6j5k4l30lk98j7h6g5f4d3s' ],
      toChannel: 1383378250,
      eventType: '138311608800106203',
      content: 
       { contentType: 2,
         toType: 1,
         originalContentUrl: 'https://sdl-stickershop.line.naver.jp/products/0/0/4/1331/android/stickers/23770.png',
         previewImageUrl: 'https://sdl-stickershop.line.naver.jp/products/0/0/4/1331/android/stickers/23770.png' } }
    Send message response:  {"failed":[],"messageId":"1466396249482","timestamp":1466396249482,"version":1}
    


Final Result



View on GitHub

Related Articles

0 意見:

技術提供:Blogger.