通过为JavaScript初学者建立一个短信测量转换器来学习如何使用Twilio功能

104 阅读13分钟

我在一个荒凉的小屋里,除了我不适应的白日梦、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 ,将文件编码为utf8Unicode的编码系统),并使用回调函数function(err) ,以防在写入文件时出现任何错误。

如果成功,控制台将显示**"JSON文件已被保存"。如果没有,它将显示一个错误信息。

在Twilio控制台创建一个服务

现在JSON文件已经准备好了,让我们继续设置一个Twilio服务,在那里我们上传文件并编写一个函数

导航到Twilio控制台,在左边的侧边栏,点击 "探索产品"。然后,在开发者工具下,点击"功能和资产",然后点击蓝色的"创建服务 "按钮。将新服务命名为"转换器",然后点击**"下一步"**。

你应该被重定向到一个新的页面,在那里你可以看到你的空服务,如下图所示:

What Twilio Functions looks like when a new service is created

最需要注意的两个区域是功能资产

创建一个资产

点击功能面板正上方的蓝色**"添加+"** 按钮,并点击**"上传文件"。然后,导航到你保存output.json 文件的地方。编辑器中会出现一个新的面板,上面有你的文件,以及表明你想让这个文件成为私有受保护公开的单选按钮。选择"公开"**。

在点击蓝色的**"上传 "**按钮之前,将下面的图片保存到你的项目目录中,因为它也将被上传:

Measurement abbreviations and example usage.

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

在这一点上,这就是文件的样子。

Expected Twilio Functions files list

确保两个文件都被设置为公开,然后点击**"上传"**。这将在 "资产"下上传两个文件,这两个文件已被保存,但还不可见,因为我们还没有部署该服务。

添加依赖性和环境变量

为了让JavaScript代码从公共链接中访问文件,需要一个名为node-fetch的额外依赖项。在设置标签下面,点击"依赖",一个新的面板将在编辑器中打开。在这里你可以添加任何通过npm可用的依赖项。

模块文本框中,输入"node-fetch",在版本框中输入**"2.x",像这样:

Import NPM modules into an application

点击"添加 "按钮,看着这个依赖关系出现在依赖关系表中。

接下来,导航到环境变量,它位于依赖关系的正上方。点击它,打开自己的标签,看起来非常相似。在这里你将创建一个环境变量来存储你的Twilio电话号码。

在"键 "下,输入TWILIO_PHONE_NUM ,在"值 "下输入E.164格式的号码。点击白色的"添加 "**按钮,它应该出现在下面。

Image of what the key value pair for adding environment variables looks like

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

A preview of a checked box in the environment variables tab

现在你已经准备好开始编写功能代码了

编写转换功能

点击你用来上传资产的那个蓝色**"添加+"按钮,但现在选择"添加功能"。这将创建一个新的函数路径,默认名称为/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 对象中找到给定的fromto 测量值,该函数告诉我们该对象中是否存在一个值。如果该对象不包含给定的测量值,那么它就不能转换它们。它做了最后一次检查,以确保qty 参数是number 类型,因为我们需要用它做一些数学运算。

eval函数如果用来接受来自外部的输入,是出了名的危险,因为它将自愿地运行恶意代码。我们在这里使用它是因为它是唯一能从字符串中解释分数的函数(以及分数中带有小数的字符串)。然而,在不完全控制输入的情况下,非常不建议使用它。

对于我们的目的来说,使用它是安全的,因为我们的函数是在一个隔离的环境中,它只在文本信息被解析并与我们自己创建的JSON文件进行比较后才执行。

函数的最后两行是神奇发生的地方。newQty 计算所需的金额,以便将from 转换为to 。我们在qtyconversionRates[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 "来找到。

Retrieve the output JSON link

如果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 功能:

Connect your Twilio phone number to the function

点击页面底部的蓝色**"保存 "**按钮,让我们来测试一下。

测试转换器

在这一点上,我们的号码应该准备好接收文本信息。发送几条,看看会发生什么!An example text message sent from the Twilio Function (part 1)An example text message sent from the Twilio Function (part 2)

注意在我的测试中,当我发送一条转换器无法识别的短信时,它就会把JPG转发给我,但当我发送一条识别格式的短信时,它就会给我发回一些相当准确的转换结果

如果你的短信看起来不正常,请看这篇文章:如何调试你的Twilio功能。错误日志包含大量的信息,可以告诉你可能发生了什么。

现在你已经熟悉了Twilio函数

恭喜你建立了一个连Gordon Ramsay都会为之骄傲的烹饪测量转换器!但这并不意味着结束,因为现在你已经知道了Twilio函数的使用方法,你可以建立数以百万计的东西。如果你还想为你的下一个项目获得更多的练习或灵感,请务必查看我们的其他Twilio博客文章。无论你将来决定做什么,我都希望你能玩得开心,做得有滋有味!