使用Dialogflow Twilio集成进行WhatsApp情感分析
本教程将告诉你如何建立一个WhatsApp聊天机器人,利用情感分析预测人们对某个特定话题的感受。
我们将使用Dialogflow和谷歌云自然语言处理API来分析推文,构建这个聊天机器人。
我们还将使用Twilio来整合WhatsApp。
前提条件
要跟随我们,你需要。
- 一个[Twilio]账户。
- 一个[谷歌云]账户。
- 一个[Twitter开发者]账户。
- 一个[WhatsApp]账户。
- 一个[Dialogflow]账户。
- 安装有[Node.js]、[npm]和[ngrok]。
创建一个谷歌云账户
用你的信用卡信息创建一个账户,但除非你超过免费层的限制,否则不会向你收费。
此外,新用户可获得[300美元的免费点数]。
在你创建了你的账户或登录后,创建一个New Project 。
对于现有用户,浏览你的仪表板app bar ,点击当前项目名称下拉菜单。

接下来,点击NEW PROJECT 按钮。

然后,为你的项目添加所需的细节,并点击CREATE ,生成一个新项目。

接下来,你需要为该项目启用云自然语言API,以进行情感分析。
要启用它,在你的新项目的主仪表板上找到Getting Started 卡。
点击Explore and Enable APIs ,并在顶部搜索栏中搜索Cloud Natural Language API 。

如果提示你激活计费,选择ENABLE BILLING 。你将不会被收取任何费用。
在Dialogflow中建立一个意图
由谷歌支持的Dialogflow提供了一个理解自然语言的框架。它使我们能够创建沟通的界面。
我们将使用Dialogflow和一个履行网络钩子。履行网络钩子允许你在Dialogflow的在线编辑器中编写JavaScript代码,并将其作为云函数部署。
我们将用它来为WhatsApp聊天机器人建立一个对话体验。如果你没有Dialogflow账户,你应该创建一个。
登录Dialogflow后,点击屏幕左上方的**+创建代理**按钮,创建一个代理。
命名您的代理,然后在谷歌项目标签下,选择您的谷歌云项目来导入。然后点击屏幕顶部的蓝色CREATE按钮。
注意,你需要对Dialogflow有一个基本的了解。
接下来,点击Intents旁边的+按钮,为对话创建一个意图。你会看到一个看起来像这样的屏幕。

在 "培训短语"标题下,点击 "添加培训短语"按钮。
要添加一个training phrase 键入一个用户可能问的问题。例如,人们对Twilio的感觉如何?
在上面的例子中,用户想了解更多关于Twilio的信息。使用Dialogflow自然语言处理,我们可以用一个接受不同参数的实体来替换搜索词。
现在,训练短语可以被改写成这样;人们对X的感觉如何?X ,在这种情况下,是一个可以接受任何参数的实体。
要做到这一点,请输入短语:人们对X的感觉如何,然后用光标突出字母X 。
这时,一个包含实体列表的对话框就会出现。选择@sys.any 作为实体,如下图所示。按return 键,提交训练短语。

对下面的训练词组重复这个过程。
- 什么是X
- 搜索X
- 评价X
- 告诉我关于X的情况

随意创建另一个意图并添加更多的培训短语。
行动和参数
训练短语中的X 是一个实体类型,可以从用户表达中提取任何参数。
换句话说,我们把X ,变成一个可以代表任何东西的变量。
Dialogflow提供了预先定义的系统实体,可以匹配常见的数据类型。
例如,有一些系统实体用于匹配日期、时间、颜色、电子邮件地址等。

响应
我们还需要向聊天机器人添加默认的回应,以防我们的查询没有返回任何结果。
向下滚动到 "回应"部分,点击ADD RESPONSES按钮。
你可以添加以下语句作为意图的默认回应。
- 我还不清楚人们对
$sys.any的感觉! - 对不起,我无法得到任何关于
$sys.any - 试着搜索除此之外的另一个词
$sys.any

履约(Fulfillment
这个意图也需要一个履行的webhook来为用户的查询提供正确的回应。为了使其发挥作用,请将启用该意图的webhook调用切换为开。

启用网络钩子后,点击屏幕上方的蓝色SAVE按钮。
接下来,从左边的菜单中点击 "履行"。将Inline Editor右边的Disabledtoggle设置为Enabled。这允许你在Dialogflow里面写你的webhook代码。

在内联编辑器中添加代码
在index.js文件标签中,添加以下代码。
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
"use strict";
const functions = require("firebase-functions");
const language = require("@google-cloud/language");
const httpRequest = require("request-promise-native");
const { dialogflow } = require('actions-on-google');
上面的代码导入了所有需要的依赖性。
接下来,用你的实际project ID ,替换占位符,如下图所示。
// Create an app instance
const app = dialogflow();
const languageClient = new language.LanguageServiceClient({
projectId: "YOUR_GCLOUD_PROJECTID"
});
上面的代码创建了一个新的Dialogflow 应用程序的实例和一个自然语言服务客户端的实例。注意,它需要你的项目ID才能工作。
创建一个Twitter开发者账户
在继续前进之前,你需要用你现有的Twitter账户或新账户创建一个开发者账户。
导航到developer.twitter.com来创建一个开发者账户。

选择你的应用程序的用例和你想使用Twitter API的原因,填写你的应用程序的描述。
由于申请数量较多,你的应用程序可能需要一些时间才能被批准。
一旦你的申请被批准,继续并创建一个应用程序。
选择密钥和令牌,然后将你的API密钥复制并保存在你可以轻松检索到的地方。
在Search Tweets: 30-Days/Sandbox 下建立一个开发环境,并命名该环境。
我将我的环境命名为testing 。

在获得你的Twitter API Key 和API Secret 之后,回到Dialogflow的内联编辑器。
在index.js 文件中,添加以下代码。
// This is the environment for the Twitter premium search api.
// See: https://developer.twitter.com/en/docs/tweets/search/api-reference/premium-search.html
const TWITTER_ENV = "testing";
// This is the endpoint for the Twitter premium search api.
// See: https://developer.twitter.com/en/docs/tweets/search/api-reference/premium-search.html
const TWITTER_SEARCH_ENDPOINT = "30day";
// Constructed a complete base url for the API call.
const TWITTER_SEARCH_URL = "https://api.twitter.com/1.1/tweets/search/"
.concat(TWITTER_SEARCH_ENDPOINT)
.concat("/")
.concat(TWITTER_ENV)
.concat(".json");
// Load the API credentials into constants for readability’s sake
const CONSUMER_KEY = 'YOUR_TWITTER_API_KEY';
const CONSUMER_SECRET = 'YOUR_TWITTER_SECRET_KEY';
process.env.DEBUG = "dialogflow:debug"; // enables lib debugging statements
接下来,添加下面的代码。
/*
Filter out the retweets from the results returned from the Twitter premium search API.
* @param searchResults
* @returns {Array}
*/
function filterRetweets(searchResults) {
let filtered = [];
searchResults.forEach((result) => {
if (!result.retweeted_status) {
// Check if details about a retweet exist, if they do, do not execute this block
filtered.push(result); // Since this is not a retweet, push it
}
});
return filtered;
}
/*
* Pluck the ids from the result array containing tweet objects returned from the Twitter premium search API.
* @param searchResults
* @returns {Array}
*/
function extractText(searchResults) {
let tweets = [];
searchResults.forEach((result) => {
tweets.push(result.text); // Push the tweet's ID into the array
});
return tweets;
}
这段代码定义了一个辅助函数,用于从搜索结果中删除所有转发,并将其保存到一个数组中。
上述代码还从API响应中提取所有推文,并将它们添加到一个数组中。
接下来,添加以下代码来访问你的应用程序上的intent() 方法。它传递意图的名称和两个参数。
然后它将输入值设置为params.any ,并请求Twitter的API。
// The Sentiment intent
app.intent('Sentiment', (agent, params) => {
const inputEntity = params.any;
let request_options = {
url: TWITTER_SEARCH_URL,
oauth: { consumer_key: CONSUMER_KEY, consumer_secret: CONSUMER_SECRET },
json: true,
headers: {
"content-type": "application/json"
},
body: { query: inputEntity.concat(" lang:en"), }
};
// Send a request to the twitter api, then return the results (body)
return httpRequest.post(request_options).then((body) => {
// Get the results without all the retweets and extract the text of the tweet to be analyzed
let tweetText = extractText(filterRetweets(body.results));
// Create a nlpClient request to detect the sentiment of all the tweets fetched
return languageClient.analyzeSentiment({
document: {
content: tweetText.join(" "),
type: "PLAIN_TEXT"
}
}).then(results => {
// Get the overall document score (all the tweets concatenated together)
const sentiment = results[0].documentSentiment.score;
// 0 -> 0.1 is a somewhat neutral score
if (sentiment >= 0 && sentiment <= 0.1) {
agent.add(`People have mixed feelings about ${inputEntity}.`);
// Less than 0 is usually negative
} else if (sentiment < 0) {
agent.add(`People feel negatively about ${inputEntity}.`);
// Greater than 0.1 usually indicates positive.
} else {
agent.add(`People feel positively about ${inputEntity}.`);
}
}).catch(err => {
console.log(err);
agent.add("Sorry, something went wrong.");
});
}).catch(err => {
console.log(err);
agent.add("Sorry, something went wrong.");
});
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
上述代码将Twitter的响应提交给自然语言处理API。
NLP返回一个情感分数(结果),你把它保存在一个变量中。
它还根据NLP API的评分是积极的还是消极的,从Action中获取一个响应。
conv.close 每当Action作出反应时,结束对话。
将你的代码添加到package.json文件中
在你的内联编辑器中,切换到package.json 标签。
在这个文件内复制并粘贴以下内容。
{
"name": "dialogflowFirebaseFulfillment",
"description": "This is the default fulfillment for a Dialogflow agent using Cloud Functions for Firebase",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"engines": {
"node": "8"
},
"scripts": {
"start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
"deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
},
"dependencies": {
"firebase-admin": "^5.13.1",
"firebase-functions": "^2.0.2",
"request": "^2.87.0",
"request-promise-native": "^1.0.5",
"dialogflow-fulfillment": "^0.5.0",
"@google-cloud/language": "2.0.0"
}
}
上述内容保存了你的项目的所有相关元数据和依赖关系。
在这一点上,你已经完成了Dialogflow履行Webhook的设置。按下内嵌编辑器中的DEPLOY按钮,部署你的云功能。
将Twilio与Dialogflow集成
在你的电脑上创建一个新的目录。
在这个文件夹中,初始化一个Node.js项目。
mkdir whatsapp-sentiments
cd whatsapp-sentiments
npm init -y
我们需要安装以下软件包。
- [Nodemon]是一个帮助开发基于Node.js的应用程序的工具,它可以在文件发生变化时自动重新启动服务器。
- [Dialogflow]包是一个Node.js的API客户端。
- [Twilio Node Helper库]提供对Twilio API的访问。
- [Express]可以让你构造一个Web应用来处理特定URL的多个HTTP请求。
- [Body-parser]是一个中间件,用于从传入的请求中提取主体。
- [UUID]用于识别需要独特的信息。
npm install twilio nodemon express body-parser dialogflow uuid
接下来,在你的_whatsapp-sentiments_目录下创建这些文件。
touch index.js && touch twilio.js && touch dfservice.js
获取你的谷歌云项目服务密钥
在你的谷歌云项目仪表板上,启动菜单栏,并导航到IAM & ADMIN ,在其下拉菜单上,点击Service Accounts 。
你会看到你的项目ID服务账户,点击它,打开服务账户的详细信息。
在Keys 下,点击ADD KEY ,你会看到一个弹出的对话框,如下图所示。
勾选JSON 密钥类型并点击CREATE 按钮。JSON文件将被自动下载。

为WhatsApp设置Twilio沙盒
导航到Twilio控制台的WhatsApp部分,激活Twilio沙箱。
沙盒允许你使用一个共享号码测试Twilio WhatsApp API,而无需等待WhatsApp批准你的Twilio号码。
勾选激活沙盒后,您需要从您的WhatsApp号码发送一条特定的信息到提供的通用Twilio WhatsApp号码。
将Twilio WhatsApp号码添加到您的联系人列表中并发送消息。
完成这一步后,你应该看到下面的成功画面。

添加代码到文件中
将你最近下载的Google cloud Project service account key 的JSON文件添加到你的代码目录,并将其重命名为config.js 。
进入你的主仪表板Twilio控制台,找到你的Account SID 和Auth Token 。
复制并粘贴这些值到config.js 文件中,如下所示。

module.exports = {
accountSid: 'YOUR_TWILIO_ACCOUNT_SID',
authToken: 'YOUR_TWILIO_AUTH_TOKEN',
"DF_LANGUAGE_CODE": "en-US", //Dialogflow default language
//The rest of the code below will contain all the details of your Google Cloud service account keys
}
用这些占位符替换你的Twilio account SID 和Auth token :'YOUR_TWILIO_ACCOUNT_SID' 和 'YOUR_TWILIO_AUTH_TOKEN'。
在index.js文件中添加这段代码。
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('uuid');
const DF = require("./dfservice");
const twilio = require('./twilio');
const app = express();
//port for local host
const port = 80;
//creates a new sessionID
const sessionIds = new Map();
这段代码允许你的应用程序使用express,body-parser,uuid,Dialogflow file, 和Twilio 库。
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post('/', async(req, res) => {
const sender = req.body.From;
const text = req.body.Body;
const from = req.body.To;
setSessionAndUser(sender);
let response;
try {
/// Response from Dialogflow
response = await DF.sendTextQueryToDialogFlow(sessionIds, sender, text);
/// Response sent to Twilio
await twilio.sendText(from, response.fulfillmentText || response.queryText, sender).then(m => console.log(m.sid))
} catch (error) {
console.log(error)
}
});
/// To check if theres an existing sessionID or not
function setSessionAndUser(senderID) {
if (!sessionIds.has(senderID)) {
sessionIds.set(senderID, uuid.v1());
}
}
app.listen(port, () => console.log(`Magic on ${port}`));
每当一个消息被发送,一个新的会话开始,你会看到发送者的号码,接收者(Universal Twilio号码)和消息。
文本查询将被发送到Dialogflow,然后响应以履行文本的形式发送到Twilio,你将从Twilio收到一条WhatsApp消息。
在dfservice.js文件中加入这段代码。
const dialogflow = require('dialogflow');
const config = require('./config');
const credentials = {
client_email: config.client_email,
private_key: config.private_key,
};
const sessionClient = new dialogflow.SessionsClient(
{
projectId: config.project_id,
credentials
}
);
module.exports = {
async sendTextQueryToDialogFlow(sessionIds, sender, text, params = {}) {
const sessionPath = sessionClient.sessionPath(config.project_id, sessionIds.get(sender));
///Request payload
const request = {
session: sessionPath,
queryInput: {
text: {
text: text,
languageCode: config.DF_LANGUAGE_CODE, ///Dialogflow default language
},
},
queryParams: {
payload: {
data: params
}
}
};
const responses = await sessionClient.detectIntent(request);
return responses[0].queryResult;
},
async sendEventToDialogFlow(sessionIds, handleDialogFlowResponse, sender, event, params = {}) {
const sessionPath = sessionClient.sessionPath(config.GOOGLE_PROJECT_ID, sessionIds.get(sender));
const request = {
session: sessionPath,
queryInput: {
event: {
name: event,
parameters: structjson.jsonToStructProto(params), //Dialogflow's v2 API uses gRPC. You'll need a jsonToStructProto method to convert your JavaScript object to a proto struct.
languageCode: config.DF_LANGUAGE_CODE,
},
}
};
const responses = await sessionClient.detectIntent(request);
const result = responses[0].queryResult;
handleDialogFlowResponse(sender, result);
}
}
上面的代码使用接收文本和sessionPath的默认请求有效载荷,向Dialogflow发送文本查询。
将检测文本的意图,并将响应发送到Twilio。
在twilio.js文件中加入这段代码。
const config = require('./config');
const client = require('twilio')(config.accountSid, config.authToken);
function sendText(from, message, to) {
console.log(from,message, to);
return client.messages
.create({
from: from,
body: message,
to: to
})
}
module.exports = { sendText }
上面的代码加载Twilio库,并从通用的Twilio WhatsApp号码发送一个消息到你的号码。
用Ngrok创建一个webhook
使用Ngrok,你可以不费吹灰之力将你的本地托管网络服务器部署到互联网上。
你可以下载Ngrok命令行界面(CLI)来开始使用。
打开你的命令行,用这个命令在localhost上运行你的项目。
Nodemon index.js
运行这个命令后,你会得到Magic on 80 的成功信息。
接下来,打开你的Ngrok CLI ,用这个命令启动一个HTTP隧道,转发到你的本地80端口。
Ngrok http 80
在你的终端,你应该看到这个。

复制Ngrok提供的URL,因为我们将在webhook中使用它。
添加webhook到Twilio
到你的Twilio控制台,导航到可编程的消息标签。
导航到sandbox settings ,在WHEN A MESSAGE COMES IN 字段中粘贴Ngrok URL ,并保存它。
你的屏幕应该看起来像这样。

测试webhook
你已经成功地将你的webhook与Twilio连接起来,现在你可以在WhatsApp上测试聊天机器人,问它一些问题,看看它的反应。
下面是我得到的结果。


结论
恭喜你!你已经成功地使用Twilio创建了一个聊天机器人。你已经成功地使用Dialogflow、WhatsApp和谷歌云自然语言处理API创建了一个聊天机器人,使用Twitter的API对Twitter上的推文进行情感分析。
有了这些知识,你现在可以使用外部API、Twilio、谷歌云API和Dialogflow为不同类型的应用程序建立聊天机器人。