【Rails】첫 LINE Bot(Gem line-bot-api)

LINE에서 답변을 반환하는 채팅봇(DB에서 데이터 표시, 스크래핑)을 구현했습니다. 전 준비의 LINE 계정 작성 순서에 대해서는 LINE Developers 등에 정중하게 기재되어 있기 때문에 할애.

개요



사사입니다만, 통근에 기차와 회사의 버스를 사용하고 있습니다.
거기서, 자신용으로 다음에 탈 수 있을 것 같은 버스의 시각과 스크래핑으로 전철의 운행 상황을 돌려 주는 LINE Bot를 실장해 보았습니다.
※ 스크래핑은 스크래핑처에 부하를 걸 우려가 있기 때문에, 실제의 운용은 하지 않고, 공부를 위한 실장입니다.
절차
  • LINE Developers 에 봇 작성을 위한 계정을 작성(할애).
  • 구현중에 움직임을 확인할 목적으로, 로컬 서버 3000에서 움직일 수 있도록, Ngrok 를 도입.
  • gem 'line-bot-api' 에서 구현.

  • Ngrok


  • localhost에서 실행되는 서버를 LAN 외부에서 액세스할 수 있도록 합니다.
  • 로컬 PC 상의 서비스를 외부 공개하는 것이 가능.

  • 이번에는 로컬 환경에서 시작한 포트 번호 3000번을 외부에서 액세스할 수 있도록 하고 싶다.

    사용방법
    ① 다운로드
    $ brew cask install ngrok
    

    ② ngrok 시작
    이번에는 포트 번호 : 3000을 지정하고 ngrok을 시작합니다.
    $ ngrok http 3000(port番号)
    

    일어나면 터미널에 표시된 아래의 https://乱数.ngrok.io 부분을 복사하십시오.
    ※ 난수 부분은 서버를 기동할 때마다 바뀌므로 그때마다 ② 이후의 작업이 필요.
        :
    Forwarding    https://乱数.ngrok.io -> http://localhost:3000
        :
    

    브라우저에서 http://localhost:3000/ 에 가면 rails 서버가 일어나는 것을 확인할 수 있다. ※ 물론, 앱도 rails s 로 기동해 둘 필요가 있다.

    ③ LineBot Webhook 설정
    LINE Developers 의 Bot 채널의 Webhook URL의 개소를, 상기 ②의 URL +/callback( 예 https://乱数.ngrok.io/callback )로 변경한다.
    ※ Webhook : 한마디로, Bot에 이벤트가 발생했을 때, Bot 인스턴스에 통지하기 위한 액세스 포인트(URL) 일. URL을 지정하여 LINE에서 POST 요청을 보냅니다.

    ④ 접속 확인
    그리고는, Bot를 친구 추가해, 동작 확인을 할 수 있다.

    코드



    DB는 mysql을 사용.
    $ rails new アプリ名 -d mysql
    

    db를 만들고 셔틀 버스 시간표 테이블을 만듭니다.
    $ rails db:create
    

    gem 소개 → bundle install
    Gemfile
    gem 'line-bot-api'
    gem 'nokogiri'
    

    라우팅



    routes.rb
    post '/callback', to: 'linebot#callback'
    

    컨트롤러



    application_controller.rb
    # gem 'line-bot-api'を使えるように宣言
    require 'line/bot'
    
    class ApplicationController < ActionController::Base
      protect_from_forgery with: :exception
    
      before_action :validate_signature, except: [:new, :create]
      def validate_signature
        body = request.body.read
        signature = request.env['HTTP_X_LINE_SIGNATURE']
        unless client.validate_signature(body, signature)
          error 400 do 'Bad Request' end
        end
      end  
    
      def client
        @client ||= Line::Bot::Client.new { |config|
          # ローカルで動かすだけならベタ打ちでもOK。
          config.channel_secret = "your channel secret"
          config.channel_token = "your channel token"
        }
      end
    end
    

    linebot 컨트롤러 만들기
    $ rails g controller linebot
    

    linebot_controller.rb
    class LinebotController < ApplicationController
      protect_from_forgery except: :sort
    
      # ルーティングで設定したcallbackアクションを呼び出す
      def callback
        body = request.body.read
        events = client.parse_events_from(body)
    
        events.each { |event|
          require "date"
          require 'nokogiri'
          require 'open-uri'
    
          #時刻表示を 時:分 に指定
          now = DateTime.now
          nowTime = now.strftime("%H:%M")
             :
         # 下記に記載
             :
          case event
          when Line::Bot::Event::Message
            case event.type
            when Line::Bot::Event::MessageType::Text
              message = {
                type: 'text',
                text: response
              }
              client.reply_message(event['replyToken'], message)
            when Line::Bot::Event::MessageType::Image, Line::Bot::Event::MessageType::Video
              response = client.get_message_content(event.message['id'])
              tf = Tempfile.open("content")
              tf.write(response.body)
            end
          end
        }
        "OK"
      end
    end
    

    linebot_controller.rb
        :
    # 1 を入力した時のアクション(DBからデータ取得)
    if event.message["text"].include?("1")
      nextBus = BusTimetableKaiseiSt.all
      nextBusKaisei = []
      nextBus.each do |nextBus|
        time = nextBus.time.strftime("%H:%M")
        if time >= nowTime
          nextBusKaisei << time
        end
    end
    #DBから現在時刻を起点に直近の3つのバスの時刻を出力
    response = 
        "開成発"+nextBusKaisei[0]+"\n
        Next "+nextBusKaisei[1]+"\n
        "+nextBusKaisei[2]+"\n\n\n
        ↓↓番号を選択↓↓\n
        1. 開成駅→会社(シャトルバス)\n
        2. 会社→開成駅(シャトルバス)\n
        3. 電車の運行状況\n
        4. 会社周辺の天気\n
        5. 東京の天気\n\n
        ※半角数字でお願いします。"
    
    # 2 を入力した時のアクション(DBからデータ取得)
      # 流れは上記と同様なので、割愛
    
    # 3 を入力した時のアクション(スクレイピングでデータ取得)
    elsif event.message["text"].include?("3")
      urlOdakyu = 'https://www.odakyu.jp/cgi-bin/user/emg/emergency_bbs.pl'
      charset = nil
      htmlOdakyu = open(urlOdakyu) do |f|
        charset = f.charset
        f.read
      end
    
      docOdakyu = Nokogiri::HTML.parse(htmlOdakyu, nil, charset)
      docOdakyu.xpath('//div[@id="pagettl"]').each do |node|
        #スクレイピング情報の出力
        response = 
          node.css('p').inner_text+"\n\n\n
          ↓↓番号を選択↓↓\n
          1. 開成駅→会社(シャトルバス)\n
          2. 会社→開成駅(シャトルバス)\n
          3. 電車の運行状況\n
          4. 会社周辺の天気\n
          5. 東京の天気\n\n
          ※半角数字でお願いします。"
      end
    
    # 4 を入力した時のアクション(スクレイピングでデータ取得)
      # 方法は上記と同様なので、割愛
    
    # 5 を入力した時のアクション(スクレイピングでデータ取得)
      # 方法は上記と同様なので、割愛
    
    # 上記以外を入力した時のアクション
    else
      response =
          "↓↓番号を選択↓↓\n
          1. 開成駅→会社(シャトルバス)\n
          2. 会社→開成駅(シャトルバス)\n
          3. 電車の運行状況\n
          4. 会社周辺の天気\n
          5. 東京の天気\n\n
          ※半角数字でお願いします。"
    end
           :
    

    좋은 웹페이지 즐겨찾기