Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Network Operator for Hubtel #32

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 68 additions & 71 deletions lib/ussd-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const EventEmitter = require('events');

class UssdMenu extends EventEmitter {

constructor (opts = {}) {
constructor(opts = {}) {
super();
const validProviders = ['hubtel', 'africasTalking'];
this.provider = opts.provider || 'africasTalking';
Expand All @@ -21,16 +21,16 @@ class UssdMenu extends EventEmitter {
this.resolve = null;
}

callOnResult () {
if(this.onResult){
callOnResult() {
if (this.onResult) {
this.onResult(this.result);
}
if(this.resolve){
if (this.resolve) {
this.resolve(this.result);
}
}

con (text) {
con(text) {
if (this.provider === 'hubtel') {
this.result = {
Message: text,
Expand All @@ -42,7 +42,7 @@ class UssdMenu extends EventEmitter {
this.callOnResult();
}

end (text) {
end(text) {
if (this.provider === 'hubtel') {
this.result = {
Message: text,
Expand All @@ -52,13 +52,13 @@ class UssdMenu extends EventEmitter {
this.result = 'END ' + text;
}
this.callOnResult();
if(this.session){
if (this.session) {
this.session.end();
}
}

testLinkRule (rule, val) {
//if rule starts with *, treat as regex
testLinkRule(rule, val) {
//if rule starts with *, treat as regex
if (typeof rule === 'string' && rule[0] === '*') {
var re = new RegExp(rule.substr(1));
return re.test(val);
Expand All @@ -71,13 +71,13 @@ class UssdMenu extends EventEmitter {
* @param string route a ussd text in form 1*2*7
* @return UssdState
*/
resolveRoute (route, callback) {
resolveRoute(route, callback) {
// separate route parts
var parts = route === ''? [] : route.split('*');
var parts = route === '' ? [] : route.split('*');
// follow the links from start state
var state = this.states[UssdMenu.START_STATE];
if(!state.next || Object.keys(state.next).length === 0){

if (!state.next || Object.keys(state.next).length === 0) {
// if first state has no next link defined
return callback(null, this.states[state.defaultNext]);
}
Expand All @@ -87,11 +87,11 @@ class UssdMenu extends EventEmitter {
if ('' in state.next) {
parts.unshift('');
}

async.whilst(
() => parts.length > 0 ,
() => parts.length > 0,
(whileCb) => {

// get next link from route
var part = parts.shift();
var nextFound = false;
Expand All @@ -100,10 +100,10 @@ class UssdMenu extends EventEmitter {
async.forEachOfSeries(
state.next,
(next, link, itCb) => {

/* called when next path has been retrieved
* either synchronously or with async callback or promise
*/
* either synchronously or with async callback or promise
*/
let nextPathCallback = (nextPath) => {
state = this.states[nextPath];
if (!state) {
Expand All @@ -115,39 +115,37 @@ class UssdMenu extends EventEmitter {
nextFound = true;
return itCb({ intended: true });
};

if (this.testLinkRule(link, part)) {
var nextPath;
// get next state based
// the type of value linked
switch (typeof next) {
case 'string':
case 'string':
// get the state based on name
nextPath = next;
break;
case 'function':
nextPath = next;
break;
case 'function':
// custom function declared
nextPath = next(nextPathCallback);
break;
nextPath = next(nextPathCallback);
break;
}
if (typeof nextPath === 'string') {
// nextPath determined synchronously,
// manually call callback
return nextPathCallback(nextPath);
}
else if (nextPath && nextPath.then) {
} else if (nextPath && nextPath.then) {
// promise used to retrieve nextPath
return nextPath.then(nextPathCallback);
}

}
else {
} else {
return itCb();
}
},
(err) => {
if (err && !err.intended) {

return whileCb(err);
}
if (!nextFound && state.defaultNext) {
Expand All @@ -158,34 +156,34 @@ class UssdMenu extends EventEmitter {

whileCb();
}
);
);

// end iterator
},
(err) => {
if(err){
if (err) {
return callback(err);
}

return callback(null, state);
}
);
}

runState (state) {
runState(state) {
if (!state.run) {
return this.emit('error', new Error(`run function not defined for state: ${state.name}`));
}
state.run(state);
}

go (stateName) {
go(stateName) {
var state = this.states[stateName];
state.val = this.val;
this.runState(state);
}

goStart () {
goStart() {
this.go(UssdMenu.START_STATE);
}

Expand All @@ -194,7 +192,7 @@ class UssdMenu extends EventEmitter {
* @param {Object} config object with implementation
* for get, set, start and end methods
*/
sessionConfig (config) {
sessionConfig(config) {
/*
the following 2 functions are used to make session
method cross-compatible between callbacks and promises
Expand All @@ -208,14 +206,13 @@ class UssdMenu extends EventEmitter {
*/
let makeCb = (resolve, reject, cb) => {
return (err, res) => {
if(err){
if(cb) cb(err);
if (err) {
if (cb) cb(err);
reject(err);
this.emit('error', err);
}
else {
if(cb) cb(null, res);
resolve(res);
} else {
if (cb) cb(null, res);
resolve(res);
}
};
};
Expand All @@ -225,13 +222,13 @@ class UssdMenu extends EventEmitter {
* chains and invoke the provided callback
*/
let resolveIfPromise = (p, resolve, reject, cb) => {
if(p && p.then){
p.then( res => {
if(cb) cb(null, res);
resolve(res);
if (p && p.then) {
p.then(res => {
if (cb) cb(null, res);
resolve(res);
}).catch(err => {
if(cb) cb(err);
reject(err);
if (cb) cb(err);
reject(err);
this.emit('error', err);
});
}
Expand All @@ -249,13 +246,13 @@ class UssdMenu extends EventEmitter {
return new Promise((resolve, reject) => {
let res = config.get(this.args.sessionId, key, makeCb(resolve, reject, cb));
resolveIfPromise(res, resolve, reject, cb);
});
});
},
set: (key, val, cb) => {
return new Promise((resolve, reject) => {
let res = config.set(this.args.sessionId, key, val, makeCb(resolve, reject, cb));
resolveIfPromise(res, resolve, reject, cb);
});
});
},
end: (cb) => {
return new Promise((resolve, reject) => {
Expand All @@ -278,37 +275,38 @@ class UssdMenu extends EventEmitter {
* state is resolved
* @return Ussd the same instance of Ussd
*/
state (name, options) {
state(name, options) {
var state = new UssdState(this);
this.states[name] = state;

state.name = name;
state.next = options.next;
state.run = options.run;
// default defaultNext to same state
state.defaultNext = options.defaultNext || name;


return this;
}

/**
* create the start state of the ussd chain
*/
startState (options) {
return this.state(UssdMenu.START_STATE, options);
startState(options) {
return this.state(UssdMenu.START_STATE, options);
}

/*
* maps incoming API arguments to the format used by Africa's Talking
*/
mapArgs (args) {
mapArgs(args) {
if (this.provider === 'hubtel') {
this.args = {
sessionId: args.SessionId,
phoneNumber: `+${args.Mobile}`,
serviceCode: args.ServiceCode,
text: args.Type === 'Initiation' ? this.parseHubtelInitiationText(args) : args.Message,
Operator: args.Operator,
text: args.Type.toLowerCase() === 'initiation' ? this.parseHubtelInitiationText(args) : args.Message,
};
} else {
this.args = args;
Expand Down Expand Up @@ -341,7 +339,7 @@ class UssdMenu extends EventEmitter {
if (this.provider === 'hubtel') {
if (this.session === null) {
return this.emit('error', new Error('Session config required for Hubtel provider'));
} else if (args.Type === 'Initiation') {
} else if (args.Type.toLowerCase() === 'initiation') {
// Parse initial message for a route
const route = this.parseHubtelInitiationText(args);
return this.session.set('route', route).then(() => route);
Expand All @@ -364,7 +362,7 @@ class UssdMenu extends EventEmitter {
* @param string args.sessionId
* @param string args.serviceCode
*/
run (args, onResult) {
run(args, onResult) {
this.mapArgs(args);
this.onResult = onResult;

Expand All @@ -382,14 +380,13 @@ class UssdMenu extends EventEmitter {
})
};

if(this.session){
if (this.session) {
this.session.start().then(run);
}
else {
} else {
run();
}

return new Promise((resolve,reject)=>{
return new Promise((resolve, reject) => {
this.resolve = resolve;
});

Expand All @@ -401,15 +398,15 @@ UssdMenu.START_STATE = '__start__';


class UssdState {
constructor (menu) {

constructor(menu) {
this.menu = menu;
this.name = null;
this.run = null;
this.defaultNext = null;
this.val = null;
}
}

}

module.exports = UssdMenu;