Node.js
RaspberryPi
linebot
GoogleHome

人の発話以外のきっかけでGoogleHomeをしゃべらせたい その2(LINEのメッセージを読み上げる)

こちらの続きです。
人の発話以外のきっかけでGoogleHomeをしゃべらせたい その1

前回までのあらすじ

  • Raspberry Pi3 Model B をセットアップ済み
  • MacBook Air OSX Yosemite(10.10.5)
  • ラズパイにNode.jsをインストール済み
  • google-home-notifierが動くことをごくカンタンに確認済み
  • Macとラズパイはssh接続したmacのターミナル、またはVNC接続した画面越しに行う(ラズパイにマウスとキーボードをつないでいないため)

今回やりたいこと

LINEのメッセージを読み上げて欲しい!
幸い、ジャストヒットな記事があったのでこちらに沿ってやってみます。
LINEに送信したメッセージを、Google Homeで読み上げ、家族に通知
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest

1. LINEのbot(Messaging API)を作成する

IFTTTでLINEをトリガーにできればよかったのですが、どうやらできないようです。代わりにbotに話しかけることをトリガーするとのこと。

具体的な作成手順は参考にした記事の通りなので、割愛します。
ポイントは以下です。

  • プラン → Developer Trial
  • Webhook送信 → 利用する
  • 自動応答メッセージ → 利用しない
  • 作ったbotと友達になる

2. Node.jsのプロジェクト作成

今回のソースを保存しておく任意のディレクトリを作成します。

  • ラズパイに任意のディレクトリを作り、作業ディレクトリとする。
  • 作業ディレクトリ内でNode.jsの設定ファイルを作成する npm init
    • いろいろ聞かれるが適当にEnter連打でとりあえずOK
  • 作業ディレクトリ内でコマンド実行
npm install google-home-notifier ngrok @line/bot-sdk express

今回はgoogle-home-notifierの他にngrok, @line/bot-sdk, expressの3つも利用します。

そして、作業ディレクトリ内に以下のファイルを作製します。

server.js
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const googlehome = require('google-home-notifier');

// for express
const port = 3000; // ngrokのポートと合わせる
const app = express();

// for LINE
const config = {
  channelSecret: '', // Messaging APIのChannel Secret
  channelAccessToken: '' // Messaging APIのアクセストークン(ロングターム)
};
const replyPrefixText = 'メッセージを受け付けました : ';

// for googlehome
const language = 'ja';
googlehome.device('Google Home', language);
googlehome.ip('192.168.XXX.XXX', language); // google home のIPを設定する
const speakPrefixText = '新しいメッセージです。';


app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

function handleEvent(event) {
  if (!checkEvent(event)) {
    return Promise.resolve(null);
  }

  return googlehomeSpeak(event.message.text)
    .then(speakedText => lineReplyMessage(event.replyToken, speakedText));
}

function checkEvent(event){
  if(event.type !== 'message') return false;
  if(event.message.type !== 'text') return false;

  // LINE の Channel基本設定 から「接続確認」をするとreplyTokenが以下の固定値になるらしい
  // 実在しないreplyTokenなので応答しないようにfalseを返す
  if(event.replyToken === '00000000000000000000000000000000') return false;
  if(event.replyToken === 'ffffffffffffffffffffffffffffffff') return false;

  return true;
}

function googlehomeSpeak(text) {
  return new Promise((resolve, reject) => {
    googlehome.notify(speakPrefixText + text, (res) => {
      console.log('googlehome response : ' + res + ', speech_text : ' + text);
      resolve(text);
    });
  });
}

function lineReplyMessage(replyToken, text) {
  return client.replyMessage(replyToken, {
    type: 'text',
    text: replyPrefixText + text
  });
}


app.listen(port);
console.log(`Server running at ${port}`);

以下の記事を参考にさせていただきました。
LINEに送信したメッセージを、Google Homeで読み上げ、家族に通知
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest

promiseでgoogle homeでしゃべる→LINEに返信するという順番に動作するよう制御したので、LINEで返信が来る=google homeでしゃべっているということになります。

3. Messaging APIのセキュリティ情報をコピーする

上記のserver.jsはLINEのセキュリティ情報を設定する箇所があります。
先程作製したMessaging APIの「Channel基本設定」画面から以下の値をserver.jsにコピーします。

コピー元(LINEのChannel基本設定画面)

  • Channel Secret
  • アクセストークン(ロングターム) ※最初は未設定なのですぐ横のボタンで発行します



コピー先

server.js
// for LINE
const config = {
  channelSecret: '', // Messaging APIのChannel Secret
  channelAccessToken: '' // Messaging APIのアクセストークン(ロングターム)
};

これでserver.jsが完成です。

4. ngrokに会員登録しauthtokenを登録する

ngrokは会員登録せずに使うと8時間しか起動できません。なので、会員登録し、発行されるauthtokenを登録します。

  • https://ngrok.com/ から「Get started for free」ボタンを押して進める
  • 登録後に表示される「Setup & Installation」画面の「③Connect your account」にあるauthtokenをメモする。
  • 先程作った作業ディレクトリで以下のコマンドを実行する
npx ngrok authtoken XXXXXXXX

以下のようなログが出ればOK

Authtoken saved to configuration file: ~~~~~~~~~

XXXXXXXXはメモしたauthtokenです。
今回はngrokを-g(グローバル)ではなくローカルにインストールしているので、npxを使ってngrokコマンドを実行しています。

5. Node.jsのサーバーを起動する

作成したserver.jsを起動します。そして、ローカル起動したserver.jsをngrokで外部に公開します。

  • ssh接続したターミナルではなく、VNC接続してラズパイ本体でターミナルを2つ起動する。
  • 1つ目のターミナルでserver.jsがあるディレクトリに移動し、以下を実行する
node server.js

以下のようなログが出ればOK

*** WARNING *** The program 'node' uses the Apple Bonjour compatibility layer of Avahi.
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see <http://0pointer.de/avahi-compat?s=libdns_sd&e=node>
*** WARNING *** The program 'node' called 'DNSServiceRegister()' which is not supported (or only supported partially) in the Apple Bonjour compatibility layer of Avahi.
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see <http://0pointer.de/avahi-compat?s=libdns_sd&e=node&f=DNSServiceRegister>
Server running at 3000
  • 2つ目のターミナルでserver.jsがあるディレクトリに移動し、以下を実行する
npx ngrok http 3000

以下のようなログが出ればOK

Session Status                online                                            
Account                       <アカウント名> (Plan: Free)                                
Version                       2.2.8                                             
Region                        United States (us)                                
Web Interface                 http://127.0.0.1:4040                             
Forwarding                    http://XXXXXXXX.ngrok.io -> localhost:3000        
Forwarding                    https://XXXXXXXX.ngrok.io -> localhost:3000  

もし、ngrokを会員登録せずに実行した場合、Accountの行の代わりに以下のような表示になります。表示されているとおり8時間しか起動できません。

Session Expires               7 hours, 59 minutes             

6. LINEのMessaging APIにURLを設定

ngrokで設定したURLをLINEのAPIに設定します。

  • LINEのChannel基本設定画面のWebhook URLに「XXXXXXXX.ngrok.io/webhook」を設定し、更新ボタンを押す(XXXXはngrokで作製したURL)。
    • 「接続確認」ボタンを押して成功すればOK

これで準備完了です!

7. botとトークしてみる

友達になったbotとトークしてみます。

自分:こんにちは
bot:メッセージを受け付けました:こんにちは

おおおおーーー!
そして少し遅れて

google home: 新しいメッセージです。こんにちは

しゃべったーーー!
嬉しい!!

※2018/05/13
ngrokのフリープランを使い、8時間制約を回避する手順を追加しました。
daaaaaasukeさんありがとうございました!