July 26th & 29th 2019
Last Friday (26th) and today (29th) I spent some time diving into the API of my favorite budgeting app. (YNAB!)
I had a random idea Friday morning to explore the ability to create an app that can receive an SMS text message like “Grocery Budget Left” and then return something like “You have $63.00 left in your groceries budget.”
I searched and figured out I would need to use TWILIO or something similar. I ended up creating an account and going through their intro docs and was able to create a very simple example in a couple of hours. What I turned out was just hard coded budget amount that I got the ID for by reading through the YNAB docs. I sent a text to the TWILIO # and got back the amount of my grocery budget.
I setup my own backend API using express and node. A cool thing I had to do to test that my app was working was push my localhost server online. I did that with NGROK which was recommended in the TWILIO docs.
It takes your localhost server and gives it a public web address for you to quickly test things with.
Today however, I wanted to go more in depth. I spent time going over the YNAB API docs and figuring out what data I would need to target to get the above example working properly.
I came up with the code to get closer to my goal. I can now text my TWILIO number “Groceries Left” and my express/node javascript parses the body of the text. It gets the first word (which I designated to be the budget to get), and uses that to get the amount left in that category from YNAB. I did this both using the YNAB API but also integrating the YNAB npm module that does this all for you on the backend.
Since some categories include sub categories, I wrote a recursive function to parse each category until it finds the matching category passed in through the SMS text message. (I had a hard time with the recursive function. I realized after awhile I was recursing inside of a for loop without any way to break out of the loop once I had the ID I needed)
Here’s what that code looks like.
getBalance Middleware
const ynab = require('ynab');
const ynabAccessToken = "XXXXXXXXXXXXXXXXXXXXX";
const ynabAPI = new ynab.API(ynabAccessToken)
getBalance = async function(req,res,next) {
let category = req.body.body.split(',')[0];
let modifier = req.body.body.split(',')[1];
let categoriesData = await ynabAPI.categories.getCategories('HARD-CODED-BUDGET-ID')
let categoryData = getCategory(categoriesData.data.category_groups.filter(cat => cat["name"] !== "Internal Master Category"), category )
req.balance = categoryData.balance;
next()
}
// This will get the correct category object from parsed data recursively
let getCategory = function(data, categoryToMatch) {
let category = undefined;
function recurse(data,categoryToMatch) {
for (let i = 0; i < data.length; i++ ) {
if (data[i].name.toLowerCase() === categoryToMatch.toLowerCase()) {
category = data[i];
break;
} else if (data[i].categories !== undefined) {
if (data[i].categories.length > 0) {
recurse(data[i].categories, categoryToMatch)
if (category) {
break;
}
}
}
}
}
recurse(data,categoryToMatch);
return category;
}
module.exports = getBalance
Node/Express Server.js
This code for now only sends back a response for testing purposes (TWILIO charges for each text message sent, although I have a trial credit, I want to use this wisely.) The code to send an SMS message is extremely simple and uses the MessagingResponse method that is required in the file below.
const http = require('http');
const express = require('express');
const MessagingResponse = require('twilio').twiml.MessagingResponse;
const bodyParser = require('body-parser');
const request = require('request')
const getBalance = require('./getBalance')
const app = express();
app.use(bodyParser.urlencoded({extended:false}));
app.post('/', getBalance, (req,res) => {
res.send(req.balance.toString())
})
http.createServer(app).listen(1337, () => {
console.log('Express server listening on port 1337')
})