TwilioのTaskRouterでオペレータに通話を割り振る

# はじめに

Twilioで開発していて、「ブラウザで着信なんかはできたけど、複数人のオペレータに割り振る時はどうするの？:thinking:」てな時のためのTaskRouterです。

大まかに以下のような流れになります。

1. 電話がかかってくる(発信者はオペレータに繋がるまで待つ)
1. (TwiMLを返すAPIが)タスクを作って「WorkFlow」に入れる
1. 「WorkFlow」はタスクを順番に「Activities(オペレータの状態)」に割り振っていく
1. 割り振られたアクティビティの状態にあるオペレータが電話をとる(例えば、オンラインアクティビティにふられたら、オンラインの状態のオペレータが電話をとる)
1. 通話が完了したらタスクの状態をCompleteにする

今回は最低限動かすために、「タスクを作ってWorkFlowに入れる処理」、「タスクを取り出してアクティビティに割り振る処理」だけ書きます。

# Twilio Consoleで準備
ひとまずWorkflow、Activities、Workerなどはあらかじめ作ってしまいます。

- 公式サイトの[TaskRouter](https://jp.twilio.com/console/taskrouter/workspaces)からWorkspacesを作成。SIDを控える
    - Workspaces > Workflows からWorkflowを作り、SIDを控える
    - Workspaces > Workers から適当な従業員を作る。ひとまずACTIVITYはAvailableにする
    - Workspaces > Activities からAvailableのSIDを控える
 
# APIを用意する:muscle:

Node.js + Expressの例

```typescript
import express = require("express");
import { Twilio, twiml } from "twilio";

const app = express();

// ここでキューにタスクをつっこむ
app.route("/enqueue").post((_, res) => {
  const voice = new twiml.VoiceResponse();
  voice.say("enqueue! enqueue!"); // エンキュー！ エンキュー! と叫ぶ
  // voice.say({ voice: "Polly.Mizuki", language: "ja-JP" }, "日本語でおk");
  const enqueue = voice.enqueue({ workflowSid: "" });
  enqueue.task({}, JSON.stringify({ hi: "test" })); // 追加で送りたい内容とか指定できる
  res.type("text/xml");
  res.send(voice.toString()); // タスク割り当ての命令の入ったTwiMLを返します
});

// タスクを取り出してアクティビティに割り振る
app.route("/assignment").post((_, res) => {
  res.send({
    instruction: "dequeue", // キューを取り出す
    post_work_activity_sid: "", // 割り振り先のアクティビティ
  });
});

// タスクを完了させる
// app.route("/complete").post(async (req, res) => {
//   const taskId = req.body.taskId; // タスクIDをリクエストボディから受けどります
// 
//   const twilio = new Twilio("アカウントのSID", "アカウントのAuthToken");
//   const workspace = twilio.taskrouter.workspaces("WorkspaceのSIDを入れる");
//   await workspace.tasks(taskId).update({ assignmentStatus: "completed" }); // TODO: エラーハンドリング
//   res.send("終了");
// });
```
※`workflowSid`と`post_work_activity_sid`には、控えておいたSIDを入れてください。
(実際に運用するなら.envなどで設定。認証やバリデータなどもつけます:spy:)

詳しくは[公式ドキュメント](https://jp.twilio.com/docs/taskrouter/tutorials/dynamic-call-center-node-express?code-sample=code-handling-twilios-requests-9&code-language=Node.js&code-sdk-version=default)や[公式Git](https://github.com/TwilioDevEd/task-router-node)にあります。

できたら[ngrok](https://ngrok.com/)などを使って、どこかに公開します。

# 作ったAPIをTwilioに設定する

- Twilioの[電話番号の設定](https://jp.twilio.com/console/phone-numbers/incoming)で、VoiceのWEB HOOKに/enqueueのURLを設定
- TaskRouterのWorkflowsの設定で、AssignmentCallbackに/assignmentのURLを設定
- TaskRouterのWorkersの設定で、ATTRIBUTESに`{"contact_uri":"client:オペレータ名"}`を入れる

オペレータ名は、クライアントが着信を受け取るためのトークン生成時に設定したやつです。

```typescript:こんな感じの
import { jwt } from "twilio";

const capability = new jwt.ClientCapability({
  accountSid: "accountSid",
  authToken: "authToken",
});
capability.addScope(
  new jwt.ClientCapability.IncomingClientScope("オペレータ名")
);
```

「そんなもの書いた覚えはない:rage:」という方いたらごめんなさい。。。
オペレータ名でなくても、コールバックには[特定の番号やSIPが設定できます](https://jp.twilio.com/docs/taskrouter/handle-assignment-callbacks)。

# ひとまず動かす
電話をかけると、「エンキュー！ エンキュー!」と言われたあと音楽が流れ、オペレータ宛に着信がくると思います:pray:

ただ、タスクをまだ完了させていないので、同じアカウントで２回目の着信を受けられません:cry:
[TaskRouter.js](https://jp.twilio.com/docs/taskrouter/js-sdk/workspace/worker#completetask)を使うか、[API](https://www.twilio.com/docs/taskrouter/api/task#update-a-task-resource)を作ってから[Enqueue](https://jp.twilio.com/docs/voice/twiml/enqueue)のAction属性で指定してあげるなどがいいかもですね（taskIdが取りにくいので、task一覧からwrapping状態のtaskを探して、complete状態にするなど）

できたら次はWorkspaces、Workers、TaskQueue、Workflowをプログラムから構成できるようにするといいかもですね