【Excelだけ】サーバー無しでチャットボットを作る

こんにちはリビットです。
最近ではクラウドサービスやフレームワークが多く普及してきたことで、チャットボットを作るのがかなり容易になりました。
ただ手段は増えましたが、サービス間のデータをやり取りするためにサーバーが必要だったり、環境を整えることができない。という方もいらっしゃるかと思います。
そういった方のために、今回はExcelを使ってチャットボットを作る方法をご紹介します。
ちなみに今回紹介するのは、プラットフォームにChatworkを使用してチャットボットを作成する手順となりますが、VBAのコードを作成したいクラウドサービス用に書き換えることで、Slackのような別のサービスでも使用可能です。
前提
チャットボットを作成する上で、ユーザーが使用する側のサービスからデータを受け取る手段として主流なのは、Webhookと呼ばれる技術です。
これはクラウドサービス側で設定するもので、クラウドサービス内でユーザが何らかのアクションを起こした際に、予め指定したサーバにPOSTリクエストを送信するという機能です。当然、そのクラウドサービスがWebhookに対応していない場合は使用できません。
ですが今回は、そもそもサーバがありません。
ではどうするか。
ポーリングします。
ポーリングについて
ポーリング(polling)とは、通信やソフトウェアにおいて、競合を回避したり、送受信の準備状況を判断したり、処理を同期したりするために、複数の機器やプログラムに対して順番に定期的に問い合わせを行い、一定の条件を満たした場合に送受信や処理を行う通信及び処理方式のことである。
Wikipedia
詳細に理解していただく必要はありませんが、重要なのは「プログラムに対して順番に定期的に問い合わせを行い」というところです。
サービス側からデータを受け取ることはできないので、こちら側から定期的に新しいアクションがないか問い合わせることで、チャットボットを実現します。
ポーリングのデメリット
この方法はチャットボットを実現する上で、Webhookに比べていくつも劣る部分があります。
レスポンスが悪くなる
Webhookであれば、
- ユーザーがアクション
- ChatworkがPOSTリクエスト
- リクエストを受け取り、返信
- ユーザーが確認
上記の流れでユーザーとのやり取りを行うこととなりますが、ポーリングの場合は、
- ユーザーがアクション
- Excelから問い合わせ
- アクションを起こしている場合は、返信を作成
- ユーザーが確認
という流れになります。
Webhookであれば、ユーザーがアクションを起こした直後に、
「こんなアクションがあったよ!」
とクラウドサービス側から教えてくれるのに対して、ポーリングでは、
「何かアクション起こしてない?」
とこちらから問い合わせるまでユーザーのアクションに気づくことができず、返事が遅くなってしまう可能性があります。
頻繁に問い合わせることで解決できるかもしれませんが、問い合わせにはAPIを使用します。
Chatworkの場合はAPIの利用回数に上限が設けられており、2020年10月時点では5分間に300回です。
利用規約に違反する可能性がある
上述のAPI使用の観点から、クラウドサービスの利用規約に違反する可能性があります。
この方法を利用する場合は、APIの使用に関して、利用規約を確認しておきましょう。
VBAの作成
前置きが長くなりましたが、ここから実際にチャットボットに使用するVBAコードを紹介していきます。
JSONの取り扱い
Chatwork APIでは、リクエストの返信がJSON形式になります。
そのため、JSONファイルを取り扱うライブラリであるJsonConverterを使用します。ファイルは以下より取得できます。
Chatwork APIの操作
以下のコードを、標準モジュール名「Chatwork」で作成します。
Private Const END_POINT As String = "https://api.chatwork.com/v2/"
' ここにチャットボットとして使用したいアカウントのAPIトークンを貼り付けてください
Private Const API_TOKEN As String = "xxxxxxxxxxxxxxxxxxxxxxxxxx"
Public Function Api(ByRef method As String, ByRef url As String, ByRef param As String) As String
Dim httpRequest As Object
Set httpRequest = CreateObject("MSXML2.XMLHTTP")
With httpRequest
Call .Open(method, END_POINT & url, False)
If method = "POST" Or method = "PUT" Then
Call .setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
End If
' 認証
Call .setRequestHeader("X-ChatWorkToken", API_TOKEN)
Call .send(IIf(param <> "", param, Null))
Api = .responseText
End With
End Function
Public Function GetContacts() As String
GetContacts = Api("GET", "contacts", "")
End Function
Public Function GetMessages(ByRef roomId As String, Optional ByRef forces As Boolean = False) As String
Dim url As String
url = "rooms/" & roomId & "/messages?force=" & IIf(forces, "1", "0")
GetMessages = Api("GET", url, "")
End Function
Public Function SendMessage(ByRef roomId As String, ByRef Message As String) As String
Dim url As String, param As String
url = "rooms/" & roomId & "/messages"
param = "body=" & Message
SendMessage = Api("POST", url, param)
End Function
ここでChatworkとの各やり取りを定義しています。
定義したのは、
- Chatworkから自分のコンタクトを取得
- 特定のルームのメッセージを取得
- 特定のルームへメッセージを送信
になります。これらを使って、コンタクトを取得→新しいメッセージを確認→返信。
という操作をExcelから行っていきます。
定時実行プログラム
以下のコードを、標準モジュール名「Batch」で作成します。
Private Const BOT_ACCOUNT As String = "チャットボットとして使いたいアカウント名"
Dim rooms As Object
Public Sub Initialize()
' 対象のチャットルームを全て取得
Set rooms = JsonConverter.ParseJson(Chatwork.GetContacts)
Call Refresh
End Sub
Public Sub Refresh()
' 前回確認時の時刻を保持(Unix Timeを変換)
Dim lastModified As Double, targetRange As Range
Set targetRange = ThisWorkbook.Worksheets(1).Range("A1")
If targetRange.value = "" Then
targetRange.value = (DateDiff("s", "1970/1/1 9:00", Now) + 32400) / 86400 + 25569
End If
lastModified = (CDbl(targetRange.value) - 25569) * 86400 - 32400
targetRange.value = (DateDiff("s", "1970/1/1 9:00", Now) + 32400) / 86400 + 25569
' 全てのチャットルームから、依頼がないか確認します
Call CheckRequest(lastModified)
' 次回実行の予約
Dim scheduled As Date
scheduled = DateAdd("s", 300, Time)
Application.OnTime scheduled, "Batch.Refresh"
End Sub
Private Sub CheckRequest(ByRef lastModified As Double)
' イテレータの準備
Dim room As Object
Dim messages As Object
Dim message As Object
' ルーム単位でメッセージを受信し、新着情報がないか確認します
For Each room In rooms
Set messages = JsonConverter.ParseJson(Chatwork.GetMessages(room("room_id"), True))
For Each message In messages
' 前回更新以降のメッセージでなければ処理しません
If CLng(message("send_time")) <= lastModified Or message("account")("name") = BOT_ACCOUNT Then
GoTo MESSAGE_CONTINUE
End If
' 返信します
Call Chatwork.SendMessage(room("room_id"), Message("body") & "に対するExcelからの返信です")
MESSAGE_CONTINUE:
Next
Next
End Sub
ここまで作成し、BatchモジュールのInitializeを実行することで、5分毎に対象アカウントへのメッセージを確認します。
実行すると、対象アカウントに対する全てのメッセージに返信を行うため、そのまま実行する際は注意してください。
例ではInitialize起動後に受け取った全てのメッセージに対して返信を行っていますが、message.bodyの内容に応じて返信メッセージを変更することで、チャットボットとしてやり取りを行うことができます。