HapiでLINE Botをサクッと試すでやったオウム返し
を依存モジュール無しでやってみます。
先に完成コード
- Channel Secret
- Channel Access Token
を指定しましょう。
外部依存は無いのでnpm i
はいりません。ちなみにNode.jsはv7.0.0です。
app.js、コピペで使えます。
'use strict';
const http = require('http');
const https = require('https');
const crypto = require('crypto');
const HOST = 'api.line.me';
const REPLY_PATH = '/v2/bot/message/reply';//リプライ用
const CH_SECRET = 'xxxxxxxx'; //Channel Secretを指定
const CH_ACCESS_TOKEN = 'xxxxxx'; //Channel Access Tokenを指定
const SIGNATURE = crypto.createHmac('sha256', CH_SECRET);
const PORT = 3000;
/**
* httpリクエスト部分
*/
const client = (replyToken, SendMessageObject) => {
let postDataStr = JSON.stringify({ replyToken: replyToken, messages: SendMessageObject });
let options = {
host: HOST,
port: 443,
path: REPLY_PATH,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'X-Line-Signature': SIGNATURE,
'Authorization': `Bearer ${CH_ACCESS_TOKEN}`,
'Content-Length': Buffer.byteLength(postDataStr)
}
};
return new Promise((resolve, reject) => {
let req = https.request(options, (res) => {
let body = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
resolve(body);
});
});
req.on('error', (e) => {
reject(e);
});
req.write(postDataStr);
req.end();
});
};
http.createServer((req, res) => {
if(req.url !== '/' || req.method !== 'POST'){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('');
}
let body = '';
req.on('data', (chunk) => {
body += chunk;
});
req.on('end', () => {
if(body === ''){
console.log('bodyが空です。');
return;
}
let WebhookEventObject = JSON.parse(body).events[0];
//メッセージが送られて来た場合
if(WebhookEventObject.type === 'message'){
let SendMessageObject;
if(WebhookEventObject.message.type === 'text'){
SendMessageObject = [{
type: 'text',
text: WebhookEventObject.message.text
}];
}
client(WebhookEventObject.replyToken, SendMessageObject)
.then((body)=>{
console.log(body);
},(e)=>{console.log(e)});
}
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('success');
});
}).listen(PORT);
console.log(`Server running at ${PORT}`);
解説っぽいもの
おうむ返しまでの流れはこんな感じのイメージです。
BOTのWebhookURLをLINE Messaging API側に登録
- ユーザーが発言`
LINE Messaging APIからWebhook Event ObjectがBOTに送信される
BOT側からReply Message APIを叩きます。この時に認証情報が必要です。
- BOTが返信
Webhook Event Object
LINEのサーバー側から送られてくるBOTに対するイベント(メッセージを受信、友達追加など)の情報です。
Reply Messsage API
指定した宛先にメッセージを返信するAPIです。
Webhook Event Object
で送られてくる情報の中からreplyToken
を取得して返信宛先とします。
この時以下の認証が発生します。
認証部分について
Signature Valdation
リクエストの送信元がLINEであることを確認するために署名検証を行わなくてはなりません。
各リクエストには X-Line-Signature ヘッダが付与されています。
X-Line-Signature ヘッダの値と、Request Body と Channel Secret から計算した Signature が同じものであることをリクエストごとに 必ず検証してください。
検証は以下の手順で行います。
Channel Secretを秘密鍵として、HMAC-SHA256アルゴリズムによりRequest Bodyのダイジェスト値を得る。
ダイジェスト値をBASE64エンコードした文字列が、Request Headerに付与されたSignatureと一致することを確認する。
とあるのでcryptoモジュールを使ってSIGNATUREを生成し、X-Line=Signatureヘッダーの値に指定します。
・
・
(中略)
・
・
const SIGNATURE = crypto.createHmac('sha256', CH_SECRET);
・
・
(中略)
・
・
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'X-Line-Signature': SIGNATURE,
'Authorization': `Bearer ${CH_ACCESS_TOKEN}`
}
・
・
(中略)
・
・
おわりに
Node.jsでは公式LINE SDKが出て無いですが、仕組みを把握できると、Node.jsでもけっこうやりたいことがやれる気がしますね。
早くNode.jsむけのSDKがでるといいなぁ。
追記: 2017/01/25
リクエストヘッダーに以下の行を追加しました。
'Content-Length': Buffer.byteLength(postDataStr)
追加してないとサーバーによっては(Azure WebAppsなんですけど)うまく動作しませんでした。
ちゃんとContent-Length
指定しましょう...汗
https://nodejs.org/api/http.html#http_http_request_options_callback