正如你可能知道的,Twilio已经发布了Flex Conversations。这是在Flex生态系统内使协调工作更加可靠和直接的一大步。在这里的一篇博文中,你可以了解更多关于Flex Conversations的新好处。Flex Conversations的推出也为实现新的很酷的功能提供了可能性,因为Conversations API为管理参与者、地址和对话生命周期提供了广泛的工具。
我们的客户经常询问的一个联络中心功能是不活动超时。为了保持你的座席的高生产力,有必要有一种自动的方式来跟踪被放弃的任务并将其清理掉。有了对话API的状态计时器功能和Twilio功能,现在就可以在Flex中实现不活动超时了。在这篇博文中,我将指导你通过配置和代码来实现这一目的。
教程的先决条件
在我们开始构建之前,你需要确保你有一个Twilio的账户。你可以在这里免费注册。
一旦你的Twilio账户创建完毕,你就可以继续创建Twilio Flex项目。
你可能需要确保对话已启用。如果你为本教程创建一个新的项目,对话应该是默认启用的。
如果你有一个现有的项目,你想迁移到对话,看一下这里的博文的入门部分。
要创建一个Flex帐户,在你的Twilio控制台导航到Flex,然后**概述,**然后点击"创建我的Flex帐户"按钮。
一旦你得到了你的Flex帐户,请注意以下细节 - 你以后会需要它们。
有了这些,你就可以开始了。
该解决方案将如何工作
我们将利用Twilio功能和对话API,即其状态定时器。这里的目标是能够配置一个超时 - 这重置每次有一个新的消息,无论是从客户或代理 - 完成任务的Flex,如果没有新的消息是在超时期间发送。此外,我们想让客户知道,通过发送消息,对话已经超时了。
在我们开始写代码之前,这里是我们的高水平计划。
代码
- 我们将编写一个名为
on_conversation_state_updated.ts的函数。顾名思义,这个函数将在对话状态通过onConversationStateUpdated事件发生变化时被调用,这将作为清理不活动任务的触发器。该函数将有以下职责。- 将与对话相关的任务设置为
completed状态 - 向客户发送一条消息,说明会话已经超时了
- 将对话状态设置为
closed(这是一个必要的步骤,以便为进一步的通信准备一个对话,使其能够被重复使用)
- 将与对话相关的任务设置为
- 我们将编写另一个名为
on_reservation_accepted.ts.的函数,该函数将在任务被代理接受的时刻首先被调用,因此选择这个名字是为了反映该函数将被调用的TaskRouter事件:onReservationAccepted。该函数将有两个职责。- 在对话上创建一个webhook,与任务相联系,在指向
onConversationStateUpdated事件的URL上触发on_conversation_state_updated.ts函数。 - 在与任务相连的对话上创建一个不活动定时器,其值在环境变量中定义。
- 在对话上创建一个webhook,与任务相联系,在指向
配置
- 我们将配置TaskRouter Workspace,以便在一个
onReservationAccepted事件中向on_reservation_accepted.ts函数的URL发送一个webhook。
下面是我们将建立的解决方案的图示。

开发者环境设置
让我们确保你有你需要的软件。
在本教程中,我将使用Typescript,但用JavaScript也应该很好。
现在我们可以开始编码了
创建项目
我们将首先使用Twilio Serverless Toolkit创建一个项目。对于这个运行,在你的shell中发出以下命令。
twilio serverless:init flex-chat-inactivity-timeout --typescript
这里有几个注意事项。
- 在命令中,我使用
flex-chat-inactivity-timeout作为我的项目名称,请随意使用不同的名称 - 添加
--typescript参数将创建一个准备用于Typescript的项目。如果你喜欢JavaScript,你可以省略这个参数。
完成这一步后,你就有了一个可以在本地环境中运行或直接部署到Twilio Functions的项目。
你会发现在src/functions 文件夹下有几个函数的例子,在src/assets 下有几个资产的例子,你可以安全地删除它们。(或者你可以忽略它们,随你喜欢。)
配置你的环境
为了运行这些代码,我们需要环境变量到位。进入项目根目录下的.env ,并更新文件,使其具有以下键和值。确保将我的占位符替换成你在前面步骤中收集的值。
下面配置中的超时对应于1 minute ,并根据ISO 8601持续时间标准进行格式化。
你可以在Twilio控制台的工作空间部分的TaskRouter下找到TaskRouter工作空间的SID。寻找一个名为 "Flex Task Assignment "的工作区。
ACCOUNT_SID=<Twilio Account SID>
AUTH_TOKEN=<Twilio Auth Token>
TIMEOUT=PT1M
TASK_ROUTER_WORKSPACE=<TaskRouter Workspace SID>
创建功能
现在是时候创建你将在里面工作的文件了。导航到src/functions,并创建两个文件,名为on_reservation_accepted.protected.ts 和on_conversation_state_updated.protected.ts 。
虽然你可以自由地为你的函数选择任何其他名称,但要确保名称以.protected.ts (或.protected.js )结尾,因为这定义了函数的可见性等级。受保护的函数只能从Twilio平台上调用。
下面是on_reservation_accepted.protected.ts 函数的完整代码,其中增加了解释代码的注释。
// Imports global types
import '@twilio-labs/serverless-runtime-types'
// Fetches specific types
import {
Context,
ServerlessCallback,
ServerlessFunctionSignature,
} from '@twilio-labs/serverless-runtime-types/types'
// Environment variables
type Env = {
TIMEOUT: string
}
// Webhook event type with needed fields
type OnReservationAccepted = {
TaskAttributes: string,
}
export const handler: ServerlessFunctionSignature<Env, OnReservationAccepted> = async function (
context: Context<Env>,
event: OnReservationAccepted,
callback: ServerlessCallback,
) {
console.log(event)
// Parse task attributes JSON string into object
let taskAttributes = JSON.parse(event.TaskAttributes)
// Get Conversation SID for task attributes
let conversationSid = taskAttributes["conversationSid"]
// Create a TwilioResponse object
const response = new Twilio.Response()
response.appendHeader('Content-Type', 'application/json')
// Create Conversation Context object
const conversationContext = context
.getTwilioClient()
.conversations
.conversations(conversationSid)
// Create a webhook on the Conversation to be fired on onConversationStateUpdated
// targeting on_conversation_state_updated function
try {
await conversationContext
.webhooks
.create({
target: 'webhook',
configuration: {
url: `https://${context.DOMAIN_NAME}/on_conversation_state_updated`,
method: 'POST',
filters: ['onConversationStateUpdated'],
},
})
} catch (err) {
console.error(err)
response.setStatusCode(500)
return callback(err, response)
}
// Create an inactivity timeout on the Conversation using timeout from environment variable
try {
await conversationContext
.update({
timers: {
inactive: context.TIMEOUT,
},
})
} catch (err) {
console.error(err)
response.setStatusCode(500)
return callback(err, response)
}
// Return success response
console.log('OK')
response.setStatusCode(200)
response.setBody({
ConversationSid: conversationSid,
})
return callback(null, response)
}
这里是on_conversation_state_updated.protected.ts 函数的完整代码,并增加了对代码的解释注释。
// Imports global types
import '@twilio-labs/serverless-runtime-types'
// Fetches specific types
import {
Context,
ServerlessCallback,
ServerlessFunctionSignature,
} from '@twilio-labs/serverless-runtime-types/types'
// Environment variables
type Env = {
TASK_ROUTER_WORKSPACE: string
}
// Webhook event type with needed fields
type OnConversationStateUpdated = {
ConversationSid: string,
ChatServiceSid: string,
StateTo: State,
Reason: string
}
// Conversation state type
type State = 'active' | 'inactive'
export const handler: ServerlessFunctionSignature<Env, OnConversationStateUpdated> = async function (
context: Context<Env>,
event: OnConversationStateUpdated,
callback: ServerlessCallback,
) {
console.log(event)
// Create a TwilioResponse object
const response = new Twilio.Response()
response.appendHeader('Content-Type', 'application/json')
// We proceed only if both conditions are met event.StateTo === 'inactive' AND event.Reason === 'TIMER'
if (event.StateTo !== 'inactive' || event.Reason !== 'TIMER') {
// Nothing to do
response.setStatusCode(200)
return callback(null, response)
}
// Initialize Twilio client
const client = context.getTwilioClient()
// Fetch tasks for Conversation SID from the event
let tasks
try {
tasks = await client.taskrouter
.workspaces(context.TASK_ROUTER_WORKSPACE)
.tasks
.list({
evaluateTaskAttributes: `conversationSid="${event.ConversationSid}"`,
},
)
} catch (err) {
console.error(err)
response.setStatusCode(500)
return callback(err, response)
}
// No tasks found or more than one task found, but both should never happen.
if (tasks.length != 1) {
response.setStatusCode(200)
response.setBody({message: `Tasks found ${tasks.length}`})
return callback(null, response)
}
// Variable to store task instance
let task = tasks[0]
// Completing task in TaskRouter
try {
await client.taskrouter
.workspaces(context.TASK_ROUTER_WORKSPACE)
.tasks(task.sid)
.update({
assignmentStatus: 'completed',
})
} catch (err) {
console.error(err)
response.setStatusCode(500)
return callback(err, response)
}
// Send timeout notification to the customer
try {
await client.conversations
.conversations(event.ConversationSid)
.messages
.create({
body: 'Your session is timed out'
})
} catch (err) {
console.error(err)
response.setStatusCode(500)
return callback(err, response)
}
// Update Conversation state to closed
try {
await client.conversations
.conversations(event.ConversationSid)
.update({
state: 'closed',
})
} catch (err) {
console.error(err)
response.setStatusCode(500)
return callback(err, response)
}
// Return success response
console.log('OK')
response.setStatusCode(200)
response.setBody({
ConversationSid: event.ConversationSid,
TaskSid: task.sid,
})
return callback(null, response)
}
部署到Twilio功能
下一步是将我们的代码部署到Twilio Functions。要做到这一点,在你的项目根部执行以下命令。
npm run deploy
如果你在部署代码时遇到问题,请检查ACCOUNT_SID 和AUTH_TOKEN 环境变量是否在你的.env 文件中正确配置。
一旦部署完成,你将看到你新创建的函数的URLs。它将看起来像下面这样。
Functions:
[protected] https://flex-chat-inactivity-timeout-0000-dev.twil.io/on_conversation_state_updated
[protected] https://flex-chat-inactivity-timeout-0000-dev.twil.io/on_reservation_accepted
复制on_reservation_accepted 函数的 URL - 我们现在准备配置 TaskRouter 工作区。
配置TaskRouter工作区
在你的Twilio控制台,导航到TaskRouter,然后是工作空间部分,然后找到一个名为 "Flex Task Assignment "的工作空间。打开工作区,进入设置。在设置页面,找到 "事件回调 "部分,选择 "特定事件 "单选按钮。确保在事件列表中只有 "接受预订 "复选框被选中。
测试
为了测试我们的解决方案,请采取以下措施。
- 登录到Flex作为一个代理,并确保你是在
Available状态。 - 发送短信到您的Flex项目配置的号码之一(查看配置的电话号码,去Twilio控制台->Flex->消息,并点击 "对话地址 "标签)。
- 接受传入的任务的Flex,并开始与 "客户 "的聊天。
- 等待一分钟,检查任务是否自动完成,你的 "客户 "得到一个消息说,该会议是超时的。
如果你得到这一切: voilà!它正在工作!现在你可以根据自己的需要自由地构建它了。
总结
使用Twilio功能和对话API,我们能够建立一个自动化的方式来处理不活跃的任务清理,以确保我们腾出时间给新的客户聊天。
该项目的完整代码可在GitHub上找到:github.com/kuschanton/…