- Make sure ngrok and the server aren't running
- Reset this repository
git stash git fetch origin git checkout main git reset --hard origin/master
- Prepare the
.env
(based ontest.env
) file with the account secrets - Make sure the promo code is valid
- Open the Twilio Console
-
Buy a new number via the Twilio Console
Alternative: Use the CLI
twilio phone-numbers:buy:mobile --country-code US # twilio phone-numbers:buy:mobile --country-code DE --address-sid ADhash --bundle-sid BUhash
-
Start the server with
yarn dev
and talk about the/hello
endpoint.env
. -
Encourage everyone to turn on their ringer.
-
Expose
process.env.SECRET
via request -
Add a second endpoint in the
src/server.ts
.all("/sms", async (_, reply) => { reply.type("text/xml"); reply.send(` <Response><Message><Body> Hello,\nhave fun with this promo code "${process.env.PROMO}"\nGreetings from Munich 🦁 </Body></Message></Response> `); })
This won't work out-of-the-box. You also need to be able to add a body parser for
content-type: application/json
.yarn add @fastify/formbody@6
import FastifyBodyParser from "@fastify/formbody"; server .register(FastifyBodyParser)
If needed, you can test this request with:
### Test post to sms endpoint POST http://localhost:3000/sms HTTP/1.1 Content-Type: application/json {}
-
Start
ngrok
and there the webhook in the console.ngrok http 3000 # or ngrok http -subdomain=<domain> 3000
-
Install Twilio client (and use factory to build TwiML)
yarn add twilio
-
Create a new file
makeCalls.ts
import { Twilio, twiml } from "twilio"; const MY_NUMBER = "4915735981024"; (async () => { const client = new Twilio( process.env.TWILIO_ACCOUNT_SID || "", process.env.TWILIO_AUTH_TOKEN || "" ); const messages = await client.messages.list({ to: MY_NUMBER, }); messages.forEach((message) => { console.log(message.body); }); const callTwiMl = new twiml.VoiceResponse(); callTwiMl.say(`Yay, It worked. Let's cut to the chase. Tabs, 2 spaces, or 4 spaces - What do you prefer? Hit 1 for tabs, 2 for 2 spaces and 4 for 4 spaces. Press # to confirm`); callTwiMl.gather({ numDigits: 1, actionOnEmptyResult: false, action: "https://<subdomain>.ngrok.io/gatherAction", }); console.log(callTwiMl.toString()); new Set(messages.map((message) => message.from)).forEach((sender) => { client.calls .create({ twiml: callTwiMl.toString(), to: sender, from: MY_NUMBER, }) .then((call) => { console.log(`Started call ${call.sid}`); }); }); })();
Add the corresponding implementation in
server.ts
..all("/gatherAction", async (request, reply) => { const options = [ "", "Vote for 'Tabs'", "Vote for 'Two Spaces'", "", "Vote for 'Four Spaces'", ]; // @ts-ignore const digit = +request.body.Digits; console.log(options[+digit]); const confirm = new twiml.VoiceResponse(); confirm.say("Thank you and goodbye."); reply.type("text/xml"); reply.send(confirm.toString()); });
Then, start the calls
npx ts-node -r dotenv/config src/makeCalls.ts
If needed, test this endpoint
### Test the "gatherAction" endpoint POST http://localhost:3000/gatherAction HTTP/1.1 Content-Type: application/json { "Digits": "2" }