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

記事のトップ画像

こんにちはリビットです。

最近ではクラウドサービスやフレームワークが多く普及してきたことで、チャットボットを作るのがかなり容易になりました。

ただ手段は増えましたが、サービス間のデータをやり取りするためにサーバーが必要だったり、環境を整えることができない。という方もいらっしゃるかと思います。

そういった方のために、今回はExcelを使ってチャットボットを作る方法をご紹介します。

ちなみに今回紹介するのは、プラットフォームにChatworkを使用してチャットボットを作成する手順となりますが、VBAのコードを作成したいクラウドサービス用に書き換えることで、Slackのような別のサービスでも使用可能です。

前提

チャットボットを作成する上で、ユーザーが使用する側のサービスからデータを受け取る手段として主流なのは、Webhookと呼ばれる技術です。

これはクラウドサービス側で設定するもので、クラウドサービス内でユーザが何らかのアクションを起こした際に、予め指定したサーバにPOSTリクエストを送信するという機能です。当然、そのクラウドサービスがWebhookに対応していない場合は使用できません。

ですが今回は、そもそもサーバがありません。

ではどうするか。

ポーリングします。

ポーリングについて

ポーリング(polling)とは、通信やソフトウェアにおいて、競合を回避したり、送受信の準備状況を判断したり、処理を同期したりするために、複数の機器やプログラムに対して順番に定期的に問い合わせを行い、一定の条件を満たした場合に送受信や処理を行う通信及び処理方式のことである。

Wikipedia

詳細に理解していただく必要はありませんが、重要なのは「プログラムに対して順番に定期的に問い合わせを行い」というところです。

サービス側からデータを受け取ることはできないので、こちら側から定期的に新しいアクションがないか問い合わせることで、チャットボットを実現します。

ポーリングのデメリット

この方法はチャットボットを実現する上で、Webhookに比べていくつも劣る部分があります。

レスポンスが悪くなる

Webhookであれば、

  1. ユーザーがアクション
  2. ChatworkがPOSTリクエスト
  3. リクエストを受け取り、返信
  4. ユーザーが確認

上記の流れでユーザーとのやり取りを行うこととなりますが、ポーリングの場合は、

  1. ユーザーがアクション
  2. Excelから問い合わせ
  3. アクションを起こしている場合は、返信を作成
  4. ユーザーが確認

という流れになります。

Webhookであれば、ユーザーがアクションを起こした直後に、

「こんなアクションがあったよ!」

とクラウドサービス側から教えてくれるのに対して、ポーリングでは、

「何かアクション起こしてない?」

とこちらから問い合わせるまでユーザーのアクションに気づくことができず、返事が遅くなってしまう可能性があります。

頻繁に問い合わせることで解決できるかもしれませんが、問い合わせにはAPIを使用します。

Chatworkの場合はAPIの利用回数に上限が設けられており、2020年10月時点では5分間に300回です。

利用規約に違反する可能性がある

上述のAPI使用の観点から、クラウドサービスの利用規約に違反する可能性があります。

この方法を利用する場合は、APIの使用に関して、利用規約を確認しておきましょう。

VBAの作成

前置きが長くなりましたが、ここから実際にチャットボットに使用するVBAコードを紹介していきます。

JSONの取り扱い

Chatwork APIでは、リクエストの返信がJSON形式になります。

そのため、JSONファイルを取り扱うライブラリであるJsonConverterを使用します。ファイルは以下より取得できます。

VBA-JSON GitHub

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の内容に応じて返信メッセージを変更することで、チャットボットとしてやり取りを行うことができます。