Walk-through: Send and receive texts via Twilio
Darklang allows you to build backends (API endpoints, workers, cron, and data storage) by writing only your business logic, using production traces.
You can access you canvas at darklang.com/a/USERNAME (or USERNAME-CANVASNAME). For this project we recommend darklang.com/a/USERNAME-twilioreminder.
We recommend building a hello world API endpoint to get a feel for Darklang, as follows:
All the major handlers work the same way, but the key for many requests is working directly with incoming data.
Daily Text Reminder App
We’re building this app: https://sample-textreminder.builtwithdark.com It’s an app that will send a text message once per day at a given time to an end user. In this case, we’ll ask the user “did you drink enough water today?” and track their response.
To try out the app and see your traces in the sample canvas text “start” to +12482653257.
Twilio Credentials
To build your own, you’ll need to set up a Twilio account. Your Twilio SID & Token are on the Dashboard.
You’ll also need to add a phone number from the “more” menu on the left-hand side, then to the phone number section.
Once you’ve added a phone number, you can configure a webhook:
Sending a Message
Darklang has a built in Twilio::sendText
function. We can call it in a REPL
with our own number to verify our webhook.
https://darklang.com/a/sample-textreminder#handler=1108947374
Receiving Responses
After we receive a response, we’ll see a 404 in the side bar section for our webhook, which we can create by hitting the “+” button.
Once the handler is created, we’re able to see the full incoming trace.
https://darklang.com/a/sample-textreminder#handler=1004312353
Processing & Storing Responses
In Darklang, you can work directly with incoming traces. More on this in Trace Driven Development. For this handler, we can parse out the things we care about: a user deciding to start/stop using our service, or telling us if they drank enough water or not.
https://darklang.com/a/sample-textreminder#handler=1004312353
At each expression, we can see what the expression evaluates to for a given trace. Here, we see the From number. In this case we also do some cleaning on the response in case the user had a capital letter or an extraneous space.
For processing the various responses, we’ll use a match statement. More on Match is available, but the tutorial example has enough context to understand how it works for this case.
Sign up & Quit
Now, let’s do the “start” case. This allows us to give the user our Twilio
number, and have them text “start” to begin receiving a daily reminder. We’ll
need a data store to keep track of our users, which can be created from the
omnibox (Cmd/Ctrl-k
) or the side bar.
https://darklang.com/a/sample-textreminder#db=973891964
Then, we can write the logic to add users to the datastore when they text “start.” In this case we use the user’s phone number as the unique key.
https://darklang.com/a/sample-textreminder#handler=1004312353
The stop case is very similar, but removes the user from the datastore.
https://darklang.com/a/sample-textreminder#handler=1004312353
To test, you can always send a new reply to the text message (or just message your number) and walk through the trace to see the live values at each step.
https://darklang.com/a/sample-textreminder#handler=1004312353
Water Tracking
Now we’ll want the cases for when the user responds to the daily question of “did you drink enough water today?”
We’ll want another data store so we can track the daily response by user (side
bar or Cmd/Ctrl-k
).
https://darklang.com/a/sample-textreminder#db=1164294845
In this case since we do not want to use the phone number as the unique key, we
generate a unique key when storing using the built in DB::generateKey
function:
https://darklang.com/a/sample-textreminder#handler=1004312353
Catch-all Case
Finally, we need a case for when we don’t match on one of our earlier responses. We’ll want to send a text back to tell the user about the error. To prevent this from blocking other actions, we’ll emit to a background worker.
Here, we emit the user’s number (user) to a background worker named Misunderstood (and see the event as a trace). Once you’ve hit “play” on the emit expression, you should have a 404 for the worker in the side bar that you can hit the + to add. If it does not appear in the sidebar, wait ~30s or refresh the browser.
https://darklang.com/a/sample-textreminder#handler=331438030
Then we can write the code similar to our earlier REPL to send a message asking for a more clear response:
https://darklang.com/a/sample-textreminder#handler=331438030
Sending Reminder Messages Once A Day
To send each user a reminder once per day, we’ll want to have a cron set to run daily. In this case we’ll get all the users, then emit to a background worker to send the messages.
https://darklang.com/a/sample-textreminder#handler=1621731316
Much like before, this will add the worker to the 404 section where you can add it, with the traces.
https://darklang.com/a/sample-textreminder#handler=337743491
The code is identical to the code in our REPL:
https://darklang.com/a/sample-textreminder#handler=337743491
This is now a fully functioning application.
You could do similar ones for:
- A reminder to stand up once per hour
- A daily reminder to do a task (fill in a personal CRM, floss)
- A weekly reminder to take out the trash
Additional Topics (for Fun!)
Cron at Time of Day
If you’d like to set the reminder to occur at a specific time of day, you can
set this using a datastore. There’s a sample canvas
here. For this
application, add the “reminder time” in the Users
table, and add a case to
allow users to specify a time (that you would then convert to a Date object).
Seeing/Displaying Responses
This API handler shows all checkins for a given user.
This API handler shows the percentage of “yes” responses for a given user.