SlackBotとGASでゆかりさん(VOICEROID+)に時報を喋ってもらう

※この記事は個人が楽しむ範疇で書いているので、やるときには各ソフトのライセンスなどをご確認の上、自己責任でお願いします。

先日ボイロ/ボカロ（ゆかりさん）有志の新年会がありまして、ゆかりさんのAI化などの話がでまして、終わったあとの**夜中にAlexaスキルとSlackBotを作る（しかも初めて作る）ということをやった結果、**中途段階の成果物として**時報をゆかりさんに喋ってもらう**というシステムが出来上がりました。

## 内容について　##

* SlackBotを作ってチャンネルに招待する
* ローカルでNodeサーバーを立ててVOICEROIDの発声をコントロールする
* 発声をテストしてみる
* Incoming Webhooksを作ってSlackにメッセージを送る
* GASから時報メッセージをSlackに投稿する
* まとめ

## SlackBotを作ってチャンネルに招待する ##

### Botを作成する ###

Botを作製する手順が結構複雑だったので、忘備録として画像つきで手順を書いています。
自分のSlackのワークスペースの左上のユーザーメニューから**[Administration] > [Manage apps]**を選択します。

<img width="498" alt="スクリーンショット 2019-02-04 20.33.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/742e08e1-26e7-c34f-10b7-57559bb2b1fd.png">

画面右上の**[Build]**のリンクをクリックします。

<img width="1110" alt="スクリーンショット 2019-02-04 20.35.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/561e68b1-cd05-835c-ceb3-b898501d3c4a.png">

左側メニューの**Start here**のすぐ下の**Building Slack apps**を選択します。

<img width="379" alt="スクリーンショット 2019-02-04 20.35.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/9df2fd4d-97d3-b53d-34eb-5cf9e0d4f486.png">

**[Create Slack app]**を選択します。

<img width="729" alt="スクリーンショット 2019-02-04 20.35.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/3b209f8a-2681-6c34-d594-4e6882525f03.png">

**App Name**の箇所に任意の名前をつけてください
**Development Slack Workspace**のプルダウンメニューには、Botを追加したいワークスペースを選択します。
**[Create App]**をクリックして次に進みます。

<img width="561" alt="スクリーンショット 2019-02-04 20.36.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/d5f71b49-1672-f34d-5cab-dbca07f0238b.png">

**Add features and functionality**内の**[Bots]**を選択します。

　<img width="1043" alt="スクリーンショット 2019-02-04 20.36.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/7b2c4086-a4c1-20e6-670c-4cf398d611de.png">

**Display name**と**Default username**を入力します。デフォルトでApp名が入りますが、任意に変更しても大丈夫です。入れ終わったら**[Add Bot User]**をクリックします。

<img width="767" alt="スクリーンショット 2019-02-04 21.25.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/3304df70-b047-e9a5-f242-1465ed80285c.png">

Botの追加が終わったら、左メニューの**[OAuth & Permissions]**を選択、**OAuth Tokens & Redirect URLs**の下の**[Install App to Workspace]**をクリックします。

<img width="1046" alt="スクリーンショット 2019-02-04 21.25.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/74a26364-1d41-a3bd-2305-e21538adbae1.png">

別のページに飛ばされます。Bot名を確認し、**[Authorize]**をクリックします。

<img width="525" alt="スクリーンショット 2019-02-04 21.26.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/53356f04-cec8-9444-1122-7193611e6ab6.png">


次の画面でOAuth Tokenが取得できます。以下のNodeサーバーで必要になるのは**Bot User OAuth Access Token**(xoxb-の文字で始まるほうをコピーします。これでSlackBotが作成されました。

<img width="770" alt="スクリーンショット 2019-02-04 21.26.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/0ffd00af-7342-7ff7-4c71-76317dc33d8f.png">

Bot作成はワークスペースにBotが追加されているかを確認しておきます。**Apps**という項目内にBot名が追加されていれば大丈夫です。

### Botをチャンネルに招待する ###

Bot用のチャンネルを作成し、BotをチャンネルにInviteします。適当なチャンネルを作成します。public/privateどちらでもかまいませんが、自分のワークスペースに他の人がいる場合は、privateチャンネルのほうがやりやすいかと思います。**Name**にチャンネル名、**Send invetes to:** から作成したBotを選択し、**[Create Channel]**をクリックします。

<img width="766" alt="スクリーンショット 2019-02-04 21.31.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/28a85e5b-9f62-3b6d-5210-5256ba1fe452.png">

チャンネル作成後、 **joined [チャンネル名] along with [Bot名]**　とチャンネルに表示されていれば、自分とともにBotも追加されています。

<img width="672" alt="スクリーンショット 2019-02-06 1.24.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/0c8924ab-4ee8-3c24-1b4a-022a874b0211.png">

## ローカルでNodeサーバーを立ててVOICEROIDの発声をコントロールする ##

Nodeサーバーからゆかりさんへの発声コントロールをする場合は何かしらのCLIツールが必要になるかと思います。
GUIツールだとおかゆぅさんの**[ゆかりねっと](http://www.okayulu.moe/)**が有名かと思いますが、今回は自作のCLIツールを使って**[VOICEROID+ 結月ゆかり EX](https://www.ah-soft.com/voiceroid/yukari_ex/)**をコントロールします。
(VOICEROID2では抑揚などのスライダーが動かすためには別のノウハウが必要になるようです。)

### Nodeサーバーのコードを用意する ###
VOICEROIDがインストールされているPCにNode.jsをインストールしておきます。
Botがいるチャンネルのメッセージを取得するコードは以下のようになります。
**\<ReplaceHereYourBotUserOAuthAccessToken\>**の箇所を取得した**Bot User OAuth Access Token**の文字列に置き換えます。(xoxb-の文字で始まるほうです。)

```index.js
const { RTMClient } = require('@slack/client');
const TOKEN = '<ReplaceHereYourBotUserOAuthAccessToken>'
const { execSync } = require('child_process');

const rtm = new RTMClient(TOKEN);
rtm.start();

rtm.on('message', (message) => {
  if(!message.text) {
    return;
  }
  execSync(`.\\bin\\VoiceroidTClient.exe Yukari ${message.text}`);
  console.log(`user:${message.user}, message: ${message.text}`);
});
```

コードサンプルはこちらに置いてます。依存パッケージがあるので、clone後はプロジェクトフォルダ内で `npm install`　のコマンドを実行しておきます。

> blkcatman/node-exec-voiceroid-example
> https://github.com/blkcatman/node-exec-voiceroid-example

また、上のexecSyncの箇所のコードからわかるように、**プロジェクトフォルダ直下にあるbinフォルダー内にVoiceroidTClient.exeを置いておく必要があります。**またVoiceroidTServer.exeもVOICEROIDをコントロールするのに必須なアプリケーションなので、同じようにbinフォルダーの中に入れます。

### VOICEROIDを操作するCLIツールをビルドする ###

下記のリポジトリからソースコードをDLできます。VS2015のCommunityEditionでビルドできます。
（.NET 4.5が必要です。）

> blkcatman/VoiceroidTalker
> https://github.com/blkcatman/VoiceroidTalker

次はReleaseビルドを前提にお話します。ビルド後に生成される以下のファイルをindex.jsonと同階層にあるbinフォルダの中に移動します。

```
VoiceroidTClient/bin/Release/VoiceroidTClient.exe
VoiceroidTServer/bin/Release/VoiceroidTServer.exe
```

### サーバーを動かす ###
あらかじめVOICEROIDを最小化していない状態で起動しておきます。

PowerShellもしくはVSコードのターミナルからnpmスクリプト経由でVoiceroidTServer.exeを実行します。**プロセスが起動しっぱなしになるため、次の処理に移る前に別のPowerShell(VSコードターミナル)のウィンドウを立ち上げます。**

```bash
npm run serve:yukari
```

同じようにnpmスクリプト経由でSlackBotのNodeサーバーを立ち上げます。

```bash
npm run serve:bot
```

## 発声をテストしてみる ##

SlackでBotがいるチャンネルで適当なメッセージを流します。
<img width="364" alt="スクリーンショット 2019-02-05 3.48.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/e56673e0-5e8d-509f-7489-ae91769376ed.png">

```
user:*********, message: テストメッセージです
user:*********, message: テストメッセージをしゃべっています
user:*********, message: テストメッセージを発声しています
```
Nodeサーバー上に上記のようにメッセージが出ていて、ゆかりさんがしゃべっていればOKです。

## Incoming Webhooksを作る　##

記事があまりにも長くなるので、詳しい説明は割愛します。Webhookの投稿先チャンネルはBotがいるチャンネルを指定します。GASからSlackにポストするための**Webhook URL**をコピーします。

<img width="811" alt="スクリーンショット 2019-02-06 0.33.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/d3a83e22-1b71-4fc7-329a-14825d82b6b3.png">

Incoming Webhooksの作成方法は[ik-fib](https://qiita.com/ik-fib)さんの記事を参考にしました。

> SlackのIncoming Webhooksを使い倒す
https://qiita.com/ik-fib/items/b4a502d173a22b3947a0

## GASから時報メッセージをSlackに投稿する ##

### スプレッドシートを用意する ###

GoogleDriveから以下のような適当なスプレッドシートを作成します。

<img width="722" alt="スクリーンショット 2019-02-05 23.56.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/0a9d7e01-478c-2aeb-8b45-d5a9c4740df9.png">

A列はラベルとして時刻を表記し、B列はゆかりさんに喋ってもらいたいテキストを入れています。0時~6時のように空欄箇所を作っておくと、その時間はゆかりさんが時報を喋らなくなります。

### スクリプトを追加する ###

**[ツール] > [スクリプト エディタ]**からスクリプトを新規に作成します。
GASのサンプルコードはGistに置きました。(コードが長めなので、こちらではコードの表示を省略します)
> blkcatman/TimeSignalToSlack.js
> https://gist.github.com/blkcatman/98ef1bbbef89651033aef260d31a67f6

61行目の**\<ReplaceHereYourWebhookURL\>**の箇所を**Webhook URL**のURLに置き換えます。

```javascript
  var url = '<ReplaceHereYourWebhookURL>'
```

### 時報のトリガーを追加する ###

初回のトリガーを登録する時は、【関数を選択】から**[createNextTimeSignalNow]**を実行すれば、現在時刻から次の時刻に変わった時に時報のポストが発火します。発火後は次の時刻でのトリガーが自動的に追加されます。

<img width="485" alt="スクリーンショット 2019-02-06 0.09.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/58376/efdf53fc-5eb4-4bb4-9f88-b8d74b4554ef.png">

### パーミッションを許可する ###
コード実行時、権限関係の警告が出ると思いますので、このコードを許可します。

## まとめ ##
前にトライした[**音声認識+VOICEROID+FaceRigでリアルタイムアバタートーキング**](https://qiita.com/blkcatman/items/5e03ede07730185cc99d)ではローカルPC内のみで完結していたので、そこから拡張するのが大変になっていました。

今回は時報というあまり大した機能ではありませんでしたが、SlackとGASなどのWebサービスと連携させることができ、構成が疎結合なシステムになったかと思います。

また、AWSやGCPなどのサービス群とも連携しやすくなったので、今後はAI化向けにカスタマイズしやすい環境になったので、いろいろな機能を追加できそうです。
音声認識周りはAlexaも使えそうなので、今後どのように機能を追加していくかいろいろ考えていきたいと思います。