LoginSignup
48

More than 5 years have passed since last update.

LINE BOTをNode.jsで外部依存モジュールを使用せずに作る

Last updated at Posted at 2016-11-19

HapiでLINE Botをサクッと試すでやったオウム返しを依存モジュール無しでやってみます。

先に完成コード

  • Channel Secret
  • Channel Access Token

を指定しましょう。

外部依存は無いのでnpm iはいりません。ちなみにNode.jsはv7.0.0です。

app.js、コピペで使えます。

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}`);

解説っぽいもの

おうむ返しまでの流れはこんな感じのイメージです。

  1. BOTのWebhookURLをLINE Messaging API側に登録
  2. ユーザーが発言`
  3. LINE Messaging APIからWebhook Event ObjectがBOTに送信される
  4. BOT側からReply Message APIを叩きます。この時に認証情報が必要です。
  5. BOTが返信

Webhook Event Object

LINEのサーバー側から送られてくるBOTに対するイベント(メッセージを受信、友達追加など)の情報です。

Reply Messsage API

指定した宛先にメッセージを返信するAPIです。

Webhook Event Objectで送られてくる情報の中からreplyTokenを取得して返信宛先とします。
この時以下の認証が発生します。

認証部分について

https://devdocs.line.me/ja/#webhook

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48