Twitter自己的研究发现,64%的客户在该平台上宁愿给支持手柄发信息,也不愿给企业打电话,75%的客户希望在15分钟内收到回复。在这篇文章中,我们将看看如何将Twitter的支持请求引入Twilio Flex,满足客户的需求,同时利用Twitter的直接消息API的互动功能,如 "快速回复 "和 "按钮"。
开始使用
在我们进入实施阶段之前,请确保你已经完成了以下工作。
- 建立一个Twilio账户(如果你还没有)。
- 创建一个Flex项目使用的支持演练。
- 完成Twitter的Webhooks入门指南中的第1和第2步,你将创建一个Twitter应用并请求访问账户活动API。
编排聊天记录
虽然Flex没有提供开箱即用的Twitter频道,但它确实为开发者提供了建立和添加自定义频道的能力。为了在Twitter上的客户和Flex上的代理之间建立一个开放的对话渠道,我们需要一个应用程序来监听每一方的更新,并相应地转发信息,使客户和代理保持同步。该应用程序应该。
- 听取直接信息发送到我们的Twitter账户的事件,并通过Flex聊天频道将其转发给Flex。如果客户已经在与代理聊天,信息将通过现有的聊天频道发送,否则将创建一个新的频道。我们将此称为
[POST] /fromTwitter端点。 - 听取Flex代理发送消息的事件,并将其转发给Twitter,作为客户的直接消息。我们将其称为
[POST] /fromFlex端点。 - 对Twitter的挑战-回应检查作出回应,以便注册的网络钩子保持有效。这是完成你先前开始的Twitter webhook配置的其余部分的先决条件。我们将把它称为
[GET]/fromTwitter端点。
下面的顺序图展示了消息的来回流动。
[GET] /fromTwitter端点
Twitter参考了在Python、JavaScript和Ruby中构建满足其挑战-响应检查的加密响应令牌的代码示例。下面是一个Express-Node.js端点的例子。
app.get('/fromTwitter', (req, res) => {
const crcToken = req.query.crc_token;
const hmac = createHmac('sha256', process.env.TWITTER_CONSUMER_SECRET)
.update(crcToken)
.digest('base64');
const resToken = `sha256=${hmac}`;
res.status(200).json({ response_token: resToken });
});
一旦这个端点建立并运行,你就可以完成Twitter的Webhooks入门指南中的第3和第4步,你将注册应用程序以接收Webhooks,并将你的Twitter账户订阅到更新。Twitter的开发者关系团队也提供了一个网络应用,你可以用它来加速这一过程。
[POST] /fromTwitter端点
现在我们准备开始接收来自Twitter的直接信息事件。我们将用来转发消息的聊天频道需要一个identity 属性--聊天中客户的唯一标识符。在这种情况下,逻辑标识符是客户的Twitter手柄,它被作为消息事件的一部分提供。首先,我们将从请求中提取Twitter手柄和ID,以及消息正文,并将这些传递给一个名为sendMessageToFlex() 的函数。
app.post('/fromTwitter', (req, res) => {
if (req.body.direct_message_events) {
const users = req.body.users;
const customer = users[Object.keys(users)[0]];
const twitterHandle = customer.screen_name;
const twitterId = customer.id;
// Check to make sure this is a message sent from a customer rather than a Direct
// Message we sent from our application
if (twitterHandle !== process.env.TWITTER_CO_HANDLE) {
const msg =
req.body.direct_message_events[0].message_create.message_data.text;
sendMessageToFlex(twilioClient, msg, twitterHandle, twitterId);
}
}
res.sendStatus(200);
});
sendMessageToFlex() 又被分成两个函数。
getChannel()- 返回与客户相关的聊天频道sendMessageToFlex()- 通过聊天频道向Flex发送消息
getChannel() 为客户返回一个聊天频道,可以是找到一个活跃的、预先存在的频道,也可以是创建一个新的。在这个阶段,还配置了一个网络钩子,以便[POST] /fromFlex 端点在代理回复时收到更新。
为了创建一个新的Flex聊天频道,必须提供一个Flex流程,告诉Twilio我们要使用的聊天服务,以及如何处理传入的信息。请访问此帖,通过 "创建一个新的Flex流程 "部分,注意您的Flex Flow SID ,并在下面的示例代码中使用Flex Chat Service SID 。该帖子的其余部分提供了关于如何在Flex上协调消息的宝贵见解,以及添加自定义聊天频道的指导,我们的实施将基于其中的大部分。
const getChannel = async (
twilioClient,
flexFlowSid,
flexChatServiceSid,
twitterHandle,
twitterId
) => {
const channelExists = await hasOpenChannel(twilioClient, twitterHandle);
// If we try and create a new channel that already exists, Twilio will just return the
// existing channel SID which we need anyway
const flexChannel = await twilioClient.flexApi.channel.create({
// Use newly created Flex Flow SID
flexFlowSid,
identity: twitterHandle,
chatUserFriendlyName: `Twitter with @${twitterHandle}`,
chatFriendlyName: twitterId,
target: `@${twitterHandle}`,
});
// Duplicating webhooks would result in duplicate flows between Twitter and Flex
// which we need to avoid so we apply the channelExists check here
if (!channelExists) {
await twilioClient.chat
.services(flexChatServiceSid)
.channels(flexChannel.sid)
.webhooks.create({
type: 'webhook',
configuration: {
method: 'POST',
url: `${process.env.NGROK_URL}/fromFlex`,
filters: ['onMessageSent'],
},
});
}
return flexChannel
};
const hasOpenChannel = async (twilioClient, twitterHandle) => {
const channels = await twilioClient.chat
.services(process.env.FLEX_CHAT_SERVICE)
.channels.list();
const openChannelExists =
channels.filter((c) => {
const { from, status } = JSON.parse(c.attributes);
// Channels are automatically set to INACTIVE when they are closed by a Flex Agent
return from.includes(twitterHandle) && status !== 'INACTIVE';
}).length > 0;
return openChannelExists;
};
sendChatMessage() ,处理最终发送消息的Flex。
const sendChatMessage = async (flexChatServiceSid, flexChannelSid, twitterHandle, msg) => {
// Source: https://www.twilio.com/blog/add-custom-chat-channel-twilio-flex
const params = new URLSearchParams();
params.append('Body', msg);
params.append('From', twitterHandle);
const res = await fetch(
`https://chat.twilio.com/v2/Services/${flexChatServiceSid}/Channels/${flexChannelSid}/Messages`,
{
method: 'post',
body: params,
headers: {
'X-Twilio-Webhook-Enabled': 'true',
Authorization: `Basic ${base64.encode(
`${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}`
)}`,
},
}
);
return res;
};
到此为止,你应该能够向你的Twitter帐户发送直接信息,并看到它出现在Flex。给它一个测试!
[POST] /fromFlex端点
在[POST] /fromTwitter 端点,我们通过为聊天频道配置的webhook订阅Flex消息事件。除了这些事件中包含的消息正文,我们还需要从相关的聊天频道资源中提取客户的Twitter ID。这个ID是Twitter通过他们的直接消息API发送消息时需要的关键用户标识,其检索在下面的getUserFromChannel() 函数中处理。
app.post('/fromFlex', async (req, res) => {
// Source will be 'API' for Twitter customer side, 'SDK' for Flex agent side
if (req.body.Source === 'SDK') {
const channelId = req.body.ChannelSid;
const twitterId = await getUserFromChannel(
twilioClient,
channelId
);
const msg = req.body.Body;
await sendMessageToTwitter(twitterClient, msg, twitterId);
}
res.sendStatus(200);
});
const getUserFromChannel = async (twilioClient, channelId) => {
const chat = await twilioClient.chat
.services(process.env.FLEX_CHAT_SERVICE)
.channels(channelId)
.fetch();
const twitterId = chat.friendlyName;
return twitterId;
};
为了完成这个流程,通过创建一个新的直接信息事件,在sendMessageToTwitter() 函数中向客户发送一个回复。请注意,在这个例子中,我们使用twit作为Twitter客户端来处理API请求,但下面的event 对象是Twitter所期望的标准格式。
const sendMessageToTwitter = async (twitterClient, msg, twitterId) => {
twitterClient.post(
'direct_messages/events/new',
{
event: {
type: 'message_create',
message_create: {
target: {
recipient_id: twitterId,
},
message_data: {
text: msg,
},
},
},
},
);
};
现在你可以测试这个过程的端到端,在Twitter和Flex之间来回发送消息。你可以尝试打开和关闭对话,以及处理来自多个Twitter账户的消息,以确保事情按预期进行。
交互式聊天功能
借助于Twitter的快速回复和按钮,可以开发丰富的聊天体验。这些功能是通过对[POST] /fromFlex 端点中用于发送直接信息的event 对象的一些小改动而实现的。
为了让Twitter在消息中显示快速回复,message_data 属性应该包含一个quick_reply 对象。下面你可以看到一个例子,说明event 对象应该是什么样子。
"event": {
"type": "message_create",
"message_create": {
"target": {
"recipient_id": "844385345234",
},
"message_data": {
"text": "Ok, this weeks 2 boxes are:",
"quick_reply": {
"type": "options",
"options": [
{
"label": "Missed a delivery 📦",
"description": "Reschedule a date and time",
},
{
"label": "General information 🍓",
"description": "Available fruit boxes and what's in season"
},
],
},
},
},
}
在这个实现中,代理使用一个 "选项 "关键字和一个选项列表来触发快速回复。一个解析函数,getTwitterMessageData() ,在关键字上分割代理的消息,并将消息的剩余部分打包成一个选项数组,有labels 和descriptions 。然后,由该函数返回的message_data 对象被作为新的直接消息事件的一部分发送。这只是在Flex用户界面中为代理人提供快速回复的许多可能方法之一。
const getTwitterMessageData = (msg) => {
let formattedMsg = msg;
let optionsObj = {};
if (msg.includes('Options')) {
const msgSplit = msg.split('Options');
const optionsSplit = msgSplit[1].split(',');
formattedMsg = msgSplit[0];
const options = optionsSplit.map((op) => {
const optionDescSplit = op.split('-');
const option = {
label: optionDescSplit[0],
};
if (optionDescSplit.length > 1) {
option.description = optionDescSplit[1];
}
return option;
});
// Package the Quick Reply object
optionsObj = {
quick_reply: {
type: 'options',
options,
},
};
}
const messageData = { text: formattedMsg, ...optionsObj, };
return messageData;
};
接下来的步骤为您的Twitter集成
这个概念验证的代码,以及交互式按钮的实现,可在这个 repo。你可能想探索一些进一步的整合,如使用Twitter的反馈卡收集支持满意度分数,或开发一个Flex插件,显示Twitter的标志和颜色,使代理很容易知道他们正在与客户沟通的Twitter。
Mark Marshall是Twilio的高级解决方案工程师。他的联系方式是mmarshall [at]。 twilio.com.