2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
はじめに
みなさん、こんにちは。
KDDIウェブコミュニケーションズ、Twilioエバンジェリストの高橋です。
今回は、Twilio Flex の後処理中に、別の着信を受ける方法について解説します。
後処理について
多くのコールセンターでは、お客様との電話対応が終了した時点で、どのような内容であったかを記録する必要があります。
そのため、Twilio Flex では後処理中は新しい着信は受けないようになっています。
しかし、一部のお客様では後処理中にも別の着信を受けたいというニーズがあります。もちろん、通話中には別の着信は受けられませんので、通話が終了している後処理に限っての話です。
オペレーターの着信可能数
そもそもオペレーターが同時にさばけるタスクの制限については、オペレーター(Flex では Worker と呼びます)ごとにどのチャネルを同時にどのくらい着信するかを決めることができるようになっています。
具体的には、TaskRouter の中の Workers で、各自のプロパティページを開くと、その人のキャパシティが見られます。
上のスクリーンショットはデフォルトのキャパシティです。
音声通話とビデオについては、同時に処理することができないために、どちらもキャパシティは「1」に設定されていることがわかります。
では、ここの数字を上げるとどうなるでしょうか?
実は、後処理中にも着信が受けられるようになります。ただし、通話中にも次の着信が入ってきてしまいますし、それを受けようとすると Flex がクラッシュします。
よって、単純にここの数値を上げるだけではうまくいきません。
そこで今回のプラグインの登場です。
シナリオ
待受中に着信を受けるためには、このキャパシティを上げるのが良さそうです。ただし、待受中になった時点であげないと上記のようにうまくありませんので、まずは待受状態になったことを検知して、キャパシティを1つあげます。
そしてタスクが終了(待受中が解除されたら)、キャパシティを1つ減らします。
このロジックでうまくいきそうです。
タスクイベントを検知する
待受中の検出には Flex Events が使えます。
待受になったことを検知するイベントは、Flex UI 2.x からの実装になります。
コードはこんな感じになります。
manager.events.addListener('taskWrapup', async (task: ITask) => {
if (task.channelType === 'voice') {
console.log(`🐸 taskWrapup`);
}
});
同様に、タスクが終了したときのコードは以下のようになります。
manager.events.addListener('taskCompleted', async (task: ITask) => {
if (task.channelType === 'voice') {
console.log(`🐸 taskCompleted`);
}
});
キャパシティを更新する
Worker のキャパシティは、以下のAPIで可能です。
ただし、Flex から直接APIを叩くしくみは用意されていないので、ここは Twilio Functions を用意することにします。
コードは以下の通りです。
const Twilio = require('twilio');
exports.handler = async function (context, event, callback) {
const response = new Twilio.Response();
response.appendHeader('Access-Control-Allow-Origin', '*');
response.appendHeader('Access-Control-Allow-Methods', 'OPTIONS, POST, GET');
response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');
try {
// Get parameters
const { workerSid, action } = event;
if (!workerSid || !action) throw new Error('Parameter(s) error.');
if (!action.match(/inc|dec/)) throw new Error('Parameter(s) error.');
// Get environment variables
const { WORKSPACE_SID } = context;
// Create twilio client
const client = context.getTwilioClient();
// Search the voice channel sid
const workerChannels = await client.taskrouter.v1
.workspaces(WORKSPACE_SID)
.workers(workerSid)
.workerChannels.list();
console.dir(workerChannels);
const voiceChannels = workerChannels.filter(
(channel) => channel.taskChannelUniqueName === 'voice',
);
if (voiceChannels.length === 0)
throw new Error(`This worker haven't a voice channel`);
// Get current capacity
const voiceChannel = await client.taskrouter.v1
.workspaces(WORKSPACE_SID)
.workers(workerSid)
.workerChannels(voiceChannels[0].sid)
.fetch();
// Update worker's task channel capacity
await client.taskrouter.v1
.workspaces(WORKSPACE_SID)
.workers(workerSid)
.workerChannels(voiceChannel.sid)
.update({
capacity:
action === 'inc'
? voiceChannel.configuredCapacity + 1
: voiceChannel.configuredCapacity > 1
? voiceChannel.configuredCapacity - 1
: voiceChannel.configuredCapacity,
});
response.appendHeader('Content-Type', 'text/plain');
response.setBody('OK');
callback(null, response);
} catch (err) {
console.error(err.message ? err.message : err);
response.appendHeader('Content-Type', 'text/plain');
response.setBody(`Error: ${err.message}`);
callback(null, response);
}
};
ちょっと長いですが、まずは Voice のタスクチャンネルを検索して、その値を増やす、もしくは減らすしくみが実装されています。
この Function を呼ぶためのパラメータとして、WorkerSid と、増やすか減らすかの action が必要です。
WorkerSid は先程のイベントで渡される task オブジェクト内に記載されています。
Flex から Function を呼び出す
ここまでできれば、あとは Flex から Function を呼び出すだけです。
先程の待受中を検知したイベントはこんな感じになります。
import * as Flex from '@twilio/flex-ui';
import { ITask, Manager } from '@twilio/flex-ui';
import axios from 'axios';
// Get environment variables
const { FLEX_APP_FUNCTIONS_DOMAIN } = process.env;
export default function wrapupAction(manager: Flex.Manager) {
manager.events.addListener('taskWrapup', async (task: ITask) => {
if (task.channelType === 'voice') {
console.log(`🐸 taskWrapup`);
console.dir(task);
const url = `https://${FLEX_APP_FUNCTIONS_DOMAIN}/change-capacity?workerSid=${task.workerSid}&action=inc`;
const res = await axios.post(url);
console.log(`🐸 taskWrapup capacity incremented.`);
}
});
}
axios を使って Function を呼び出しています。その際に、workerSidとactionパラメータを付与しています。
同様に、タスクが完了したときのコードは以下のようになります。
import * as Flex from '@twilio/flex-ui';
import { ITask, Manager } from '@twilio/flex-ui';
import axios from 'axios';
// Get environment variables
const { FLEX_APP_FUNCTIONS_DOMAIN } = process.env;
export default function completedAction(manager: Flex.Manager) {
manager.events.addListener('taskCompleted', async (task: ITask) => {
if (task.channelType === 'voice') {
console.log(`🐸 taskCompleted`);
console.dir(task);
const url = `https://${FLEX_APP_FUNCTIONS_DOMAIN}/change-capacity?workerSid=${task.workerSid}&action=dec`;
const res = await axios.post(url);
console.log(`🐸 taskWrapup capacity decremented.`);
}
});
}
完成したプラグイン
以上を実装したプラグインを公開してありますので、ぜひご利用ください。
まとめ
最初は、Worker のステータスを使ってできないかと考えたのですが、色々と調べると、着信中も待受中も Worker のステータス自体は変わらないことがわかり、今回の方法を考えました。
とりあえず、こちらでテストしたところはうまく行っていますが、転送とか途中で強制切断されたときとかの動作については完全にテストされているわけではないので、その点がご容赦ください。
何か不具合などがでましたら、コメントいただけると助かります。
Twilio(トゥイリオ)とは
https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング(SMS /チャット)、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。
Twilioに関するご相談などがございましたら、ぜひ毎週水曜日の午後に開催しております相談会をご利用ください。
Twilio相談会(毎週水曜日)