Skip to content

Latest commit

 

History

History
430 lines (327 loc) · 18 KB

File metadata and controls

430 lines (327 loc) · 18 KB

一、构建实时短信呼叫中心

我们将为我们的第一个项目建立一个方便的短信呼叫中心。

此呼叫中心将处理来自用户的传入 SMS 消息;它不会处理语音,只是短信。我们将在其他几章中讨论语音。

你想知道 Flybase 的一个美好之处吗?它很容易与其他服务集成。

在这一章中,我们将一起使用 Flybase 和 Twilio 来构建一个实时短信呼叫中心。

这可以用作客户帮助台,客户发送文本消息请求帮助,代理从他们的 web 浏览器发送回复。

实际的电话工作将由 Twilio 处理,Flybase 将存储数据并实时显示聊天内容。我们将使用 Node.js 来发送和接收文本消息,并使用 HTML 前端来处理实际的聊天。

设置

我们将使用一些工具来构建这个应用。在继续之前,您需要设置好这些:

如果你还没有,现在就注册( https://app.flybase.io/signup )一个免费的 Flybase 帐户,然后创建一个新的应用。您将在呼叫中心使用您的应用。

入门指南

我们首先需要设置我们的 Node.js 应用。

除了 Twilio 和 Flybase 模块,我们将使用 express 框架( http://expressjs.com/ )来设置我们的 Node web 服务器,以接收来自 Twilio 的 POST 请求,因此我们需要安装 Express 包。我们还将使用 body-parser 模块,所以我们也将安装它。

让我们创建我们的package.json文件:

javascript
{
      "name": "sms-contact-center",
      "version": "0.0.1",
      "description": "SMS Contact Center powered by Flybase, Twilio and Node.js",
      "main": "app.js",
      "repository": "https://github.com/flybase/sms-contact",
      "scripts": {
              "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [
            "twilio",
            "Flybase",
            "sms"
      ],
      "author": "Roger Stringer",
      "license": "MIT",
      "dependencies": {
            "twilio": "~1.6.0",
            "ejs": "~0.8.5",
            "express": "~3.4.8",
            "flybase": "~1.7.8",
            "node-buzz": "~1.1.0",
            "moment": "~2.5.1",
            "less-middleware": "~0.2.1-beta",
            "body-parser" : "~1.4.2",
            "method-override" : "~2.0.2"
      },
      "engines": {
            "node": "0.10.26"
      }
}

保存该文件,并从终端运行以下命令:

javascript
npm install

这将创建一个包含我们想要使用的所有模块的node_modules文件夹。

让我们设置我们的文件夹结构;创建一个名为views的文件夹。这是我们将保持前端的地方。

现在,创建一个名为“public”的文件夹这将托管我们的静态文件。在该文件夹中,创建一个css文件夹和一个js文件夹;我们稍后将回到这些。

在 app.js 文件的开头,我们需要 require express 并将其初始化为一个名为 app 的变量。

我们还将使用 bodyParser 中间件( https://github.com/expressjs/body-parser )来方便地使用我们将在 POST 请求中获得的数据。

创建一个名为app.js的新文件,并需要 twilio、express 和 flybase 包:

javascript
var express = require('express');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var twilio = require('twilio');
var  path = require('path');

var app = express();
app.set('views', path.join(process.cwd(), 'views'));
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + '/public'));

var port = process.env.PORT || 8080; // set our port

var client = twilio('ACCOUNTSID', 'AUTHTOKEN');
var twilio_number = 'YOUR-NUMBER';

var api_key = "YOUR-API-KEY";
var appname = "YOUR-FLYBASE-APP";
var collection = "smscontact";
var messagesRef = require('flybase').init(appname, collection, api_key);

// backend routes =========================

ACCOUNTSIDAUTHTOKENYOUR-NUMBER替换为您将使用的 Twilio 凭证和 Twilio 帐户中的电话号码。

然后,用您的 Flybase API 键替换YOUR-API-KEYYOUR-flybase-APPsmscontact,并创建一个集合来使用。如果您还没有创建收藏,当您第一次保存数据时,系统会自动为您创建一个收藏,因此如果您愿意,您可以将收藏名称设置为smscontact

Flybase 使用集合来组织应用中的数据,因此一个应用可以有几个集合。如果您熟悉关系数据库,这相当于一个表。

这是我们应用的开始。接下来,我们将告诉它当新的短信进来和代理回复短信时该做什么。

发送和接收文本

Twilio 使用 webhooks ( https://en.wikipedia.org/wiki/Webhook )来让您的服务器知道何时有消息或电话进入我们的应用。我们需要设置一个端点,我们可以告诉 Twilio 将它用于消息传递 webhook。

我们将为/message添加一个用一些 TwiML (Twilio 标记语言, www.twilio.com/docs/api/twiml )响应的路由。TwiML 是一组基本指令,当你收到来电或短信时,你可以用它来告诉 Twilio 该做什么。我们的代码将如下所示:

javascript
app.post('/message', function (request, response) {
      var d = new Date();
      var date = d.toLocaleString();

      messagesRef.push({
            sid: request.param('MessageSid'),
            type:'text',
            direction: "inbound",
            tstamp: date,
            fromNumber:request.param('From'),
            textMessage:request.param('Body'),
            fromCity:request.param('FromCity'),
            fromState:request.param('FromState'),
            fromCountry:request.param('FromCountry')
      });

      var resp = new twilio.TwimlResponse();
      resp.message('Thanks for the message, an agent will get back to you shortly.');
      response.writeHead(200, {
            'Content-Type':'text/xml'
      });
      response.end(resp.toString());
});

这将监听任何传入的短信,并将它们存储在你的 Flybase 应用中。

一旦收到消息,我们使用 Twilio Node 库初始化一个新的TwimlResponse。然后我们使用消息关键字( www.twilio.com/docs/api/twiml/sms/message )来设置我们想要用什么来响应消息。在这种情况下,我们只需说“感谢您的消息,代理人会尽快回复您。”然后,我们将响应的内容类型设置为text/xml,并发送我们构建的 TwimlResponse 的字符串表示。

每当客户向我们设置的电话号码发送消息时,它都会向他们发送响应并将消息存储在 Flybase 中。如果代理正在监视客户端,那么他们会立即看到消息,并可以发送回复。

现在,让我们添加一条名为/reply的路线。当我们的代理想要回复一条消息时,我们将通过 AJAX 调用它:

javascript
app.post('/reply', function (request, response) {
      var d = new Date();
      var date = d.toLocaleString();

      messagesRef.push({
            type:'text',
            direction: "outbound",
            tstamp: date,
            fromNumber:request.param('From'),
            textMessage:request.param('Body'),
            fromCity:'',
            fromState:'',
            fromCountry:''
      });

      client.sendMessage( {
            to:request.param('To'),
            from:twilio_number,
            body:request.param('Body')
      }, function( err, data ) {
            console.log( data.body );
      });
});

这将在我们的 Flybase 应用中存储回复作为出站回复,并将消息发送给客户。

最后,让我们设置我们的服务器监听端口8080,并告诉它当我们从浏览器中查看它时该做什么:

javascript
// frontend routes =========================

// route to handle all angular requests
app.get('*', function(req, res) {
    res.render('home', {
        apikey:api_key,
        appname:appname,
        collection:collection
    });
});

var server = app.listen(port, function() {
      console.log('Listening on port %d', server.address().port);
});

现在我们已经构建了我们的服务器,我们需要告诉 Twilio 使用这个消息 URL 作为我们的消息请求 URL。

向您的 Twilio 号码发送短信,您应该会收到回复。如果你不知道,看看 Twilio 应用监视器( www.twilio.com/user/account/developer-tools/app-monitor )来帮助确定哪里出了问题。

这是我们呼叫中心的后端部分;它监听传入的短信,将它们存储在我们的 Flybase 应用中,然后在代理回复时发送回复。

现在,我们需要构建我们的代理系统,在这里代理可以观察收到的消息并回复它们。

我们现在就来建造它。

客户

我们让 Node.js 应用监听要发送和接收的消息。现在让我们设置我们的客户端,这是代理将从他们的 web 浏览器中看到的内容。

当有消息进来时,我们会显示一个显示消息的聊天框,然后发送回复。

首先,让我们创建视图。在/views文件夹中,创建名为home.ejs的文件:

  HTML
<!doctype html>
<html>
<head>
      <link href='http://fonts.googleapis.com/css?family=Lato:400,300italic,400italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
      <link rel="stylesheet" type="text/css" href="http://angular-ui.github.com/ng-grid/css/ng-grid.css" />
      <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
      <link rel="stylesheet" type="text/css" href="/css/style.css">

      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
      <script src="https://cdn.flybase.io/flybase.js?latest=1"></script>
      <script src="/js/chat.js"></script>

      <title>SMS Contact Center, powered by Flybase and Twilio</title>
</head>
<body>
      <div class='container'>
            <br />
            <div class="well">
                  <p class='homefont'>Welcome to your SMS Contact Center</p>

                  <p class='homefont'>This call center is the start of a modern day call center.</p>

                  <p class='homefont'>Take a look around and give us a try.</p>
            </div>
            <hr/>
            <h3>Incoming messages</h3>
            <div id="templateContainer"></div>
      </div>
      <script>
            $(function(){
// initializes our Flybase object
                   var flybaseRef = new flybase("<%= apikey %>", "<%= appname %>", "<%= collection %>");
// start our chatManager.
                   var myChatManager = new chatManager( flybaseRef );
                   myChatManager.updateChats();
            });
      </script>
</body>
</html>

这个文件将作为我们的 HTML 文件,我们使用 EJS,所以我们可以传递我们的 Flybase 设置,而不必在多个地方配置它们。EJS 可以方便地将模板功能添加到 Node.js 应用中。

现在,让我们创建我们的 CSS。在我们之前创建的/public/css文件夹中,创建一个名为 style.css 的新文件:

css
body{font-size:12pt;font-family:helvetica}
.chatWindow{float:left;margin:20px;border:1px solid #000;width:300px;background:#e5e5e5;border-radius:5px}
.chatName{margin-bottom:10px;background:#666;color:#fff;padding:4px}
.messages{padding:4px}
.message_outbound{color:blue;text-align:right}
.tstamp{font-size:9px;padding:2px;margin-bottom:10px;border-bottom:1px dotted #666;color:#666}
.error{color:red;text-align:center}
.messageForm textarea{float:left;width:220px;margin:5px}

最后,我们希望设置我们的应用的大脑。我们把最大的文件留到了最后。

public/js 文件夹中,新建一个名为 chat.js 的文件:

chat.js
var chatManager = function(flybaseRef) {
      this.flybaseRef = flybaseRef;
};

chatManager.prototype = {
      chats: [],
      getChat: function(fromNumber) {
            var foundChat = null;
            for (c = 0; c < this.chats.length; c++) {
                  if (this.chats[c].from == fromNumber) {
                        foundChat = this.chats[c];
                  }
            }

            if (foundChat == null) {
                  foundChat = new chat( this.flybaseRef );
                  foundChat.init(fromNumber);
                  foundChat.displayTemplate();
                  this.chats.push(foundChat);
            }
            return foundChat;
      },
      updateChats: function() {
            var _this = this;
            this.flybaseRef.once('value', function (data) {
                  data.forEach( function(message){
                        var row = message.value();
                        _this.getChat( row.fromNumber ).addMessage(
                              row.textMessage,
                              row.tstamp,
                              row.direction
                        );
                  });
            });
            this.flybaseRef.on('added', function (data) {
                  var row = data.value();
                  _this.getChat( row.fromNumber ).addMessage(
                        row.textMessage,
                        row.tstamp,
                        row.direction
                  );
            });
      }
};

var chat = function(flybaseRef) {
      this.flybaseRef = flybaseRef;
};
chat.prototype = {
      init: function(name) {
            this.from = name;
            this.chatName = 'chat-' + this.from;
            this.buttonName = 'submit-' + this.from;
            this.textName = 'reply-' + this.from;
      },
      replyMessage: function(message) {
            var _this = this;
            $.ajax({
                  type: "POST",
                  url: "/reply",
                  data: {
                        'To': this.from,
                        'Body': message,
                        'From': this.from
                  },
                  dataType: "json",
                  success: function(data) {
                        // your message was sent
                  }
            });
      },
      displayTemplate: function() {
            var content = '<div class="chatName">Chat with ' + this.from + '</div> \
            <div class="messages" id="' + this.chatName + '"></div> \
            <div class="messageForm"><textarea id="' + this.textName + '"></textarea><button id="' + this.buttonName + '">Reply</button></div> \
        </div>';

            content = '<div class="chatWindow" id="' + this.tmplName + '">' + content + '</div>';

            $('#templateContainer').append(content);
            var _this = this;

            $('#' + this.buttonName).click(function() {
                  _this.replyMessage($('#' + _this.textName).val());
                  $('#' + _this.textName).val('');
            });
      },
      addMessage: function(message, tstamp, direction) {
            $('#' + this.chatName).append("<div class='message_" + direction + "'>" + message + "<div class='tstamp'>" + tstamp + "</div></div>");
      }
};

我们的 chatManage 类是这样设置的,当它加载时,它首先使用value事件触发器获取一个保存的文本消息列表,并按照发送它们的电话号码显示它们。

我们将所有往来于同一号码的消息视为一个会话,因此对于每个聊天会话,我们会看到一个显示代理和客户之间消息的框,以及一个用于发送新消息的文本框。

然后,我们使用added事件触发器监听任何新消息,然后将它们显示在适当的聊天框中。

chat类告诉我们的应用如何显示聊天框,以及如何处理发送新的回复。

在这种情况下,当代理发送消息时,我们将它发布到我们的后端/reply路由,在那里它被保存到我们的 Flybase 应用,然后作为文本消息发送给客户。

我们还存储消息来自的方向,或者是inbound或者是outbound。这样,我们可以设计每条消息的样式,使其看起来与您在手机上查看聊天记录时相似。客户的文本将出现在左侧,代理的回复将出现在右侧。

现在让我们启动我们的应用:

javascript
node app.js

我们已经告诉我们的应用在端口 8080 上运行,所以如果你去你的网络浏览器并输入http://localhost:8080/,你应该看到你的呼叫中心。

顺便说一句,如果你在本地运行这个,你需要确保在进入下一步之前已经运行了 ngrok。如果你以前没有用过 ngrok ( https://ngrok.com/ ),Twilio 的凯文·维尼里已经整理了一个很棒的教程( www.twilio.com/blog/2013/10/test-your-webhooks-locally-with-ngrok.html )来帮助你入门。

摘要

我们做到了!既然你已经构建了一个简单的短信呼叫中心应用( https://github.com/flybaseio/sms-contact ),那么你就有机会用它来创造一些东西了。

接下这个项目,继续做下去。一些想法是,你实际上可以完全删除/reply AJAX 调用,而是创建一个outbound文本队列来存储消息,然后向出站集合添加一个added侦听器,该侦听器既可以向客户发送回复,又可以将其添加到message集合中,这样它就会出现在聊天窗口中。

这将消除对 AJAX 调用的需要,并且在一次向几个客户发送多个回复的情况下增加一些队列支持。