Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8691

LINE WORKS Bot APIをひと通り触ってみる(node.js)#1

$
0
0

最近、アンタッチャブルが奇跡の復活を遂げましたね。嬉しい限りです。:grin:
どうも@shotamako初めての投稿 & LINEWORKS Advent Calendar 2019 / 20日目の記事です。

本記事では、LINE WORKS Bot のメッセージ受信 APIをnode.jsでひと通り触ってみたいと思います。

0. はじめに

記事の流れになります。

  1. こんなの作ります
  2. 環境準備
  3. 作ってみる
  4. 動かしてみる
  5. まとめ

1. こんなの作ります

LINE WORKS Botのメッセージ受信(callback)には下表のタイプが存在し、そのタイプによって送信するメッセージを切替えるBotを作ります。

callbackタイプ説明
messageメンバーからのメッセージ
joinBot が複数人トークルームに招待された
leaveBot が複数人トークルームから退室した
joinedメンバーが Bot のいるトークルームに参加した
leftメンバーが Bot のいるトークルームから退室した
postbackpostback タイプのメッセージ

Botの利用開始 (message)

04_welcomebot.png

メンバーからのメッセージを受信 (message)

05_sendmessage.png

Bot が複数人トークルームに招待された (join)

13_addpanda.png

Bot が複数人トークルームから退室した (leave)

Botがトークルームから退室したコールバックのため、メッセージを送信することはできません。

メンバーが Bot のいるトークルームに参加した (joined)

10_addpanda.png

メンバーが Bot のいるトークルームから退室した (left)

09_outpanda.png

postback タイプのメッセージ (postback)

postbackは、次回の記事で書きま〜す。

2. 環境準備

まずは LINE WORKS Bot API の利用準備と開発環境を整えたいと思います。

LINE WORKS Bot APIの利用準備

  1. LINE WORKS の Developer Console で(今回開発する)Botサーバーが LINE WORKS と通信するために必要な接続情報の発行とBotの登録を行います。
    ↓こちらの記事を参考に作業していただければと思います。
    LINE WORKSで初めてのBot開発!(前編)の「Developer ConsoleでAPIを使うための設定とBotを登録する
    ※Bot登録の際に指定する Callback URL は、ngrokを利用して取得するとローカルデバッグができるのでとっても便利です。
    (記事:ローカル環境で LINEWORKS Bot を動かす話が大変参考になりました)

  2. LINE WORKS の管理画面で、Developer Console で登録したBotをメンバーが利用できる様に設定します。
    ↓こちらの記事を参考に作業していただければと思います。
    LINE WORKSで初めてのBot開発!(後編)の「Botを公開し利用する

開発環境

  • VS Code:IDE
  • node.js+Express:Botサーバー
  • dotenv:アプリケーションの環境変数定義
  • ngrok:ローカルデバッグ

node.jsでいろいろpackageを利用してますが省略します。

4. 作ってみる

まずはメインのjs (server.js)

server.jsでは、リクエストの受け口、改竄防止や LINE WORKS の Access token を取得するプログラムを書いてます。
(Access token をちゃんと管理してません。近々に対応策を書こうと思います。。。DBが必要になるな〜。。。)
あと、BotMessageServiceクラスのsendメソッドでメッセージ送受信を制御してます。

server.js
constexpress=require('express');constapp=express();require('dotenv').config();constcrypto=require('crypto');constjwt=require('jsonwebtoken');constrequest=require('request');constBotMessageService=require('./BotMessageService');varport=process.env.PORT||3000app.listen(port,function(){console.log('To view your app, open this link in your browser: http://localhost:'+port);});app.use(express.json({verify:(req,res,buf,encoding)=>{// メッセージの改ざん防止constdata=crypto.createHmac('sha256',process.env.API_ID).update(buf).digest('base64');constsignature=req.headers['x-works-signature'];if(data!==signature){throw'NOT_MATCHED signature';}}}));/* 
* 疎通確認API
*/app.get('/',function(req,res){res.send('起動してます!');});/**
 * LINE WORKS からのメッセージを受信するAPI
 */app.post('/callback',asyncfunction(req,res,next){res.sendStatus(200);try{constserverToken=awaitgetServerTokenFromLineWorks();constbotMessageService=newBotMessageService(serverToken);awaitbotMessageService.send(req.body);}catch(error){returnnext(error);}});/** 
 * JWTを作成します。
 * @return {string} JWT
 */functioncreateJWT(){constiss=process.env.SERVER_ID;constiat=Math.floor(Date.now()/1000);constexp=iat+60;constcert=process.env.PRIVATE_KEY;returnnewPromise((resolve,reject)=>{jwt.sign({iss:iss,iat:iat,exp:exp},cert,{algorithm:'RS256'},(error,jwtData)=>{if(error){console.log('createJWT error')reject(error);}else{resolve(jwtData);}});});}/**
 * LINE WORKS から Serverトークンを取得します。
 * @return {string} Serverトークン
 */asyncfunctiongetServerTokenFromLineWorks(){constjwtData=awaitcreateJWT();// 注意:// このサンプルでは有効期限1時間のServerトークンをリクエストが来るたびに LINE WORKS から取得しています。// 本番稼働時は、取得したServerトークンを NoSQL データベース等に保持し、// 有効期限が過ぎた場合にのみ、再度 LINE WORKS から取得するように実装してください。constpostdata={url:`https://authapi.worksmobile.com/b/${process.env.API_ID}/server/token`,headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',},form:{grant_type:encodeURIComponent('urn:ietf:params:oauth:grant-type:jwt-bearer'),assertion:jwtData}};returnnewPromise((resolve,reject)=>{// LINE WORKS から Serverトークンを取得リクエストrequest.post(postdata,(error,response,body)=>{if(error){console.log('getServerTokenFromLineWorks error');reject(error);}else{resolve(JSON.parse(body).access_token);}});});}

メッセージの送受信制御 (BotMessageService.js)

ベタ書きですが、BotMessageServiceクラスの_getResponse(callbackEvent)メソッドでメッセージの受信内容を解釈して、送信するメッセージ内容を決定してます。
実際 LINE WORKSにメッセージを送信しているところは、send(callbackEvent)メソッドです。

BotMessageService.js
constrequest=require('request');constCALL_BACK_TYPE={message:'message',join:'join',leave:'leave',joined:'joined',left:'left',postback:'postback',};/**
 * BotMessageServiceクラス
 */module.exports=classBotMessageService{/**
   * BotMessageServiceを初期化します。
   * @param {string} serverToken Serverトークン
   */constructor(serverToken){this._serverToken=serverToken;}/**
   * LINE WORKS にBotメッセージを送信します。
   * @param {object} callbackEvent リクエストのコールバックイベント
   */asyncsend(callbackEvent){letres=this._getResponse(callbackEvent);if(!res){return;}returnnewPromise((resolve,reject)=>{// LINE WORKS にメッセージを送信するリクエストrequest.post(this._createMessage(res),(error,response,body)=>{if(error){console.log('BotService.send error')console.log(error);}console.log(body);// 揉み消してます!resolve();});});}/**
   * LINE WORKS に送信するBotメッセージを作成して返します。
   * @param {object} res レスポンスデータ
   */_createMessage(res){return{url:`https://apis.worksmobile.com/${process.env.API_ID}/message/sendMessage/v2`,headers:{'Content-Type':'application/json;charset=UTF-8',consumerKey:process.env.CONSUMER_KEY,Authorization:`Bearer ${this._serverToken}`},json:res};}/**
   * メンバーIDを連結して返します。
   * @param {Array} memberList メンバーリスト
   */_buildMember(memberList){letresult='';if(memberList){memberList.forEach(m=>{if(result.length>0){result+=',';}result+=m;});}returnresult;}/**
   * Bot実装部
   * @param {object} callbackEvent リクエストのコールバックイベント
   * @return {string} レスポンスメッセージ
   */_getResponse(callbackEvent){console.log(callbackEvent);letres={botNo:Number(process.env.BOT_NO),};if(callbackEvent.source.roomId){// 受信したデータにトークルームIDがある場合は、送信先にも同じトークルームIDを指定します。res.roomId=callbackEvent.source.roomId;}else{// トークルームIDがない場合はBotとユーザーとの1:1のチャットです。res.accountId=callbackEvent.source.accountId;}switch(callbackEvent.type){caseCALL_BACK_TYPE.message:// メンバーからのメッセージif(callbackEvent.content.postback=='start'){// メンバーと Bot との初回トークを開始する画面で「利用開始」を押すと、自動的に「利用開始」というメッセージがコールされるconsole.log(`start`);res.content={type:'text',text:'ト〜クルームに〜〜。ボトやまが〜くる〜!'};returnres;}console.log(CALL_BACK_TYPE.message);res.content={type:'text',text:'からの〜〜〜。'};break;caseCALL_BACK_TYPE.join:// Bot が複数人トークルームに招待された// このイベントがコールされるタイミング//  ・API を使って Bot がトークルームを生成した//  ・メンバーが Bot を含むトークルームを作成した//  ・Bot が複数人のトークルームに招待された// ※メンバー1人と Bot のトークルームに他のメンバーを招待したらjoinがコールされる(最初の1回だけ)//  招待したメンバーを退会させ、再度他のメンバーを招待するとjoinedがコールされるこれ仕様?//  たぶん、メンバー1人と Botの場合、トークルームIDが払い出されてないことが原因だろう。。。console.log(CALL_BACK_TYPE.join);res.content={type:'text',text:'うぃーん!'};break;caseCALL_BACK_TYPE.leave:// Bot が複数人トークルームから退室した// このイベントがコールされるタイミング//  ・API を使って Bot を退室させた//  ・メンバーが Bot をトークルームから退室させた//  ・何らかの理由で複数人のトークルームが解散したconsole.log(CALL_BACK_TYPE.leave);break;caseCALL_BACK_TYPE.joined:{// メンバーが Bot のいるトークルームに参加した// このイベントがコールされるタイミング//  ・Bot がトークルームを生成した//  ・Bot が他のメンバーをトークルームに招待した//  ・トークルームにいるメンバーが他のメンバーを招待したconsole.log(CALL_BACK_TYPE.joined);res.content={type:'text',text:`${this._buildMember(callbackEvent.memberList)}いらっしゃいませ〜そのせつは〜`};break;}caseCALL_BACK_TYPE.left:{// メンバーが Bot のいるトークルームから退室した// このイベントがコールされるタイミング//  ・Bot が属するトークルームでメンバーが自ら退室した、もしくは退室させられた//  ・何らかの理由でトークルームが解散したconsole.log(CALL_BACK_TYPE.left);res.content={type:'text',text:`${this._buildMember(callbackEvent.memberList)}そうなります?`};break;}caseCALL_BACK_TYPE.postback:// postback タイプのメッセージ// このイベントがコールされるタイミング//  ・メッセージ送信(Carousel)//  ・メッセージ送信(Image Carousel)//  ・トークリッチメニュー// ※次回の記事で作り込みます。console.log(CALL_BACK_TYPE.postback);break;default:console.log('知らないコールバックですね。。。');returnnull;}returnres;}}

環境変数 (.env)

「LINE WORKS Bot APIの利用準備」で発行した接続情報を設定する。

.env
API_ID="API ID"
CONSUMER_KEY="Consumer key"
SERVER_ID="Server ID"
PRIVATE_KEY="認証キー"
BOT_NO="Bot No"

5. 動かしてみる

いざデバッグ開始!

1. VS Code のターミナルでプログラムが使用している node.js の package をインストール

VsCodeTerminal
npm install

2. デバッグボタン(F5)クリック!

vscode.png

3. http 3000 で ngrok 起動!

Terminal
ngrok http 3000

ngrok2.png
※ ForwardingもとのURLが変わった場合は、Developer Console で Botの Callback URL の変更を必ずしてください。

4. スマフォを手に持って LINE WORKS を動かす

アクター

  • Bot:ボトやま
  • メンバー1:栗井 (スマートフォンを操作している人)
  • メンバー2:パンダD

シナリオ1:ボトやまの利用を開始してみる (message)

01_roomlist.png
02_selectbot.png
03_openbot.png
04_welcomebot.png

想定通りのうごきですね。

シナリオ2:栗井からメッセージを送信してみる (message)

05_sendmessage.png

想定通りのうごきですね。

シナリオ3:栗井とパンダDのトークルームにボトやまを招待してみる (join)

11_kaproom.png
12_addmenber.png
02_selectbot.png
13_addpanda.png

想定通りのうごきですね。

シナリオ4:栗井/パンダD/ボトやまのトークルームからボトやまを退室させてみる (leave)

Botがトークルームから退室したコールバックのため、メッセージを送信することはできません。
(consoleログが出力されます)

シナリオ5:栗井とボトやまのトークルームにパンダDを招待してみる (joined)

06_addmenber.png
07_selectpanda.png

↓ あれ? Callback タイプが joined だと思いきや、join みたいですね。。。想定と違う。。。(なので、一度パンダDに退室してもらう)

08_openpanda.png

↓パンダD退室

09_outpanda.png

↓もう一度招待する

10_addpanda.png

↑これが想定通りの動き(なんでだろう。。。)

シナリオ6:栗井/パンダD/ボトやまのトークルームからパンダDを退室させてみる (left)

09_outpanda.png

想定通りの動き

6. まとめ

LINE WORKS Bot APIのメッセージ受信部分の動作をひと通り確認できました。(一部気になるところがありますが。。。)
今回作成たコードは GitHub の line-works-bot01-nodeで公開してま〜す。(issueがあればお知らせください。修正します。)

次回クリスマス記事もがんばります!メッセージ送信API!

Link


Viewing all articles
Browse latest Browse all 8691

Trending Articles