Python, ngrok, LINEを使ってgoogle homeを喋らせる。

先人達がもう散々やってきている内容で、qiitaにも腐る程記事があります。
下記の記事をベースに作成しております。投稿主様に感謝:clap:
[LINEでボイスメッセージを送信し、その音声ファイルをGoogle Homeに喋らせる - Qiita](https://qiita.com/tutti225/items/210bc7eb33140f57197a)

## 構成図

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384111/23e8ea26-a56e-45c9-d3c8-a3d79df68e39.png)

参考にしたQiita記事と概ね相違はありません。
強いて言えばRaspberry Piを使用せずにWindows10をサーバーとして使用しているところ、LINEのボイスメッセージには対応していない、2点でしょうか。
家にRaspberry Piがない & ちょうど余ってるWin10がある！ってことでサーバーはWindows10にしてます。
LINEのボイスメッセージは、そもそもボイスメッセージは使わないということ、Slackに転用するのでボイスメッセージは不要ということで今回は省きました。

## 大まかな処理の流れ

1. 自身のLINEアカウントからLINE Messaging APIにテキストメッセージを送信
2. LINE Messaging APIから、Webhookでサーバーにテキストを送信
3. サーバーでGoogle Homeが喋れる用にmp3ファイル作成
4. mp3をWebサーバーで公開
5. Pychromecastで、Google Homeにmp3ファイルを送信
6. Google Homeが喋る(ﾔｯﾀｱ)

## 事前準備

### 準備するもの

#### ハード
- LINEを送信する端末
- Windows10 PC(サーバー用)

#### ソフト
1. ngrokインストール
2. Pythonインストール
3. Pythonライブラリインストール
    1. Pychromecast
    2. flask
    3. gTTS
    4. line-bot-sdk
4. サーバー(Windows10)とおうちのルーターの設定

ngrokはLINE Messaging APIのWebhookをサーバーが受信できるようにポートを開けるために使用します。
PythonとPythonのライブラリは、メインの処理とサーバー構築に必要になります。
Windowsの方はコマンドプロンプトから、Macの方はTerminalから下記を実行してください。

### 早速準備しちゃう

下記の準備はすべてWindows10 PCで行います。

### 1. ngrokインストール

下記リンクから、ngrokをインストールします。
[https://ngrok.com/](https://ngrok.com/)
[Get Started for free]から、インストールできますがngrokのアカウントを作成するか、GithubまたはGoogleのアカウントでSign-upする必要があります。

### 2. Pythonインストール

下記リンクから、ダウンロードします。
[https://www.python.org/downloads/](https://www.python.org/downloads/)
ダウンロードしたexeファイルを起動するとインストールを開始できますので、案内にしたがって進めます。

### 3. Pythonライブラリインストール

今回の処理に必要なライブラリをインストールします。

    $ pip install pychromecast
    $ pip install line-bot-sdk
    $ pip install flask
    $ pip install gTTS

### 4. サーバー(Windows10)とおうちのルーターの設定

後々、Windows10でサーバーを立てますが、その時にこの設定をやっておかないと無事死亡します。
私は10時間ぐらい無駄にしました。

#### Windeows10の設定

##### IPアドレスの固定

下記リンク参考に設定します。
ここで指定したプライベートIPアドレスは、後々使用します。
[IPアドレスの固定（DHCP設定） Windows 10](https://support.eonet.jp/connect/net/multi_bbr/eo-rt100/quick/ip_static_win10.html)

##### ポートの開放

これまた下記リンクを参考に設定します。
送信受信ともに設定をしてしまってよいです。今回は**8000**で設定します。
この番号も後々使います。
※ポート開放はセキュリティ的に弱くなってしまうので自己責任で。
[ポート開放とは？ 基本的なやり方と確認方法 - UX MILK](https://uxmilk.jp/45410)

#### ルーターの設定
##### ポートマッピング

これは各ルーターによって設定方法が異なるので、お使いのルーターの型番から設定方法を調べてください。
ポートマッピングの設定では、IPアドレスは**Windows10の固定IPアドレス**を指定、ポートは**8000**を指定する。
下記リンクは私のおうちルーターの設定です。もしかしたら使ってる人もいるかもしれませんね。
[PR-500KI 500MI ポート開放設定の説明](https://www.akakagemaru.info/port/pr500-portfw.html)

以上で準備完了なので、いざ実装に。

## いざ実装！

### 1. LINE Messaging APIのアカウント作成・設定

下記リンクも参考にLINE Developersのアカウントを作成してください。個人で使用されている方は個人のLINEアカウントで問題ないです。
[Messaging APIを利用するには - LINE Developers](https://developers.line.biz/ja/docs/messaging-api/getting-started/)
作成したら、ログインして左側のメニューからプロバイダーの[プロバイダーリスト]を開き、[新規プロバイダー作成]をクリックします。
プロバイダー名を入力してください。※何でも良いです。

作成できたら、遷移した画面の[Messaging API]を選択して、流れに従って項目を入力して設定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384111/654cea88-7252-9e53-f547-b09bf261af5b.png)

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384111/e88fcaaf-007a-2cb4-0763-a521826cc18d.png)

アプリ名：なんでもいいです。LINE上で表示される名前になります。
アプリ説明：なんでもいいです。
大業種：個人で問題ないです。
小業種：なんでもいいです。
メールアドレス：自身のメールアドレス
プライバシーポリシーURL：入力不要
サービス利用規約URL：入力不要
以上の入力できたら案内に従い進めてください。
完了すると作成されたプロバイダーのチャネル一覧にMessaging APIが表示されています。あとで使います。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384111/21b7aace-26b8-1b87-cb01-527d57e0bd7d.png)

作成されたら、自身のLINEの友達に追加しておきます。メッセージをこいつに送信しなきゃならないのです。

### 2. サーバー main.pyのコード全文

このmain.pyの処理としては、Webhookで送信されたLINEメッセージを受け取ったら、mp3へ変換しファイルとして保存、保存したファイルをWebサーバーを介してGoogle Homeへ送信という流れです。
mp3の保存場所は、私の場合、デスクトップ上に"Readaloud"というフォルダを作成してその中に保存しています。
main.pyの保存場所もreadaloudフォルダのすぐ下です。

また、`line_bot_api`と`handler`の部分は、それぞれの括弧内に、LINE Messaging APIのアクセストークンとChannel Secretを入力してください。
コメントはできる限り挿入したつもり。

```python:main.py
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage, AudioMessage
)
import pychromecast
from gtts import gTTS

import urllib.parse
from flask import Flask, request, abort
import time

app = Flask(__name__)

line_bot_api = LineBotApi('aaaaa') # LINE Messaging APIのアクセストークンを記載  
handler = WebhookHandler('bbbbb')　# LINE Messaging APIのChannel Secretを記載

# Flaskのルーティング(URL/パスとfunctionを紐づけ), methodsはPOSTの時、つまりLINEのWebhookを受信したら反応する
@app.route("/readmessage", methods=['POST'])
def receivemessage():
	# リクエストヘッダーから署名検証のための値を取得
	signature = request.headers['X-Line-Signature']
	
	# リクエストボディを取得
	body = request.get_data(as_text=True)
	app.logger.info("Request body: " + body)
	
	# 署名を検証し、問題なければhandleに定義されている関数を呼び出す
	try:
	    handler.handle(body, signature)
	#失敗した際は400を返す
	except InvalidSignatureError:
	    abort(400)
	
	return 'OK'

# Google Homeを探す
googlehome_name = "Living Room" # 設定されているGoogle Homeの名前を入力
chromecasts = pychromecast.get_chromecasts() 
cast = next(cc for cc in chromecasts if cc.device.friendly_name == googlehome_name)

# addメソッドには、イベントのモデルを入れる
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
	#メッセージを受信したら、サーバーのコマンドラインに表示
	print(event.message.text)
	
	# Google Homeが実際に読み上げる文章を作成
	readtext = 'LINEメッセージです。'+event.message.text
	# readtextを音声に変換
	tts = gTTS(text=readtext, lang='ja')
	# mp3ファイルとして、保存する
	tts.save('~~~~~~~~~/tmp.mp3')
	
	# 保存されるまで念のため処理を一時的に止める
	# timeモジュール内のメソッド
	time.sleep(3)
	
	# イベントが発生するかタイムアウトになるまで現在のスレッドを待機させる。
	cast.wait()
	
	# メディアコントローラーオブジェクトの作成
	mc = cast.media_controller
	# 再生するmp3ファイル指定しGoogle Homeへ送信
	# IPアドレスは事前準備で設定したWindows10のIPアドレスを指定
	mc.play_media('http://192.168.XXX.XXX:8000/tmp.mp3','audio/mp3')
	# Google Homeが非アクティブの時にも再生されるようにする。
	mc.block_until_active()    

# サーバーの起動, portはここでは5000を指定
if __name__ == "__main__":
    app.run(host='127.0.0.1', port=5000)
```

### 3. mp3server.pyのコード全文
mp3server.pyの役割は、main.pyで作成したmp3ファイルを外部に公開し、Google Homeに送信できるようにします。
このmp3server.pyファイルも、デスクトップに作成したフォルダ内に保存で問題ありません。
1点だけ注意、`@app.route("/<string:file_name>", methods=['GET'])`としてルーティングを設定しているので、**main.pyでmp3ファイルを保存したディレクトリと同じ場所に保存してください**。

```python:mp3server.py
import os # OSの機能にかかるディレクトリ取得などを行うモジュール
from flask import Flask, make_response　
app = Flask(__name__)


@app.route("/<string:file_name>", methods=['GET'])
def getMP3File(file_name):
	# レスポンスヘッダ及びレスポンスボディの情報を持つオブジェクトを作成
	response = make_response()
	
	if not os.path.exists(file_name):
	    return response
	
	# responseに情報を追加する
	response.data = open(file_name, "rb").read()
	response.headers['Content-Disposition'] = 'attachment; filename=' + file_name
	response.mimetype = 'audio/mp3'
	return response
	
# サーバーの起動
if __name__ == '__main__':
    # 外部からアクセスできるようにhostは0.0.0.0を指定する
    # ポートは事前準備で設定したポートを指定
    app.run(debug=False, host='0.0.0.0', port=8000)
```


### 4.ngrokの起動~Webhookの設定

事前準備でインストールしたngrokを起動します。
起動したら`ngrok http 5000`を入力して実行します。
すると、下記のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384111/2c5121b5-ec14-cbb0-3862-390dbeaaef09.png)

Forwardingに記載のアドレスをLINE Messaging APIの方で使用します。
事前準備にて作成したMessaging APIアカウントの基本設定画面に移動します。
[Webhook送信]を「送信する」に設定、[Webhook URL]にはngrokのForwardingに表示されているURLに/readmessageを付け加えて設定しておきます。とにかく下記の画像のように設定すればGood.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384111/eccfcbe6-f154-0ede-5fa8-5aa3c8f145e6.png)

この時に、接続確認ボタンを押すとngrokの画面上では`502 Bad Gateway`が赤字で表示されるはずです。
これは正常です。

### 5. サーバーの立ち上げ

2, 3で作成した、main.pyとmp3server.pyを立ち上げます。
各ファイルをダブルクリックすると立ち上がり、画面が表示されるはずですが、すぐに消えたらコードが間違えている可能性大なので、今すぐに戻って見直してください。

立ち上がったら、サーバーの準備は完了です。

### 6. LINEでメッセージを送信

5が完了した段階で、ngrok, main.py, mp3server.pyがWindows10(サーバー)で起動していることになります。
この状態で、iPhoneでもPCでもなんでもいいですが、LINEメッセージを適当に送ります。
テストでもTESTでもQiitaでもなんでもいいです。とりあえず送信したらGoogle Homeが話してくれることでしょう。

## 今後の展望

- Slackへの転用
- サーバーをDocker内で構築

## 最後に

変なところで躓いたし、時間もかかったけど楽しい。Google Homeがしゃべるだけで幸せ。

## その他参考サイト

下記サイトの主に感謝:clap:
[Pychromecast 3.2.0 - Pypi](https://pypi.org/project/PyChromecast/)
[Python 3 から Google Home に喋らせる（低遅延） - Qiita](https://qiita.com/rukihena/items/8af9b8baed49542c033d)
[Pythonを使ってGoogle Homeを喋らせてみる - Qiita](https://qiita.com/attakei/items/c5de47f3c1f53992c353)
[line-bot-sdk-pythonを使ってみた - 雑食日記](https://keinumata.hatenablog.com/entry/2018/05/08/122348)
[Flaskのエラーハンドラメモ - ようへいの日々精進XP](https://inokara.hateblo.jp/entry/2016/10/01/123923)
[Flaskのルーティング部分をざっくりと理解する - Qiita](https://qiita.com/habakan/items/67fb25c34a35db373c07)
[いまさらながら Flask についてまとめる 〜Routing〜 - 適当おじさんの適当ブログ](https://www.subarunari.com/entry/2017/09/28/001946)
[ChromecastをPythonで制御する - 傀儡子の館.Python](https://plaza.rakuten.co.jp/kugutsushi/diary/201601100000/)
[Pythonのsleepについて - TECH:Academy magazine](https://techacademy.jp/magazine/15822)
[グループ化したGoogle Homeで時間になったら音楽を鳴らす - Qiita](https://qiita.com/aoirohn/items/fc55b1a7cae4ed54a9ae)
[4. Flaskを使いこなす1](https://study-flask.readthedocs.io/ja/latest/04.html)
[【これでバッチリ！】Pythonのosモジュール使い方まとめ - 侍エンジニア](https://www.sejuku.net/blog/67787)