SlackとGASで作る雑談Bot ( docomo 自然対話 ( 雑談対話 ) API )
GAS(Google Apps Script)とdocomo自然対話(雑談対話)APIを使用して、SlackのChat Botを作成します。 Botに話しかけると、気の利いた言葉(面白い言葉)を返してくれて、楽しいですよ〜(^ ^)
GASを使用すると、サーバレスで気軽にBotを作ることができますのでオススメです!
以下の記事で使っていたdocomo雑談対話APIの提供が終了したため、docomo自然対話(雑談対話)APIで同じ内容のBotを作成します。
nmmmk.hatenablog.com
構造
自然対話(雑談対話)APIは旧雑談対話APIと異なり、ユーザー毎にappIdを取得することで、ユーザー毎の会話のステータス管理が行えるようになっています。
本プログラムでは、取得したappIdをFusion Tablesに保存するようにしています。
以下のような流れで動作します。
1. Slack上のBotに話しかける。
2. Slackから受信したメッセージからユーザーIDを取得する。
3. ユーザーIDを元にFusion TablesからappIdを取得する。
4. appIdが取得できなかった場合は、docomo自然対話(雑談対話)APIに対してユーザー登録を行い、appIdを取得、Fusion TablesへのappId登録を行う。
5. docomo自然対話(雑談対話)APIにメッセージを送信する。
6. docomo自然対話(雑談対話)APIから回答が返ってくる。
7. Slackへdocomo自然対話(雑談対話)APIの回答を送信する。
Slackの設定
Incoming Webhooksの設定
GASからメッセージを受け取れる様に、Incoming Webhooksの設定を行います。
以下にアクセスして設定してください。
https://api.slack.com/apps
Create New Appを押下します。
Incoming Webhooksの設定をONにします。
Add a Bot Userを押下します。
Botの名称を設定します。
アプリケーションをインストールします。
投稿先を設定します。
Botのアイコンを設定します(任意)
Outgoing Webhooksの設定
GASへメッセージを送信できる様に、Outgoing Webhooksの設定を行います。
以下にアクセスして、発信Webフックの設定を追加します。
https://slack.com/apps/A0F7VRG6Q-outgoing-webhooks
発信Webフック インテグレーションの追加を押下します。
インテグレーションの設定部分までスクロールします。
引き金となる言葉には、メッセージの送信相手+「:」と考えてください。
例えば、以下の様な投稿の仕方となります。
例)ZatsudanBot: テストメッセージURLの部分は、後述するGASのURLを設定します。
トークンの部分はGASで使用します。
docomo自然対話(雑談対話)APIの登録
今回使用するのは、docomo自然対話(雑談対話)APIです。
dev.smt.docomo.ne.jp
必要事項を入力して、APIを申請し、APIキーを取得します。
appIdの保管場所として、Fusion Tablesを作成する
カラムとして、UserId: Text, AppId: Textのテーブルを作成します。
Fusion Tablesの使用方法は、以下の記事を参考にしてください。
nmmmk.hatenablog.com
GASのソースコードとして、定数関連を定義する
定数 | 内容 |
---|---|
WWWWW | Outgoing Webhooks設定時に割り当てられたトークンを使用 |
XXXXX | Incoming Webhooksの設定時に取得した投稿先のチャンネルのURLを使用 |
YYYYY | Fusion TablesのIDをを使用 |
ZZZZZ | 自然対話(雑談対話)API KEYを使用 |
// Outgoing webhooksのToken var SlackOutgoingWebhooksToken = "WWWWW"; // Slackの投稿先チャンネル var SlackPostChannel = "XXXXX"; // FusionTableのID var FusionTableId = "YYYYY"; // Docomo APIのキー var DocomoApiKey = "ZZZZZ";
GASでSlackからのメッセージを受け取れる様にする
基本となるソースコード
doPost()でSlackからのメッセージを受け取ります。
また、addLog()で受信した情報をスプレッドシートに書き込みます。
AAAAAの部分はスプレッドシートのID、BBBBBの部分はシート名を指定してください。
※スプレッドシートのIDは、スプレッドシートのURLの下記の部分です。
https://docs.google.com/spreadsheets/d/スプレッドシートのID/edit#gid=0
//----------------------------- // Slackからのメッセージ受信 //----------------------------- function doPost(e) { // SlackのOutgoing WebhooksのTokenであるかを比較 if (e.parameter.token != SlackOutgoingWebhooksToken) { return; } // テキスト部を抜き出し var text = e.parameter.text; // 「:」でテキスト分割する text = text.split(":"); if (text[1].length > 1) { addLog(text[1]); } } //----------------------------- // スプレッドシートへのログ出力 //----------------------------- function addLog(text) { var spreadsheetId = "AAAAA"; var sheetName = "BBBBB"; var spreadsheet = SpreadsheetApp.openById(spreadsheetId); var sheet = spreadsheet.getSheetByName(sheetName); sheet.appendRow([new Date(),text]); return text; }
GASをウェブアプリケーションとして公開する
スクリプトエディタにて公開 -> ウェブアプリケーションとして導入 を選択します。
公開設定を行います。 以下の様に設定します。
プロジェクトバージョンは、新規作成
アプリケーションにアクセスできるユーザーは、全員(匿名ユーザーを含む)
割り当てられたURLをSlackのOutgoing WebhooksのURLに設定します。
※ソースコードを修正した場合は、1, 2の手順を毎回行う必要がありますので注意してください。
Slackからメッセージを送信する
以下の様にメッセージを送信します。
ZatsudanBot: テスト
スプレッドシートを確認すると、「テスト」が記録されていますのでGASでのメッセージ受信ができていることが分かります。
メンションの変換文字列を取得する
現時点では、メンションでの呼び出しに対応していません。
@ZatsudanBot テスト と投稿しても反応しません。
仮に、Outgoing Webhooksの「引き金となる言葉」を「@ZatsudanBot」に変更しても、期待した動作とはなりません。
「@ZatsudanBot」が「 <@UAG876W49>」の様なユーザーID文字列に変換されているためです。
ユーザーID文字列を取得するために、以下のメッセージを送信します。
ZatsudanBot: @ZatsudanBot
スプレッドシートを参照すると、「 <@UAG876W49>」の記載があることが分かります。
これがユーザーIDです。
取得したIDをOutgoing Webhooksの「引き金となる言葉」に設定します。
Slackへの投稿処理を準備する
function postSlack(text) { // Incoming WebhooksのURL var url = SlackPostChannel; var payload = { text: text }; var options = { "method" : "POST", "headers": {"Content-type": "application/json"}, "payload": JSON.stringify(payload) }; UrlFetchApp.fetch(url, options); }
docomo自然対話(雑談対話)APIとの送受信処理を作成する
ユーザー登録処理
function registUser() { var regist_options = { 'botId': 'Chatting', 'appKind': 'Smart Phone' } var options = { 'method': 'POST', 'contentType': 'application/json', 'payload': JSON.stringify(regist_options) }; // APIのURL var registUrl = "https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/registration?APIKEY=" + DocomoApiKey; // docomo APIと通信する var response = UrlFetchApp.fetch(registUrl, options); // docomo APIからの回答はJSON形式なのでオブジェクト変換 var content = JSON.parse(response.getContentText()); // docomo APIから取得したappIdを返す return content.appId; }
雑談対話処理
function getDialogueMessage(appId, mes, recvTime) { // 送信時刻 var sendTime = dateToString(new Date()); var dialogue_options = { 'language': 'ja-JP', 'botId': 'Chatting', 'appId': appId, 'voiceText': mes, 'appRecvTime': recvTime, 'appSendTime': sendTime, } var options = { 'method': 'POST', 'contentType': 'application/json', 'payload': JSON.stringify(dialogue_options) }; // APIのURL var dialogueUrl = "https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/dialogue?APIKEY=" + DocomoApiKey; // docomo APIと通信する var response = UrlFetchApp.fetch(dialogueUrl, options); // docomo APIからの回答はJSON形式なのでオブジェクト変換 var content = JSON.parse(response.getContentText()); // docomo APIから取得した回答部分を呼び出し元に戻す return content.systemText.expression; } function dateToString(date) { return Utilities.formatDate(date, "JST", "yyyy-MM-dd hh:mm:ss"); }
Fusion TablesへのappIdの登録・取得処理を作成する
appId登録
function insertAppIdByUser(user, appId) { var tableId = FusionTableId; var sql = 'INSERT INTO ' + tableId + '(UserId, AppId) VALUES (' + '\'' + user + '\'' + ',\'' + appId + '\'' + ')'; var res = FusionTables.Query.sql(sql); return res; }
appId取得
function getAppIdFromUser(user) { var tableId = FusionTableId; var sql = 'SELECT AppId FROM ' + tableId + ' WHERE UserId=' + '\'' + user + '\''; var res = FusionTables.Query.sql(sql); var appId = ''; if (res.rows) { appId = res.rows[0][0]; } return appId; }
メンション対応、Fusion Tables制御、docomo API制御を追加する
doPost()を以下の様に変更します。
※addLog()は削除しましたが、必要に応じて実装してください。
function doPost(e) { // Slackのoutgoing-webhooksのTokenであるかを比較 if (e.parameter.token != SlackOutgoingWebhooksToken) { return; } // 受信時刻 var recvTime = dateToString(new Date()); // テキスト部を抜き出し var text = e.parameter.text; // <@UCV7J74BF>の部分は削除 text = text.substring(13); if (text.length > 1) { var userId = e.parameter.user_id; var appId = getAppIdFromUser(userId); if (appId === '') { // docomo APIと通信し、ユーザー登録(appId取得)を行う appId = registUser(); // Fusion TableにappIdを登録 insertAppIdByUser(userId, appId); } // docomo APIへメッセージを送信し、回答を受信する var dialogueMessage = getDialogueMessage(appId, text, recvTime); // 返信メッセージ作成 var message = "<@" + userId + "> " + dialogueMessage; // Slackへ送信 postSlack(message); } }
最終ソースコード
全体
// Outgoing webhooksのToken var SlackOutgoingWebhooksToken = "WWWWW"; // Slackの投稿先チャンネル var SlackPostChannel = "XXXXX"; // FusionTableのID var FusionTableId = "YYYYY"; // Docomo APIのキー var DocomoApiKey = "ZZZZZ"; //---------------------------------------------------------- // Slackからのメッセージ受信 //---------------------------------------------------------- function doPost(e) { // Slackのoutgoing-webhooksのTokenであるかを比較 if (e.parameter.token != SlackOutgoingWebhooksToken) { return; } // 受信時刻 var recvTime = dateToString(new Date()); // テキスト部を抜き出し var text = e.parameter.text; // <@UCV7J74BF>の部分は削除 text = text.substring(13); if (text.length > 1) { var userId = e.parameter.user_id; var appId = getAppIdFromUser(userId); if (appId === '') { // docomo APIと通信し、ユーザー登録(appId取得)を行う appId = registUser(); // Fusion TableにappIdを登録 insertAppIdByUser(userId, appId); } // docomo APIへメッセージを送信し、回答を受信する var dialogueMessage = getDialogueMessage(appId, text, recvTime); // 返信メッセージ作成 var message = "<@" + userId + "> " + dialogueMessage; // Slackへ送信 postSlack(message); } } //---------------------------------------------------------- // Fusion TableからappIdを取得 //---------------------------------------------------------- function getAppIdFromUser(user) { var tableId = FusionTableId; var sql = 'SELECT AppId FROM ' + tableId + ' WHERE UserId=' + '\'' + user + '\''; var res = FusionTables.Query.sql(sql); var appId = ''; if (res.rows) { appId = res.rows[0][0]; } return appId; } //---------------------------------------------------------- // Fusion TableにappIdを登録 //---------------------------------------------------------- function insertAppIdByUser(user, appId) { var tableId = FusionTableId; var sql = 'INSERT INTO ' + tableId + '(UserId, AppId) VALUES (' + '\'' + user + '\'' + ',\'' + appId + '\'' + ')'; var res = FusionTables.Query.sql(sql); return res; } //---------------------------------------------------------- // Slackへ投稿 //---------------------------------------------------------- function postSlack(text) { // 投稿先チャンネル var url = SlackPostChannel; var payload = { "text": text }; var options = { "method" : "POST", "headers": {"Content-type": "application/json"}, "payload": JSON.stringify(payload) }; UrlFetchApp.fetch(url, options); } //---------------------------------------------------------- // docomo APIと通信し、ユーザー登録(appId取得)を行う //---------------------------------------------------------- function registUser() { var regist_options = { 'botId': 'Chatting', 'appKind': 'Smart Phone' } var options = { 'method': 'POST', 'contentType': 'application/json', 'payload': JSON.stringify(regist_options) }; // APIのURL var registUrl = "https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/registration?APIKEY=" + DocomoApiKey; // docomo APIと通信する var response = UrlFetchApp.fetch(registUrl, options); // docomo APIからの回答はJSON形式なのでオブジェクト変換 var content = JSON.parse(response.getContentText()); // docomo APIから取得したappIdを返す return content.appId; } //---------------------------------------------------------- // docomo APIへメッセージを送信し、回答を受信する //---------------------------------------------------------- function getDialogueMessage(appId, mes, recvTime) { // 送信時刻 var sendTime = dateToString(new Date()); var dialogue_options = { 'language': 'ja-JP', 'botId': 'Chatting', 'appId': appId, 'voiceText': mes, 'appRecvTime': recvTime, 'appSendTime': sendTime, } var options = { 'method': 'POST', 'contentType': 'application/json', 'payload': JSON.stringify(dialogue_options) }; // APIのURL var dialogueUrl = "https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/dialogue?APIKEY=" + DocomoApiKey; // docomo APIと通信する var response = UrlFetchApp.fetch(dialogueUrl, options); // docomo APIからの回答はJSON形式なのでオブジェクト変換 var content = JSON.parse(response.getContentText()); // docomo APIから取得した回答部分を呼び出し元に戻す return content.systemText.expression; } //---------------------------------------------------------- // date -> 文字列変換(yyyy-MM-dd hh:mm:ss形式文字列で返す) //---------------------------------------------------------- function dateToString(date) { return Utilities.formatDate(date, "JST", "yyyy-MM-dd hh:mm:ss"); }
実行してみる!
ごもっともです・・・