📅

Microsoft Bookings API と Microsoft Bot Framework v4 を使って予約ボットを作ってみた

に公開

はじめに

Microsoft Graph にはプレビューで Microsoft Bookings API があります。これを使うことで Microsoft Bookings で提供されている公開ページ以外でも予約できます。カスタムのページを作ることもできますが、今回はせっかくなので Microsoft Bot Framework と Azure Bot Service を使い、LINE から予約できるようにします。

サンプル コード

https://github.com/karamem0/bookings-bot

実行手順

Microsoft Bookings API について

Microsoft Bookings API を確認すると、すべてのエンドポイントはアプリケーションのアクセス許可が使えず、委任されたアクセス許可しか利用できません。バックエンドで動作するアプリケーションを作成したい場合、この仕様は非常に困ります。仕方がないのでビジネスに対して管理者として設定されているユーザーで OAuth の Resource Owner Password Credentials Grant によってトークンを取得します。この方法は推奨されていないため注意が必要です。

https://docs.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth-ropc?WT.mc_id=M365-MVP-5002941

他に方法がないため、現状はこの方法を選択します。GA になった際には改善されることを期待しています。

Microsoft Graph にはベータ版の SDK を使ってアクセスします。Resource Owner Password Credentials Grant に対応する適切な AuthenticationProvider が提供されていないため、自作します。ボットは長時間動作するため、トークンの有効期限も考慮する必要があります。

private class UsernamePasswordProvider : IAuthenticationProvider
{

    private readonly IPublicClientApplication application;

    private readonly NetworkCredential credentials;

    private AuthenticationResult authenticationResult;

    public UsernamePasswordProvider(IPublicClientApplication application, NetworkCredential credentials)
    {
        this.application = application;
        this.credentials = credentials;
    }

    public async Task AuthenticateRequestAsync(HttpRequestMessage request)
    {
        if (this.authenticationResult == null ||
            this.authenticationResult.ExpiresOn <= DateTime.UtcNow)
        {
            this.authenticationResult =
                await application
                    .AcquireTokenByUsernamePassword(
                        null,
                        this.credentials.UserName,
                        this.credentials.SecurePassword)
                    .ExecuteAsync();
        }
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.authenticationResult.AccessToken);
    }

}

Microsoft Bot Framework について

Microsoft Bot Framework v4 については日本マイクロソフト株式会社の中村憲一郎さんの記事が参考になります。この記事を見れば、基本的な内容は理解できます。

https://qiita.com/kenakamu/items/6dc043cfc1f199032883

今回のようにフローに従ってボットを動作させたい場合、Microsoft Bot Framework v4 では WaterfallDialog を利用します。WaterfallStep にあらかじめ定義したメソッドを追加しておくと、その順番でメソッドが処理されます。各メソッドでは 直前にユーザーから送信されたメッセージを受信する ことと 次にユーザーに対してメッセージを送信する ことを行います。本来は 1 つのメソッドに 2 つ以上の責務を持たせるのは望ましくないため、実際に開発する際はさらに別のメソッドに分割することを推奨します。今回はサンプルのため、そのままにしています。

実際の処理の流れは以下の通りです。

  • Microsoft Bookings のビジネス一覧を選択
  • 選択されたビジネスで提供されるサービス一覧を選択
  • 予約対象の日付を選択
  • 予約対象の時間を選択
  • 名前を入力
  • メールアドレスを入力
  • 予約内容を確認

日時に関しては、Microsoft Bookings には営業時間やサービスごとの時間設定もあるため、内部的には細かい処理を行っています。このあたりは API 側で処理してもらえるとより便利だと感じました。

最終的に収集したデータを使って予約を作成します。Microsoft のドキュメントを見ると登録内容として多くの項目がありますが、最低限の内容でも登録可能です。注意点は以下の 2 点です。

  • CustomerId を指定すること。指定しない場合、同じメールアドレスで顧客データが複数作成されてしまうため、事前にメールアドレスをキーにして顧客データを検索する必要がある。
  • StaffMemberIds を指定すること。指定しない場合、Microsoft Teams 会議が作成されない。今回はスタッフを指定しないため、実行ユーザーの ID を指定する。
private async Task<DialogTurnResult> SubmitBookingAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    if ((bool)stepContext.Result)
    {
        // プロファイルを取得する
        var bookingProfile = await this.bookingProfileAccessor.GetAsync(stepContext.Context, () => new BookingProfile(), cancellationToken);
        try
        {
            // 予約を作成する
            await this.graphServiceClient
                .BookingBusinesses[bookingProfile.BookingBusinessId]
                .Appointments
                .Request()
                .AddAsync(new Graph.BookingAppointment()
                {
                    CustomerId = bookingProfile.BookingCustomerId,
                    CustomerName = bookingProfile.BookingCustomerName,
                    CustomerEmailAddress = bookingProfile.BookingCustomerEmail,
                    End = new Graph.DateTimeTimeZone()
                    {
                        DateTime = bookingProfile.BookingEndTime.ToUniversalTime().ToString("s"),
                        TimeZone = "UTC",
                    },
                    ServiceId = bookingProfile.BookingServiceId,
                    ServiceName = bookingProfile.BookingServiceName,
                    Start = new Graph.DateTimeTimeZone()
                    {
                        DateTime = bookingProfile.BookingStartTime.ToUniversalTime().ToString("s"),
                        TimeZone = "UTC",
                    },
                    StaffMemberIds = new[] { bookingProfile.BookingStaffMemberId }
                });
            // メッセージを送信する
            await stepContext.Context.SendActivityAsync(MessageFactory.Text(StringResources.CompleteBookingMessage));
        }
        catch (Exception ex)
        {
            // メッセージを送信する
            await stepContext.Context.SendActivityAsync(MessageFactory.Text(ex.Message));
        }
    }
    else
    {
        // メッセージを送信する
        await stepContext.Context.SendActivityAsync(MessageFactory.Text(StringResources.CancelBookingMessage));
    }
    // ダイアログを終了する
    return await stepContext.EndDialogAsync(null, cancellationToken);
}

LINE からの実行

作成したコードを Azure App Service にデプロイします。通常通りデプロイすれば問題ありませんが、時刻を扱うため、WEBSITE_TIME_ZONE を指定することを忘れないようにします。

Azure Bot Service を作成し、Azure App Service に関連付けます。LINE Developer に登録して設定すれば完了です。

https://docs.microsoft.com/ja-jp/azure/bot-service/bot-service-channel-connect-line?WT.mc_id=M365-MVP-5002941

実際に実行した画面例です。

予約が完了するとメールが送信されます。

Microsoft Bookings からも予定を確認できます。

おわりに

Microsoft Bookings API はプレビューのため、まだできないことが多く、本番運用では利用できません。ただし API の GA が予告されているため、今後に期待できます。COVID-19 の影響でオンサイトやオンラインにかかわらず予約の需要は高まっているため、ぜひ活用していただきたいです。

https://techcommunity.microsoft.com/t5/microsoft-bookings-blog/a-new-more-powerful-and-customizable-microsoft-bookings-is-here/ba-p/2401907?WT.mc_id=M365-MVP-5002941

Discussion