最近、アンタッチャブルが奇跡の復活を遂げましたね。嬉しい限りです。
どうも@shotamako初めての投稿 & LINEWORKS Advent Calendar 2019 / 20日目の記事です。
本記事では、LINE WORKS Bot のメッセージ受信 APIをnode.jsでひと通り触ってみたいと思います。
0. はじめに
記事の流れになります。
- こんなの作ります
- 環境準備
- 作ってみる
- 動かしてみる
- まとめ
1. こんなの作ります
LINE WORKS Botのメッセージ受信(callback)には下表のタイプが存在し、そのタイプによって送信するメッセージを切替えるBotを作ります。
callbackタイプ | 説明 |
---|---|
message | メンバーからのメッセージ |
join | Bot が複数人トークルームに招待された |
leave | Bot が複数人トークルームから退室した |
joined | メンバーが Bot のいるトークルームに参加した |
left | メンバーが Bot のいるトークルームから退室した |
postback | postback タイプのメッセージ |
Botの利用開始 (message)
![]() |
---|
メンバーからのメッセージを受信 (message)
![]() |
---|
Bot が複数人トークルームに招待された (join)
![]() |
---|
Bot が複数人トークルームから退室した (leave)
Botがトークルームから退室したコールバックのため、メッセージを送信することはできません。
メンバーが Bot のいるトークルームに参加した (joined)
![]() |
---|
メンバーが Bot のいるトークルームから退室した (left)
![]() |
---|
postback タイプのメッセージ (postback)
postbackは、次回の記事で書きま〜す。
2. 環境準備
まずは LINE WORKS Bot API の利用準備と開発環境を整えたいと思います。
LINE WORKS Bot APIの利用準備
LINE WORKS の Developer Console で(今回開発する)Botサーバーが LINE WORKS と通信するために必要な接続情報の発行とBotの登録を行います。
↓こちらの記事を参考に作業していただければと思います。
LINE WORKSで初めてのBot開発!(前編)の「Developer ConsoleでAPIを使うための設定とBotを登録する」
※Bot登録の際に指定する Callback URL は、ngrokを利用して取得するとローカルデバッグができるのでとっても便利です。
(記事:ローカル環境で LINEWORKS Bot を動かす話が大変参考になりました)LINE WORKS の管理画面で、Developer Console で登録したBotをメンバーが利用できる様に設定します。
↓こちらの記事を参考に作業していただければと思います。
LINE WORKSで初めてのBot開発!(後編)の「Botを公開し利用する」
開発環境
node.jsでいろいろpackageを利用してますが省略します。
4. 作ってみる
まずはメインのjs (server.js)
server.jsでは、リクエストの受け口、改竄防止や LINE WORKS の Access token を取得するプログラムを書いてます。
(Access token をちゃんと管理してません。近々に対応策を書こうと思います。。。DBが必要になるな〜。。。)
あと、BotMessageServiceクラスのsendメソッドでメッセージ送受信を制御してます。
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)メソッドです。
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の利用準備」で発行した接続情報を設定する。
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 をインストール
npm install
2. デバッグボタン(F5)クリック!
3. http 3000 で ngrok 起動!
ngrok http 3000
※ ForwardingもとのURLが変わった場合は、Developer Console で Botの Callback URL の変更を必ずしてください。
4. スマフォを手に持って LINE WORKS を動かす
アクター
- Bot:ボトやま
- メンバー1:栗井 (スマートフォンを操作している人)
- メンバー2:パンダD
シナリオ1:ボトやまの利用を開始してみる (message)
![]() |
---|
![]() |
---|
![]() |
---|
![]() |
---|
想定通りのうごきですね。
シナリオ2:栗井からメッセージを送信してみる (message)
![]() |
---|
想定通りのうごきですね。
シナリオ3:栗井とパンダDのトークルームにボトやまを招待してみる (join)
![]() |
---|
![]() |
---|
![]() |
---|
![]() |
---|
想定通りのうごきですね。
シナリオ4:栗井/パンダD/ボトやまのトークルームからボトやまを退室させてみる (leave)
Botがトークルームから退室したコールバックのため、メッセージを送信することはできません。
(consoleログが出力されます)
シナリオ5:栗井とボトやまのトークルームにパンダDを招待してみる (joined)
![]() |
---|
![]() |
---|
↓ あれ? Callback タイプが joined だと思いきや、join みたいですね。。。想定と違う。。。(なので、一度パンダDに退室してもらう)
![]() |
---|
↓パンダD退室
![]() |
---|
↓もう一度招待する
![]() |
---|
↑これが想定通りの動き(なんでだろう。。。)
シナリオ6:栗井/パンダD/ボトやまのトークルームからパンダDを退室させてみる (left)
![]() |
---|
想定通りの動き
6. まとめ
LINE WORKS Bot APIのメッセージ受信部分の動作をひと通り確認できました。(一部気になるところがありますが。。。)
今回作成たコードは GitHub の line-works-bot01-nodeで公開してま〜す。(issueがあればお知らせください。修正します。)
次回クリスマス記事もがんばります!メッセージ送信API!