LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

Node.jsでLINE Botを作るハンズオン資料 β版

Last updated at Posted at 2017-02-05

はじめに

この資料は【優勝賞金1000万】 Node.jsでLINE Botを作るハンズオン!での利用を想定して作っています。

ハンズオンをしたフィードバックをもとにアップデート予定です。

LINE BOT AWARDS

別資料参照

LINE Messaging API

LINE Botを作るためのAPIです。

Messaging APIを利用することで、あなたのサービスをLINEのトークルーム上で提供することができます。
LINEユーザー一人一人に合わせたユーザー体験を提供できます。

スクリーンショット 2017-02-05 9.10.01.png

Push APIとReply API

スクリーンショット 2017-02-05 9.09.22.png

基本的にPushとReplyの二つのAPIがあります。

PushはBotアプリケーション側が起点となってユーザーにアクションします。
Replyはユーザーの発言を起点としてBotアプリケーションを呼び出します。

作ってみよう

1. ビジネスアカウント(Botアカウント)を作成する

LINEのBotはTwitterなどと違い、通常のユーザーのアカウントをBot化することは出来ず、ビジネスアカウントと呼ばれるアカウントを作る必要があります。

まずはLINE Business CenterのMessagin APIのページにアクセスします。

二つあるボタンの右側の 「Developer Trialを始める」を選択しましょう。

ちなみに、「Messaging APIを始める」を選択するとPush APIを試すことが出来ないで注意しましょう。

LINEへのログイン

LINEアカウントへのログインを求められます。

設定がまだの方はLINEのスマートフォンアプリ画面から 設定>アカウントでメールアドレスを設定できます。

会社/事業者の登録

ポップアップが出るので、 「会社/事業者を選択」を選択します。

ビジネスアカウントということで、事業者の選択を行います。

「会社/事業者を追加する」を選択します。(既に事業者追加をしている場合は事業者名を選択して次に進みましょう)

入力画面に進むので必要な情報を入力します。 法人/個人の選択はどちらでも問題ありません。この資料を見ている人は個人開発の場合が多いと思いますので個人を選択して問題ありません。

ビジネスアカウントの登録

登録した会社名or事業者名を選択します。

スクリーンショット 2017-02-05 9.33.59.png

アカウント名を入力、業種を選び 「確認する」 を選択します。 画像はこの時点では無くても大丈夫です。アカウント名も後から変更可能です。

確認画面に遷移するので 「申し込む」を選択します。申し込みという表現になってますが次の画面で開設が出来ます。

これでビジネスアカウントの開設が出来ました。

2. ビジネスアカウントのBot設定

ここまでで、ビジネスアカウントの開設が完了しています。

次にアカウントの設定を行うため、申し込み完了画面下部の 「LINE@MANAGERへ」を選択しましょう。

一度閉じてしまった場合は、アカウントリストからLINE@MANAGERの画面に遷移できます。

ビジネスアカウントのBot化

LINE@MANAGERのトップページは以下のような画面になります。

サイドバーの アカウント設定 > Bot設定 と進み、 「APIを利用する」を選択しましょう。

注意メッセージが表示されますが、特に気にせず 「確認」を選択します。

「OK」を押しましょう。

これでビジネスアカウントをBotとして利用することが出来ます。

Botの基本設定

Bot設定の画面に遷移します。 利用可能なAPIの項目にREPLY_MESSAGEPUSH_MESSAGEの二つが表示されていることを確認して下さい。

一番最初の画面の選択で 「Developer Trialを始める」を選択していれば大丈夫ですが、 「Messaging APIを始める」を選択していた場合はREPLY_MESSAGEしか利用できません。

また設定ですが、スクリーショットと同様にWebhook送信を「利用する」にしましょう。
その他は以下の設定にしておくことをオススメします。

  • Webhook送信: 利用する
  • Botのグループトーク参加: 利用する
  • 自動応答メッセージ: 利用しない
  • 友達追加時あいさつ: 利用する

設定が終わったら「保存」を選択しましょう。

Botと友達になろう

Bot設定画面のステータスの項目にある「LINE Developersで設定する 」を選択します。

スクリーンショット 2017-02-05 10.23.57.png

新規タブで 「LINE Developers」の画面が開きます。

ここで表示されるChannel SecretChannel Access Tokenは後で利用するので留意しておいて下さい。

また、ここで表示されるQRコードを使って、自分が作成したBotアカウントと友達になりましょう。

ここからはスマートフォン画面のキャプチャになります。QRコードリーダーを開きBotを友達追加しましょう。

「追加」を選択します。

「同意する」を選択します。

Botが友達に追加されました。

3. Node.jsでBot開発

ここからは手元のターミナルなどでの作業がメインになります。
Node.jsのインストールがまだの方は参考記事などをもとに準備をして下さい。

ちなみに、筆者の環境は以下になります。

  • Node.js v7.4.0
  • npm v4.0.5
  • macOS Sierra 10.12

プロジェクトを作成とハローワールド

Node.jsのプロジェクトはpackage.jsonがあるディレクトリが起点となります。
まずはnpm initコマンドpackage.jsonを作成します。

$ npm init --yes
Wrote to /Users/n0bisuke/dotstudio/2_events/20170127gs/linesample/package.json:

{
  "name": "linesample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

次にプログラムのメインとなるapp.jsを作成します。

$ touch app.js

ディレクトリ内にapp.jspackage.jsonの二つのファイルがあることを確認しましょう。

$ ls
app.js       package.json

エディタで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];
        console.log(WebhookEventObject);        
        //メッセージが送られて来た場合
        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('su');
    });

}).listen(PORT);

console.log(`Server running at ${PORT}`);

先ほどの「LINE developers」の画面で表示されたChannel SecretChannel Access Tokenをソースコードのconst CH_SECRET = 'xxxxxxxx';const CH_ACCESS_TOKEN = 'xxxxxx';の部分に値を指定しましょう。

ここで表示されるChannel SecretChannel Access Tokenは後で利用するので留意しておいて下さい。

アプリケーションを起動してみます。

$ node app.js

起動すると以下がターミナルに表示されます。

Server running at 3000

http://localhost:3000/にブラウザでアクセスしてこんにちはと表示されることを確認しましょう。

ngrokでトンネリング

LINE Botを作るためにはWebhookURLをLINE developersに登録する必要があります。
PaaSなどにホスティングして試すのはデバッグが大変なので、手元のPCのlocalhostにグローバルからアクセスできるようにするトンネリングツールを利用しましょう。

などが有名です。今回はngrokを利用します。

まずはngrokのサイトにいき、本体をダウンロードして解凍します。

Macの場合、デフォルトでダウンロードフォルダ(~/Downloads)に保存されます。

$ cd ~/Downloads
$ pwd
/Users/n0bisuke/Downloads
$ ./ngrok help

エラーが出なければOKです。

次に、ngrokにユーザー登録をします。サイトからユーザー登録をしましょう。
ユーザー登録後のページで表示されるコマンドを実行します。

$ ./ngrok authtoken xxxxxxxxxxxxxxxxxxx
Authtoken saved to configuration file: /Users/n0bisuke/.ngrok2/ngrok.yml

これでユーザーアカウントとPCが紐付けられます。この作業は最初の1回だけです。

次にトンネリングサーバーを起動します。 ./ngrok http ポート名と指定します。
今回はNode.jsアプリケーションを3000番ポートで利用するので3000を指定しましょう。

$ ./ngrok http 3000
ngrok by @inconshreveable                                    (Ctrl+C to quit)

Session Status                online
Account                       n0bisuke (Plan: Free)
Version                       2.1.18
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://1148cbd9.ngrok.io -> localhost:3000
Forwarding                    https://1148cbd9.ngrok.io -> localhost:3000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00

起動すると表示されるhttps://で始まるアドレスを利用します。
この場合はhttps://1148cbd9.ngrok.ioです。このアドレスは起動の度に変わるため、ngrokは起動し続けて開発を行うことをおススメします。

以下のように、ngrokのプロセスとNode.jsアプリケーションのプロセスを並行して実行しての開発になります。

おうむ返しBot

LINE developersの画面下部の 「EDIT」を選択します。

ここに先ほどのアドレスを指定しましょう。

この状態でBotに話しかけてみて下さい。

Botがおうむ返ししてくれます。

また、おうむ返しの際にコンソールに表示される、userIdをメモしておきましょう。

4. 電車の遅延状況お知らせBot

電車の遅延情報を取得するやり方は色々ありますが、今回はこちらのTwitterBotから情報取得するBotを作ります。

このアカウントは#TrainDelayというハッシュタグをつけて投稿しています。
この情報を取得しましょう。

まずは、Twitterを利用するためのnpmモジュールを追加します。

$ npm i --save twitter

次にtwitterのアクセストークンを取得します。

ちなみに先に電話番号認証しておけよなって言われます。

Error
You must add your mobile phone to your Twitter profile before creating an application. Please read https://support.twitter.com/articles/110250-adding-your-mobile-number-to-your-account-via-web for more information.

作れたらPermissionsを変えましょう。

keys and Access TokensからAccess Tokenを生成しましょう。以下の4つのキーを利用します。

アプリケーションのキー
* Consumer Key (API Key)
* Consumer Secret (API Secret)

アプリケーションに登録するユーザーのキー
* Access Token
* Access Token Secret

試しにツイート取得してみる

新規でtweet.jsなどを作成し、以下を実行してみましょう。

tweet.js
'use strict';

const Twitter = require('twitter');
const tw = new Twitter({
  consumer_key: '',
  consumer_secret: '',
  access_token_key: '',
  access_token_secret: ''
});

tw.stream('statuses/filter', {'track': '#botawards'}, function(stream) {
  stream.on('data', function (data) {
    console.log(data.text);
  });
});

どうでしょうか? #botawardsのハッシュタグが投稿されると反応があると思います。

PUSH通知の仕組みを作ってみる

app.jsを編集してみましょう。

https://devdocs.line.me/ja/#messaging-api を見ながらAPIを確認してみましょう。

まずはPATHを追加します。

const PUSH_PATH = '/v2/bot/message/multicast'; //push用

pushClientという関数を定義します。

const pushClient = (userId, SendMessageObject) => {    
    let postDataStr = JSON.stringify({ to: userId, messages: SendMessageObject });
    let options = {
        host: HOST,
        port: 443,
        path: PUSH_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();
    });
};

呼び出してみます。

let pushSendMessageObject = [{type: 'text',text: 'こんにちは'}];
pushClient([`自分のUserIDを指定`],pushSendMessageObject)
.then((body)=>{
    console.log(body);
},(e)=>{console.log(e)});

ここまでで実行するとアプリケーションを立ち上げた時点でこんにちはBot側から話しかけてきます。

Twitterとプッシュ通知を連携させる

app.jstweet.jsを組み合わせて通知を作ってみます。

チャレンジしてみましょう。

ここまでの内容を組み合わせると...通知Botができますね。
もくもくタイムでソース公開します。

完成コード

以下のapp.jsが完成形となります。

app.js
'use strict';

const http = require('http');
const https = require('https');
const crypto = require('crypto');
const Twitter = require('twitter');
//Twitterから4つのキーを指定
const tw = new Twitter({
  consumer_key: '', 
  consumer_secret: '',
  access_token_key: '',
  access_token_secret: ''
});

const TARGET_HASHTAG = '#TrainDelay'; //ハッシュタグを指定
const MY_USERID = ''; //LINEのユーザーIDを指定
const HOST = 'api.line.me'; 
const REPLY_PATH = '/v2/bot/message/reply';//リプライ用
const PUSH_PATH = '/v2/bot/message/multicast'; //push用
const CH_SECRET = ''; //Channel Secretを指定
const CH_ACCESS_TOKEN = ''; //Channel Access Tokenを指定
const SIGNATURE = crypto.createHmac('sha256', CH_SECRET);
const PORT = 3000;

/**
 * httpリクエスト部分
 */
const replyClient = (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();
    });
};

const pushClient = (userId, SendMessageObject) => {    
    let postDataStr = JSON.stringify({ to: userId, messages: SendMessageObject });
    let options = {
        host: HOST,
        port: 443,
        path: PUSH_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();
    });
};

tw.stream('statuses/filter', {'track': TARGET_HASHTAG}, (stream) => {
  stream.on('data', (data) => {
      let pushSendMessageObject = [{type: 'text',text: data.text}];
        pushClient([MY_USERID],pushSendMessageObject)
        .then((body)=>{
            console.log(`「${data.text}」をプッシュ通知成功`);            
        },(e)=>{console.log(e)});
  });
});

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];   
        console.log(WebhookEventObject);     
        //メッセージが送られて来た場合
        if(WebhookEventObject.type === 'message'){
            let SendMessageObject;
            if(WebhookEventObject.message.type === 'text'){
                SendMessageObject = [{
                    type: 'text',
                    text: WebhookEventObject.message.text
                }];
            }
            replyClient(WebhookEventObject.replyToken, SendMessageObject)
            .then((body)=>{
                console.log(body);
            },(e)=>{console.log(e)});
        }

        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('su');
    });

}).listen(PORT);

console.log(`Server running at ${PORT}`);

5. ホスティングする

Herokuに作成したBotアプリケーションをホスティングしましょう。
アカウントを持っていない人はアカウント作成をして下さい。

以下からダッシュボードに移動します。

New > Create new appを選択します。

任意のApp Nameを入力して 「Create App」を選択します。
ちなみにアンダースコア(_)は使えないようです。

アプリケーションが作成されるとアプリケーションの管理画面が表示されます。

今回はn0bisuke-gs-lineというアプリケーションにしました。

ファイルの追加とソースコードの修正

プロジェクト内で.gitignoreという名前のファイルを作成し、以下を記述します。

node_modules

また、Procfileという名前のファイルを作成し、以下を記述します。

web: node app.js

app.jsconst PORT = 3000;を以下に変更します。

const PORT = process.env.PORT || 3000;

参考までに、この時点でのファイル構成は以下のようになっています。

$ ls -a
.            .gitignore   node_modules
..           Procfile     package.json
.git         app.js       tweet.js

Herokuコマンドでデプロイしてみよう

こちらからHerokuコマンドを取得します。

$ brew install heroku

brewコマンドが使えない場合(command not found: brewとか出る場合)は参考記事からインストールして下さい。

あとはダッシュボードの記述通りに進めます。

$ heroku login

メールアドレスとパスワードを求められるので、Herokuにユーザー登録した際の値を入力しましょう。
パスワード入力の際には表示はされません。

Enter your Heroku credentials.
Email: (メールアドレスを入力)
Password (typing will be hidden): (パスワードを入力)
Logged in as xxxxxxxx@xxx.xxx

作成しているBotアプリケーションのフォルダに移動しておいて下さい。
gitリポジトリを登録します。

$ git init
$ heroku git:remote -a n0bisuke-gs-line (Herokuのアプリケーション名)

デプロイ実行

$ git add .
$ git commit -am "make it better"
$ git push heroku master
Counting objects: 727, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (663/663), done.
Writing objects: 100% (727/727), 855.00 KiB | 0 bytes/s, done.
Total 727 (delta 143), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:

省略...

remote: Verifying deploy.... done.
To https://git.heroku.com/n0bisuke-gs-line.git
 * [new branch]      master -> master

これでhttps://Herokuのアプリケーション名.herokuapp.com/にアクセスするとこんにちはと表示され、デプロイが完了です。

https://n0bisuke-gs-line.herokuapp.com/

最後に LINE developersの画面でWebhook URLを先ほどngrokのアドレスにしていましたが、Herokuのアドレスに差し替えましょう。

VERIFYを押して、 Successと表示されれば問題なく動作しています。

ここまでで、作成したアプリケーションのホスティングが完了です。
手元のNode.jsプロセスを終了してもBotは稼働してくれます。

付録: SSH Keyの設定

アプリケーションをデプロイするにあたりPCのSSH KeyをHerokuに登録する必要があります。

まずは.sshフォルダを作ります。既にある人は大丈夫です。
ls ~/.sshでファイルが表示される人はここはスキップして下さい。

$ cd
$ ls .ssh
ls: .ssh: No such file or directory
$ mkdir .ssh

.sshフォルダに移動して、ssh-keygenコマンドを実行します。

$ cd .ssh
$ ssh-keygen
ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/nobisuke/.ssh/id_rsa): (エンターキー入力)
Enter passphrase (empty for no passphrase): (エンターキー入力)
Enter same passphrase again: (エンターキー入力)
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
...
The key's randomart image is:
+--[ RSA 2048]----+
|  .o     ..      |
|  . + o  ..      |
| . . B o .+      |
|. . * .oo= o     |
| o o   .S o      |
|E        +       |
|.       .        |
|                 |
|                 |
+-----------------+

全てエンターで進めます。
id_rsaid_rsa.pubが作成されます。

$ ls
id_rsa id_rsa.pub

id_rsa.pubの中身を確認します。

$ more id_rsa.pub

長い文字列が表示されます。
この文字列をHerokuの設定画面で登録しましょう。

0

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