본문 바로가기

Node.js

[카톡봇] openAPI와 카카오톡API를 이용한 버스 도착 정보 조회


Nodejs와 공공API를 이용한 버스도착정보 카톡봇입니다.

공부하면서 연습용으로 만든 것이라 조잡합니다.

날씨는 처음에 request연습할 때 넣었던 것이고, 대화창에서 마지막 버튼은 장난으로 ㅎㅎ

showBustArrivalData.js

버스 정보를 알려주는 module

/* NodeJs 샘플 코드 */

//228000710 외대앞 버정 stationId
//228000723 정문 앞 버정 stationId M5107이 들어오는 길
/*
routeName, routeId
5100, 200000115

9, 200000103

1112, 234000016

5500-1, 234000001

7000, 200000112
M5107, 234001243
*/

var request = require('request');
var requestP=require('request-promise');
var cheerio=require('cheerio');
var url = 'http://openapi.gbis.go.kr/ws/rest/busarrivalservice/station';
var serviceKey='=★본인 서비스 키로 바꿔줘야함★';
var stationId='228000710'
var stationIdM='228000723'
//stationId of m5107 : 228000723
var queryParams = '?' + encodeURIComponent('serviceKey') + serviceKey; /* Service Key*/
queryParams += '&' + encodeURIComponent('stationId') + '='; /* 정류소 ID */
var routeIdArr={
  id5100:200000115,
  id9:200000103,
  id1112:234000016,
  id5500_1:234000001,
  id7000:200000112,
  id5107:234001243
}

function routeIdtoName(id){
  var name;
  switch(id){
    //5100
    case 200000115:{ name='5100';
      break;}

      //9
    case 200000103:{ name='9';
      break;}

      //1112
    case 234000016:{ name='1112';
      break;}

      //5500-1
    case 234000001:{ name='5500-1';
      break;}

      //7000
    case 200000112:{ name='7000';
      break;}

      //5107
    case 234001243:{ name='M5107';
      break;}
  }

  return name;
}

var resultStr=""

async function showBusArrivalData(){

  //_stationId는 M5107과 그 외 버스에 따라 다르다.
  var _stationId=stationId;
  await requestP({
      url: url + queryParams+encodeURIComponent(_stationId),
      method: 'GET'
      }, function (error, response, body) {
        function getData(routeId){

          $('routeId').each(function(){

            if($(this).text()==routeId){

              var busObj=$(this).closest('busArrivalList');
              var predictTime=$('predictTime1', busObj).text();
              //console.log(routeIdtoName(routeId)+' BUS '+predictTime+'분 전')
              resultStr+='['+routeIdtoName(routeId)+' BUS]  '+predictTime+'분 전\n';
            }

          })
        }
      $=cheerio.load(body);
      for(var name in routeIdArr) {
        if(name!='id5107') getData(routeIdArr[name])
      }
    });


  //for M5107
  _stationId=stationIdM;
  await requestP({
      url: url + queryParams+encodeURIComponent(_stationId),
      method: 'GET'
      }, function (error, response, body) {
        function getData(routeId){
          $('routeId').each(function(){
            if($(this).text()==routeId){
              var busObj=$(this).closest('busArrivalList');
              var predictTime=$('predictTime1', busObj).text();
              //console.log(routeIdtoName(routeId)+' BUS '+predictTime+'분 전')
              resultStr+='['+routeIdtoName(routeId)+' BUS]  '+predictTime+'분 전\n';
            }

          })
        }
      $=cheerio.load(body);
      getData(routeIdArr['id5107']);
  });

  return new Promise((resolve, reject)=>{
    resolve(resultStr)
  })
}


module.exports=showBusArrivalData

ktbot.js

위의 모듈을 이용해 카톡봇을 구현하는 프로그램

//수정 의문 내용
//@@

var express=require('express');
var app=express();
var cheerio=require('cheerio');
var request=require('request');
var requestP=require('request-promise');
var phantom=require('phantom');
var bodyParser=require('body-parser');
var fs=require('fs');
var showBusArrivalData=require('./showBusArrivalData.js')
//var str=await showBusArrivalData());
//console.log(str);
function setRouter(){
//public 내의 파일들 업로드
  app.use(express.static('public'));

  app.get('/', function(req, res){
    res.send('kakaobot Jinsu')
  })
}
async function autoWeather(){
  var url = 'https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=1&ie=utf8&query=%EB%82%A0%EC%94%A8';
  var instance=await phantom.create();
  var page=await instance.createPage();
  var status=await page.open(url);
  console.log("page status : "+status);
  var clipRect=await page.evaluate(function () {
    return document.querySelector('[class="table_info weekly _weeklyWeather"]').getBoundingClientRect();
  });

  page.property('clipRect', {
    top:    clipRect.top,
    left:   clipRect.left,
    width:  clipRect.width,
    height: clipRect.height
  });
  page.render('./public/weather.png');

  console.log('autoWeahter finished in funciton')
}

async function autoSearch(){
  var url="https://www.naver.com/";
  var rank_str="";

//@@여기를 callback말고 Promise, await으로 구성해보는 게 깔끔할듯?
		await requestP(url, function(error, response, html){
		    if (error) {
          console.log('search request err : '+err);
          throw error;
        }

		    var $ = cheerio.load(html);
		    var cnt=0;

		    $('.ah_item').each(function(){
		    	if(cnt<10){
		    		var str=$(this).text();
					cnt++;
					rank_str+=cnt+" : "+str.substr(4, str.length-6)+"\n";
		    	}
		    })
		    //console.log("autoSearch finished in the function");
		});

		return new Promise((resolve, reject)=>{
      //console.log('rank : '+rank_str)
      resolve(rank_str);
    });
}


setRouter();
autoWeather();//@@테스트용으로 바로 보려고 그냥 넣은거지 카톡으로 진짜 할 거면 이제 안 해도 됨
autoSearch();

// parse application/json
app.use(bodyParser.json());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));



const keyboardContents = {
    type: 'buttons',
    buttons: ["날씨", "네이버 검색 순위", "Bus", "Jinsu--^_^"]
};

//초기 상태 get
app.get('/keyboard', function(req, res){
  res.set({
      'content-type': 'application/json'
  }).send(JSON.stringify(keyboardContents));
  console.log("someone GET /keyboard");
});

//카톡 메시지 처리
app.post('/message',async function (req, res) {
    const _obj = {
        userKey: req.body.userKey,
        type: req.body.type,
        content: req.body.content
    };

    //keyboardContents는 항상 들어감
    var responseContents={
      keyboard:keyboardContents
    };

    //확인용
    console.log('userType : '+_obj.userKey)
    console.log('userContents : '+_obj.content)


    //네이버 검색 순위
    if(_obj.content == '날씨')
    {
      var rank=await autoSearch();
      //console.log('autoSearch finished in message')

      //@@message에 keyboard안넣으면 다시 default인 텍스트로 감??
      responseContents.message={
                "photo": {
                  //@@내 컴퓨터에 있는 이미지는 불가능한듯..
                  "url": "http://124.50.93.166:3000/weather.png",
                  "width": 588,
                  "height": 228
                }
      };
    }
    else if(_obj.content == '네이버 검색 순위')
    {
        var rank=await autoSearch();
        //console.log('autoSearch finished in message')

        //@@message에 keyboard안넣으면 다시 default인 텍스트로 감??
        responseContents.message={
                "text": rank
        };
    }

    else if(_obj.content =='Bus'){
      var busDataStr=await showBusArrivalData();
      console.log('result'+busDataStr);
      setTimeout(function(){console.log('timeout : '+busDataStr)}, 3000)
      responseContents.message={
              "text": busDataStr
      };
    }

    else if(_obj.content == 'Jinsu--^_^'){
      responseContents.message={
              "text": '진수는 정말.. 멋져...',
              "photo": {
                //@@내 컴퓨터에 있는 이미지는 불가능한듯..
                "url": "http://124.50.93.166:3000/jinsu.png",
                "width": 457,
                "height": 133
              }
      };
    }

    //카톡으로 전송
    res.set({
        'content-type': 'application/json'
    }).send(JSON.stringify(responseContents));

});
app.listen(3000, function() {
  console.log('app is listening to 3000')
});