【译】如何将Twitter直接信息添加到Twilio Flex中

1,088 阅读5分钟

Twitter自己的研究发现,64%的客户在该平台上宁愿给支持手柄发信息,也不愿给企业打电话,75%的客户希望在15分钟内收到回复。在这篇文章中,我们将看看如何将Twitter的支持请求引入Twilio Flex,满足客户的需求,同时利用Twitter的直接消息API的互动功能,如 "快速回复 "和 "按钮"。

twilio-twitter-flex-view.png

开始使用

在我们进入实施阶段之前,请确保你已经完成了以下工作。

编排聊天记录

虽然Flex没有提供开箱即用的Twitter频道,但它确实为开发者提供了建立和添加自定义频道的能力。为了在Twitter上的客户和Flex上的代理之间建立一个开放的对话渠道,我们需要一个应用程序来监听每一方的更新,并相应地转发信息,使客户和代理保持同步。该应用程序应该。

  1. 听取直接信息发送到我们的Twitter账户的事件,并通过Flex聊天频道将其转发给Flex。如果客户已经在与代理聊天,信息将通过现有的聊天频道发送,否则将创建一个新的频道。我们将此称为[POST] /fromTwitter 端点。
  2. 听取Flex代理发送消息的事件,并将其转发给Twitter,作为客户的直接消息。我们将其称为[POST] /fromFlex 端点。
  3. 对Twitter的挑战-回应检查作出回应,以便注册的网络钩子保持有效。这是完成你先前开始的Twitter webhook配置的其余部分的先决条件。我们将把它称为[GET]/fromTwitter 端点。

下面的顺序图展示了消息的来回流动。

Flow between Twitter and Flex

[GET] /fromTwitter端点

Twitter参考了在PythonJavaScriptRuby中构建满足其挑战-响应检查的加密响应令牌的代码示例。下面是一个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 对象的一些小改动而实现的。

twilio-twitter-flex-quick-replies.png

为了让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() ,在关键字上分割代理的消息,并将消息的剩余部分打包成一个选项数组,有labelsdescriptions 。然后,由该函数返回的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.