60 min
aihara
  1. 1. Information
    1. 1.1. Ingredients
    2. 1.2. Functions
    3. 1.3. Requirements
      1. 1.3.1. Database
  2. 2. Create the application
    1. 2.1. Frontend
    2. 2.2. Backend
  3. 3. Customize the application
    1. 3.1. Frontend
    2. 3.2. Backend
  4. 4. Deploy the application
    1. 4.1. Backend
    2. 4.2. Frontend
  5. 5. Download
  6. 6. Version

LINE is a popular messaging application in Japan or another countries. LINE also provides business account and WebAPIs to enable you to use LINE in your business.

In this tutorial, we provide how to create a basic communication application that has following functions for your customers or internal-use.

  • Chatbot (It’s simple but extendable.)
  • Chat UI for LINE business account manager.
  • Persistent data store using MongoDB to store LINE messages.

Information

Ingredients

  • Template: Chat
  • API Loigcs: Line Messaging API, MongoDB

Functions

  • Responding automatically as Chatbot if messages matching keywords.
  • Providing UI for you to chat with followers.
  • Storing messages in MongoDB.

Requirements

Database

To run this program, you need to create MongoDB collections.

  • collections
    • sample_message
    • sample_stream

Create the application

Frontend

  1. Choose the Chat Template.

  2. Customize the UI in K5 Playground.

Backend

  1. Add a WebAPI.

    Create a new WebAPI named webhook and activate POST method to receive messages from LINE.

  2. Edit the API Logic of POST webhook endpoint.

    1. Add the LINE Messaging API Receive Message API Logic from Messaging menu at the right pane.

    2. Set the channel secret to extract the signature.

      1
      2
      3
      4
      5
      // ~~ code omitted ~~

      var secret = '[SECRET]';

      // ~~ code omitted ~~
    3. Add the MongoDB Save Document API Logic from Database menu at the right pane.

    4. Edit the code to create a stream.

      This application handles a follow event as a opening a stream where you can talk with the follower. In this code, we query follower’s name by adding getProfile method and save it by using the API Logic.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      function getProfile(userId) {
      var accessToken = '[ACCESS TOKEN]';
      var options = {
      url: 'https://api.line.me/v2/bot/profile/' + userId,
      json: true,
      method: 'get',
      headers: {
      'Authorization': 'Bearer ' + accessToken,
      },
      };
      return new Promise(function(resolve, reject) {
      Request(options, function(error, response, body) {
      if (!error && response.statusCode == 200) {
      resolve(body);
      }
      });
      });
      }

      function addStream(event, profile) {
      var collectionName = 'sample_stream';
      var document = {
      stream_id: event.source.userId,
      name: profile.displayName,
      };
      var url = 'mongodb://' + config.datasource.user + ':' + config.datasource.password + '@' + config.datasource.host + ':' + config.datasource.port + '/' + config.datasource.database;

      MongoClient.connect(url, function(err, db) {
      if (err) {
      // @todo handle error
      throw err;
      }
      db.collection(collectionName).save(document, function(err, result) {
      db.close();
      if (err) {
      // @todo handle error
      throw err;
      }
      return;
      });
      });
      }

      var lineEvents = JSON.parse(req.body);

      lineEvents.events.forEach(function(event) {
      if(event.type === 'follow') {
      getProfile(event.source.userId).then(function(profile) {
      addStream(event, profile);
      });
      }
      });
      next({});
    5. Add the MongoDB Save Document API Logic from Database menu at the right pane.

    6. Edit the code to save a LINE message in MongoDB.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      function getProfile(userId) {
      var accessToken = '[ACCESS TOKEN]';
      var options = {
      url: 'https://api.line.me/v2/bot/profile/' + userId,
      json: true,
      method: 'get',
      headers: {
      'Authorization': 'Bearer ' + accessToken,
      },
      };
      return new Promise(function(resolve, reject) {
      Request(options, function(error, response, body) {
      if (!error && response.statusCode == 200) {
      resolve(body);
      }
      });
      });
      }

      function saveMessage(event, profile) {
      // @see http://mongodb.github.io/node-mongodb-native/2.1/tutorials/crud/
      var collectionName = 'sample_message'; // @todo set collection name
      var document = {
      stream_id: event.source.userId,
      user_id: profile.displayName,
      message: event.message.text,
      replyMessage_id: '',
      }; // @todo set document data for insert
      var url = 'mongodb://' + config.datasource.user + ':' + config.datasource.password + '@' + config.datasource.host + ':' + config.datasource.port + '/' + config.datasource.database;
      return new Promise(function(resolve, reject) {
      MongoClient.connect(url, function(err, db) {
      if (err) {
      // @todo handle error
      reject(err);
      }
      db.collection(collectionName).save(document, function(err, result) {
      db.close();
      if (err) {
      // @todo handle error
      reject(err);
      }
      resolve(result);
      });
      });
      });
      }

      var lineEvents = JSON.parse(req.body);
      var promises = [];
      lineEvents.events.forEach(function(event) {
      if(event.type === 'message' && event.message.type === 'text') {
      promises.push(getProfile(event.source.userId).then(function(profile) {
      return saveMessage(event, profile);
      }));
      }
      });

      Promise.all(promises).then(function() {
      next({});
      });
    7. Add the LINE Messaging API Send Message API Logic from Messaging menu at the right pane.

    8. Edit the code to implement a chatbot.

      In this code, we implement a BOT. BOT automatically responds followers if their messages match match specific keywords. faq defines keywords and responses. So the code is as below.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      // @see https://devdocs.line.me/en/#push-message
      // @todo set Channel Access Token and ID of the receiver
      var accessToken = '[ACCESS TOKEN]';
      var faqs = [
      {
      question: 'url',
      answer: 'http://playground.cloud.global.fujitsu.com',
      },
      {
      question: 'How are you',
      answer: 'I\'m fine!',
      }
      ];

      function sendMessage(userId, text) {
      // @todo Send message object
      var message = [
      {
      "type": "text",
      "text": text,
      },
      ];
      var options = {
      url: 'https://api.line.me/v2/bot/message/push',
      method: 'post',
      headers: {
      'Authorization': 'Bearer ' + accessToken,
      },
      json: {
      "to": userId,
      "messages": message,
      }
      };
      return new Promise(function(resolve, reject) {
      Request(options, function(error, response, body) {
      if (!error && response.statusCode == 200) {
      resolve(body);
      } else {
      res.end(JSON.stringify(error));
      reject(error);
      }
      });
      });
      }

      var lineEvents = JSON.parse(req.body);
      var answers = [];
      var promises = [];
      lineEvents.events.forEach(function(event) {
      if(event.type === 'message' && event.message.type === 'text') {
      faqs.forEach(function(faq) {
      if(event.message.text.includes(faq.question)) {
      promises.push(sendMessage(event.source.userId, faq.answer));
      answers.push({
      stream_id: event.source.userId,
      user_id: 'bot',
      message: faq.answer,
      replyMessage_id: '',
      });
      }
      });
      }
      });
      Promise.all(promises).then(function() {
      next(answers);
      });
    9. Add the MongoDB Save Document API Logic from Database menu at the right pane.

    10. Set the connection configuration for MongoDB to the config.js.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      if (!results.length) {
      next({});
      return;
      }
      // @see http://mongodb.github.io/node-mongodb-native/2.1/tutorials/crud/
      var collectionName = 'sample_message'; // @todo set collection name
      var document = results; // @todo set document data for insert
      var url = 'mongodb://' + config.datasource.user + ':' + config.datasource.password + '@' + config.datasource.host + ':' + config.datasource.port + '/' + config.datasource.database;
      MongoClient.connect(url, function(err, db) {
      if (err) {
      // @todo handle error
      throw err;
      }
      db.collection(collectionName).insertMany(document, function(err, result) {
      db.close();
      if (err) {
      // @todo handle error
      throw err;
      }
      next({});
      });
      });
  3. Edit the API Logic of POST sample_messages endpoint.

    1. Find the LINE Messaging API Send Message API Logic from Messaging menu at the right pane.

    2. Add the logic after the Stream Send Message API Logic.

    3. Edit the code to send a message to LINE.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // ~~ code omitted ~~

      var accessToken = '[ACCESS TOKEN]';
      var messageReceiverId = req.body.stream_id;

      // @todo Send message object
      var message = [
      {
      "type": "text",
      "text": req.body.message,
      },
      ];

      // ~~ code omitted ~~
  4. Download the application.

Customize the application

Frontend

Change _id tostream_id.

  1. Edit the app\components\StreamList\StreamList.js.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // ~~ code omitted ~~

    static propTypes = {
    currentStreamId: PropTypes.string,
    streamList: PropTypes.arrayOf(
    PropTypes.shape({
    stream_id: PropTypes.string,

    // ~~ code omitted ~~

    componentWillMount() {
    // push task to the stack
    const tasks = [
    () => { // task: select default stream
    if (!this.props.currentStreamId &&
    this.props.streamList.length > 0) {
    // select top of stream list for default stream
    const stream = this.props.streamList[0];
    StreamActionCreators.selectStream(stream);
    MessageActionCreators.getMessages({ streamId: stream.stream_id });

    // ~~ code omitted ~~

    handleCreateStream = () => {
    StreamActionCreators.createStream({
    name: (this.newstreamName ? this.newstreamName.value : 'new stream'),
    resolve: (stream) => {
    this.newstreamName.value = '';
    MessageActionCreators.getMessages({ streamId: stream.stream_id });

    // ~~ code omitted ~~

    handleSelectStream(stream) {
    StreamActionCreators.selectStream(stream);
    MessageActionCreators.getMessages({ streamId: stream.stream_id });

    // ~~ code omitted ~~

    const streamListItems = streamList.map(stream => (
    <ListItem
    style={styles.streamListItem}
    onClick={() => this.handleSelectStream(stream)}
    key={`StreamList-Stream-${stream.stream_id}`}
    value={stream.stream_id}

    // ~~ code omitted ~~
  2. Edit the app\components\StreamContent\StreamContent.js.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // ~~ code omitted ~~

    static propTypes = {
    currentStream: PropTypes.shape({
    stream_id: PropTypes.string,

    // ~~ code omitted ~~

    componentWillReceiveProps(nextProps) {
    if (this.props.currentStream.stream_id !==
    nextProps.currentStream.stream_id) {

    // ~~ code omitted ~~

    this.setState({
    timerId: setInterval(() => {
    MessageActionCreators.getMessages({
    streamId: nextProps.currentStream.stream_id,

    // ~~ code omitted ~~

    <HorizontalLayout style={styles.entry}>
    <VerticalLayout expandable style={styles.entryForm}>
    <input
    type="text"
    ref={(component) => { this.userId = component; }}
    placeholder={'Name'}
    style={styles.userId}
    tabIndex={0}
    />
    <textarea
    ref={(component) => { this.message = component; }}
    placeholder={'Message'}
    style={styles.message}
    tabIndex={0}
    onKeyDown={
    event => this.handleEnterMessage(event, currentStream.stream_id, replyMessageId)

    // ~~ code omitted ~~

    <VerticalLayout style={styles.entrySubmit}>
    <RaisedButton002
    label="post"
    onClick={() => this.handleSubmitMessage(currentStream.stream_id, replyMessageId)}

    // ~~ code omitted ~~

Backend

  1. Edit swagger(api/swagger.json) to receive LINE events.

    Add parameter property to /webhook.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    "/webhook": {
    "post": {
    "description": "",
    "parameters": [{
    "name": "data",
    "in": "body",
    "schema": {
    "type": "string"
    }
    }],
    "responses": {
    "200": {
    "description": ""
    }
    },
    "operationId": "webhookPOST",
    "x-swagger-router-controller": "Chat"
    }
    }
  2. Edit the config.js to set the datasource to connect to MongoDB.

Deploy the application

Finally, all you need to do is deploying backend and frontend.

Backend

1
2
cd backend
cf push [APP_NAME]

Frontend

1
2
3
4
5
6
cd frontend
npm install
set API_URL=[Backend URL] # for Windows
export API_URL=[Backend URL] # for Mac/Linux
npm run build
cf push [APP_NAME] -p public

Download

Version

  • Chat Template: v1.0.0