Shelby Hagman, Author at Perficient Blogs https://blogs.perficient.com/author/shagman/ Expert Digital Insights Mon, 13 Jul 2020 19:34:56 +0000 en-US hourly 1 https://blogs.perficient.com/files/favicon-194x194-1-150x150.png Shelby Hagman, Author at Perficient Blogs https://blogs.perficient.com/author/shagman/ 32 32 30508587 CodeExchange – twilio-dev-container https://blogs.perficient.com/2020/05/06/code-exchange-twilio-dev-container/ https://blogs.perficient.com/2020/05/06/code-exchange-twilio-dev-container/#comments Wed, 06 May 2020 14:00:23 +0000 https://blogs.perficient.com/?p=273789

Twilio has introduced a new program for developers and partners to share code called CodeExchange.  This provides a curated list of Twilio-focused content and we are taking our first steps to contribute back to the wider community with examples and tooling.  This post will focus on onboarding and how to get a quick start environment with Twilio and Docker.

Prerequisites

To get started plan on completing the following:

  • Twilio Account and access to ACCOUNT_SID and AUTH_TOKEN
  • Docker
  • ngrok Account

Problem

Onboarding developers is an eternal problem.  We need to make sure developers have the software and tools required for them to be successful on the job and avoid spending days installing and configuring their machines.  There could be an extensive guide or series of video tutorials that provide a structure for new developers.  Each project may also require supplying credentials to target specific environmental resources.  Tools will change over time, and we want to minimize any setup time encountered.  How do we support developers on different platforms and architectures?  Can we provide them a safe playground to experiment with the technologies we use?

Docker and Containers

If you are new to Containers or Docker, I would suggest finding supplemental material to cover the benefits and use cases.  For the scope of this article, we will be focused on using Docker and Containers to provide a reusable environment.  Containers allow us to define what tools will be available and provide any necessary setup of those tools.  Using a container we can also choose what files and configuration are allowed into and out of that system.  With that in mind, we can map in environment variables via files that we could update external to the container and see those updates immediately within the container.  We can also map in host folders and any changes made within the container are reflected back allowing code to be developed in a tool and run within the container.

Docker and Containers – Google, Amazon, Microsoft

Ideal Twilio Environment

Building on the concept of containers, we can begin to describe what would make an ideal Twilio environment.  We will focus on enabling access to the Twilio Serverless Toolkit. This will include the ability to use twilio-run and twilio-cli.  Using twilio-run will enable hosting and debugging Twilio Functions locally and can be paired with ngrok to connect to Twilio service webhooks.  Running twilio-cli commands will keep us out of the Twilio Console where it is easy to make modifications without purpose.  Within our group, we are staying out of the Twilio console as much as possible.

Gathering Credentials

To get started with these tools we will need to gather ACCOUNT SID and AUTH TOKEN from within the Twilio Console Dashboard.

Twilio Console Dashboard

Within the ngrok Dashboard, we can find our authentication token via the Authentication->Your Authtoken side navigation.

Ngrok Auth Token

We will take the above values and plug them into the configuration that will be used within the docker container.

Twilio Dev Container

To bring it all together and bundle up our desired environment we created a Twilio-focused container.  Our group now has a public GitHub located at – github.com/Perficient-CES where we can host shareable code.  The twilio-dev-container aims to solve our onboarding problem and provide a reusable environment for Twilio brainstorming.  A shortlist of node tools are:

The container provides a default twilio-run project at /src/js/ with some enhanced npm scripts:

  • start – twilio-run with live changes and running on port 5566
  • remote – start command plus add ngrok tunnel
  • debug – remote command plus enable inspection (debugging) on port 5858

Additional features include:

  • A sample launch configuration is provided to enable debugging of container JS via inspect and Python via ptvsd found within the .vscode folder
  • Mapping in /scratch folder not connected to /src and ignored on commit
  • Mapping a volume over /js/node_modules to avoid container modules escaping to host system

More information can be found in the README.md.

In the next sections, we will quickly run through configuration and general usage.

Configuration

This information is contained within the repository, but it is still useful to cover the areas within the container that require some configuration.

ACCOUNT_SID and AUTH_TOKEN

The ACCOUNT_SID and AUTH_TOKEN from Twilio will need to be placed into two locations:

.env (here)
/src
    /js
        .env (here)

There are .env.sample files with entries for the environment variables.  The names are specific to each tool and are used as follows:

  • twilio-cli
    • TWILIO_ACCOUNT_SID – ACCOUNT_SID
    • TWILIO_AUTH_TOKEN – AUTH_TOKEN
  • twilio-run
    • ACCOUNT_SID – ACCOUNT_SID
    • AUTH_TOKEN – AUTH_TOKEN

We will need to create .env files in the same locations with the appropriate values.  We can do this by duplicating the .env.sample files and/or renaming them to .env.

ngrok

We will want to add an authtoken value into the top-level ngrok.yml file.

Usage

There is a scripts folder with scripts designed to aid in running the container:

  • *-run – run the specific container contained within the folder, exposing ports, and dive into /bin/bash
  • *-rebuild – rebuild the layers involved with the container
    • Useful when something goes wrong and it used to work, it will also bring down the latest tool versions
  • *-delete-volume – delete shared volumes created by the container

With configuration added to the appropriate files, we can start the container by running the *-run command for our specific platform.  For Windows machines use the .bat files and for every other system use .sh files.

Terminal

Once in the container, we are free to hack away.  We could run some twilio-cli commands or write a Twilio function the sky is the limit.  When finished with the container we can exit back to the host terminal by typing exit.

Final Thoughts

For our developers, being able to run a single command and have Docker bring together the necessary pieces is on course to make parts of our onboarding process easier.  We already have a few different flavors of these containers that focus on other technologies, and they make excellent playgrounds. The added benefit of a safety net that if some script or command breaks the environment that the developer can just use the same starting command is the icing on the cake.

]]>
https://blogs.perficient.com/2020/05/06/code-exchange-twilio-dev-container/feed/ 3 273789
COVID-19 – Using Twilio to build a Remote Workers Solution https://blogs.perficient.com/2020/04/02/covid-19-using-twilio-to-build-a-remote-workers-solution/ https://blogs.perficient.com/2020/04/02/covid-19-using-twilio-to-build-a-remote-workers-solution/#comments Thu, 02 Apr 2020 13:00:07 +0000 https://blogs.perficient.com/?p=272453

Our group has been working through ideas over the past week to offer up solutions.  These solutions are built leveraging the technologies we use to build enterprise solutions.  In this post I will walk through a solution we were building leading up to the Hackathon.  The content will be high level and include links back to relevant documentation where appropriate.

Operation: Work From Home

Many businesses in our current crisis have been deemed essential and must find solutions to keeping both workers and customers safe.  What aspects of the business can be conducted not requiring the worker or customers to come into direct contact?  Twilio and other telecommunications technologies can help move businesses into a remote-friendly world.

Our group started with a simple premise: create simple solutions that require the least amount of buy-in from the business.  We want to create solutions that do not require the business to invest into hardware or that are permanent.  The solution should not require the business to become experts on the technology or need a dedicated person to babysit a console dashboard.

The Problem

Workers are currently receiving phone calls from customers via the main business line or other workers transferring a call.  To work remotely the calls received on the main business line would need to be routed to a worker’s home or cell phone.

High-Level Solution

This feels like it could be a cut and dry scenario.  Have the business port the number to Twilio, setup Flex/TaskRouter with the workers in appropriate queues, and create a Studio flow to direct customers to the correct queue.  This makes a few assumption that a business may not want to buy into.  If the business buys into Twilio

  • Porting number to Twilio
    • How long until the carrier starts routing traffic transfers control?
    • What process would the Business need to take to return control if they do not need Twilio post-crisis?
  • Flex
    • Will all workers have a computer at home?

To make the process more reversible for the Business we can look into options for forwarding calls from the main business line to a Twilio owned phone number.  Most carriers offer an option to either forward from the line itself or use tools to direct incoming calls to a new number.  This process is much quicker than requesting a number port and waiting for the operation to complete.

If our workers do not have a computer at home, we need to consider changing the assumption to require them to have either a home or cell phone available.  Without a computer we cannot consider using Flex in its current state, lack of mobile client.  We can tie a worker’s home or cell phone to a worker in TaskRouter with some additional heavy-lifting to connect that worker with a calling customer.

We will split the solution into two parts: Worker Intake and Basic Flow.  Worker Intake will cover the responsibilities of a worker getting enabled for remote work.  Basic Flow will cover the experience from the customer and how they are connected with a worker.

Worker Intake

Operation Work From Home Worker Intake Skilling

The diagram above outlines a simple intake system for enabling workers to handle remote calls.  The worker would place a call or send a text to a Twilio number and that would start a Studio flow.  That flow would determine if that work is entering an online or offline state.  This state indicates what activity to provide to the TaskRouter worker.  When going online the worker also needs to indicate which skill or queue they are servicing.  After the data has been gathered Functions will be called to create or return a worker’s ID (SID) and update activity and attributes.

Basic Flow

Operation Work From Home Basic Flow

The diagram above outlines the basic flow including the customer experience and the heavy-lifting required to connect that caller with a worker.  A caller would be forwarded from the main business line into a Twilio number and that would start a Studio flow.  The Studio flow would be business specific and determine the appropriate queue for the customer.  When that call is enqueued into TaskRouter it will execute a workflow looking for eligible workers based on worker and task attributes.

Assuming a worker is found a reservation is placed on the worker and an assignment callback will execute a Function to place a call to that worker based on the phone number used in the intake system.  It does get tricky that a worker can have any number of life events happen that prevent them from answering in a timely fashion.  To avoid an answering machine connecting with a customer we can use AMD, answering machine detection, when placing the call to know if a machine or human answered the call.  If a machine answers the call we will need to reject the worker’s reservation to put them back into an available state and hangup on the answering machine.

We can also run into issues placing a call to a worker’s phone number which may either fail the call or report the line as busy.  In either case we will need to reject that worker’s reservation to allow TaskRouter to continue searching for workers.  In the event that no worker’s are available we may want to either direct that caller to a voicemail box or run custom TwiML.  We can handle this case by creating special queues with no workers in them and listening to TaskRouter events when that special queue is entered.

If there was no problem calling the worker then we can dial the queue supplying the reservation ID (SID) and that will connect the customer with the worker.

Tech and Further Reading

This is a good deal of information to digest without accompanying code.  In this section I will call out the key documentation and areas to dive into that helped build this solution.

Twilio Badge Red

Twilio Studio

Runtime

TaskRouter

Voice

Diagrams

The diagrams in this were created using diagrams.net and the source can be found at the following link – Operation: Work from Home.xml.  Download and open this file at diagrams.net to see the original work.

 

]]>
https://blogs.perficient.com/2020/04/02/covid-19-using-twilio-to-build-a-remote-workers-solution/feed/ 2 272453
Enterprise Connect 2019 – View from the Booth https://blogs.perficient.com/2019/03/28/enterprise-connect-2019-view-from-the-booth/ https://blogs.perficient.com/2019/03/28/enterprise-connect-2019-view-from-the-booth/#respond Thu, 28 Mar 2019 13:30:18 +0000 https://blogs.perficient.com/?p=237990

Last week I had the opportunity to attend Enterprise Connect in Orlando, Florida. I was an agent at the Live Contact Center Experience in the Twilio booth.  My team was responsible for creating that demo experience for Twilio. We did some impromptu training of managers and sales engineering staff before the masses entered at 3PM on Monday.  With the help of the team back at home, our sales staff, and the many Twilions, we were able to drive a compelling story around a cloud-based contact center built with Twilio Flex.  Maybe the chance at winning cool swag helped, just a bit.

Live Contact Center Experience

To participate in the experience a customer could either SMS, WhatsApp, or Voice to connect with a provided phone number.  The customer connects to an interactive quiz IVR.  After that, they would be placed into a queue to be picked up by a Flex agent.  The agent would do a small interaction with the customer, perhaps tell them they won some cool swag or discuss quiz answers.  When the agent completes that interaction we send a followup SMS to the customer. The SMS includes an order ID, a picture their new swag, and tell them to pick up at the booth.  At some point later, the customer returns to the booth with their order ID and they get to interact with the agent to pick up their order.  The agent marks that order ID complete and a final customer satisfaction survey is sent to gather final thoughts.

To recap the demo consisted of:

  • An IVR to collect customer details
  • After the IVR the customer is transferred to a Flex Agent
  • We customized the agent experience by adding additional plugins
  • Followup SMS with order information
  • Final customer satisfaction survey after order pick up

Below are the final numbers for modalities and some minor metrics around quiz results, satisfaction surveys, and remaining swag.

Final metrics including quiz scores, survey scores, and remaining swag

IVR

We leveraged Twilio Studio to build out a simple IVR to gather the customer name and walk them through a simple quiz.  The quiz was about Twilio product offerings.  The answers could be found around the booth, in sessions, and if you asked nicely, most staff would take the time to talk you through it.  The quiz did offer a bonus question if the customer got the first three correct which entered them into a raffle on Wednesday.

Flex

On the Flex side of things we create plugins which provided:

  • Overall metrics
  • Quiz results during agent interactions
  • Order pickup
  • Raffle winner picker

The biggest draw was seeing the agent experience.

Twilio Flex Agent interaction with SMS customer The plugin helped the agent see all the pertinent data about the customer and their quiz interaction.  We would know which questions were wrong and what swag the customer had won.  During the expo hall hours we had two monitors dedicated to the agent experience and a third monitor showing the live metrics.

Additional Plumbing

We additionally had Twilio Functions and Dynamo tables to manage tracking our customers and their quiz scores, swag orders, and raffle winners.  When there is some piece of data or functionality you cannot achieve in Twilio Flex that functionality is one HTTP call away.

Final Thoughts

The Live Contact Center Experience was able to showcase a neat scenario for people interested in Twilio Flex.  Our demo put a small amount of customization on top of the default Twilio Flex install.  We look forward to the coming year as we see more customers getting their feet wet with Twilio and Flex and help them build their solutions.

]]>
https://blogs.perficient.com/2019/03/28/enterprise-connect-2019-view-from-the-booth/feed/ 0 237990
Twilio Functions – Common Cases https://blogs.perficient.com/2018/10/15/twilio-functions-common-cases/ https://blogs.perficient.com/2018/10/15/twilio-functions-common-cases/#respond Mon, 15 Oct 2018 15:00:51 +0000 https://blogs.perficient.com/?p=232427

Twilio Functions are the glue that connect user code and Studio to the Twilio environment. They offer a convenient way to execute JavaScript while remaining inside of the Twilio ecosystem.  Functions have access to environment variables and shared NPM packages.  Functions also have access to the Twilio REST Helper Library. With this Studio flows can be extended to access more APIs.  This article provides a few common cases we encountered building Twilio offerings for clients.  It also builds upon Understanding Twilio Studio Flow.

Supporting CORS

It may become necessary to make a request from a domain external to the Twilio project.  To support Cross-Origin Resource Sharing, CORS, responding to OPTIONS request is necessary.  To achieve this consider the following example:

exports.handler = function(context, event, callback) {
const response = new Twilio.Response();
response.appendHeader('Access-Control-Allow-Origin', '*');
response.appendHeader('Access-Control-Allow-Methods', 'GET, OPTIONS, PUT, POST, DELETE');
response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');
// check if the event has any data and if not assume this is the OPTIONS request
if (Object.keys(event).length === 0) {
response.setStatusCode(200);
callback(null, response);
} else {
// call method here to do actual work
method(context, event, callback, response);
}
};
view raw cors.js hosted with ❤ by GitHub

The above code relies on the assumption that all OPTIONS requests will have no request body, event is an empty object. This assumption breaks down when dealing with GET requests. A GET request will have no request body. It is possible to support GET, but it would require duplicating the work in both the OPTIONS and GET. I would suggest sticking to CORS-enabled POST request for Twilio Functions that need to be accessed outside of the Twilio environment.

Calling Other Twilio Functions and Assets

Twilio Functions have access to the Runtime Client which provides access to Functions, Assets, and Sync.  For now let us ignore Sync and focus on Functions and Assets.  A typical Twilio Function will export a handler method.  Using the Runtime Client we can get the path of that function and proceed to load that module and call:

exports.handler = function(context, event, callback) {
let path = Runtime.getFunctions()['function_name'].path;
let fn = require(path);
fn.handler(context, event, (error, response) => {
if (error) {
console.log(error);
// handle error case
} else {
console.log(response);
// handle response
}
});
};
view raw fn.js hosted with ❤ by GitHub

Putting common functionality into Assets is another way to share code across Twilio Functions.  The code below handles loading an asset JavaScript and calling a method exposed via exports.

function log (data, message) {
try {
if (data && data.debug) {
console.log(message);
}
} catch (error) {
console.log(`Encountered error logging: ${error.message}`);
}
}
module.exports = {
log
};
view raw asset.js hosted with ❤ by GitHub
exports.handler = function(context, event, callback) {
let path = Runtime.getAssets()['asset.js'].path;
let assetJS = require(path);
assetJS.log({
debug: true
}, 'asset test message');
callback(null, {});
};
view raw fn.js hosted with ❤ by GitHub

A or B

This method has helped in numerous Studio cases where two paths converge and only one value will be present in the Twilio Function call.  It handles both JSON and raw data values.

function AorB (event) {
try {
console.log(JSON.stringify(event));
} catch {
// no-op
}
let response = new Twilio.Response();
response.setStatusCode(200);
let body;
if (event.a) {
body = event.a;
} else if (event.b) {
body = event.b;
} else {
console.log('a and b both undefined');
}
console.log(`responding with body: ${body}`);
if (event.json && event.json.toLowerCase() == 'true') {
response.appendHeader('Content-Type', 'application/json');
body = JSON.parse(body);
}
response.setBody(body);
return response;
}
view raw aorb.js hosted with ❤ by GitHub
]]>
https://blogs.perficient.com/2018/10/15/twilio-functions-common-cases/feed/ 0 232427
Understanding Twilio Studio Flow https://blogs.perficient.com/2018/08/15/understanding-twilio-studio-flow/ https://blogs.perficient.com/2018/08/15/understanding-twilio-studio-flow/#respond Wed, 15 Aug 2018 14:00:40 +0000 https://blogs.perficient.com/?p=230084

Twilio Studio offers a canvas where a masterful contact center designer can capture a flow that can be readable and provide a great customer experience.  There are many great tutorials offered by Twilio and what I hope to offer in this article is how I have come to understand flows in general, their execution, and debugging.  I will include a set of reference material at the end as well as directing to Twilio documentation.

 

Trigger AKA The Entry Point

Twilio Studio Trigger with an incoming message and incoming call set

Each flow contains a single Trigger widget which will be the entry point into the system.  The trigger is designed to handle chat channels, voice, and web requests.  Twilio uses the term Omnichannel to describe the ability to target all these various entry points.  Flows benefit from this mindset as they can be designed to handle more than just one specific target.  A flow could be initially designed for SMS and later have voice capabilities added in without much disruption.  As Twilio adds channels, more persons/devices are able to reach a flow.  If you have a need for integrating a new technology there is always the option of talking via web requests.

 

Thinking in Terms of Flow

The building blocks of any flow are the widgets.  The goal of a widget is to operate on information and potentially generate a response.  This can be as simple as sending an SMS or saying a voice prompt, but can get more complex with requests to Twilio Functions which can make requests to external systems.  I prefer to approach widgets with the mind set: What are my inputs, outputs, and where do we go next?

 

Inputs

I categorize my inputs into the following categories:

  1. Static – Hard-coded data
  2. Internal – data provided by the flow
  3. External – data retrieved from an external system such as database, Web API, CRM system
  4. Blended – building a better experience by including the internal/external with static data

Starting with a simple goal of greeting the texter/caller, we quickly run through a demonstration of each style and see how blending together can make a more better overall experience.

  1. Hello from Perficient’s Development Team.  What can we help you with?
  2. Hello from Perficient’s Development Team, we notice that you are calling from #+1312….. and will connect you with our Chicago-based team.  What can we help you with?
  3. Hello Shelby from Perficient’s Development Team, What can we help you with?
  4. Hello Shelby from Perficient’s Development Team, we notice that you are calling from #+1312….. and will connect you with our Chicago-based team.  What can we help you with?

It should feel like a no-brainer if you have an external system with customer data, do a lookup first before attempting to start the interaction with the customer.  If the data is there it can add to the overall experience.  Keep in mind that the flow should also handle the case where a customer is not present.  To avoid responding with: Hello undefined from Perficient’s Development Team…

 

Outputs

A subset of widgets provide information back to the flow that can be consumed in later widgets.  Using HTTP Request/Run Function widgets to pull data from external sources and transforming that data into either plain text or JSON can bring context to the person calling.  There is a large opportunity to start leveraging Natural Language Understanding, NLU, services to process responses, either chat or voice, looking for intent and related context.  This can trim down complex flows into a matter of gathering, NLU with processing, and skipping through the traditional IVR system.  Getting the customer where they want faster.

 

Transitions

Example gather with fallback gather to capture a customer's name

Each widget has transitions to dictate where to go next.  If a customer fails to provide enough information during a gathering step should the flow continue as normal?  Avoid sending the customer through an infinite loop of asking for information if their response does not fit what the flow is expecting.  If a widget supports multiple transitions make sure to evaluate it to ensure that in a failure case the flow does not abruptly leave a customer in the abyss.  Dead ends in a flow can be missed opportunities..

 

Flow Execution

Executions page with two listed executions

Each run through a flow is captured in a set of logs per Flow called Executions.  It is handy to dive into them to see how a flow went and verify the data used in widgets.  I have written a few Twilio Functions only to later learn that I was not correctly referencing that data inside the flow.

 

Simple Execution

Execution page loaded with debugging information loaded for the Say widget

In a simple flow that has uses the Say/Play widget we might expect an execution to look like the above.  The trigger entry point always occur followed by steps attached to that channel.  In this case it was a call and we can see that the flow transition to a widget named Say.  Expand a widget out to see further information about what the widget achieved and the transition.  I did not expand out the Widget & Flow Properties because it contains all information about the flow up to the point after executing the widget.  This is the place to look when a HTTP Request/Run Function widget feels like it is not working as intended.

 

Execution Cleanup

Executions log with Stop Execution circled in red

Rarely, an execution may get into a state where the flow does not appear to be processing.  If there is an execution active for a telephone number it will be unable to restart the flow and typically provide an error in the debugger.  Look at the specific execution on the Executions page and make sure it is does not have a Stop Execution option available.  While a flow is actively running it will be possible to end execution via that link.  Data prior to stopping is available by clicking into that execution.

 

General Debugging

The following are what I have been using to debug flows:

  1. Locate the execution in question via Logs/Executions for the specific flow
  2. Find the Widget that is not behaving and expand out the Widget & Flow Properties
  3. Start looking at what are the inputs/outputs of the widget
  4. If the widget is an Run Function, I load up the specific function’s page via Manage and re-execute the flow looking for any expected console output
    • Stop the execution if necessary
    • Add more console logging if necessary
  5. The Debugger can also provide some context as a last resort

 

Common Task

Aggregate flow with customer lookup, name gathering, and aggregating branches into hello person

The most common task I have done in a flow by far is aggregating data from two branches back into one.  If the flow branches to get a new customer’s name, it may make sense that at a later point this branch can rejoin the main.  To rejoin a branch it will involve aggregating data between the two branches.  There are two ways to think about this aggregation:

  1. Check multiple parameters in the next widget
  2. Send multiple parameters into a single widget and get A or B out

In either of the above options the widget must support multiple parameters or have a complex statement.  Without an aggregation step a flow can quickly increase in size to support duplicate widgets in each branch.  We decided to create a function to handle choosing between two JSON parameters as follows:

exports.handler = function(context, event, callback) {
    console.log(JSON.stringify(event));
    let response = new Twilio.Response();
    response.setStatusCode(200);
    response.appendHeader('Content-Type', 'application/json');
    
    if (event.a) {
        response.setBody(JSON.parse(event.a));
    } else if (event.b) {
        response.setBody(JSON.parse(event.b));
    } else {
        response.setStatusCode(404);
        console.log('a and b both undefined');
    }
    
    callback(null, response);
};

As the flow executes we would expect that A represents a new customer’s name and B represents an existing customer’s name.  We retrieve B at the start during the customer lookup.  If we do not find them we need to execute the path get A.  Once we have both we can aggregate into our function and we expect that the flow will only reach the function with one of the two values defined and return that result as the aggregated data.  The aggregated data then feeds into the main branch again and we greet both A and B with the same message.

 

References


]]>
https://blogs.perficient.com/2018/08/15/understanding-twilio-studio-flow/feed/ 0 230084