Twilio Flexで営業時間判定

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilioエバンジェリストの高橋です。

[Twilio Flex Advent Calendar 2021](https://qiita.com/advent-calendar/2021/twilio-flex) 23日目の記事となります。
実は、過去に[Twilio Studioで営業時間判定を行う](https://qiita.com/mobilebiz/items/4fd4ba20b369654a1ccf)という記事を書いたのですが、当然Flexでも使えるわりにはTwilio CLIを使ったものだったのでちょっとハードルが高いかなと思い、CLIなしでもいけるようにリライトしたものです。

また、21日目に[@PlanetMeron](https://qiita.com/PlanetMeron)さんに、[Twilio Flex&Studioで営業時間外に着信した時に留守電音声を流す](https://qiita.com/PlanetMeron/items/a141dd9c330aa190f5c6)という素敵な記事を書いていただいたので、ぜひそちらも参考にしてください。

# シナリオ

今回は、Twilio Studio から営業時間判定をする方法を解説します。
Twilio Studio はドラッグ・アンド・ドロップでフローが作成できるため簡単にコールフローが作れますが、今回のようなケースでは Studio だけではなかなかうまくいきません。
そこで、Studio と Twilio Functions を連携させて、Functions 側で営業日や営業時間を判定するようにします。

## 準備

すでにFlexがセットアップされているものとします。
セットアップ方法は、以下の記事を御覧ください。
[Twilio Flexの始め方（セットアップ編）](https://qiita.com/mobilebiz/items/d06a29214b437339d226)

## Twilio Functions

でははじめに、Twilio Functionsを使って営業時間判定をするコードを作成していきます。

- Twilio管理コンソールにログインします。
- 左側のスライドメニューから**Functions**を選択し、さらに**Services**を選択します。
![スクリーンショット 2021-12-21 9.53.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/85c0ce26-2c30-a5f8-98df-dd85bba972c4.png)

- すでにアドベントカレンダーの過去記事などでServiceを作っている方は「Flex」というサービスがリストされますので、そちらを選択します。
- もし「Flex」が無い方は、**Create Service**ボタンを押して、あたらしく「Flex」というサービスを作成してください。
![スクリーンショット 2021-12-21 11.26.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b6854131-af4b-615b-0946-905423e59261.png)

- **Add**ボタンを押して、プルダウンから「Add Function」を選択します。
![スクリーンショット 2021-12-21 11.28.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/89c56c99-4161-ef40-2803-272004cfca83.png)

- **Path**欄には、「/check-open」と入力します。
- 右側のコード欄を以下のコードで置き換えます。

```javascript
const axios = require("axios");

// 営業日、営業時間の設定
const BUSINESS_HOURS = {
  dayOfWeek: ["月", "火", "水", "木", "金", "土", "日"], // 営業日を記述、休業日は削除（例： ["月", "火", "水"]）
  startTime: "9:00", // 営業開始時間を24時間表記で指定
  endTime: "18:00", // 営業終了時間を24時間表記で指定
  holiday: true, // 祝日営業するならtrue
};

exports.handler = async function (context, event, callback) {
  try {
    // 結果を保持する変数
    let ret = true;

    // 現在の日時を取得
    let now = new Date();
    now.setTime(now.getTime() + 1000 * 60 * 60 * 9); // JSTに変換

    // 本日の曜日を取得
    const dayOfWeek = ["日", "月", "火", "水", "木", "金", "土"][now.getDay()];

    // 本日の営業開始時間を設定
    const startTime = new Date(now.getTime());
    startTime.setHours(
      BUSINESS_HOURS.startTime.split(":")[0],
      BUSINESS_HOURS.startTime.split(":")[1]
    );

    // 本日の営業終了時間を設定
    const endTime = new Date(now.getTime());
    endTime.setHours(
      BUSINESS_HOURS.endTime.split(":")[0],
      BUSINESS_HOURS.endTime.split(":")[1]
    );
    console.log(now, startTime, endTime);

    // 本日が祝日か判定（http://s-proj.com/utils/holiday.html）
    options = {
      method: "GET",
      url: "http://s-proj.com/utils/checkHoliday.php?kind=ph",
    };
    const res = await axios(options);
    const isHoliday = res.data === "holiday";

    if (BUSINESS_HOURS.dayOfWeek.filter((d) => d === dayOfWeek).length === 0) {
      // 休業日と判定
      ret = false;
    } else if (now < startTime || now > endTime) {
      // 営業時間外と判定
      ret = false;
    } else if (!BUSINESS_HOURS.holiday && isHoliday) {
      // 祝日と判定
      ret = false;
    }
    callback(null, { Result: ret });
  } catch (err) {
    console.error(err);
    callback(err);
  }
};
```

### コードの説明

営業日かどうかの判定基準となる営業日と営業時間の指定を行います。
5行目の配列が営業日となります。おやすみの曜日は配列から削除してください。
6〜7行目が営業時間の指定です。それぞれ24時間表記で指定します。
8行目は祝日判定です。祝日をお休みにする場合は`true`にし、祝日が休みではない場合は`false`にします。

修正が必要なのは上記のみですので、それ以外はそのままでOKです。

このコードが実行されると、現在の日時曜日で営業時間内かどうかが判定されJSON形式で結果が戻ります。

```json
{
  Result: true or false
}
```
trueが営業時間内、falseが営業時間外です。

- **Save**ボタンを押します。

## 依存関係の設定

- **Settings**メニューの中の**Dependencies**を選択します。
![スクリーンショット 2021-12-21 11.44.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/49b02659-ea54-32ff-1a68-349474d5970c.png)

![スクリーンショット 2021-12-21 11.49.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/96b9cd08-b244-21b4-ed30-f93ab879ffba.png)

- **MODULE**欄に「axios」、**VERSION**欄に「0.24.0」と入力して、**Add**ボタンを押します。

## デプロイ

- **Deploy All**ボタンを押します。
- しばらくしてデプロイが終了すると、以下のように緑色のアイコンに変わります。

![スクリーンショット 2021-12-21 16.36.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/d72a3118-a7ca-b75c-09cf-004cc434a56e.png)

# Studioフローの設定

では、今作成したFunctionsをStudioフローから呼び出しましょう。

- Twilio 管理コンソールにログインします。
- 左側のスライドメニューから、**Studio**を選択します。
- 青い＋アイコンを押して、新しいフローを作成します。
![スクリーンショット 2021-12-21 16.52.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/d12bc9b2-c78b-df95-fcb2-21ecb8d7ab9b.png)

- **FLOW NAME**に「Voice IVR BusinessHours」と入力します。
- **Next**ボタンを押します。
![スクリーンショット 2021-12-21 16.54.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/2d8aac2d-e13f-b145-6bd2-f006e37dae79.png)

- **Start from scratch.** が選択されていることを確認して、**Next**ボタンを押します。

ウィジェットを配置していきます。
まずはFunctionを呼び出すウィジェットを配置していきます。

![スクリーンショット 2021-12-21 16.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/295379be-86f1-40ab-1ed0-ce010e04a773.png)

- **Run Function**ウィジェットを配置します。
- **Trigger**ウィジェットの**Incoming Call**と書かれた赤い丸とつなぎます。
- **WIDGET NAME**に「CheckOpen」と入力します。
- **SERVICE**のプルダウンから「Flex」を選択します。
- **ENVIRONMENT**のプルダウンから「ui」を選択します。
- **FUNCTION**のプルダウンから先程作成したcheck-openを選択します。
- **Save**ボタンを押します。

次に、Functionの戻り値によって分岐するウィジェットを配置していきます。

![スクリーンショット 2021-12-21 17.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/bb02b871-1f2a-f525-0597-6224d39d1b79.png)

- **Split Based On**ウィジェットを配置します。
- **CheckOpen**ウィジェットの**Success**と書かれた線とつなぎます。
- **WIGET NAME**には「Split」と入力します。
- **VARIABLE TO TEST**には「widgets.CheckOpen.parsed.Result」と入力します。最後に「.Result」と入力することを忘れないでください。
- **Transitions**タブに移動します。

![スクリーンショット 2021-12-22 8.16.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b4674be2-147b-0230-27fb-5eba1cc59c0e.png)

- **IF VALUE EQUAL_TO TRUE**のプルダウンリストから「Equal To」を選択します。
- その下の入力欄には「true」と入れます。
- **Set condition**リンクを押します。
- **Save**ボタンを押します。

次に、営業時間内にFlexに繋ぐためのウィジェットを配置していきます。

![スクリーンショット 2021-12-22 8.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/e098d1d5-6f42-3a63-0166-ad03af509ea4.png)

- **Send To Flex**ウィジェットを配置します。
- **Split**ウィジェットの**If value equal_to true**の線とつなぎます。
- **WIDGET NAME**には「SendToAnyone」と入力します。
- **WORKFLOW**のプルダウンから「Assign to Anyone」を選択します。
- **CHANNEL**のプルダウンから「Voice」を選択します。
- **ATTRIBUTES**には、`{"type":"inbound","name":"{{trigger.call.From}}"}`と入力します。
- **Save**ボタンを押します。

最後に、営業時間外だった場合にガイダンスを流すウィジェットを配置します。

![スクリーンショット 2021-12-22 8.28.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/5395b5c3-bb00-9f77-18fb-7c0e0d75f6ae.png)

- **SayPlay**ウィジェットを配置します。
- **Split**ウィジェットの**No Condition Matches**の線とつなぎます。
- **WIDGET NAME**には「SayGuide」と入力します。
- **SAY OR PLAY MESSAGE OR DIGITS**のプルダウンから「Say a Message」を選択します。
- **TEXT TO SAY**の入力欄に、「申し訳ございません。ただいま営業時間外となっております。お手数ですが、後ほどおかけ直しください。」と入れます。
- **LANGUAGE**のプルダウンから「Japanese」を選択します。
- **MESSAGE VOICE**のプルダウンから「[Polly]Mizuki」を選択します。
- **Save**ボタンを押します。

以上でStudioフローは完成です。

- **Publish**ボタンを押して、設定をパブリッシュします。

## テスト

購入済みの Twilio 番号に、上記のフローを割り当ててテストしてください。  
営業時間外の場合は、その旨のガイダンスが流れます。営業時間内の場合は、Flex側に着呼します。

## 謝辞

祝日判定には、[こちら](http://s-proj.com/utils/holiday.html)の API を利用させていただいております。

## 免責事項

本プログラムの動作についての保証はいたしませんので、自己責任の範疇でご利用ください。

# まとめ

今回は、Flexの運用では必要となる営業時間判定について説明しました。
営業時間の判定は、TaskRouter側で実装することもできるのですが、Studio内で判定してしまったほうがわかりやすいと思います。
また、この記事では営業日判定には曜日と祝日しか利用していませんが、それ以外の条件（夏季休暇など）を入れる場合は、Functionを色々と変更してみると良いでしょう。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング（SMS /チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2015 Hall of Doers<br />2019 Twilio Champions