Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

africastalking

Package Overview
Dependencies
Maintainers
4
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

africastalking - npm Package Compare versions

Comparing version 0.7.0-beta.1 to 0.7.0

4

index.js

@@ -1,3 +0,3 @@

'use strict';
'use strict'
module.exports = require('./lib');
module.exports = require('./lib')

@@ -0,588 +1,564 @@

const validate = require('validate.js')
const _ = require('lodash')
const { phoneValidator } = require('./utils')
const validate = require('validate.js');
const _ = require('lodash');
const { phoneValidator } = require('./utils');
class ActionBuilder {
constructor () {
this.finalized = false;
this.xml = '<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response>';
constructor () {
this.finalized = false
this.xml = '<?xml version="1.0" encoding="UTF-8"?><Response>'
this.buildAction = function (action) {
this.buildAction = function (action) {
const {
tag,
text,
children,
attributes
} = action
const {
tag,
text,
children,
attributes
} = action;
this.xml = this.xml.concat('<' + tag)
this.xml = this.xml.concat('<' + tag)
if (attributes && Object.keys(attributes).length > 0) {
Object.entries(attributes)
.forEach(([key, value]) => {
this.xml = this.xml.concat(` ${key}="${value}"`)
})
}
if (attributes && Object.keys(attributes).length > 0) {
Object.entries(attributes)
.forEach(([key, value]) => {
this.xml = this.xml.concat(` ${key}="${value}"`);
});
if (children && Object.keys(children).length > 0) {
this.xml = this.xml.concat('>')
Object.entries(children)
.forEach(([child, options]) => {
switch (child) {
case 'say':
this.say(options.text, options.attributes)
break
case 'play':
this.play(options.url)
break
case 'getDigits':
this.getDigits(options.children, options.attributes)
break
case 'dial':
this.dial(options.phoneNumbers, options.attributes)
break
case 'record':
this.record(options.children, options.attributes)
break
case 'enqueue':
this.enqueue(options.attributes)
break
case 'dequeue':
this.dequeue(options.phoneNumber, options.attributes)
break
case 'redirect':
this.redirect(options.text)
break
case 'conference':
this.conference()
break
case 'reject':
this.redirect()
break
default:
throw new Error('Invalid child')
}
if (children && Object.keys(children).length > 0) {
this.xml = this.xml.concat('>');
Object.entries(children)
.forEach(([child, options]) => {
switch (child) {
case 'say':
this.say(options.text, options.attributes);
break;
case 'play':
this.play(options.url);
break;
case 'getDigits':
this.getDigits(options.children, options.attributes);
break;
case 'dial':
this.dial(options.phoneNumbers, options.attributes);
break;
case 'record':
this.record(options.children, options.attributes);
break;
case 'enqueue':
this.enqueue(options.attributes);
break;
case 'dequeue':
this.dequeue(options.phoneNumber, options.attributes);
break;
case 'redirect':
this.redirect(options.text);
break;
case 'conference':
this.conference();
break;
case 'reject':
this.redirect();
break;
default:
throw new Error('Invalid child');
}
});
this.xml = this.xml.concat(`</${tag}>`);
} else {
if (text) {
this.xml = this.xml.concat(`>${text}</${tag}>`);
} else {
this.xml = this.xml.concat('/>');
}
}
})
this.xml = this.xml.concat(`</${tag}>`)
} else {
if (text) {
this.xml = this.xml.concat(`>${text}</${tag}>`)
} else {
this.xml = this.xml.concat('/>')
}
}
}
}
build () {
if (this.finalized) throw new Error('This builder has been finalized by a call to build()');
build () {
if (this.finalized) throw new Error('This builder has been finalized by a call to build()')
this.finalized = true;
this.xml = this.xml.concat('</Response>');
return this.xml;
}
this.finalized = true
this.xml = this.xml.concat('</Response>')
return this.xml
}
say(text, attributes = {}) {
let params = _.cloneDeep(attributes);
params.text = text;
say (text, attributes = {}) {
const params = _.cloneDeep(attributes)
params.text = text
let _self = this;
let validationError;
let validationError
let _validateParams = function () {
const constraints = {
text: function (value) {
if (value && !validate.isString(value)) {
return {
format: 'text must be a string'
}
}
return {
presence: true
}
},
voice: function (value) {
let choices = ['man', 'woman'];
if (value && !choices.includes(value)) {
return {
format: 'invalid option'
}
}
return null;
},
playBeep: function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null;
}
};
let error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
validationError = new Error(msg);
const _validateParams = function () {
const constraints = {
text: function (value) {
if (value && !validate.isString(value)) {
return {
format: 'text must be a string'
}
}
return {
presence: true
}
},
voice: function (value) {
const choices = ['man', 'woman']
if (value && !choices.includes(value)) {
return {
format: 'invalid option'
}
}
return null
},
playBeep: function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null
}
}
_validateParams();
if (validationError) {
throw validationError;
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
const action = {
tag: 'Say',
text,
attributes
}
this.buildAction(action);
return this;
validationError = new Error(msg)
}
}
play(url) {
let attributes = { url };
let _self = this;
let validationError;
_validateParams()
let _validateParams = function () {
if (validationError) {
throw validationError
}
const constraints = {
url: {
presence: true,
url: true
}
};
const action = {
tag: 'Say',
text,
attributes
}
let error = validate(attributes, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
this.buildAction(action)
return this
}
validationError = new Error(msg);
}
}
play (url) {
const attributes = { url }
_validateParams();
let validationError
if (validationError) {
throw validationError;
const _validateParams = function () {
const constraints = {
url: {
presence: true,
url: true
}
}
const action = {
tag: 'Play',
attributes
const error = validate(attributes, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
this.buildAction(action);
return this;
validationError = new Error(msg)
}
}
getDigits(children, attributes = {}) {
let params = {
children: _.cloneDeep(children),
attributes: _.cloneDeep(attributes)
}
_validateParams()
let _self = this;
let validationError;
if (validationError) {
throw validationError
}
let _validateParams = function () {
const action = {
tag: 'Play',
attributes
}
const constraints = {
children: function (value) {
const possibleChildren = ['say', 'play'];
if (value && !possibleChildren.includes(Object.keys(value)[0])) {
return {
format: 'digits has invalid child'
}
}
return {
presence: true
}
},
"attributes.numDigits": function (value) {
if (!validate.isInteger(value)) {
return {
format: 'must be an integer'
}
}
},
"attributes.timeout": function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
},
"attributes.callbackUrl": {
url: true
},
};
this.buildAction(action)
return this
}
let error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
getDigits (children, attributes = {}) {
const params = {
children: _.cloneDeep(children),
attributes: _.cloneDeep(attributes)
}
validationError = new Error(msg);
let validationError
const _validateParams = function () {
const constraints = {
children: function (value) {
const possibleChildren = ['say', 'play']
if (value && !possibleChildren.includes(Object.keys(value)[0])) {
return {
format: 'digits has invalid child'
}
}
return {
presence: true
}
},
'attributes.numDigits': function (value) {
if (!validate.isInteger(value)) {
return {
format: 'must be an integer'
}
}
},
'attributes.timeout': function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
},
'attributes.callbackUrl': {
url: true
}
}
_validateParams();
if (validationError) {
throw validationError;
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
const action = {
tag: 'GetDigits',
children,
attributes
}
this.buildAction(action);
return this;
validationError = new Error(msg)
}
}
dial(phoneNumbers, attributes = {}) {
let params = _.cloneDeep(attributes);
params.phoneNumbers = phoneNumbers;
_validateParams()
let _self = this;
let validationError;
if (validationError) {
throw validationError
}
let _validateParams = function () {
const action = {
tag: 'GetDigits',
children,
attributes
}
const constraints = {
phoneNumbers: function (value) {
this.buildAction(action)
return this
}
if (value && !validate.isString(value)) {
return {
format: 'must be a string'
};
}
return {
presence: true
}
},
record: function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null
},
sequential: function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null;
},
callerId: function (value) {
dial (phoneNumbers, attributes = {}) {
const params = _.cloneDeep(attributes)
params.phoneNumbers = phoneNumbers
if (!phoneValidator(value).isValid) {
return {
format: 'must not contain invalid phone number'
};
};
return null;
},
ringBackTone: {
url: true
},
maxDuration: function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
}
};
let validationError
let error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
validationError = new Error(msg);
const _validateParams = function () {
const constraints = {
phoneNumbers: function (value) {
if (value && !validate.isString(value)) {
return {
format: 'must be a string'
}
}
return {
presence: true
}
},
record: function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null
},
sequential: function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null
},
callerId: function (value) {
if (!phoneValidator(value).isValid) {
return {
format: 'must not contain invalid phone number'
}
};
return null
},
ringBackTone: {
url: true
},
maxDuration: function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
}
}
_validateParams();
if (validationError) {
throw validationError;
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
const action = {
tag: 'Dial',
attributes: params
}
validationError = new Error(msg)
}
}
this.buildAction(action);
return this;
_validateParams()
if (validationError) {
throw validationError
}
record(children, attributes = {}) {
let params = {
children: _.cloneDeep(children),
attributes: _.cloneDeep(attributes)
}
const action = {
tag: 'Dial',
attributes: params
}
let _self = this;
let validationError;
this.buildAction(action)
return this
}
let _validateParams = function () {
record (children, attributes = {}) {
const params = {
children: _.cloneDeep(children),
attributes: _.cloneDeep(attributes)
}
const constraints = {
children: function (value) {
const possibleChildren = ['say', 'play'];
if (value && !possibleChildren.includes(Object.keys(value)[0])) {
return {
format: 'digits has invalid child'
}
}
return {
presence: true
}
},
"attributes.maxLength": function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
},
"attributes.timeout": function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
},
"attributes.trimSilence": function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null;
},
"attributes.playBeep": function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null;
},
"attributes.callbackUrl": {
url: true
},
};
let validationError
let error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
validationError = new Error(msg);
const _validateParams = function () {
const constraints = {
children: function (value) {
const possibleChildren = ['say', 'play']
if (value && !possibleChildren.includes(Object.keys(value)[0])) {
return {
format: 'digits has invalid child'
}
}
return {
presence: true
}
},
'attributes.maxLength': function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
},
'attributes.timeout': function (value) {
if (!validate.isNumber(value)) {
return {
format: 'must be time in seconds'
}
}
},
'attributes.trimSilence': function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null
},
'attributes.playBeep': function (value) {
if (!validate.isBoolean(value)) {
return {
format: 'invalid option'
}
}
return null
},
'attributes.callbackUrl': {
url: true
}
}
_validateParams();
if (validationError) {
throw validationError;
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
const action = {
tag: 'Record',
children,
attributes
}
this.buildAction(action);
return this;
validationError = new Error(msg)
}
}
enqueue(attributes) {
let params = _.cloneDeep(attributes);
let _self = this;
let validationError;
_validateParams()
let _validateParams = function () {
if (validationError) {
throw validationError
}
const constraints = {
holdMusic: {
url: true
}
};
const action = {
tag: 'Record',
children,
attributes
}
let error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
this.buildAction(action)
return this
}
validationError = new Error(msg);
}
}
enqueue (attributes) {
const params = _.cloneDeep(attributes)
_validateParams();
let validationError
if (validationError) {
throw validationError;
const _validateParams = function () {
const constraints = {
holdMusic: {
url: true
}
}
const action = {
tag: 'Enqueue',
attributes
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
this.buildAction(action);
return this;
validationError = new Error(msg)
}
}
dequeue(phoneNumber, attributes = {}) {
let params = _.cloneDeep(attributes);
params.phoneNumber = phoneNumber;
_validateParams()
let _self = this;
let validationError;
if (validationError) {
throw validationError
}
let _validateParams = function () {
const action = {
tag: 'Enqueue',
attributes
}
const constraints = {
phoneNumber: function (value) {
this.buildAction(action)
return this
}
if (value && !phoneValidator(value).isValid) {
return {
format: 'must not contain invalid phone number'
};
}
return {
presence: true
}
}
};
dequeue (phoneNumber, attributes = {}) {
const params = _.cloneDeep(attributes)
params.phoneNumber = phoneNumber
let error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
let validationError
validationError = new Error(msg);
const _validateParams = function () {
const constraints = {
phoneNumber: function (value) {
if (value && !phoneValidator(value).isValid) {
return {
format: 'must not contain invalid phone number'
}
}
return {
presence: true
}
}
}
_validateParams();
if (validationError) {
throw validationError;
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
const action = {
tag: 'Dequeue',
attributes: params
}
this.buildAction(action);
return this;
validationError = new Error(msg)
}
}
redirect(text) {
let _self = this;
let validationError;
_validateParams()
let _validateParams = function () {
if (validationError) {
throw validationError
}
const constraints = {
text: function (value) {
const action = {
tag: 'Dequeue',
attributes: params
}
if (value && !validate.isEmpty(value)) {
return {
url: true
};
}
return {
presence: true
}
},
};
this.buildAction(action)
return this
}
let error = validate({ text }, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + "; ";
}
redirect (text) {
let validationError
validationError = new Error(msg);
const _validateParams = function () {
const constraints = {
text: function (value) {
if (value && !validate.isEmpty(value)) {
return {
url: true
}
}
return {
presence: true
}
}
}
_validateParams();
if (validationError) {
throw validationError;
const error = validate({ text }, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
const action = {
tag: 'Redirect',
text
};
validationError = new Error(msg)
}
}
this.buildAction(action);
return this;
_validateParams()
if (validationError) {
throw validationError
}
conference() {
let _self = this;
const action = {
tag: 'Redirect',
text
}
const action = {
tag: 'Conference'
};
this.buildAction(action)
return this
}
this.buildAction(action);
return this;
conference () {
const action = {
tag: 'Conference'
}
reject() {
let _self = this;
this.buildAction(action)
return this
}
const action = {
tag: 'Reject'
};
reject () {
const action = {
tag: 'Reject'
}
this.buildAction(action);
return this;
}
this.buildAction(action)
return this
}
}
module.exports = ActionBuilder;
module.exports = ActionBuilder

@@ -1,92 +0,92 @@

'use strict';
'use strict'
const Joi = require('@hapi/joi');
const { phoneValidator } = require('./utils');
const initializeAxios = require('./customAxios');
const Joi = require('@hapi/joi')
const { phoneValidator } = require('./utils')
const initializeAxios = require('./customAxios')
class Airtime {
constructor(config) {
this.config = config;
}
constructor (config) {
this.config = config
}
send(options) {
return new Promise((resolve, reject) => {
const { error, value } = this.validateOptions(options);
send (options) {
return new Promise((resolve, reject) => {
const { error, value } = this.validateOptions(options)
if (error) {
const combinedMessages = error.details.map(d => d.message).join(';');
reject(new Error(combinedMessages));
return;
}
if (error) {
const combinedMessages = error.details.map(d => d.message).join(';')
reject(new Error(combinedMessages))
return
}
const rawRecipients = value.recipients;
const recipients = rawRecipients.map(r => ({
phoneNumber: r.phoneNumber,
amount: `${r.currencyCode} ${r.amount}`,
}));
const customAxios = value.idempotencyKey ? initializeAxios(this.config, value.idempotencyKey) : initializeAxios(this.config);
const rawRecipients = value.recipients
const recipients = rawRecipients.map(r => ({
phoneNumber: r.phoneNumber,
amount: `${r.currencyCode} ${r.amount}`
}))
const customAxios = value.idempotencyKey ? initializeAxios(this.config, value.idempotencyKey) : initializeAxios(this.config)
const requestBody = {
recipients: JSON.stringify(recipients)
};
const requestBody = {
recipients: JSON.stringify(recipients)
}
if(options.maxNumRetry && (options.maxNumRetry > 0) ){
requestBody['maxNumRetry'] = Number(options.maxNumRetry);
};
customAxios.airtime.sendAirtimeRequest(requestBody)
.then(function (response) {
if (response.status === 201) {
resolve(response.data);
} else {
reject(response.data || response.error);
}
})
.catch(function (err) {
reject(err);
});
});
}
if (options.maxNumRetry && (options.maxNumRetry > 0)) {
requestBody.maxNumRetry = Number(options.maxNumRetry)
};
findTransactionStatus(transactionId) {
return new Promise((resolve, reject) => {
if(!transactionId){
throw new Error(`transactionId should be provided`);
}
customAxios.airtime.sendAirtimeRequest(requestBody)
.then(function (response) {
if (response.status === 201) {
resolve(response.data)
} else {
reject(response.data || response.error)
}
})
.catch(function (err) {
reject(err)
})
})
}
const { airtime } = initializeAxios(this.config);
findTransactionStatus (transactionId) {
return new Promise((resolve, reject) => {
if (!transactionId) {
throw new Error('transactionId should be provided')
}
airtime.findTransactionStatus(transactionId)
.then(function (resp) {
const httpStatus = resp.status;
if (httpStatus === 200) {
resolve(resp.data);
} else {
reject(resp.data);
};
}).catch(function (error) {
return reject(error);
});
const { airtime } = initializeAxios(this.config)
airtime.findTransactionStatus(transactionId)
.then(function (resp) {
const httpStatus = resp.status
if (httpStatus === 200) {
resolve(resp.data)
} else {
reject(resp.data)
};
}).catch(function (error) {
return reject(error)
})
}
})
}
validateOptions(options) {
const schema = Joi.object().keys({
idempotencyKey: Joi.string().optional(),
recipients: Joi.array().items(
Joi.object({
phoneNumber:Joi.string().required().custom((value)=>{
return phoneValidator(value).isValid? value : new Error(`the phone number ${value} is invalid`);
}),
currencyCode: Joi.string().required(),
amount: Joi.number().required(),
}),
).min(1).required(),
maxNumRetry: Joi.number().optional(),
}).required();
validateOptions (options) {
const schema = Joi.object().keys({
idempotencyKey: Joi.string().optional(),
recipients: Joi.array().items(
Joi.object({
phoneNumber: Joi.string().required().custom((value) => {
return phoneValidator(value).isValid ? value : new Error(`the phone number ${value} is invalid`)
}),
currencyCode: Joi.string().required(),
amount: Joi.number().required()
})
).min(1).required(),
maxNumRetry: Joi.number().optional()
}).required()
return schema.validate(options);
}
return schema.validate(options)
}
}
module.exports = Airtime;
module.exports = Airtime

@@ -1,36 +0,35 @@

'use strict';
'use strict'
const initializeAxios = require('./customAxios');
const initializeAxios = require('./customAxios')
class Application {
constructor(config) {
this.config = config;
}
constructor (config) {
this.config = config
}
fetchApplicationData() {
return new Promise((resolve, reject) => {
const customAxios = initializeAxios(this.config);
customAxios.application.getApplicationData()
.then(function (response) {
if (response.status === 200) {
resolve(response.data);
} else {
reject(response.data || response.error);
}
})
.catch(function (err) {
reject(err);
});
fetchApplicationData () {
return new Promise((resolve, reject) => {
const customAxios = initializeAxios(this.config)
});
}
customAxios.application.getApplicationData()
.then(function (response) {
if (response.status === 200) {
resolve(response.data)
} else {
reject(response.data || response.error)
}
})
.catch(function (err) {
reject(err)
})
})
}
/* backward compatibility */
fetchAccount() {
return this.fetchApplicationData();
}
/* end backward compatibility */
/* backward compatibility */
fetchAccount () {
return this.fetchApplicationData()
}
/* end backward compatibility */
}
module.exports = Application;
module.exports = Application

@@ -1,39 +0,38 @@

'use strict';
'use strict'
const BASE_DOMAIN = "africastalking.com";
const BASE_SANDBOX_DOMAIN = "sandbox." + BASE_DOMAIN;
const BASE_DOMAIN = 'africastalking.com'
const BASE_SANDBOX_DOMAIN = 'sandbox.' + BASE_DOMAIN
const initUrls = function (sandbox) {
const baseDomain = sandbox ? BASE_SANDBOX_DOMAIN : BASE_DOMAIN
const baseUrl = 'https://api.' + baseDomain + '/version1'
const baseDomain = sandbox ? BASE_SANDBOX_DOMAIN : BASE_DOMAIN;
const baseUrl = "https://api." + baseDomain + "/version1";
exports.BASE_URL = baseUrl
exports.BASE_URL = baseUrl;
exports.CHECKOUT_TOKEN_URL = 'https://api.' + baseDomain + '/checkout/token/create'
exports.CHECKOUT_TOKEN_URL = "https://api." + baseDomain + "/checkout/token/create";
exports.AUTH_TOKEN_URL = 'https://api.' + baseDomain + '/auth-token/generate'
exports.AUTH_TOKEN_URL = "https://api." + baseDomain + "/auth-token/generate";
exports.USER_URL = baseUrl + '/user'
exports.USER_URL = baseUrl + "/user";
exports.SMS_URL = baseUrl + '/messaging'
exports.SMS_URL = baseUrl + "/messaging";
exports.AIRTIME_URL = baseUrl + '/airtime/send'
exports.AIRTIME_URL = baseUrl + "/airtime/send";
exports.VOICE_URL = 'https://voice.' + baseDomain
exports.VOICE_URL = "https://voice." + baseDomain;
exports.MOBILE_DATA_URL = 'https://bundles.' + baseDomain
exports.MOBILE_DATA_URL = "https://bundles." + baseDomain;
exports.CONTENT_URL = sandbox
? baseUrl
: 'https://content.' + baseDomain + '/version1'
exports.CONTENT_URL = sandbox
? baseUrl
: 'https://content.' + baseDomain + '/version1';
exports.INSIGHTS_URL = 'https://insights.' + baseDomain
}
exports.INSIGHTS_URL = "https://insights." + baseDomain;
};
// no sandbox by default
initUrls(false);
initUrls(false)
exports.enableSandbox = function () {
initUrls(true);
};
initUrls(true)
}

@@ -1,22 +0,22 @@

const qs = require('querystring');
const qs = require('querystring')
function airtime ({ createAxiosInstance, endpoints, username }) {
return {
sendAirtimeRequest: data => {
return createAxiosInstance().post(endpoints.SEND_AIRTIME, qs.stringify({
...data,
username,
}));
},
findTransactionStatus: transactionId => {
return createAxiosInstance('application/json').get(endpoints.FIND_AIRTIME_TRANSACTION, {
params: {
transactionId,
username,
}
});
return {
sendAirtimeRequest: data => {
return createAxiosInstance().post(endpoints.SEND_AIRTIME, qs.stringify({
...data,
username
}))
},
findTransactionStatus: transactionId => {
return createAxiosInstance('application/json').get(endpoints.FIND_AIRTIME_TRANSACTION, {
params: {
transactionId,
username
}
};
})
}
}
};
module.exports = airtime;
module.exports = airtime
function application ({ createAxiosInstance, endpoints, username }) {
return {
getApplicationData: () => {
return createAxiosInstance().get(endpoints.GET_APPLICATION_DATA, {
params: { username },
});
},
};
return {
getApplicationData: () => {
return createAxiosInstance().get(endpoints.GET_APPLICATION_DATA, {
params: { username }
})
}
}
};
module.exports = application;
module.exports = application

@@ -1,45 +0,45 @@

const axios = require('axios');
const airtime = require('./airtime');
const application = require('./application');
const axios = require('axios')
const airtime = require('./airtime')
const application = require('./application')
function initializeAxios (config, idempotencyKey=null) {
const { username, apiKey, format } = config;
function initializeAxios (config, idempotencyKey = null) {
const { username, apiKey, format } = config
const baseURL = username === 'sandbox'
? 'https://api.sandbox.africastalking.com'
: 'https://api.africastalking.com';
const baseURL = username === 'sandbox'
? 'https://api.sandbox.africastalking.com'
: 'https://api.africastalking.com'
const createAxiosInstance = (contentType = 'application/x-www-form-urlencoded') => {
let headers = {
apiKey,
'Content-Type': contentType,
Accept: format,
};
if (idempotencyKey) {
headers['Idempotency-Key'] = idempotencyKey;
}
return axios.create({
baseURL,
headers,
});
};
const createAxiosInstance = (contentType = 'application/x-www-form-urlencoded') => {
const headers = {
apiKey,
'Content-Type': contentType,
Accept: format
}
if (idempotencyKey) {
headers['Idempotency-Key'] = idempotencyKey
}
return axios.create({
baseURL,
headers
})
}
const endpoints = {
SEND_AIRTIME: '/version1/airtime/send',
FIND_AIRTIME_TRANSACTION: '/query/transaction/find',
GET_APPLICATION_DATA: '/version1/user',
};
const endpoints = {
SEND_AIRTIME: '/version1/airtime/send',
FIND_AIRTIME_TRANSACTION: '/query/transaction/find',
GET_APPLICATION_DATA: '/version1/user'
}
const opts = {
createAxiosInstance,
endpoints,
username,
};
const opts = {
createAxiosInstance,
endpoints,
username
}
return {
airtime: airtime(opts),
application: application(opts),
};
return {
airtime: airtime(opts),
application: application(opts)
}
};
module.exports = initializeAxios;
module.exports = initializeAxios

@@ -1,75 +0,73 @@

'use strict';
const validate = require('validate.js');
const _ = require('lodash');
'use strict'
const validate = require('validate.js')
const _ = require('lodash')
const Common = require('./common');
const Common = require('./common')
const Application = require('./application');
const Token = require('./token');
const SMS = require('./sms');
const USSD = require('./ussd');
const Airtime = require('./airtime');
const Voice = require('./voice');
const MobileData = require('./mobileData');
const Insights = require('./insights');
const Application = require('./application')
const Token = require('./token')
const SMS = require('./sms')
const USSD = require('./ussd')
const Airtime = require('./airtime')
const Voice = require('./voice')
const MobileData = require('./mobileData')
const Insights = require('./insights')
class AfricasTalking {
constructor(options) {
constructor (options) {
this.options = _.cloneDeep(options)
this.options = _.cloneDeep(options);
validate.validators.isString = function (value, options, key, attributes) {
return validate.isEmpty(value) || validate.isString(value) ? null : 'must be a string'
}
validate.validators.isString = function (value, options, key, attributes) {
return validate.isEmpty(value) || validate.isString(value) ? null : "must be a string";
};
const constraints = {
format: {
inclusion: ['json', 'xml']
},
username: {
presence: true,
isString: true
},
apiKey: {
presence: true,
isString: true
}
}
const constraints = {
format: {
inclusion: ['json', 'xml']
},
username: {
presence: true,
isString: true
},
apiKey: {
presence: true,
isString: true
}
};
const error = validate(this.options, constraints)
if (error) {
throw error
}
const error = validate(this.options, constraints);
if (error) {
throw error;
}
switch (this.options.format) {
case 'xml':
this.options.format = 'application/xml'
break
case 'json': // Get json by default
default:
this.options.format = 'application/json'
}
switch (this.options.format) {
case "xml":
this.options.format = "application/xml";
break;
case "json": // Get json by default
default:
this.options.format = "application/json";
}
const isSandbox = this.options.username.toLowerCase() === 'sandbox'
if (isSandbox) {
Common.enableSandbox()
}
this.USSD = USSD
this.SMS = new SMS(this.options)
this.TOKEN = new Token(this.options)
this.VOICE = new Voice(this.options)
this.AIRTIME = new Airtime(this.options)
this.INSIGHTS = new Insights(this.options)
this.MOBILE_DATA = new MobileData(this.options)
this.APPLICATION = new Application(this.options)
var isSandbox = this.options.username.toLowerCase() === 'sandbox';
if (isSandbox) {
Common.enableSandbox();
}
this.USSD = USSD;
this.SMS = new SMS(this.options);
this.TOKEN = new Token(this.options);
this.VOICE = new Voice(this.options);
this.AIRTIME = new Airtime(this.options);
this.INSIGHTS = new Insights(this.options);
this.MOBILE_DATA = new MobileData(this.options);
this.APPLICATION = new Application(this.options);
/* For backward compatibility */
this.ACCOUNT = this.APPLICATION;
/* End */
}
/* For backward compatibility */
this.ACCOUNT = this.APPLICATION
/* End */
}
}
module.exports = function (options) {
return new AfricasTalking(options);
};
return new AfricasTalking(options)
}

@@ -1,98 +0,102 @@

'use strict';
'use strict'
const axios = require('axios');
const validate = require('validate.js');
const axios = require('axios')
const validate = require('validate.js')
const Common = require('./common');
const Common = require('./common')
const {
phoneValidator
} = require('./utils');
phoneValidator
} = require('./utils')
class Insights {
constructor(options) {
this.options = options;
};
constructor (options) {
this.options = options
};
checkSimSwapState(phoneNumbers) {
const _self = this;
checkSimSwapState (phoneNumbers) {
const _self = this
let validationError
const constraints = {
phoneNumbers: function (value) {
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
},
};
}
const constraints = {
phoneNumbers: function (value) {
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
}
}
if (!validate.isArray(value) && !validate.isString(value)) {
return {
format: 'must be a string or an array strings (phone numbers)',
};
}
if (!validate.isArray(value) && !validate.isString(value)) {
return {
format: 'must be a string or an array strings (phone numbers)'
}
}
if (validate.isString(value)) {
if (!phoneValidator(value).isValid) {
return {
format: 'must be a valid phone number'
};
}
}
if (validate.isArray(value)) {
let invalidPhoneNumbers = []
value.forEach(function (phoneNumber) {
if (!phoneValidator(phoneNumber).isValid) {
invalidPhoneNumbers.push(phoneNumber);
}
});
if (invalidPhoneNumbers.length > 0) {
return {
format: 'must NOT contain invalid phone number'
}
}
}
return null;
if (validate.isString(value)) {
if (!phoneValidator(value).isValid) {
return {
format: 'must be a valid phone number'
}
};
}
}
const error = validate({ phoneNumbers }, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + '; ';
if (validate.isArray(value)) {
const invalidPhoneNumbers = []
value.forEach(function (phoneNumber) {
if (!phoneValidator(phoneNumber).isValid) {
invalidPhoneNumbers.push(phoneNumber)
}
validationError = new Error(msg);
})
if (invalidPhoneNumbers.length > 0) {
return {
format: 'must NOT contain invalid phone number'
}
}
}
return null
}
}
return new Promise((resolve, reject) => {
const config = {
method: 'post',
url: `${Common.INSIGHTS_URL}/v1/sim-swap`,
headers: {
apiKey: _self.options.apiKey,
Accept: _self.options.format,
'Content-Type': 'application/json'
},
data: JSON.stringify({
username: _self.options.username,
phoneNumbers,
})
};
axios(config)
.then(function (resp) {
const results = resp.data;
if (!results || results.status !== 'Processed') {
return reject(results || 'Unexpected error');
};
return resolve(results)
})
.catch(function (error) {
return reject(error);
});
});
const error = validate({ phoneNumbers }, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
validationError = new Error(msg)
}
return new Promise((resolve, reject) => {
if (validationError) {
return reject(validationError)
}
const config = {
method: 'post',
url: `${Common.INSIGHTS_URL}/v1/sim-swap`,
headers: {
apiKey: _self.options.apiKey,
Accept: _self.options.format,
'Content-Type': 'application/json'
},
data: JSON.stringify({
username: _self.options.username,
phoneNumbers
})
}
axios(config)
.then(function (resp) {
const results = resp.data
if (!results || results.status !== 'Processed') {
return reject(results || 'Unexpected error')
};
return resolve(results)
})
.catch(function (error) {
return reject(error)
})
})
}
};
module.exports = Insights;
module.exports = Insights

@@ -1,157 +0,154 @@

'use strict';
'use strict'
const Joi = require('@hapi/joi');
const axios = require('axios');
const { phoneValidator } = require('./utils');
const Common = require('./common');
const Joi = require('@hapi/joi')
const axios = require('axios')
const { phoneValidator } = require('./utils')
const Common = require('./common')
class MobileData {
constructor(options) {
this.options = options;
constructor (options) {
this.options = options
}
validationOptions ({ context, options }) {
const schemas = {
send: Joi.object().keys({
idempotencyKey: Joi.string().optional(),
productName: Joi.string().required(),
recipients: Joi.array().items(
Joi.object({
phoneNumber: Joi.string().required().custom((value) => {
return phoneValidator(value).isValid ? value : new Error(`the phone number ${value} is invalid`)
}),
quantity: Joi.number().required(),
unit: Joi.string().valid('MB', 'GB').required(),
validity: Joi.string().valid('Day', 'Week', 'BiWeek', 'Month', 'Quarterly').required(),
metadata: Joi.object().optional()
})
).min(1).required()
}).required(),
findTransaction: Joi.object().keys({
transactionId: Joi.string().required()
}).required()
}
validationOptions({context, options}) {
const schemas = {
send: Joi.object().keys({
idempotencyKey: Joi.string().optional(),
productName: Joi.string().required(),
recipients: Joi.array().items(
Joi.object({
phoneNumber: Joi.string().required().custom((value) => {
return phoneValidator(value).isValid ? value : new Error(`the phone number ${value} is invalid`);
}),
quantity: Joi.number().required(),
unit: Joi.string().valid('MB', 'GB').required(),
validity: Joi.string().valid('Day', 'Week', 'BiWeek', 'Month', 'Quarterly').required(),
metadata: Joi.object().optional(),
}),
).min(1).required(),
}).required(),
if (!schemas[context]) throw new Error(`schema context '${context}' does not exist`)
findTransaction: Joi.object().keys({
transactionId: Joi.string().required(),
}).required(),
};
return schemas[context].validate(options)
};
if(!schemas[context]) throw new Error(`schema context '${context}' does not exist`);
send (options) {
return new Promise((resolve, reject) => {
const { error, value } = this.validationOptions({ context: 'send', options })
return schemas[context].validate(options);
};
if (error) {
const combinedMessages = error.details.map(d => d.message).join(';')
reject(new Error(combinedMessages))
return
} else {
const headers = {
apikey: this.options.apiKey,
Accept: 'application/json',
'Content-Type': 'application/json'
}
send(options) {
return new Promise((resolve, reject) => {
const { error, value } = this.validationOptions({ context:'send', options});
if (value.idempotencyKey) {
headers['Idempotency-Key'] = value.idempotencyKey
}
if (error) {
const combinedMessages = error.details.map(d => d.message).join(';');
reject(new Error(combinedMessages));
return;
const data = {
username: this.options.username,
productName: value.productName,
recipients: value.recipients
}
axios({
method: 'post',
url: `${Common.MOBILE_DATA_URL}/mobile/data/request`,
headers,
data: JSON.stringify(data)
})
.then(function (resp) {
const httpStatus = resp.status
if (httpStatus === 200 || httpStatus === 201) {
resolve(resp.data)
} else {
reject(resp.data)
};
})
.catch(function (error) {
return reject(error)
})
};
})
}
const headers = {
apikey: this.options.apiKey,
Accept: 'application/json',
'Content-Type': 'application/json',
}
findTransaction (options) {
return new Promise((resolve, reject) => {
const { error, value } = this.validationOptions({ context: 'findTransaction', options })
if (error) {
const combinedMessages = error.details.map(d => d.message).join(';')
reject(new Error(combinedMessages))
return
} else {
const username = this.options.username
const transactionId = value.transactionId
const apikey = this.options.apiKey
if (value.idempotencyKey) {
headers['Idempotency-Key'] = value.idempotencyKey;
}
const data = {
username: this.options.username,
productName: value.productName,
recipients: value.recipients
};
axios({
method: 'post',
url: `${Common.MOBILE_DATA_URL}/mobile/data/request`,
headers,
data: JSON.stringify(data)
})
.then(function (resp) {
const httpStatus = resp.status;
if (httpStatus === 200 || httpStatus === 201) {
resolve(resp.data);
} else {
reject(resp.data);
};
})
.catch(function (error) {
return reject(error);
});
};
axios({
method: 'get',
maxBodyLength: Infinity,
url: `${Common.MOBILE_DATA_URL}/query/transaction/find?username=${username}&transactionId=${transactionId}`,
headers: {
apikey,
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
}
findTransaction(options){
return new Promise((resolve, reject) => {
const { error, value } = this.validationOptions({ context:'findTransaction', options});
if (error) {
const combinedMessages = error.details.map(d => d.message).join(';');
reject(new Error(combinedMessages));
return;
.then(function (resp) {
const httpStatus = resp.status
if (httpStatus === 200 || httpStatus === 201) {
resolve(resp.data)
} else {
const username = this.options.username;
const transactionId = value.transactionId;
const apikey = this.options.apiKey;
axios({
method: 'get',
maxBodyLength: Infinity,
url: `${Common.MOBILE_DATA_URL}/query/transaction/find?username=${username}&transactionId=${transactionId}`,
headers: {
apikey,
Accept: 'application/json',
'Content-Type': 'application/json',
}
})
.then(function (resp) {
const httpStatus = resp.status;
if (httpStatus === 200 || httpStatus === 201) {
resolve(resp.data);
} else {
reject(resp.data);
};
})
.catch(function (error) {
return reject(error);
});
reject(resp.data)
};
})
}
})
.catch(function (error) {
return reject(error)
})
};
})
}
fetchWalletBalance(){
return new Promise((resolve, reject) => {
const username = this.options.username;
const apikey = this.options.apiKey;
axios({
method: 'get',
maxBodyLength: Infinity,
url: `${Common.MOBILE_DATA_URL}/query/wallet/balance?username=${username}`,
headers: {
apikey,
Accept: 'application/json',
'Content-Type': 'application/json',
}
})
.then(function (resp) {
const httpStatus = resp.status;
if (httpStatus === 200 || httpStatus === 201) {
resolve(resp.data);
} else {
reject(resp.data);
};
})
.catch(function (error) {
return reject(error);
});
fetchWalletBalance () {
return new Promise((resolve, reject) => {
const username = this.options.username
const apikey = this.options.apiKey
axios({
method: 'get',
maxBodyLength: Infinity,
url: `${Common.MOBILE_DATA_URL}/query/wallet/balance?username=${username}`,
headers: {
apikey,
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
.then(function (resp) {
const httpStatus = resp.status
if (httpStatus === 200 || httpStatus === 201) {
resolve(resp.data)
} else {
reject(resp.data)
};
})
}
.catch(function (error) {
return reject(error)
})
})
}
}
module.exports = MobileData;
module.exports = MobileData

@@ -1,422 +0,415 @@

'use strict';
'use strict'
const axios = require('axios');
const validate = require('validate.js');
const _ = require('lodash');
const { phoneValidator } = require('./utils');
const axios = require('axios')
const validate = require('validate.js')
const _ = require('lodash')
const { phoneValidator } = require('./utils')
const Common = require('./common')
const Common = require('./common');
class SMS {
constructor(options) {
constructor (options) {
const _self = this
const _self = this;
this.options = options
this.options = options;
this._send = function (params, isBulk, isPremium) {
let validationError
this._send = function (params, isBulk, isPremium) {
let validationError;
// Validate params
const _validateParams = function () {
const constraints = {
to: function (value, attributes, attributeName, options, constraints) {
if (validate.isEmpty(value)) {
return {
presence: { message: 'is required' }
}
}
// Validate params
const _validateParams = function () {
const constraints = {
to: function (value, attributes, attributeName, options, constraints) {
if (validate.isEmpty(value)) {
return {
presence: { message: 'is required' },
};
}
if (!validate.isArray(value) && !validate.isString(value)) {
return {
format: 'must be a string or an array strings (phone numbers)',
};
}
if (validate.isString(value)) {
if (!phoneValidator(value).isValid) {
return {
format: 'must be a valid phone number'
};
}
}
if (validate.isArray(value)) {
let invalidPhoneNumbers = []
value.forEach(function (phoneNumber) {
if(!phoneValidator(phoneNumber).isValid){
invalidPhoneNumbers.push(phoneNumber);
}
});
if (invalidPhoneNumbers.length > 0) {
return {
format: 'must NOT contain invalid phone number'
}
}
}
return null;
},
from: {
isString: true,
},
message: {
presence: true,
},
};
if (isBulk) {
constraints.enqueue = {
inclusion: [true, false],
};
if (!validate.isArray(value) && !validate.isString(value)) {
return {
format: 'must be a string or an array strings (phone numbers)'
}
if (isPremium) {
constraints.keyword = {
presence: true,
isString: true,
};
constraints.linkId = {
presence: false,
isString: true,
};
constraints.retryDurationInHours = {
numericality: true,
};
}
}
const error = validate(params, constraints);
if (error) {
let msg = '';
for (let k in error) {
msg += error[k] + '; ';
if (validate.isString(value)) {
if (!phoneValidator(value).isValid) {
return {
format: 'must be a valid phone number'
}
validationError = new Error(msg);
}
};
}
_validateParams();
// Multiple recipients?
if (validate.isArray(params.to)) {
params.to = params.to.join();
}
return new Promise(function (resolve, reject) {
if (validationError) {
return reject(validationError);
};
const body = {
username: _self.options.username,
to: params.to,
message: params.message
};
if (params.from) {
body.from = params.from
if (validate.isArray(value)) {
const invalidPhoneNumbers = []
value.forEach(function (phoneNumber) {
if (!phoneValidator(phoneNumber).isValid) {
invalidPhoneNumbers.push(phoneNumber)
}
if (isBulk) {
body.bulkSMSMode = 1;
if (params.enqueue) {
body.enqueue = 1;
}
if (params.enqueue === false) {
body.enqueue = 0;
}
})
if (invalidPhoneNumbers.length > 0) {
return {
format: 'must NOT contain invalid phone number'
}
if (isPremium) {
body.bulkSMSMode = 0;
body.keyword = params.keyword;
body.linkId = params.linkId;
if (params.retryDurationInHours) {
body.retryDurationInHours = params.retryDurationInHours;
}
}
const url = isBulk ? Common.SMS_URL : Common.CONTENT_URL + '/messaging';
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format,
};
}
}
return null
},
from: {
isString: true
},
message: {
presence: true
}
}
if (isBulk) {
constraints.enqueue = {
inclusion: [true, false]
}
}
if (isPremium) {
constraints.keyword = {
presence: true,
isString: true
}
constraints.linkId = {
presence: false,
isString: true
}
constraints.retryDurationInHours = {
numericality: true
}
}
axios({
method:'POST',
url,
headers,
data: new URLSearchParams(body)
})
.then(function (response) {
if (response.status === 201) {
// API returns CREATED on success!?
resolve(response.data);
} else {
reject(response.data);
}
})
.catch(function (error) {
reject(error)
});
const error = validate(params, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
validationError = new Error(msg)
}
}
_validateParams()
// Multiple recipients?
if (validate.isArray(params.to)) {
params.to = params.to.join()
}
return new Promise(function (resolve, reject) {
if (validationError) {
return reject(validationError)
}
const body = {
username: _self.options.username,
to: params.to,
message: params.message
}
if (params.from) {
body.from = params.from
}
if (isBulk) {
body.bulkSMSMode = 1
if (params.enqueue) {
body.enqueue = 1
}
if (params.enqueue === false) {
body.enqueue = 0
}
}
if (isPremium) {
body.bulkSMSMode = 0
body.keyword = params.keyword
body.linkId = params.linkId
if (params.retryDurationInHours) {
body.retryDurationInHours = params.retryDurationInHours
}
}
const url = isBulk ? Common.SMS_URL : Common.CONTENT_URL + '/messaging'
});
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format
}
axios({
method: 'POST',
url,
headers,
data: new URLSearchParams(body)
})
.then(function (response) {
if (response.status === 201) {
// API returns CREATED on success!?
resolve(response.data)
} else {
reject(response.data)
}
})
.catch(function (error) {
reject(error)
})
})
}
}
send(params){
const opts = _.cloneDeep(params);
send (params) {
const opts = _.cloneDeep(params)
if (Array.isArray(opts)) {
const results = opts.map((opt) => {
return this._send(opt, true, false);
});
if (Array.isArray(opts)) {
const results = opts.map((opt) => {
return this._send(opt, true, false)
})
return Promise.allSettled(results).then((results) => {
return results.map((result) => {
if (result.status === 'fulfilled') {
return result.value;
} else {
return {
SMSMessageData: {
Message: result.reason,
status: 'failed',
},
};
}
});
});
} else {
return this._send(opts, true, false);
}
return Promise.allSettled(results).then((results) => {
return results.map((result) => {
if (result.status === 'fulfilled') {
return result.value
} else {
return {
SMSMessageData: {
Message: result.reason,
status: 'failed'
}
}
}
})
})
} else {
return this._send(opts, true, false)
}
}
sendBulk(params) {
return this.send(params);
}
sendBulk (params) {
return this.send(params)
}
sendPremium (params) {
const opts = _.cloneDeep(params)
return this._send(opts, false, true)
}
sendPremium(params) {
const opts = _.cloneDeep(params);
return this._send(opts, false, true);
}
fetchMessages = function (params) {
const _self = this
const opts = _.cloneDeep(params) || {}
opts.lastReceivedId = opts.lastReceivedId || 0
return new Promise(function (resolve, reject) {
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format
}
fetchMessages = function (params) {
const _self = this;
const opts = _.cloneDeep(params) || {};
opts.lastReceivedId = opts.lastReceivedId || 0;
return new Promise(function (resolve, reject) {
const query = new URLSearchParams({
username: _self.options.username,
lastReceivedId: opts.lastReceivedId,
keyword: opts.keyword,
shortCode: opts.shortCode
}).toString()
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format
};
const url = `${Common.SMS_URL}?${query}`
const query = new URLSearchParams({
username: _self.options.username,
lastReceivedId: opts.lastReceivedId,
keyword: opts.keyword,
shortCode: opts.shortCode,
}).toString();
axios({
method: 'GET',
url,
headers
})
.then(function (response) {
if (response.status === 200) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch(function (error) {
reject(error)
})
})
}
const url = `${Common.SMS_URL}?${query}`;
createSubscription (params) {
const _self = this
const opts = _.cloneDeep(params) || {}
const constraints = {
shortCode: {
presence: true,
isString: true
},
keyword: {
presence: true,
isString: true
},
phoneNumber: {
presence: true,
isString: true
}
}
const validationError = validate(opts, constraints)
const body = {
username: _self.options.username,
shortCode: opts.shortCode,
keyword: opts.keyword,
phoneNumber: opts.phoneNumber
}
return new Promise(function (resolve, reject) {
if (validationError) {
return reject(validationError)
}
axios({
method:'GET',
url,
headers
})
.then(function (response) {
if (response.status === 200) {
resolve(response.data);
} else {
reject(response.data);
}
})
.catch(function (error) {
reject(error)
});
});
};
const url = `${Common.CONTENT_URL}/subscription/create`
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format
}
createSubscription(params) {
const _self = this;
const opts = _.cloneDeep(params) || {};
const constraints = {
shortCode: {
presence: true,
isString: true,
},
keyword: {
presence: true,
isString: true,
},
phoneNumber: {
presence: true,
isString: true,
},
};
const validationError = validate(opts, constraints);
const body = {
username: _self.options.username,
shortCode: opts.shortCode,
keyword: opts.keyword,
phoneNumber: opts.phoneNumber,
};
return new Promise(function (resolve, reject) {
if (validationError) {
return reject(validationError);
axios({
method: 'POST',
url,
headers,
data: new URLSearchParams(body)
})
.then(function (response) {
if (response.status === 201) {
// API returns CREATED on success!?
resolve(response.data)
} else {
reject(response.data)
}
})
.catch(function (error) {
reject(error)
})
})
};
const url = `${Common.CONTENT_URL}/subscription/create`;
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format
};
fetchSubscription (params) {
const _self = this
const opts = _.cloneDeep(params) || {}
axios({
method:'POST',
url,
headers,
data: new URLSearchParams(body)
})
.then(function (response) {
if (response.status === 201) {
// API returns CREATED on success!?
resolve(response.data);
} else {
reject(response.data);
}
})
.catch(function (error) {
reject(error)
});
});
};
const constraints = {
shortCode: {
presence: true,
isString: true
},
keyword: {
presence: true,
isString: true
},
lastReceivedId: {
numericality: true
}
}
const validationError = validate(opts, constraints)
opts.lastReceivedId = opts.lastReceivedId || 0
return new Promise(function (resolve, reject) {
// throw validation error inside the promise chain
if (validationError) {
return reject(validationError)
};
fetchSubscription(params) {
const _self = this;
const opts = _.cloneDeep(params) || {};
const constraints = {
shortCode: {
presence: true,
isString: true,
},
keyword: {
presence: true,
isString: true,
},
lastReceivedId: {
numericality: true,
},
};
const validationError = validate(opts, constraints);
opts.lastReceivedId = opts.lastReceivedId || 0;
return new Promise(function (resolve, reject) {
// throw validation error inside the promise chain
if (validationError) {
return reject(validationError);
};
const headers ={
apikey: _self.options.apiKey,
Accept: _self.options.format,
};
const headers = {
apikey: _self.options.apiKey,
Accept: _self.options.format
}
const query = new URLSearchParams({
username: _self.options.username,
lastReceivedId: opts.lastReceivedId,
keyword: opts.keyword,
shortCode: opts.shortCode,
}).toString();
const query = new URLSearchParams({
username: _self.options.username,
lastReceivedId: opts.lastReceivedId,
keyword: opts.keyword,
shortCode: opts.shortCode
}).toString()
const url = `${Common.CONTENT_URL}/subscription?${query}`;
const url = `${Common.CONTENT_URL}/subscription?${query}`
axios({
method:'GET',
url,
headers
})
.then(function (response) {
if (response.status === 200) {
resolve(response.data);
} else {
reject(response.data);
}
})
.catch(function (error) {
reject(error)
});
});
};
deleteSubscription(params) {
const _self = this;
const options = _.cloneDeep(params);
let validationError;
const _validateParams = function () {
const constraints = {
shortCode: {
presence: true,
isString: true,
},
keyword: {
presence: true,
isString: true,
},
phoneNumber: {
presence: true,
isString: true,
},
};
const error = validate(options, constraints);
const makeErrorMessage = function (error) {
let msg = '';
for (let k in error) {
msg += error[k] + '; ';
}
validationError = new Error(msg);
};
if (error) {
makeErrorMessage(error);
axios({
method: 'GET',
url,
headers
})
.then(function (response) {
if (response.status === 200) {
resolve(response.data)
} else {
reject(response.data)
}
};
_validateParams();
return new Promise(function (resolve, reject) {
if (validationError) {
return reject(validationError);
}
const { username, apiKey, format } = _self.options;
const { shortCode, keyword, phoneNumber } = options;
const body = {
username,
shortCode,
keyword,
phoneNumber,
};
})
.catch(function (error) {
reject(error)
})
})
};
const url = `${Common.CONTENT_URL}/subscription/delete`;
const headers = {
apiKey: apiKey,
Accept: format,
'Content-Type': 'application/x-www-form-urlencoded',
};
deleteSubscription (params) {
const _self = this
const options = _.cloneDeep(params)
let validationError
const _validateParams = function () {
const constraints = {
shortCode: {
presence: true,
isString: true
},
keyword: {
presence: true,
isString: true
},
phoneNumber: {
presence: true,
isString: true
}
}
const error = validate(options, constraints)
axios({
method:'POST',
url,
headers,
data: new URLSearchParams(body)
})
.then(function (response) {
if (response.status === 200) {
// API deleted successfully
resolve(response.data);
} else {
reject(response.data);
}
})
.catch(function (error) {
reject(error)
});
});
};
const makeErrorMessage = function (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
validationError = new Error(msg)
}
if (error) {
makeErrorMessage(error)
}
}
_validateParams()
return new Promise(function (resolve, reject) {
if (validationError) {
return reject(validationError)
}
const { username, apiKey, format } = _self.options
const { shortCode, keyword, phoneNumber } = options
const body = {
username,
shortCode,
keyword,
phoneNumber
}
const url = `${Common.CONTENT_URL}/subscription/delete`
const headers = {
apiKey,
Accept: format,
'Content-Type': 'application/x-www-form-urlencoded'
}
axios({
method: 'POST',
url,
headers,
data: new URLSearchParams(body)
})
.then(function (response) {
if (response.status === 200) {
// API deleted successfully
resolve(response.data)
} else {
reject(response.data)
}
})
.catch(function (error) {
reject(error)
})
})
};
};
module.exports = SMS;
module.exports = SMS

@@ -1,40 +0,40 @@

'use strict';
'use strict'
const axios = require('axios');
const Common = require('./common');
class Token{
constructor(options){
this.options = options;
};
const axios = require('axios')
const Common = require('./common')
class Token {
constructor (options) {
this.options = options
};
generateAuthToken() {
const _self = this;
return new Promise((resolve,reject) => {
const config = {
method: 'post',
url: Common.AUTH_TOKEN_URL,
headers: {
apiKey: _self.options.apiKey,
Accept: _self.options.format,
'Content-Type': 'application/json'
},
data : JSON.stringify({
username: _self.options.username,
})
};
axios(config)
.then(function (resp) {
const results = resp.data;
if (!results.token || results.token === 'None') {
return reject(results.description);
};
return resolve(results)
})
.catch(function (error) {
return reject(error);
});
});
}
generateAuthToken () {
const _self = this
return new Promise((resolve, reject) => {
const config = {
method: 'post',
url: Common.AUTH_TOKEN_URL,
headers: {
apiKey: _self.options.apiKey,
Accept: _self.options.format,
'Content-Type': 'application/json'
},
data: JSON.stringify({
username: _self.options.username
})
}
axios(config)
.then(function (resp) {
const results = resp.data
if (!results.token || results.token === 'None') {
return reject(results.description)
};
return resolve(results)
})
.catch(function (error) {
return reject(error)
})
})
}
};
module.exports = Token;
module.exports = Token

@@ -1,11 +0,11 @@

'use strict';
'use strict'
const bodyParser = require('body-parser');
const validate = require('validate.js');
const bodyParser = require('body-parser')
const validate = require('validate.js')
// as per specs http://docs.africastalking.com/ussd
const CONTENT_TYPE = "text/plain";
const SESSION_CONTINUE = "CON ";
const SESSION_END = "END ";
const RESPONSE_CODE = 200;
const CONTENT_TYPE = 'text/plain'
const SESSION_CONTINUE = 'CON '
const SESSION_END = 'END '
const RESPONSE_CODE = 200

@@ -18,49 +18,45 @@ /**

*/
function ExpressHandler(handler) {
function ExpressHandler (handler) {
return [ // connect/express middleware
return [ // connect/express middleware
bodyParser.urlencoded({ extended: true }),
bodyParser.json(),
bodyParser.urlencoded({extended: true}),
bodyParser.json(),
function (req, res, next) {
const params = {
sessionId: req.body.sessionId,
serviceCode: req.body.serviceCode,
phoneNumber: req.body.phoneNumber,
text: req.body.text
}
function (req, res, next) {
handler(params, (opts) => {
const badOptions = validate(opts, {
response: {
presence: true,
isString: true
},
endSession: {
presence: true,
inclusion: [true, false]
}
})
const params = {
sessionId: req.body.sessionId,
serviceCode: req.body.serviceCode,
phoneNumber: req.body.phoneNumber,
text: req.body.text
};
// express uses next(err) to pass back errors
if (badOptions) {
return next(badOptions)
}
handler(params, (opts) => {
let response = opts.response
const badOptions = validate(opts, {
response: {
presence: true,
isString: true
},
endSession: {
presence: true,
inclusion: [true, false]
}
});
// express uses next(err) to pass back errors
if (badOptions) {
return next(badOptions);
}
let response = opts.response;
if (opts.endSession) {
response = SESSION_END + response;
} else {
response = SESSION_CONTINUE + response;
}
res.contentType(CONTENT_TYPE);
res.status(RESPONSE_CODE).send(response);
});
if (opts.endSession) {
response = SESSION_END + response
} else {
response = SESSION_CONTINUE + response
}
]
res.contentType(CONTENT_TYPE)
res.status(RESPONSE_CODE).send(response)
})
}
]
}

@@ -70,2 +66,2 @@

module.exports = ExpressHandler;
module.exports = ExpressHandler

@@ -1,37 +0,34 @@

const libphonenumber = require('google-libphonenumber');
const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance();
const libphonenumber = require('google-libphonenumber')
const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance()
function phoneValidator(suspiciousPhoneNumber){
try{
// Parse the phone number
const parsedNumber = phoneUtil.parseAndKeepRawInput(suspiciousPhoneNumber, null);
function phoneValidator (suspiciousPhoneNumber) {
try {
// Parse the phone number
const parsedNumber = phoneUtil.parseAndKeepRawInput(suspiciousPhoneNumber, null)
// Check if the phone number is valid
if (phoneUtil.isValidNumber(parsedNumber)) {
// Check if the phone number is valid
if (phoneUtil.isValidNumber(parsedNumber)) {
// Get the country code
const countryCode = phoneUtil.getRegionCodeForNumber(parsedNumber)
const phoneNumber = phoneUtil.format(parsedNumber, libphonenumber.PhoneNumberFormat.E164)
// Get the country code
const countryCode = phoneUtil.getRegionCodeForNumber(parsedNumber);
const phoneNumber = phoneUtil.format(parsedNumber, libphonenumber.PhoneNumberFormat.E164);
return {
isValid: true,
countryCode,
phoneNumber,
};
} else {
return {
isValid: false,
errorMessage: 'Invalid phone number',
};
}
}catch(error){
return {
isValid: false,
errorMessage: 'Error parsing phone number',
};
return {
isValid: true,
countryCode,
phoneNumber
}
} else {
return {
isValid: false,
errorMessage: 'Invalid phone number'
}
}
} catch (error) {
return {
isValid: false,
errorMessage: 'Error parsing phone number'
}
}
}
module.exports = { phoneValidator };
module.exports = { phoneValidator }

@@ -1,260 +0,257 @@

'use strict';
const validate = require('validate.js');
const _ = require('lodash');
const axios = require('axios');
const { phoneValidator } = require('./utils');
'use strict'
const validate = require('validate.js')
const _ = require('lodash')
const axios = require('axios')
const { phoneValidator } = require('./utils')
const Common = require('./common');
const Common = require('./common')
const Builder = require('./actionbuilder')
class Voice {
constructor(options) {
this.options = options;
this.ActionBuilder = Builder;
}
call({callFrom, callTo, clientRequestId}){
return new Promise((resolve,reject)=>{
if(!callTo || !callFrom){
reject(`Both "callTo" and "callFrom" are required`)
};
constructor (options) {
this.options = options
this.ActionBuilder = Builder
}
if(typeof callTo === 'string'){
callTo = callTo.split(',');
};
call ({ callFrom, callTo, clientRequestId }) {
return new Promise((resolve, reject) => {
if (!callTo || !callFrom) {
reject(new Error('Both "callTo" and "callFrom" are required'))
};
if(!Array.isArray(callTo)){
reject('"callTo" can only be an array of phoneNumbers, or a string of comma-separated phoneNumbers');
};
if (typeof callTo === 'string') {
callTo = callTo.split(',')
};
const constraints = {
clientRequestId: {
isString: true,
},
callFrom:function (value) {
const suspect = phoneValidator(value);
if(suspect.isValid){
callFrom = suspect.phoneNumber;
return null;
}else{
return { format: "callFrom must be a valid phonenumber" }
};
},
callTo: function(value){
const invalidPhoneNumbers = [];
const validPhoneNumbers = [];
if (!Array.isArray(callTo)) {
reject(new Error('"callTo" can only be an array of phoneNumbers, or a string of comma-separated phoneNumbers'))
};
value.forEach(function (phoneNumber) {
const suspect = phoneValidator(phoneNumber);
if(suspect.isValid){
validPhoneNumbers.push(suspect.phoneNumber)
}else{
invalidPhoneNumbers.push(phoneNumber);
};
});
const constraints = {
clientRequestId: {
isString: true
},
callFrom: function (value) {
const suspect = phoneValidator(value)
if (suspect.isValid) {
callFrom = suspect.phoneNumber
return null
} else {
return { format: 'callFrom must be a valid phonenumber' }
};
},
callTo: function (value) {
const invalidPhoneNumbers = []
const validPhoneNumbers = []
if(invalidPhoneNumbers.length){
return {
format: `callTo contains the following invalid phoneNumber(s): ${invalidPhoneNumbers}`
}
}else{
callTo = validPhoneNumbers;
return null;
}
}
value.forEach(function (phoneNumber) {
const suspect = phoneValidator(phoneNumber)
if (suspect.isValid) {
validPhoneNumbers.push(suspect.phoneNumber)
} else {
invalidPhoneNumbers.push(phoneNumber)
};
})
const error = validate({callFrom, callTo, clientRequestId}, constraints);
if (error) {
let msg = "";
for (var k in error) {
msg += error[k] + "; ";
};
reject(msg);
}else{
if (invalidPhoneNumbers.length) {
return {
format: `callTo contains the following invalid phoneNumber(s): ${invalidPhoneNumbers}`
}
} else {
callTo = validPhoneNumbers
return null
}
}
}
const config = {
method: 'post',
url: `${Common.VOICE_URL}/call`,
headers: {
apikey: this.options.apiKey,
Accept: this.options.format,
"Content-Type":"application/x-www-form-urlencoded"
},
data : new URLSearchParams({
username: this.options.username,
to: callTo.join(','),
from: callFrom,
clientRequestId: clientRequestId
})
};
axios(config)
.then(function (resp) {
const httpStatus = resp.status;
const error = validate({ callFrom, callTo, clientRequestId }, constraints)
if (httpStatus === 200 || httpStatus === 201) {
// API returns CREATED on success
resolve(resp.data);
} else {
reject(resp.data);
};
})
.catch(function (error) {
return reject(error);
});
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
};
reject(msg)
} else {
const config = {
method: 'post',
url: `${Common.VOICE_URL}/call`,
headers: {
apikey: this.options.apiKey,
Accept: this.options.format,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: new URLSearchParams({
username: this.options.username,
to: callTo.join(','),
from: callFrom,
clientRequestId
})
}
axios(config)
.then(function (resp) {
const httpStatus = resp.status
if (httpStatus === 200 || httpStatus === 201) {
// API returns CREATED on success
resolve(resp.data)
} else {
reject(resp.data)
};
})
}
getNumQueuedCalls(params) {
})
.catch(function (error) {
return reject(error)
})
};
})
}
let options = _.cloneDeep(params);
let _self = this;
getNumQueuedCalls (params) {
const options = _.cloneDeep(params)
const _self = this
// Validate params
let _validateParams = function () {
// Validate params
const _validateParams = function () {
const constraints = {
phoneNumbers: function (value) {
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
}
}
var constraints = {
phoneNumbers: function (value) {
return null
}
}
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
};
}
const error = validate(options, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
throw new Error(msg)
}
}
return null;
}
};
_validateParams()
let error = validate(options, constraints);
if (error) {
var msg = "";
for (var k in error) {
msg += error[k] + "; ";
}
throw new Error(msg);
}
};
return new Promise(function (resolve, reject) {
const config = {
method: 'post',
url: `${Common.VOICE_URL}/queueStatus`,
headers: {
apikey: _self.options.apiKey,
Accept: _self.options.format
},
data: JSON.stringify({
username: _self.options.username,
phoneNumbers: options.phoneNumbers
_validateParams();
})
}
axios(config)
.then(function (resp) {
const httpStatus = resp.status
return new Promise(function (resolve, reject) {
const config = {
method: 'post',
url: `${Common.VOICE_URL}/queueStatus`,
headers: {
apikey: _self.options.apiKey,
Accept: _self.options.format
},
data : JSON.stringify({
username: _self.options.username,
phoneNumbers: options.phoneNumbers
})
};
axios(config)
.then(function (resp) {
const httpStatus = resp.status;
if (httpStatus === 200 || httpStatus === 201) {
// API returns CREATED on success
resolve(resp.data)
} else {
reject(resp.data)
};
})
.catch(function (error) {
return reject(error)
})
})
}
if (httpStatus === 200 || httpStatus === 201) {
// API returns CREATED on success
resolve(resp.data);
} else {
reject(resp.data);
};
})
.catch(function (error) {
return reject(error);
});
});
}
uploadMediaFile(params) {
let options = _.cloneDeep(params);
let _self = this;
uploadMediaFile (params) {
const options = _.cloneDeep(params)
const _self = this
// Validate params
let _validateParams = function () {
// Validate params
const _validateParams = function () {
const constraints = {
url: function (value) {
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
}
}
var constraints = {
url: function (value) {
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
};
}
if (!(/^https?\/\//).test(value)) {
return {
format: 'must contain a VALID URL (http(s)://...)'
}
}
if (!(/^https?\:\/\//).test(value)) {
return {
format: 'must contain a VALID URL (http(s)://...)'
};
}
return null
},
phoneNumber: function (value) {
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
}
}
return null;
},
phoneNumber: function (value) {
return null
}
}
if (validate.isEmpty(value)) {
return {
presence: {
message: 'is required'
}
};
}
const error = validate(options, constraints)
if (error) {
let msg = ''
for (const k in error) {
msg += error[k] + '; '
}
throw new Error(msg)
}
}
return null;
}
};
_validateParams()
let error = validate(options, constraints);
if (error) {
var msg = "";
for (var k in error) {
msg += error[k] + "; ";
}
throw new Error(msg);
}
};
return new Promise(function (resolve, reject) {
const config = {
method: 'post',
url: `${Common.VOICE_URL}/mediaUpload`,
headers: {
_validateParams();
apikey: _self.options.apiKey,
Accept: _self.options.format
},
data: JSON.stringify({
username: _self.options.username,
url: options.url,
phoneNumber: options.phoneNumber
return new Promise(function (resolve, reject) {
const config = {
method: 'post',
url: `${Common.VOICE_URL}/mediaUpload`,
headers: {
apikey: _self.options.apiKey,
Accept: _self.options.format
},
data : JSON.stringify({
username: _self.options.username,
url: options.url,
phoneNumber: options.phoneNumber
})
};
})
}
axios(config)
.then(function (resp) {
const httpStatus = resp.status;
axios(config)
.then(function (resp) {
const httpStatus = resp.status
if (httpStatus === 200 || httpStatus === 201) {
// API returns CREATED on success
resolve(resp.data);
} else {
reject(resp.data);
};
})
.catch(function (error) {
return reject(error);
});
});
}
if (httpStatus === 200 || httpStatus === 201) {
// API returns CREATED on success
resolve(resp.data)
} else {
reject(resp.data)
};
})
.catch(function (error) {
return reject(error)
})
})
}
}
module.exports = Voice;
module.exports = Voice
{
"name": "africastalking",
"version": "0.7.0-beta.1",
"version": "0.7.0",
"description": "Official AfricasTalking node.js API wrapper",

@@ -8,4 +8,9 @@ "main": "index.js",

"test": "nyc mocha ./test --exit --recursive",
"test-windows": "nyc node_modules/mocha/bin/_mocha ./test"
"test-windows": "nyc node_modules/mocha/bin/_mocha ./test",
"lint": "standard --fix",
"pretest": "standard"
},
"pre-commit": [
"test"
],
"repository": {

@@ -22,3 +27,4 @@ "type": "git",

"payments",
"airtime"
"airtime",
"mobile_data"
],

@@ -32,10 +38,22 @@ "author": "Africa's Talking",

"engines": {
"node": ">=6"
"node": ">=18"
},
"standard": {
"ignore": [
"/example"
],
"globals": [
"it",
"describe",
"before",
"after",
"context"
]
},
"dependencies": {
"@hapi/joi": "^16.1.7",
"axios": "^1.6.8",
"axios": "^1.7.2",
"body-parser": "^1.20.2",
"dotenv": "^16.4.5",
"google-libphonenumber": "^3.2.34",
"google-libphonenumber": "^3.2.35",
"lodash": "^4.17.21",

@@ -48,6 +66,8 @@ "querystring": "^0.2.0",

"mocha": "^10.4.0",
"nyc": "^15.1.0",
"nyc": "^17.0.0",
"pre-commit": "^1.2.2",
"should": "^13.2.3",
"supertest": "^6.3.4"
"standard": "^17.1.0",
"supertest": "^7.0.0"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc