Mental Health SMS Check-Ins with Twilio + Airtable
It’s important to check in on your mental health, especially in times like these.
The problem is it’s hard to remember to do this yourself. We get caught up in everything
that is going on and forget all about taking care of ourselves.
This SMS bot solves that problem by checking in with you everyday via SMS and saving your
responses in Airtable. It's a mindfulness journal that you won't
forget to write in. If you want to take a break, your bot even
supports pause
and resume
keywords.
After copying and deploying your own version of it, you can create your own questions and
have friends sign up to join you in answering them everyday.
I created my own mindfulness bot called Harold a few months ago and it has helped me make
more progress on my daily habits than I have in years. There are so many useful applications
of this stack and I'm excited to see what bots the Autocode community builds with it!
Setting up your own version is easy! The only things you'll need beforehand are
a Twilio account (don't buy a number yet) and a copy of this Airtable base:
You can clone this base by clicking here.
Once you've cloned the Airtable base, click and install the app. You'll be
prompted to choose a name for your bot as an environment variable and to link
the base you just cloned and your Twilio phone number. Follow the instructions,
then after you've deployed, text register
to your Twilio number to start receiving check-ins!
What Your Bot Can Do
Your bot can respond to a few user commands, all routed within the
functions/events/twilio/sms/received.js
endpoint. This runs whenever someone
messages the phone number for the bot.
The first thing this handler does is check to see whether the incoming phone
number has registered already like this:
let phoneNumberQuery = await lib.airtable.query['@0.5.3'].select({
table: 'Phone Numbers',
where: [{
'Phone Number__is': `${event.From}`
}]
});
From there, depending on what the contents of the incoming text, a few things
can happen:
- Texting
register
if the incoming number has not registered will call the
insert method of the
airtable.query API and
register the incoming phone number for daily check-ins.
- Texting
pause
will pause daily check-ins for the incoming number by setting
a field called Paused
on the appropriate record in the Phone Numbers
Airtable table.
- Texting
resume
will resume daily check-ins for the incoming number by
unsetting the above field.
- Texting anything else will be interpreted as a response to the daily
check-in. If the user has not registered, they will be prompted to do so first.
Otherwise, the bot checks to see whether the user has responded to the question
within the last day:
let recentAnswerQuery = await lib.airtable.query['@0.5.3'].select({
table: `Answers`,
where: [{
'Question__contains': mostRecentlyAskedQuestion.id,
'Phone Number__contains': phoneNumberQuery.rows[0].id
}]
});
let recentAnswer = recentAnswerQuery.rows.find((answer) => {
return new Date(answer.createdTime) > new Date(new Date().getTime() - 86400000);
});
If they have, the bot updates their existing response. Otherwise, the bot
creates a new record of the response in Airtable with linked records matching
the day's question and the phone number.
Note: All Airtable linked records are many-to-many joins behind the scenes,
hence the need for __contains
. If you want to learn more about querying
Airtable through Autocode, you can check out the
spec they use, KeyQL.
The other part of the bot is a scheduler that runs every day at 830am PST (you
can change this to whatever time/timezone combination you want in your own
version). This bot selects either an unsent or the least recently sent question
and does a for
loop over all the registered phone numbers:
await lib.airtable.query['@0.5.3'].records.update({
table: `Questions`,
id: leastRecentQuestion.id,
fields: {
'Last Sent At': new Date().toISOString()
}
});
let phoneNumberQuery = await lib.airtable.query['@0.5.3'].select({
table: 'Phone Numbers',
where: [{
'Paused__not': true
}]
});
for (let i = 0; i < phoneNumberQuery.rows.length; i++) {
await lib.twilio.messages['@0.1.1'].create({
to: phoneNumberQuery.rows[i].fields['Phone Number'],
body: `Hey there! It's your robot friend ${process.env.BOT_NAME} with today's question:\n\n${leastRecentQuestion.fields['Question']}`
});
}
That's just a high level summary. Feel free to dive into the code and make
modifications of your own!
Follow me on Twitter @bagelsangranola if you
want to say hi or have any questions.
And if you want to track your daily habits, sign up here for
early access to my bot, Harold. He's a great support system for making progress on side projects.
Without him I don't think I would be writing this!
Shout out to Jacob Lee @ Autocode for the help and support on this project!