我在一个荒凉的小屋里,除了我不适应的白日梦、2G的覆盖率,以及台面上摊开的我唯一能写下的菜的配料,什么都没有。只有一个问题。在拼命翻找租来的厨柜后,我发现只有一个汤匙孤零零地挂在一个原本空荡荡的环上。
在这道菜中,成分的比例很重要,但我之前并没有真正坐下来,记住所有的转换率。没有网络服务,甚至连谷歌都救不了我,但我还是尽力用眼看着,做着应付。
结果很糟糕,我发誓永远不会再陷入这样的困境了。
通过一些对初学者友好的JavaScript和Twilio Functions的托管,我们可以做一个测量转换器,可以在任何有手机服务的地方使用。在本教程中,我们就可以做到这一点。愿我们的菜谱不再受制于转换率知识的缺失。
编写并导出一个JSON文件
JSON是一种数据格式,用于将信息存储在一个整洁的小包中,可以在网络上发送,并轻松转换为JavaScript对象,并作为JavaScript对象访问。尽管我们可能会在很多事情上使用JSON,比如用于API调用或在服务器和客户端之间发送信息,但由于JSON格式的限制性,手动编写JSON文件会导致很多问题。
因此,为了简化事情,我们将使用我们的IDE来创建一个JSON文件,该文件将存储我们所有的转换率,这样我们就可以减少我们需要直接放入Twilio Function的代码量。
首先,在你的电脑上导航到一个你想存储项目文件的地方,在终端输入这些命令来建立一个项目目录:
mkdir json-exporter && cd json-exporter
打开你的IDE,进入你刚刚建立的目录。创建一个新的.js 文件,并粘贴以下代码:
const fs = require('fs');
const conversionRates = {
"tsp" : {
"tsp": "1",
"tbsp": "1/3",
"oz": "1/6",
"c": "1/48.692",
"q" : "1/192",
"pt" : "1/96",
"gal": "1/768",
"ml" : "4.929",
"l" : "1/202.9",
},
"tbsp" : {
"tsp" : "3",
"tbsp" : "1",
"oz": "1/2",
"c": "1/16.231",
"q" : "1/64",
"pt" : "1/32",
"gal": "1/256",
"ml" : "14.787",
"l" : "1/67.628",
},
"oz" : {
"tsp": "6",
"tbsp": "2",
"oz": "1",
"c": "1/8",
"q" : "1/32",
"pt" : "1/16",
"gal": "1/128",
"ml" : "29.574",
"l" : "1/33.814",
},
"c" : {
"tsp": "48",
"tbsp": "16",
"oz": "8",
"c": "1",
"q" : "1/4",
"pt" : "1/2",
"gal": "1/16",
"ml" : "236.6",
"l" : "1/4.227",
},
"q" : {
"tsp": "192",
"tbsp": "64",
"oz": "32",
"c": "4",
"q" : "1",
"pt" : "2",
"gal": "1/4",
"ml" : "946.4",
"l" : "1/1.057",
},
"pt" : {
"tsp": "96",
"tbsp": "32",
"oz": "16",
"c": "2",
"q" : "1/2",
"pt" : "1",
"gal": "1/8",
"ml" : "473.2",
"l" : "1/2.113",
},
"gal" : {
"tsp": "768",
"tbsp": "256",
"oz": "128",
"c": "16",
"q" : "4",
"pt" : "8",
"gal": "1",
"ml" : "3785",
"l" : "3.785",
},
"ml" : {
"tsp": "1/4.929",
"tbsp": "1/14.787",
"oz": "1/29.574",
"c": "1/236.6",
"q" : "1/946.4",
"pt" : "1/473.2",
"gal": "1/3785",
"ml" : "1",
"l" : "1/1000",
},
"l" : {
"tsp": "202.9",
"tbsp": "67.628",
"oz": "33.814",
"c": "4.167",
"q" : "1.057",
"pt" : "2.113",
"gal": "1/3.785",
"ml" : "1000",
"l" : "1",
}
};
const jsonContent = JSON.stringify(conversionRates);
fs.writeFile("output.json", jsonContent, 'utf8', function (err) {
if (err) {
console.log("An error occurred while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
运行这段代码。你会注意到,一个名为output.json 的新文件与你的.js 文件出现在同一目录下。请注意这个文件,因为当我们在下一节创建Function时,我们将把它上传到一个叫做Assets的远程目录。
如果你已经对上述代码的工作原理有了扎实的了解,请随意跳到下一节,在那里我们将进行Twilio函数。如果没有,请继续阅读,我将解释上面的代码是怎么做的。
JSON导出器代码解释
这里有很多事情要做,让我们把它分解一下。顶部导入的fs模块是一个非常方便的NodeJS库,它让我们的JavaScript代码与机器的文件系统互动。
fs模块是在创建了一个名为conversionRates 的JavaScript对象后使用的,该对象以conversionRates[ ‘measurement converting from’ ][ ‘measurement converting to’ ] 的形式存储了两个不同测量值之间的所有转换率。当我们稍后在Twilio服务中使用JSON文件时,该对象的用途就会很明显。
在对象定义下面,一个名为stringify的JSON函数接收conversionRates 对象作为参数。这个函数将该对象转换为JSON字符串,并将其分配给变量jsonContent 。在这一点上,jsonContent 不再作为一个JavaScript对象被访问,而正是这个字符串内容被写入文件。
最后,fs writeFile函数将文件名设置为output.json ,将文件内容写入jsonContent ,将文件编码为utf8 (Unicode的编码系统),并使用回调函数function(err) ,以防在写入文件时出现任何错误。
如果成功,控制台将显示**"JSON文件已被保存"。如果没有,它将显示一个错误信息。
在Twilio控制台创建一个服务
现在JSON文件已经准备好了,让我们继续设置一个Twilio服务,在那里我们上传文件并编写一个函数
导航到Twilio控制台,在左边的侧边栏,点击 "探索产品"。然后,在开发者工具下,点击"功能和资产",然后点击蓝色的"创建服务 "按钮。将新服务命名为"转换器",然后点击**"下一步"**。
你应该被重定向到一个新的页面,在那里你可以看到你的空服务,如下图所示:

最需要注意的两个区域是功能和资产。
创建一个资产
点击功能面板正上方的蓝色**"添加+"** 按钮,并点击**"上传文件"。然后,导航到你保存output.json 文件的地方。编辑器中会出现一个新的面板,上面有你的文件,以及表明你想让这个文件成为私有、受保护或公开的单选按钮。选择"公开"**。
在点击蓝色的**"上传 "**按钮之前,将下面的图片保存到你的项目目录中,因为它也将被上传:

点击**"添加其他文件 "**按钮,上传上面的图片。对于那些给我们的Twilio号码发短信但没有提供我们的转换器识别的格式的人来说,这将是一个很好的视觉帮助。如果发送者有手机数据可用,除了正常的短信之外,这张图片也会被发送--以防他们发现自己身处荒凉的小屋。
在这一点上,这就是文件的样子。

确保两个文件都被设置为公开,然后点击**"上传"**。这将在 "资产"下上传两个文件,这两个文件已被保存,但还不可见,因为我们还没有部署该服务。
添加依赖性和环境变量
为了让JavaScript代码从公共链接中访问文件,需要一个名为node-fetch的额外依赖项。在设置标签下面,点击"依赖",一个新的面板将在编辑器中打开。在这里你可以添加任何通过npm可用的依赖项。
在模块文本框中,输入"node-fetch",在版本框中输入**"2.x",像这样:

点击"添加 "按钮,看着这个依赖关系出现在依赖关系表中。
接下来,导航到环境变量,它位于依赖关系的正上方。点击它,打开自己的标签,看起来非常相似。在这里你将创建一个环境变量来存储你的Twilio电话号码。
在"键 "下,输入TWILIO_PHONE_NUM ,在"值 "下输入E.164格式的号码。点击白色的"添加 "**按钮,它应该出现在下面。

同时确保添加你的Twilio账户凭证的复选框被选中。

现在你已经准备好开始编写功能代码了
编写转换功能
点击你用来上传资产的那个蓝色**"添加+"按钮,但现在选择"添加功能"。这将创建一个新的函数路径,默认名称为/path_1 。继续,将这个函数重命名为/convert 。确保该函数被设置为受保护**,然后按"Enter"。
编辑器中已经有一些预先写好的代码。来吧,删除所有东西。然后,在编辑器中为你的/convert 函数粘贴以下代码:
const fetch = require('node-fetch');
function convertMeasurement(conversionRates, qty, from, to) {
if (!conversionRates.hasOwnProperty(from) ||
!conversionRates.hasOwnProperty(to) ||
!typeof eval(qty) === "number") {
return 'Unknown';
}
const newQty = eval(qty) * eval(conversionRates[from][to]);
return `${qty} ${from}(s) converts to ${newQty.toFixed(2)} ${to}(s)`;
}
如果你理解这段代码,请跳到下一节。
convertMeasurement函数的解释
还记得我提到我们将找出如何在我们的函数中使用conversionRates 对象吗?好吧,就在这里那么,这里发生了什么?
convertMeasurement 接受四个参数 ( , , , 和 )。 是前面定义的JavaScript对象,只是在这个函数中,我们需要将JSON转换成一个对象,以便我们访问数据。我们在下一节做这个。conversionRates qty from to conversionRates
qty 存储我们要转换的测量量。例如,如果我想把5汤匙转换成茶匙, 将被设置为 。 是被转换的测量值(在前面的例子中,这将是汤匙), 是被转换成qty 5 from to 的测量值(茶匙)。
该函数做的第一件事是使用hasOwnProperty函数检查,以确保它能在conversionRates 对象中找到给定的from 和to 测量值,该函数告诉我们该对象中是否存在一个值。如果该对象不包含给定的测量值,那么它就不能转换它们。它做了最后一次检查,以确保qty 参数是number 类型,因为我们需要用它做一些数学运算。
eval函数如果用来接受来自外部的输入,是出了名的危险,因为它将自愿地运行恶意代码。我们在这里使用它是因为它是唯一能从字符串中解释分数的函数(以及分数中带有小数的字符串)。然而,在不完全控制输入的情况下,非常不建议使用它。
对于我们的目的来说,使用它是安全的,因为我们的函数是在一个隔离的环境中,它只在文本信息被解析并与我们自己创建的JSON文件进行比较后才执行。
函数的最后两行是神奇发生的地方。newQty 计算所需的金额,以便将from 转换为to 。我们在qty 和conversionRates[from][to] 上使用eval ,因此我们可以在一行中解释除法和乘法。返回的字符串通过使用一个模板字面将其放在一起,并最终在回复中送回。我们还使用toFixed方法将我们的newQty 变量四舍五入到两个小数位。
编写出口处理函数
Twilio可以运行我们的函数的唯一方法是,它有exports.handler方法和一个回调函数,作为函数执行结束的信号。在我们的函数中,我们将从exports.handler 中调用convertMeasurement ,并根据发送者是否发送了可解析的文本信息来制作两个不同的响应。
在convertMeasurement 函数的下面,在函数编辑器中粘贴以下内容:
exports.handler = async function(context, event, callback) {
// Setting up a twiml MessagingResponse for a simple SMS reply
const twiml = new Twilio.twiml.MessagingResponse();
// Accessing the body of the sender's message and parsing it
const body = event.Body ? event.Body.toLowerCase() : null;
const tokens = body.split(' ');
const [quantity, fromMeasure,, toMeasure] = tokens;
// Using the node-fetch module to access the JSON file we uploaded
let obj = {};
try {
const response = await fetch("YOUR OUTPUT JSON LINK");
obj = await response.json();
const s = JSON.stringify(obj);
obj = JSON.parse(s);
} catch(e) {
console.log("An error occurred while fetching JSON: ", e);
}
// Calling convertMeasurement, and if conversion is successful, will send an SMS reply
const result = convertMeasurement(obj,quantity,fromMeasure,toMeasure);
if (result !== 'Unknown') {
twiml.message(result);
return callback(null, twiml);
}
// If convertMeasurement returns 'Unknown', we will send our chart and a different message
const msg = 'Hi there! Please make sure your message is formatted correctly. ' +
'Accepted measurements include: tsp, tbsp, oz, c, q, pt, gal, ml, and l.' +
'\n\nSee the chart for reference and note accepted measurements are ' +
'in the right column.\n\nExample usage: "5 c to tbsp" or ".5 gal to l"';
const twilioClient = context.getTwilioClient(); // Configured in "Environment Variables" tab
twilioClient.messages
.create({
from: context.TWILIO_PHONE_NUM,
to: event.From,
mediaUrl: 'YOUR CONVERSION CHART JPG LINK',
body: ''
})
.then((message) => {
console.log('MMS successfully sent');
return callback(null, msg);
})
.catch((error) => {
console.error(error);
return callback(error);
});
};
exports.handler 函数从发送者那里接收文本信息(通过event.Body 访问),并将其解析为qty,from, 和to 参数,用于convertMeasurement 函数中。
解析后的第一个块是一个try/catch块,我们试图解析我们上传的JSON文件。如果你把注意力转向这个块中代码的高亮部分,你会看到fetch 函数中的一个占位符:’YOUR OUTPUT JSON LINK’ 。我们需要收集我们的JSON文件资产的链接,并将其粘贴在这里。这个链接可以通过点击资产旁边的三个叠加的点,然后点击 "复制URL "来找到。

如果try/catch块成功,convertMeasurement 函数返回一个字符串,其中包含转换或Unknown 。我们在if语句中确保返回的字符串不等于'未知',如果true ,将使用twiml.message 来存储该字符串。然后回调函数将twiml消息发回给发送者,这样就结束了函数的执行。
然而,如果返回的字符串确实等于Unknown ,代码就会构造一个响应,表明发件人没有发送一个正确格式的文本,并将我们的conversion-chart.jpg ,同时发送消息。Twilio函数通过REST客户端自动连接你的账户凭证,行const twilioClient = context.getTwilioClient(); 。
与JSON文件资产类似,我们需要以同样的方式复制URL,并将其粘贴到mediaUrl 属性中:mediaUrl: 'YOUR CONVERSION CHART JPG LINK' 。
一旦这两行被修改,点击编辑器窗口下面的蓝色**"保存 "按钮,然后点击"全部部署"**。
不是所有的Twilio号码都能发送MMS信息。在继续下一节之前,请确保你的号码是可以发送彩信的。
连接你的Twilio电话号码
回到Twilio控制台仪表盘,在左侧侧边栏的**"电话号码>管理>活动号码 "下导航到你的活动号码。选择你在本教程中早先作为环境变量添加的号码,向下滚动到Messaging。
在**"A MESSAGE COMES IN "下的方框中选择"Function",在SERVICE下选择"Converter'",在ENVIRONMENT下选择-"ui"**,在最后一个方框中选择我们的/convert 功能:

点击页面底部的蓝色**"保存 "**按钮,让我们来测试一下。
测试转换器
在这一点上,我们的号码应该准备好接收文本信息。发送几条,看看会发生什么!

注意在我的测试中,当我发送一条转换器无法识别的短信时,它就会把JPG转发给我,但当我发送一条识别格式的短信时,它就会给我发回一些相当准确的转换结果
如果你的短信看起来不正常,请看这篇文章:如何调试你的Twilio功能。错误日志包含大量的信息,可以告诉你可能发生了什么。
现在你已经熟悉了Twilio函数
恭喜你建立了一个连Gordon Ramsay都会为之骄傲的烹饪测量转换器!但这并不意味着结束,因为现在你已经知道了Twilio函数的使用方法,你可以建立数以百万计的东西。如果你还想为你的下一个项目获得更多的练习或灵感,请务必查看我们的其他Twilio博客文章。无论你将来决定做什么,我都希望你能玩得开心,做得有滋有味!