Coding Journal

Jordan Vidrine

Daily Coding Journal

A Self-taught coder from the Cajun Prairie in rural Louisiana, currently working as a designer for Discourse.

I am always trying to better myself, and do so through learning new skills, trying out new routines, traveling, and pouring energy into hobbies. Since 2014, I have been recording, editing, and producing my own music under the name Skies Speak.

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')
})

This post is continued here

Back to blog...