ckportfolio.com - Parsing Twilio Text Messages via Node.js

Parsing Twilio Text Messages via Node.js

Twilio

Twilio is a company specializing in enabling programmatic control over phone and SMS communication, and one may "rent" a phone number from Twilio and decide how the phone number will behave when it receives a phone call or a text message. For example, one could create a Node.js application that watches a specific phone number for incoming voice messages, records each voice message, and send an email notification to the owner.

As part of this exercise, some heavy lifting has been done to minimize the complexity involved in using Twilio. We have a script responsible for recording all incoming messages in a publicly available text file, and we will be using Node.js to "watch" this text file for any updates and respond accordingly.

Testing Twilio Behaviour

Try sending two types of text message -- a simple "hello world" and a website URL to +16474908124 (updated April 2023). All the text messages will be stored in a chronological fashion in http://ocad.ckprototype.com/twilio/logs.txt. In the terminal, you can verify whether this file exists on the same server by using the following command:

Terminal Command

ls -l /var/www/ocad.ckprototype.com/twilio/logs.txt

Now that we can verify that this file is updated each time the above phone number receives a text message, we can move onto updating the main.js file to take on the challenge of taking a screenshot dynamically.

Watching a File

In addition to Puppeteer, the project requires another package named nodejs-tail that allows Node.js to watch a file and display the most recent update should it detect any change. We can install this using the same command:

Terminal Command

npm i nodejs-tail

With the new challenge ahead, the main.js file needs to be reorganized to accommodate both packages. After simply commenting out the current code, we can experiment with the following snippet. Note that while we are keeping the original puppeteer variable, we have commented out the original screenshot snippet:

main.js

var file = '/var/www/ocad.ckprototype.com/twilio/logs.txt';

console.log("Listening indefinitely for any new text messages. Press ctrl+C to exit.")

const Tail = require('nodejs-tail');
const puppeteer = require('puppeteer');

const tail = new Tail(file);

tail.on('line', (line) => {
    console.log("Received: " + line);
})

tail.watch();

The above code snippet sets out to achieve the following:

  1. Create a variable based on the location of the text file
  2. Instantiate both nodejs-tail and puppeteer packages for later use
  3. Instruct the package to display the last appended text upon detecting change
  4. Ask the package to watch the file

Because this script will continue running unless otherwise instructed, it is also important to display a reminder that one can use control+C to exit the script.

Upon launching the script with node main.js, we can now test whether the script can truly watch for file changes, and in turn, respond to our text messages. If successful, the terminal will display the very same text message.

Taking Screenshots Dynamically

We can now connect two distinct code snippets together to create a cohesive experience, and we can do so by reintroducing the Puppeteer code snippet in the section responsible for displaying the last appended text:

main.js

...

tail.on('line', (line) => {

    var url = line;
    var screenshot = Date.now()+".png";

    (async () => {

        if (url.indexOf("http") == -1) {
            console.log("Invalid URL: " + url);

        } else {

            console.log("Loading the website: " + url);

            const browser = await puppeteer.launch();
            const page = await browser.newPage();
            await page.goto(url, {waitUntil: 'networkidle2'});

            await page.screenshot({path: screenshot})
            await browser.close();

            console.log("Screenshot successfully created: "+screenshot);

        }

    })();

})

...

The above code snippet presents the following changes:

  • Instead of setting a fixed filename under screenshot, we dynamically generate the timestamp-based filename
  • The url variable based on the last appended text undergoes a simple check to see if it begins with http

Upon launching via Node.js, the main.js script will continue to watch for incoming text messages and take the website screenshots as applicable, until terminated.

Fin