Skip to content

feat: add new reminder-message template #385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"voice-javascript-sdk",
"voicemail",
"verify-sna",
"reminder-message",
"flex-dialpad"
]
}
14 changes: 14 additions & 0 deletions reminder-message/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# description: SID of a Twilio Messaging Service that will be used for your reminder email.
# format: sid
# link: https://console.twilio.com/us1/develop/sms/services
# required: true
MESSAGING_SERVICE_SID=

# description: Delay of how many minutes it should take after the incoming message to sent out a reminder. Minimum: 15 minutes
# format: number
# required: false
DELAY_IN_MINUTES=

# description: The path to the webhook
# configurable: false
TWILIO_SMS_WEBHOOK_URL=/respond
4 changes: 4 additions & 0 deletions reminder-message/.owners
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dkundel
alisontanu
pthirumurthi
# Insert your Github username here
8 changes: 8 additions & 0 deletions reminder-message/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## [Unreleased]

## [1.0.0]
### Added
- Initial release.

59 changes: 59 additions & 0 deletions reminder-message/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# reminder-message

Schedule a reminder message to be sent a specified time after the initial message

## Pre-requisites

### Environment variables

This project requires some environment variables to be set. A file named `.env` is used to store the values for those environment variables. To keep your tokens and secrets secure, make sure to not commit the `.env` file in git. When setting up the project with `twilio serverless:init ...` the Twilio CLI will create a `.gitignore` file that excludes `.env` from the version history.

In your `.env` file, set the following values:

| Variable | Description | Required |
| :------- | :---------- | :------- |
| `MESSAGING_SERVICE_SID` | The SID of a valid Messaging Service that is used to send out the scheduled message | Yes |
| `DELAY_IN_MINUTES` | Configurable time to delay the delivery of the reminder message. Defaults to the minimum of 15 minutes | No |

### Function Parameters

`/respond` is protected and requires a valid Twilio signature as well as the following parameters:

| Parameter | Description | Required |
| :-------- | :---------- | :------- |
| `From` | The phone number the incoming message was sent from. Gets automatically passed by Twilio | Yes |

## Create a new project with the template

1. Install the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart#install-twilio-cli)
2. Install the [serverless toolkit](https://www.twilio.com/docs/labs/serverless-toolkit/getting-started)

```shell
twilio plugins:install @twilio-labs/plugin-serverless
```

3. Initiate a new project

```
twilio serverless:init example --template=reminder-message && cd example
```

4. Start the server with the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart):

```
twilio serverless:start
```

5. Open the web page at <https://localhost:3000/index.html> and enter your phone number to test

ℹ️ Check the developer console and terminal for any errors, make sure you've set your environment variables.

## Deploying

Deploy your functions and assets with either of the following commands. Note: you must run these commands from inside your project folder. [More details in the docs.](https://www.twilio.com/docs/labs/serverless-toolkit)

With the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart):

```
twilio serverless:deploy
```
105 changes: 105 additions & 0 deletions reminder-message/assets/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Get started with your Twilio Functions!</title>

<link rel="icon" href="https://twilio-labs.github.io/function-templates/static/v1/favicon.ico">
<link rel="stylesheet" href="https://twilio-labs.github.io/function-templates/static/v1/ce-paste-theme.css">
<script src="https://twilio-labs.github.io/function-templates/static/v1/ce-helpers.js" defer></script>
<script>
window.addEventListener('DOMContentLoaded', (_event) => {
inputPrependBaseURL();
});
</script>
</head>

<body>
<div class="page-top">
<header>
<div id="twilio-logo">
<a href="https://www.twilio.com/" target="_blank" rel="noopener">
<svg class="logo" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 60 60">
<title>Twilio Logo</title>
<path class="cls-1"
d="M30,15A15,15,0,1,0,45,30,15,15,0,0,0,30,15Zm0,26A11,11,0,1,1,41,30,11,11,0,0,1,30,41Zm6.8-14.7a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,36.8,26.3Zm0,7.4a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,36.8,33.7Zm-7.4,0a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,29.4,33.7Zm0-7.4a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,29.4,26.3Z" />
</svg>
</a>
</div>
<nav>
<span>Your Twilio application</span>
<aside>
<svg class="icon" role="img" aria-hidden="true" width="100%" height="100%" viewBox="0 0 20 20"
aria-labelledby="NewIcon-1577">
<path fill="currentColor" fill-rule="evenodd"
d="M6.991 7.507c.003-.679 1.021-.675 1.019.004-.012 2.956 1.388 4.41 4.492 4.48.673.016.66 1.021-.013 1.019-2.898-.011-4.327 1.446-4.48 4.506-.033.658-1.01.639-1.018-.02-.03-3.027-1.382-4.49-4.481-4.486-.675 0-.682-1.009-.008-1.019 3.02-.042 4.478-1.452 4.49-4.484zm.505 2.757l-.115.242c-.459.9-1.166 1.558-2.115 1.976l.176.08c.973.465 1.664 1.211 2.083 2.22l.02.05.088-.192c.464-.973 1.173-1.685 2.123-2.124l.039-.018-.118-.05c-.963-.435-1.667-1.117-2.113-2.034l-.068-.15zm10.357-8.12c.174.17.194.434.058.625l-.058.068-1.954 1.905 1.954 1.908a.482.482 0 010 .694.512.512 0 01-.641.056l-.07-.056-1.954-1.908-1.954 1.908a.511.511 0 01-.71 0 .482.482 0 01-.058-.626l.058-.068 1.954-1.908-1.954-1.905a.482.482 0 010-.693.512.512 0 01.64-.057l.07.057 1.954 1.905 1.954-1.905a.511.511 0 01.71 0z">
</path>
</svg>
Live
</aside>
</nav>
</header>
</div>
<main>
<div class="content">
<h1>
<img src="https://twilio-labs.github.io/function-templates/static/v1/success.svg" />
<div>
<p>Welcome!</p>
<p>Your live application with Twilio is ready to use!</p>
</div>
</h1>
<section>
<h2>Get started with your application</h2>
<p>
Follow these steps to try out your new app:
</p>
<p>
This app is configured to handle incoming messages to your Twilio phone number, respond with a
default message or if you send "Y" to the number it will schedule reminder message to be sent out in
15 minutes (or a different delay if you used another delay).
</p>
<ol class="steps">
<li>Text any message to your Twilio phone number.</li>
<li>You should receive a welcoming response.</li>
<li>Text "Y" to your number.</li>
<li>You should receive a confirmation message and another message will be scheduled.</li>
<li>After 15 minutes (or whatever delay you configured) you'll receive another message to your
number.</li>
</ol>
</section>
<section>
<!-- APP_INFO_V2 -->
</section>
<section>
<h2>Troubleshooting</h2>
<ul>
<li>
Check the
<a href="https://www.twilio.com/console/phone-numbers/incoming" target="_blank" rel="noopener">
phone number configuration
</a>
and make sure the Twilio phone number you want for your app has a SMS webhook
configured to point at the following URL
<form>
<label for="twilio-webhook">Webhook URL</label>
<input type="text" id="twilio-webhook" class="function-root" readonly=true value="/respond">
</form>
</li>
<li>Make sure that you did not configure your <code>DELAY_IN_MINUTES</code> longer than 7 days or
shorter than 15 minutes. <a
href="https://support.twilio.com/hc/en-us/articles/4412165297947-Message-Scheduling-FAQs-and-Limitations">Check
out the Message Scheduling FAQ for details on limitations.</a> </li>
</ul>
</section>
</div>
</main>
<footer>
<span class="statement">We can't wait to see what you build.</span>
</footer>
</body>

</html>
34 changes: 34 additions & 0 deletions reminder-message/functions/respond.protected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
exports.handler = async function (context, event, callback) {
const twiml = new Twilio.twiml.MessagingResponse();

if (typeof event.Body === 'string' && event.Body.toLowerCase() === 'y') {
let delay = parseInt(context.DELAY_IN_MINUTES, 10);
if (isNaN(delay)) {
delay = 15;
}

const client = context.getTwilioClient();
try {
await client.messages.create({
from: context.MESSAGING_SERVICE_SID,
to: event.From, // sending to the phone number that sent us a message
body: "Here's the reminder for your appointment. Have a great day!",
scheduleType: 'fixed',
sendAt: new Date(Date.now() + delay * 60 * 1000 + 10000), // add the delay to the current time and add a 10 second buffer to avoid hitting the minimum delay of 15 minutes
});
twiml.message(
`Thank you! We will send you a reminder in ${delay} minutes`
);
} catch (err) {
console.error(err);
twiml.message(
'Something went wrong with scheduling the message. Please check your Twilio logs.'
);
}
} else {
twiml.message(
'Hello there! Please send "Y" to confirm your appointment and we will send you a reminder.'
);
}
return callback(null, twiml);
};
9 changes: 9 additions & 0 deletions reminder-message/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "reminder-message",
"version": "1.0.0",
"private": true,
"dependencies": {
"@twilio-labs/runtime-helpers": "^0.1.2",
"twilio": "^3.83.1"
}
}
83 changes: 83 additions & 0 deletions reminder-message/tests/respond.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const helpers = require('../../test/test-helper');
const { handler } = require('../functions/respond.protected');
const Twilio = require('twilio');

const mockTwilioClient = {
messages: {
create: jest.fn(() => {
return Promise.resolve({
sid: 'my-new-sid',
});
}),
},
};

const context = {
getTwilioClient: () => mockTwilioClient,
};
const event = {};

beforeAll(() => {
helpers.setup(context);
});

afterAll(() => {
helpers.teardown();
});

test('returns a MessagingResponse', (done) => {
const callback = (_err, result) => {
expect(result).toBeInstanceOf(Twilio.twiml.MessagingResponse);
done();
};

handler(context, event, callback);
});

test('sends default message', (done) => {
const callback = (_err, result) => {
expect(result.toString()).toMatch(
'<Message>Hello there! Please send "Y" to confirm your appointment and we will send you a reminder.</Message>'
);
done();
};

handler(context, event, callback);
});

test('sends scheduled message confirmation with 5 minutes default', (done) => {
const callback = (_err, result) => {
expect(result.toString()).toMatch(
'<Message>Thank you! We will send you a reminder in 15 minutes</Message>'
);
done();
};

handler(context, { ...event, Body: 'Y' }, callback);
});

test('sends confirmation with configured delay', (done) => {
const callback = (_err, result) => {
expect(result.toString()).toMatch(
'<Message>Thank you! We will send you a reminder in 30 minutes</Message>'
);
done();
};

handler(
{ ...context, DELAY_IN_MINUTES: '30' },
{ ...event, Body: 'Y' },
callback
);
});

test('handles lower case y as confirmation', (done) => {
const callback = (_err, result) => {
expect(result.toString()).toMatch(
'<Message>Thank you! We will send you a reminder in 15 minutes</Message>'
);
done();
};

handler(context, { ...event, Body: 'y' }, callback);
});
5 changes: 5 additions & 0 deletions templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@
"id": "transfers",
"name": "Transfers",
"description": "Transfers a call to another number"
},
{
"id": "reminder-message",
"name": "Send scheduled reminder messages",
"description": "Schedule a reminder message to be sent a specified time after the initial message"
}
]
}
Loading