「OK Google、プログラムをビルドして!」

突然ですが、皆さんはこんなことを思ったことはありませんか?

  • プログラミングしながら別のプログラムをビルドしたい
  • 大事なテスト中によそ見せずにビルドも平行して行いたい
  • ゲームで遊んでいる最中も何か作業をしている感を出したい

私はこれらが何とかならないかと悩まされることが多々ありました。
そこで、普段は毎朝アラームとニュースや天気予報を教えてもらうためにしか使っていなかった
Google Home を使用して、会話でプログラムをビルドする環境を作ってみました。

構想

さて、会話してビルドする際の流れを考えてみます。
今回は、ビルド自体は Jenkins さんにお任せしたいと思います。

  1. Google Home にビルドしたい旨を伝える
  2. Google Home がどのプロジェクト(Jenkinsジョブ)をビルドするか聞く
  3. プロジェクト名(Jenkinsジョブ)を答える
  4. Jenkins で指定したジョブのビルドを開始する

この流れが出来ればやりたいことが実現出来そうです。
図にするとこんなイメージです。

IFTTT を使ってみる

まずは、IFTTT という、Web上の様々なサービス同士を連携させられるサービスを使用してみます。

Googleアカウントなどから IFTTT のアカウントを作成し、
アイコンをクリックすると表示されるメニューから Create を選択します。

最初のサービスは GoogleAssistant の “Say a simple phrase” を、
次のサービスは Webhooks の “Make a web request” を選択して
それぞれ下図のように設定します。

これで、Google Home に「Jenkinsさんビルドして」とお願いすると、ビルドが開始されるようになりました。
ほんの数分で出来てしまいました。とっても簡単です。

しかし、ジョブ名を指定できないため、最初にやりたかったことが全てクリアできていません。

Action on Google + Dialogflow を使ってみる

そこで、Action on Google と Dialogflow の出番です。
これらは、一言でいうとGoogleアシスタントの処理を自分で作れてしまうサービスです。

また、Firebase の環境が必要になるので、事前にアカウントの準備をしておきます。

Action on Google の設定

  1. Action on Google のページから Actions Console を開く
  2. 新しいプロジェクトを作成し、”Actions SDK” を選択する
  3. 左側の Actions から “Add your first action” を選択する
  4. Custom intent を選択した状態で BUILD を選択する

これで、Dialogflow のページが開きます。

Dialogflow の設定

  1. 左側の Intents を選択する
  2. “Default Welcome Intent” を選択し、”Text Response” に適当なテキストを設定する
    最初に話す言葉になります。
  3. 新規 Intents を作成して、適当な名前を付ける
    ここでは JobName という名前にします。
  4. “Action and parameters” に Entity に @sys.any を持つパラメータを作成する
    他にも時間や地名を設定することもできます。
  5. “Fulfillment” のスイッチを両方 ON にする
    Node.js でレスポンスを実装できるようになります。Firebase を使用します。
  6. “Responses” の “Set this intent as end of conversation” を ON にする
    Text Response は先に説明した Node.js に記述するので、空のままで大丈夫です。
  7. SAVE する
  8. 左側の Fulfillment を選択し、”Inline Editor” を ENABLED にする
    index.js に以下のようなプログラムを実装します。
    ここで “View execution logs in the Firebase console” から Firebase のページを開いておきます。
'use strict';
 
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const httpRequest = require('request');
 
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
 
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });
  console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
  console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
  
  function jenkinsBuild(agent) {
    let jobName = request.body.queryResult.queryText;
    agent.add(jobName + 'のビルドをお願いしてみます');
    
    httpRequest.post(
      {
          url: 'http://(JenkinsのURL)/job/jobName/build?token=(トークン名)'
      },
      function(error, response, body) {
        console.log('error: ' + error);
        console.log('response: ' + response);
        console.log('body: ' + body);
        
        if(!error) {
          console.log('success!');
        } else {
          console.log('failure...');
        }
      }
    );
  }

  // Run the proper function handler based on the matched Dialogflow intent name
  let intentMap = new Map();
  intentMap.set('JobName', jenkinsBuild);
  agent.handleRequest(intentMap);
});

これで準備完了です。

テスト

実際にテストしてみます。
左側 “Integrations” を選択すると様々なサービスが表示されるので、
Google Assistant を選び TEST をクリックします。
Simulator が開くので、実際に下図のようにテストしてみます。

会話後に Jenkins を確認すると、なぜかビルドが走っていません……
Firebase のログを確認してみると、
>Error: getaddrinfo EAI_AGAIN
というエラーが出ていました。

Firebase の罠

何が原因なのか調べてみると、Firebase のプランに問題があるようでした。
なんと、無料の Spark プランだと外部に接続できないのです。

諦めきれずにさらに調べてみると、この程度のテストであれば
従量制の Blaze プランの無料分で大丈夫そうだということが判明したため、
プランを切り替えて再度テストしたところ、うまくビルドが走りました。

まとめ

Firebase には驚かされてしまいましたが、
なんとかやりたかったことを実現させることが出来ました!
(実際に正式なアプリにするにはデプロイの手順を踏む必要はありますが。)

なお、Firebase は外部APIに接続できないだけなので、
Line BOT や Slack などを経由することで様々なことが無料で行えてしまいます。
ラズパイとの連携も非常に強力です。

今後も、この Google Home と仲良くしていきたいと思います。
Amazon Echo とも仲良くしてみたくなりました。