Watson으로 쉽게 만드는 카카오톡 ChatBot

이 포스팅은 5. [응용] 회의실 예약 조회 및 취소 기능 추가하기에 이어지는 포스팅입니다. 이 포스팅에서는 챗봇을 텔레그램과 연동하고, 스케줄러를 이용하여 예약 시간이 도달하기 전에 사용자에게 알림을 주는 기능을 개발합니다.

이전 포스팅을 완료하지 못하신 분은 tutorial6 브랜치를 checkout하여 진행하십시오.

< 이 포스팅과 연결된 글 목록 >

6. 예약 시간이 되면 ChatBot이 알려주기 (텔레그램)

6.1 텔레그램과 연동하기

먼저 어플리케이션을 텔레그램과 연동합니다.

1) Node.js 어플리케이션 텔레그램과 연동하기를 참조하여 bot을 생성합니다.

2) 1번 단계에서 생성한 bot token을 TELEGRAM_TOKEN으로, 각자 어플리케이션의 public url을 PUBLIC_URL로 .env파일에 등록합니다.

# Telegram
TELEGRAM_TOKEN=376884728:AAH0EcH3tPqBZ6G30tI8nEZCrchXXXXXXXX

# Public URL
PUBLIC_URL=https://meetingroom-reservation-with-camomile-xxxx.eu-gb.mybluemix.net

3) Command창에서 아래 명령을 실행하여 node-telegram-bot-api 모듈을 설치합니다.

npm install node-telegram-bot-api --save

4) /api 밑에 telegram 폴더를 생성하고 cron.js와 message.js를 생성합니다.

chatbot-tutorial
  - api
    - telegram
      - cron.js
      - message.js

4) /api/telegram/message.js를 통해 텔레그램 메신저와 대화 서비스를 연동합니다. 먼저 필요한 모듈을 import 합니다.

'use strict';

const conversation = require('../message');
const config = require('../../util/config');
const cloudant = require('../../util/db');
const db = cloudant.db;

const token = process.env.TELEGRAM_TOKEN;
const url = process.env.PUBLIC_URL;

5) token을 인자로 Telegram bot 인스턴스를 생성합니다.

const TelegramBot = require('node-telegram-bot-api');
let bot = new TelegramBot(token);

6) Telegram에 webhook url로 등록할 api를 생성합니다.

let postMessage = (req, res) => {
  bot.processUpdate(req.body);
  res.sendStatus(200);
};

module.exports = {
    'initialize' : (app, options) => {
        app.post(`/bot${token}`, postMessage);
    }
};

7) /api/index.js에 필요한 모듈을 import하고 webhook url을 initialize하도록 추가합니다.

'use strict';

console.log('APIs initialize');

const conversation = require('./message');
const kakao_keyboard = require('./kakao/keyboard');
const kakao_message = require('./kakao/message');
const telegram_bot = require('./telegram/message');
const telegram_scheduler = require('./telegram/cron');

module.exports = {
    'initialize': (app, options) => {
        conversation.initialize(app, options);
        kakao_keyboard.initialize(app, options);
        kakao_message.initialize(app, options);
        telegram_bot.initialize(app, options);
    }
};

8) /api/telegram/message.js에 생성한 api의 url을 등록합니다.

bot.setWebHook(`${url}/bot${token}`);

9) 텔레그램으로 도착하는 메세지를 Conversation api의 input text로 하고 Conversation api의 output text를 텔레그램으로 보내는 메세지로 함으로써 두 서비스를 연동합니다. 채팅창 id를 key로 하여 대화의 context를 cloudant에 저장합니다.

bot.on('message', msg => {
  let user_key = msg.chat.id;
  let content = {
    text : msg.text
  };

  db.get(user_key).then(doc => {
    conversation.getConversationResponse(content, doc.context).then(data => {
      db.insert(Object.assign(doc, {
        'context': Object.assign(data.context, {
          'timezone' : "Asia/Seoul"
        }),
      }));

      bot.sendMessage(user_key, getOutputText(data));
    }).catch(function(err){
      bot.sendMessage(user_key, JSON.stringify(err.message));
    });
  }).catch(function(err) {
    // first communication
    conversation.getConversationResponse(content, doc.context).then(data => {
      db.insert({
        '_id' : user_key+"", // cloudant의 doc id는 반드시 string 타입이어야 합니다.
        'user_key' : user_key+"",
        'context': data.context,
        'type' : 'telegram'
      }).then(function(){
      }).catch(function(err){
        console.log(err)
      });
      
      bot.sendMessage(user_key, getOutputText(data));  

    }).catch(function(err){
      bot.sendMessage(user_key, JSON.stringify(err.message));
    });
    
  });
});

function getOutputText(data){
  var output = data.output;
  if(output.text && Array.isArray(output.text)){
    return output.text.join('\\n');
  }
  else if(output.text){
    return output.text;
  }
  else return "";
}

6.2 스케줄러를 통해 예약 알림 서비스 개발

1) 먼저 node-schedule 모듈을 설치합니다.

npm install node-schedule --save

2) cron.js에 필요한 모듈을 import하고 telegram의 bot instance를 생성합니다.

'use strict';

const schedule = require('node-schedule');
const request = require('request');
const moment = require('moment');
const TelegramBot = require('node-telegram-bot-api');

const cloudant = require('../../util/db');
const db = cloudant.db;
const config = require('../../util/config');
const token = process.env.TELEGRAM_TOKEN;

let bot = new TelegramBot(token);

3) Schedule Job을 생성합니다. 첫번째 인자 값을 0으로 설정함으로써(‘0 * * * *’) 매 시간 0분에 스케줄러가 동작하도록 설정합니다.

 
let job = schedule.scheduleJob('0 * * * *', function(){
  console.log("I'm working :-D");

  // 하룻동안의 예약 정보를 가져옵니다.
  let startTimestamp = new moment();
  let endTimestamp = new moment(startTimestamp).day(startTimestamp.day() + 1);
  //var endTimestamp = new moment(startTimestamp).month(startTimestamp.month() + 1);

  // /book/search/bysite API는 site id, start time, end time을 Query parameter로 받아 해당 시간의 예약 리스트를 return해주는 api입니다.
  request({
    method : 'GET',
    url : process.env.RBS_URL + "/book/search/bysite",
    headers : {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    qs: {
      "siteid" : "camomile",
      "start" : startTimestamp.valueOf(),
      "end" : endTimestamp.valueOf()
    }
  }, function(err, httpResponse, body){
    body = JSON.parse(body);
    if(err){
        console.error(JSON.stringify(err));
    }
    else{
      if(body && body.length > 0){
        let resvs = [];
        for(let resv of body){
             // 예약 시간이 1시간 이내로 남으면 메세지를 전달합니다.
             if(moment(resv.start).diff(moment(new Date()), 'minutes') < 60){
               let message = "[Meeting Alert] "+ moment(resv.start).utcOffset('+0900').format(config.dateTimeFormat) + " ~ " + moment(resv.end).utcOffset('+0900').format(config.dateTimeFormat) + ", " + resv.roomid + ", " + resv.purpose;
               let userId = resv.user.userid;

               //DB에서 userId가 같은 document를 찾아 telegram의 chat id를 가져옵니다. 이 view 생성에 대한 내용은 /util/db.js에 있습니다.
               db.view('context', 'telegram', {key: userId}).then(body => {
                 if(body.total_rows > 0){
                   let user_key = body.rows[0].id;
                   bot.sendMessage(user_key, message);
                 }
               });
              }
        }
      }
    }
  });
});

6.3 테스트

1) 먼저 로컬에서 실행해서 syntax error가 없는지 확인합니다.

npm start

2) 어플리케이션을 블루믹스로 올립니다. webhook 기능은 어플리케이션이 퍼블릭 네트워크로 공개되어야 사용할 수 있습니다.

cf push

3) 텔레그램 메신저를 통해 예약합니다.

4) 예약 시간 전에 다음과 같이 알림 메세지가 도착합니다.

안녕하세요. 이 포스팅은 Watson으로 쉽게 만드는 카카오톡 ChatBot 시리즈의 마지막 글입니다.
이 게시물이 챗봇을 제작하시는 분들에게 도움이 되셨길 바랍니다. 이 시리즈를 완료한 소스 코드는 링크를 참조하십시오.