DEV Community

Tony
Tony

Posted on • Updated on

A free utility for easy routing in USSD applications

TL;DR Use the ussd-router utility to manage USSD routing headaches. Install on npm or view the source code.

Case Study: The headache in building USSD apps

Imagine you are building a USSD application as follows:

Alt Text

Perhaps, you would write code as follows:

import express from 'express';
import bodyParser from 'body-parser';

const app = express();
const port = 3000;

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.post('/webhook/ussd', (req, res) => {
  const { body: { text } } = req;

  const footer = '\n0: Back 00: Home';
  let msg = '';

  if (text === '') {
    msg += '1: Buy data bundles';
    msg += '\n2: Buy calls and sms bundles';

  } else if (text === '1') {
    msg += '1: Daily bundles';
    msg += '\n2: Weekly bundles';
    msg += footer;

  } else if (text === '1*1') {
    msg += '1. Buy for my number';
    msg += '\n2. Buy for other number';
    msg += footer;

  } else if (text === 1*1*1) {
    // more logic
  }

  res.send(msg);
});

app.listen(port, () => {
  console.log('Server running...');
});
Enter fullscreen mode Exit fullscreen mode

The important part is in the if-else flow, which allows you to add several screens to the USSD application:

This works flawlessly! But what happens when a user decides to navigate to the previous screen by pressing ‘0’ or to the home screen by pressing ‘00’?

You guessed it. This introduces an unsavory chain of if-else statements:

const footer = '\n0: Back 00: Home';
let msg = '';

if (rawText === '' || rawText === '1*00' || rawText === '1*0' || rawText === '1*1*00') {
  msg += '1: Buy data bundles';
  msg += '\n2: Buy calls and sms bundles';

} else if (rawText === '1' || rawText === '1*1*0') {
  msg += '1: Daily bundles';
  msg += '\n2: Weekly bundles';
  msg += footer;

} else if (rawText === '1*1' || rawText === '1*1*1*0') {
  msg += '1: Buy for my number';
  msg += '\n2: Buy for other number';
  msg += footer;
}
Enter fullscreen mode Exit fullscreen mode

Ugh. That’s one huge spaghetti of code.

Mind you, I didn’t even explore all the paths a user could take while navigating. In fact, as the user navigates deeper and deeper and decides to navigate to the previous/home screen, it may get too complicated for you to even handle the logic.

The solution

As illustrated, you may decide to handle the routing on your own, but that will add unnecessary boilerplate to your code.

Or you could use the ussd-router library.

The ussd-router library will help you remove the boilerplate needed to handle navigation in your application to the previous/home screen, and let you rest easy as you concentrate on your business logic.

import { ussdRouter } from 'ussd-router';

const text1 = ussdRouter('544*1*2'); // '544*1*2'
const text2 = ussdRouter('544*1*2*00*3'); // '544*1*3'
const text3 = ussdRouter('544*1*2*0*1*2'); // '1*2'
const text4 = ussdRouter('544*1*2*0*1*2*00*3'); // '1*3'
Enter fullscreen mode Exit fullscreen mode

Thus, you can update your code as follows:

import { ussdRouter } from 'ussd-router';

app.post('/webhook/ussd', (req, res) => {
  const { body: { text: rawText } } = req;

  const text = ussdRouter(rawText);
  let msg = '';

  if (text === '') {
    // do something
  } else if (text === '1') {
    // do something else
  }
});
Enter fullscreen mode Exit fullscreen mode

And you never again have to worry about handling navigation in your USSD application.

What if I want to use a different keyword to go to the home/previous screen?

By default 0 is the keyword used to go to the home screen, while 00 is used to go to the previous screen.

If you want to change this, simply update this as follows:

const text1 = ussdRouter(rawText, '98', '99');
Enter fullscreen mode Exit fullscreen mode

Here, 98 is the keyword that will be used to go to the home screen, while 99 will be used to go to the previous screen.

Getting the library

If you use node.js, you can install the npm package as follows:

npm i ussd-router
Enter fullscreen mode Exit fullscreen mode

If you don’t use node.js, you can view the source code, and transpile the algorithm into the programming language of your choice.

Cheers!

Want to get started on building USSD apps today? Visit https://africastalking.com/

Top comments (8)

Collapse
 
ditsarl profile image
ditsarl

Hi ! How can use ussd-router with python ussd application ?
Is it possible ?

Collapse
 
tawn33y profile image
Tony • Edited

Hi! Not directly. You will need to rewrite the code from Typescript to Python. The good news is that it's just 35 lines of code.

Here's the source: github.com/tawn33y/ussd-router/blo...

Let me know if you face any issues.
Cheers!

Collapse
 
ditsarl profile image
ditsarl

Hi ! Thanks a lot. I have encoded the lines below and it's work perfectly :

coding: utf-8

def goBack(str, keyword='00'):

strArray = str.split('*')
newStrArray = []

for i in range(len(strArray)):

if strArray[i] == keyword:
    newStrArray = newStrArray[slice(0, -1)]
else:
    newStrArray.append(strArray[i])
Enter fullscreen mode Exit fullscreen mode

return "*".join(newStrArray)

def goToHome(str, keyword='0'):

strArray = str.split('*')
newStr = str

for i in reversed(range(len(strArray))):
if strArray[i] == keyword:
newStr = "*".join(strArray[i+1:])
break

return newStr

def ussdRouter(str, goToHomeKeyword='0', goBackKeyword='00'):
return goBack(goToHome(str, goToHomeKeyword), goBackKeyword)

Thread Thread
 
tawn33y profile image
Tony

This is great!! 🙌🏽

Thanks for sharing! When you get a minute, feel free to create a python package for the same, and I can add a hyperlink pointer to this in the js package for future people who would like a python version

Thread Thread
 
ditsarl profile image
ditsarl
Thread Thread
 
tawn33y profile image
Tony

Awesome! 🙌🏽

Collapse
 
derdusm profile image
Derdus Mosoti

Very helpful.
There is a very comprehensive course on USSD and SMS featuring the Africa's Talking #USSD gateway. skillsday.co/courses/building-and-...

This course also implements a simple PHP router function, which achieves a similar result.

Collapse
 
code1crusader profile image
Fredylan Marais

How can this be achieved with a USSD gateway that does not append each new request with a '*' ?