Appointment Reminder
Python for Appointment Reminders
⏱ 15 minutes build time || Difficulty Level: Intermediate || Github Repo
Tutorial pre-reqs
- Telnyx Portal Account and an API Key
- A provisioned SMS enabled number
- Messaging Profile.
- Familiarity with the messaging quickstart guide.
- Familiarity with running celery
- Redis Running on port 6379
Configuration
Firstly, create a config_file.cfg
file in your project directory. Flask will load this at startup.
NoteThis file contains a secret key, it should not be committed to source control.
API_KEY='YOUR_API_KEY'
FROM_NUMBER='YOUR_TELNYX_NUMBER'
We’ll also place Flask in debug mode and assume all numbers are in the U.S.
DEBUG=True
COUNTRY_CODE='+1'
NoteAfter pasting the above content, Kindly check and remove any new line added
Install the necessary packages for the application
$ pip install telnyx
$ pip install flask
$ pip install celery
$ pip install redis
NoteAfter pasting the above content, Kindly check and remove any new line added
Server initialization
Create a file schedule_meeting_server.py
and install the basic requirements
The first piece of our application sets up the Telnyx library, Flask, and Celery.
import uuid
from datetime import datetime, timedelta
import telnyx
from celery import Celery
from flask import Flask, request, render_template, flash
app = Flask(__name__)
app.secret_key = uuid.uuid4()
app.config.from_pyfile('config_file.cfg')
celery = Celery('schedule_meeting_server', broker='redis://localhost:6379')
telnyx.api_key = app.config['API_KEY']
NoteAfter pasting the above content, Kindly check and remove any new line added
Collect user input
We'll create a simple HTML form which collects the meeting date, time, customer name, and phone number.
The Flask route: @app.route('/', methods=['GET', 'POST'])
will serve the files created in the folder "templates"
$ mkdir templates
$ touch templates/index.html
$ touch templates/success.html
NoteAfter pasting the above content, Kindly check and remove any new line added
index.html
Inside the newly created templates/index.html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<meta charset="utf-8">
<title>Telnyx Meeting Scheduler</title>
</head>
<body>
<!--If form submission results in error, flash error message -->
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<div>
<h1>Telnyx Meeting Scheduling</h1>
<form action="{{ url_for('schedule_meeting') }}" method="post">
<div>
<label for="name">Your name: </label>
<input type="text" name="customer_name" required>
</div>
<div>
<label for="treatment">Meeting name: </label>
<input type="text" name="meeting_name" required>
</div>
<div>
<label for="meeting-date">Appointment date: </label>
<input type="date" id="meeting_date" name="meeting_date" required>
</div>
<div>
<label for="meeting_time">Meeting time: </label>
<input type="time" id="meeting_time" name="meeting_time" required>
</div>
<div>
<label for="phone">Your mobile number: </label>
<input type="tel" id="phone" name="phone" required>
</div>
<div>
<input type="submit" value="Schedule now!">
</div>
</form>
</div>
</body> </html>
NoteAfter pasting the above content, Kindly check and remove any new line added
success.html
Inside templates/success.html
<html>
<head>
<title>Telnyx Meeting Scheduler</title>
</head>
<body>
<p>{{name}}, thanks for booking a meeting for <em>{{meeting_name}}</em>!</p>
<p>You are scheduled for {{meetingDT}} and will receive a courtesy reminder at {{phone}} three hours before your meeting.</p>
</body>
</html>
NoteAfter pasting the above content, Kindly check and remove any new line added
Implement the SMS notification
Create a simple function that sends an SMS message parameterized on the destination number and message. The decorator @celery.task
allows us to schedule this function to run in the future.
@celery.task
def send_reminder(to, message):
telnyx.Message.create(
to=to,
from_=app.config['FROM_NUMBER'],
text=message
)
NoteAfter pasting the above content, Kindly check and remove any new line added
Note
from
is a reserved word in Python. The Telnyx Python Library adds an underscore character to any parameter that would conflict with a reserved keyword.
Parse User Input and Schedule the Message
Setup our route which will handle both GET
and POST
requests.
@app.route('/', methods=['GET', 'POST'])
def schedule_meeting():
if request.method == "POST":
# ...
return render_template('index.html')
NoteAfter pasting the above content, Kindly check and remove any new line added
Now, within the conditional, first parse the user date/time input.
meeting_date = datetime.strptime(request.form['meeting_date'], '%Y-%m-%d')
meeting_time = datetime.strptime(request.form['meeting_time'], '%H:%M').time()
meeting_datetime = datetime.combine(meeting_date, meeting_time)
NoteAfter pasting the above content, Kindly check and remove any new line added
Next, only allow meetings to be scheduled that are three hours and five minutes in the future or later.
now = datetime.now()
if meeting_datetime - timedelta(hours=3, minutes=5) < now:
flash('Appointment time must be at least 3:05 hours from now')
return render_template('index.html')
NoteAfter pasting the above content, Kindly check and remove any new line added
Then, compute the reminder time and message, and schedule the reminder.
Remind the User
Remind the user 3 hours before the meeting.
reminder_datetime = meeting_datetime - timedelta(hours=3)
message = "{customer_name}, you have a meeting scheduled for {meeting_time}".format(customer_name=request.form['customer_name'], meeting_time=str(meeting_datetime))
to = "{country_code}{phone}".format(country_code=app.config['COUNTRY_CODE'], phone=request.form['phone'])
send_reminder.apply_async([to, message], eta=reminder_datetime)
NoteAfter pasting the above content, Kindly check and remove any new line added
Finally, render the success template.
return render_template('success.html',
name=request.form['customer_name'],
meeting_name=request.form['meeting_name'],
phone=request.form['phone'],
meeting_datetime=str(meeting_datetime))
NoteAfter pasting the above content, Kindly check and remove any new line added
And at the end of the file, start the server.
if __name__ == '__main__':
app.run(port=5010)
NoteAfter pasting the above content, Kindly check and remove any new line added
Final schedule_meeting_server.py
All together your schedule_meeting_server.py
file should look something like:
import uuid
from datetime import datetime, timedelta
import telnyx
from celery import Celery
from flask import Flask, request, render_template, flash
app = Flask(__name__)
app.secret_key = uuid.uuid4()
app.config.from_pyfile('config_file.cfg')
celery = Celery('schedule_meeting_server', broker='redis://localhost:6379')
telnyx.api_key = app.config['API_KEY']
@celery.task
def send_reminder(to, message):
telnyx.Message.create(
to=to,
from_=app.config['FROM_NUMBER'],
text=message
)
@app.route('/', methods=['GET', 'POST'])
def schedule_meeting():
if request.method == "POST":
meeting_date = datetime.strptime(request.form['meeting_date'], '%Y-%m-%d')
meeting_time = datetime.strptime(request.form['meeting_time'], '%H:%M').time()
meeting_datetime = datetime.combine(meeting_date, meeting_time)
now = datetime.now()
if meeting_datetime - timedelta(hours=3, minutes=5) < now:
flash('Appointmenttest time must be at least 3:05 hours from now')
return render_template('index.html')
reminder_datetime = meeting_datetime - timedelta(hours=3)
message = "{customer_name}, you have a meeting scheduled for {meeting_time}".format(customer_name=request.form['customer_name'], meeting_time=str(meeting_datetime))
to = "{country_code}{phone}".format(country_code=app.config['COUNTRY_CODE'], phone=request.form['phone'])
send_reminder.apply_async([to, message], eta=reminder_datetime)
return render_template('success.html', name=request.form['customer_name'], meeting_name=request.form['meeting_name'],
phone=request.form['phone'], meeting_datetime=str(meeting_datetime))
return render_template('index.html')
if __name__ == '__main__':
app.run(port=5010)
NoteAfter pasting the above content, Kindly check and remove any new line added
Running the project
Make sure redis is running in the background, and then start the Celery task and Python server. Assuming your code is in schedule_meeting_server.py
.
In one terminal, launch the Celery worker (be sure to activate your virtual environment)
$ celery -A schedule_meeting_server.celery worker
NoteAfter pasting the above content, Kindly check and remove any new line added
Then launch the python flask server
$ python schedule_meeting_server.py
NoteAfter pasting the above content, Kindly check and remove any new line added
Node for Appointment Reminders
⏱ 15 minutes build time || Difficulty Level: Intermediate || Github Repo
Pre-reqs
- Telnyx Portal Account and an API Key
- A provisioned SMS enabled number
- Messaging Profile.
- Familiarity with the messaging quickstart guide.
Configuration
- Run the
npm init
command accepting the defaults npm install
the necessary packages to run the application.- Create a
config.json
file in your project directory - Create a
templates
folder for our template views - Create the
index.js
file to host the express application - Create the
index.html
andsuccess.html
in thetemplates
folder
$ npm init -y
$ npm i express
$ npm i express-nunjucks
$ npm i moment
$ npm i nunjucks
$ npm i telnyx
$ mkdir templates
$ touch config.json index.js templates/index.html templates/success.html
NoteAfter pasting the above content, Kindly check and remove any new line added
First, use this guide to provision an SMS number and messaging profile, and create an API key. Then add those to the config file, along with your country code.
{
"COUNTRY_CODE": "+1",
"FROM_NUMBER": "YOUR_TELNYX_NUMBER"
}
NoteAfter pasting the above content, Kindly check and remove any new line added
NoteThis file contains a secret key, it should not be committed to source control.
Server initialization
First, import the config.json
file and initialize the Telnyx library.
import Telnyx from 'telnyx';
const telnyx = new Telnyx("YOUR_API_KEY");
NoteAfter pasting the above content, Kindly check and remove any new line added
Then create an express app that watches the templates directory with Nunjucks
, a simple templating language, and parses form data.
import moment from 'moment';
import express from 'express';
import expressNunjucks from 'express-nunjucks';
const app = express();
app.use(express.urlencoded());
app.set('views', `${__dirname}/templates`);
expressNunjucks(app, {
watch: true,
noCache: true,
});
NoteAfter pasting the above content, Kindly check and remove any new line added
Collect user input
Fill out the index.html
in the templates
folder with a simple HTML form, which collects the meeting date, time, customer name, and phone number.
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<meta charset="utf-8">
<title>Telnyx Meeting Scheduler</title>
</head>
<body>
<!--If form submission results in error, flash error message -->
{% if message %}
<h2>{{ message }}</h2>
{% endif %}
<div>
<h1>Telnyx Meeting Scheduling</h1>
<form action="/" method="post">
<div>
<label for="name">Your name: </label>
<input type="text" name="customer_name" >
</div>
<div>
<label for="treatment">Meeting name: </label>
<input type="text" name="meeting_name" >
</div>
<div>
<label for="meeting-date">Appointment date: </label>
<input type="date" id="meeting_date" name="meeting_date" >
</div>
<div>
<label for="meeting_time">Meeting time: </label>
<input type="time" id="meeting_time" name="meeting_time" >
</div>
<div>
<label for="phone">Your mobile number: </label>
<input type="tel" id="phone" name="phone" >
</div>
<div>
<input type="submit" value="Schedule now!">
</div>
</form>
</div>
</body>
</html>
NoteAfter pasting the above content, Kindly check and remove any new line added
Then back in index.js
serve the index file with a new express route.
app.get('/', (req, res) => {
res.render('index');
});
NoteAfter pasting the above content, Kindly check and remove any new line added
Implement the SMS notification
Create a function that sends an SMS message parameterized on the destination number and text.
function sendReminder(to, message) {
telnyx.messages.create({
to: `${config.COUNTRY_CODE}${to}`,
from: config.FROM_NUMBER,
text: message
});
}
NoteAfter pasting the above content, Kindly check and remove any new line added
Parse user input and schedule the message
Within the POST handler, parse the meeting time and compute how far into the future it is. We're using the moment
Javascript library for this. Note that moment will use the current timezone for both datetime instances.
app.post('/', (req, res) => {
const meetingDatetime = moment(
`${req.body.meeting_date} ${req.body.meeting_time}`,
'YYYY-MM-DD hh:mm');
const now = moment();
const delta = moment.duration(meetingDatetime.diff(now));
// ...
});
NoteAfter pasting the above content, Kindly check and remove any new line added
If the meeting is sooner than 3 hours, 5 minutes from now, return an error.
if (delta < moment.duration({hours: 3, minutes: 5})) {
res.render('index', {message: 'Can only schedule meetings at least 3 hours, 5 minutes in the future'});
} else {
// ...
}
NoteAfter pasting the above content, Kindly check and remove any new line added
Remind the user
If the time is valid, compute when to send the reminder and schedule the function call. Note that we're using setTimeout
here, but a production solution should make use of an asynchronous processing toolkit.
const reminderDatetime = meetingDatetime.subtract(3, 'hours');
const message = `${req.body.customer_name}, you have a meeting scheduled for ${meetingDatetime}`;
setTimeout(() => {
sendReminder(req.body.phone, message);
}, reminderDatetime.diff(now));
NoteAfter pasting the above content, Kindly check and remove any new line added
Finally, fill out the success template success.html
in the templates
folder
<html>
<head>
<title>Telnyx Meeting Scheduler</title>
</head>
<body>
<p>{{name}}, thanks for booking a meeting for <em>{{meetingName}}</em>!</p>
<p>You are scheduled for {{meetingDT}} and will receive a courtesy reminder at {{phone}} three hours before your meeting.</p>
</body>
</html>
NoteAfter pasting the above content, Kindly check and remove any new line added
Then render the template after the setTimeout
statement.
res.render('success', {
name: req.body.customer_name,
meetingName: req.body.meeting_name,
meetingDT: meetingDatetime,
phone: req.body.phone
});
NoteAfter pasting the above content, Kindly check and remove any new line added
Start the Server
Start the Express app.
const port = 3000;
app.listen(port, () => console.log(`App running on port ${port}`));
NoteAfter pasting the above content, Kindly check and remove any new line added
Final index.js
All together your index.js
file should look something like:
import config from'./config.json';
import Telnyx from 'telnyx';
import moment from 'moment';
import express from 'express';
import expressNunjucks from 'express-nunjucks';
const telnyx = new Telnyx("YOUR_API_KEY");
const app = express();
app.use(express.urlencoded());
app.set('views', `${__dirname}/templates`);
expressNunjucks(app, {
watch: true,
noCache: true,
});
app.get('/', (req, res) => {
res.render('index');
});
function sendReminder(to, message) {
telnyx.messages.create({
to: `${config.COUNTRY_CODE}${to}`,
from: config.FROM_NUMBER,
text: message
});
}
app.post('/', (req, res) => {
const meetingDatetime = moment(
`${req.body.meeting_date} ${req.body.meeting_time}`,
'YYYY-MM-DD hh:mm');
const now = moment();
const delta = moment.duration(meetingDatetime.diff(now));
if (delta < moment.duration({hours: 3, minutes: 5})) {
res.render('index', {message: 'Can only schedule meetings at least 3 hours, 5 minutes in the future'});
} else {
const reminderDatetime = meetingDatetime.subtract(3, 'hours');
const message = `${req.body.customer_name}, you have a meeting scheduled for ${meetingDatetime}`;
setTimeout(() => {
sendReminder(req.body.phone, message);
}, reminderDatetime.diff(now));
res.render('success', {
name: req.body.customer_name,
meetingName: req.body.meeting_name,
meetingDT: meetingDatetime,
phone: req.body.phone
});
}
});
const port = 3000;
app.listen(port, () => console.log(`App running on port ${port}`));
NoteAfter pasting the above content, Kindly check and remove any new line added
Running the project
Launch the index.js
file from the command line and open your browser to http://localhost:3000
to set an appointment reminder.
$ node index.js
App running on port 3000
NoteAfter pasting the above content, Kindly check and remove any new line added
Ruby for Appointment Reminders
⏱ 15 minutes build time || Difficulty Level: Intermediate || Github Repo
Pre-reqs
- Telnyx Portal Account and an API Key
- A provisioned SMS enabled number
- Messaging Profile.
- Familiarity with the messaging quickstart guide.
Configuration
Create a config.yml
file in your project directory.
$ touch config.yml
NoteAfter pasting the above content, Kindly check and remove any new line added
Then, use this guide to provision an SMS number and messaging profile, and create an API key. Then add those to the config.yml
file, along with your country code.
api_key: "YOUR_API_KEY"
country_code: "+1"
from_number: "YOUR_TELNYX_NUMBER"
NoteAfter pasting the above content, Kindly check and remove any new line added
Server initialization
Create a scheduler.rb
file for the application and install the Gems needed to run the application.
$ touch scheduler.rb
$ gem install 'telnyx'
$ gem install 'sinatra'
$ gem install 'rufus-scheduler'
$ gem install 'activesupport'
NoteAfter pasting the above content, Kindly check and remove any new line added
The first piece of our application loads the config YAML file, configures Telnyx, and starts a Rufus scheduler.
require 'sinatra'
require 'date'
require 'active_support/all'
require 'rufus/scheduler'
require 'telnyx'
require 'yaml'
$config = YAML.load(File.open('config.yml').read)
Telnyx.api_key = $config['api_key']
scheduler = Rufus::Scheduler.new
NoteAfter pasting the above content, Kindly check and remove any new line added
Collect user input
Create a simple HTML form within a views
folder named index.erb
which collects the meeting date, time, customer name, and phone number.
$ mkdir views
$ touch views/index.erb
NoteAfter pasting the above content, Kindly check and remove any new line added
The index.erb
file should look something like
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<meta charset="utf-8">
<title>Telnyx Meeting Scheduler</title>
</head>
<body>
<% if defined?(message) %>
<h2><%= message %></h2>
<% end %>
<div>
<h1>Telnyx Meeting Scheduling</h1>
<form action="/" method="post">
<div>
<label for="name">Your name: </label>
<input type="text" name="customer_name" >
</div>
<div>
<label for="treatment">Meeting name: </label>
<input type="text" name="meeting_name" >
</div>
<div>
<label for="meeting-date">Appointment date: </label>
<input type="date" id="meeting_date" name="meeting_date" >
</div>
<div>
<label for="meeting_time">Meeting time: </label>
<input type="time" id="meeting_time" name="meeting_time" >
</div>
<div>
<label for="phone">Your mobile number: </label>
<input type="tel" id="phone" name="phone" >
</div>
<div>
<input type="submit" value="Schedule now!">
</div>
</form>
</div>
</body>
</html>
NoteAfter pasting the above content, Kindly check and remove any new line added
We'll serve it with Sinatra.
get '/' do
erb :index
end
NoteAfter pasting the above content, Kindly check and remove any new line added
Implement the SMS notification
Create a simple function that sends an SMS message parameterized on the destination number and text.
def send_reminder(to, message)
Telnyx::Message.create(
from: $config['from_number'],
to: "#{$config['country_code']}#{to}",
text: message
)
end
NoteAfter pasting the above content, Kindly check and remove any new line added
Parse user input and schedule the message
Within the POST handler, parse the meeting time, and compute how far into the future it is. Note that we are inserting the current timezone into the user submitted data in order to match with the output of DateTime.now
.
post '/' do
meeting_datetime = DateTime.strptime(
"#{params[:meeting_date]} #{params[:meeting_time]} #{DateTime.now.strftime('%Z')}",
'%Y-%m-%d %H:%M %Z')
current_datetime = DateTime.now
delta = meeting_datetime.to_time - current_datetime.to_time
# ...
end
NoteAfter pasting the above content, Kindly check and remove any new line added
If the meeting is sooner than 3 hours, 5 minutes from now, return an error.
if delta < 11100 # 3 hours, 5 minutes in seconds
return erb :index, :locals => {:message => 'Can only schedule meetings at least 3 hours 5 minutes in advance'}
else
# ...
end
NoteAfter pasting the above content, Kindly check and remove any new line added
Remind the User
If the time is valid, compute when to send the reminder and schedule the function call.
meeting_dt_formatted = meeting_datetime.strftime('%Y-%m-%d %l:%M %p')
reminder_time = meeting_datetime.to_time - 3.hours
scheduler.at reminder_time do
message = "#{params[:customer_name]}, you have a meeting scheduled for #{meeting_dt_formatted}"
send_reminder params[:phone], message
end
NoteAfter pasting the above content, Kindly check and remove any new line added
We'll use a new template confirm the reminder was set.
$ touch views/success.erb
NoteAfter pasting the above content, Kindly check and remove any new line added
The success.erb
file should look something like:
<html>
<head>
<title>Telnyx Meeting Scheduler</title>
</head>
<body>
<p><%= name %>, thanks for booking a meeting for <em><%= meeting_name %></em>!</p>
<p>You are scheduled for <%= meeting_dt %> and will receive a courtesy reminder at <%= phone %> three hours before your meeting.</p>
</body>
</html>
NoteAfter pasting the above content, Kindly check and remove any new line added
Finally, render the success template`
erb :success, :locals => {
:name => params[:customer_name],
:meeting_name => params[:meeting_name],
:meeting_dt => meeting_dt_formatted,
:phone => params[:phone]
}
NoteAfter pasting the above content, Kindly check and remove any new line added
Final scheduler.rb
All together, your scheduler.rb
should look something like:
require 'sinatra'
require 'date'
require 'active_support/all'
require 'rufus/scheduler'
require 'telnyx'
require 'yaml'
$config = YAML.safe_load(File.open('config.yml').read)
Telnyx.api_key = $config['api_key']
scheduler = Rufus::Scheduler.new
def send_reminder(to, message)
Telnyx::Message.create(
from: $config['from_number'],
to: "#{$config['country_code']}#{to}",
text: message
)
end
get '/' do
erb :index
end
post '/' do
meeting_datetime = DateTime.strptime(
"#{params[:meeting_date]} #{params[:meeting_time]} #{DateTime.now.strftime('%Z')}",
'%Y-%m-%d %H:%M %Z'
)
current_datetime = DateTime.now
delta = meeting_datetime.to_time - current_datetime.to_time
if delta < 11_100 # 3 hours, 5 minutes in seconds
return erb :index, locals: { message: 'Can only schedule meetings at least 3 hours 5 minutes in advance' }
else
meeting_dt_formatted = meeting_datetime.strftime('%Y-%m-%d %l:%M %p')
reminder_time = meeting_datetime.to_time - 3.hours
scheduler.at reminder_time do
message = "#{params[:customer_name]}, you have a meeting scheduled for #{meeting_dt_formatted}"
send_reminder params[:phone], message
end
erb :success, locals: {
name: params[:customer_name],
meeting_name: params[:meeting_name],
meeting_dt: meeting_dt_formatted,
phone: params[:phone]
}
end
end
NoteAfter pasting the above content, Kindly check and remove any new line added
Running the project
Launch the scheduler.rb
file from the command line and open your browser to http://localhost:4567
to set an appointment reminder.
$ ruby scheduler.rb
[2020-09-24 13:16:39] INFO WEBrick 1.6.0
[2020-09-24 13:16:39] INFO ruby 2.7.1 (2020-03-31) [x86_64-darwin19]
== Sinatra (v2.1.0) has taken the stage on 4567 for development with backup from WEBrick
[2020-09-24 13:16:39] INFO WEBrick::HTTPServer#start: pid=75889 port=4567
NoteAfter pasting the above content, Kindly check and remove any new line added