africastalking
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -5,2 +5,3 @@ 'use strict'; | ||
var validate = require('validate.js'); | ||
var _ = require('lodash'); | ||
@@ -10,20 +11,51 @@ var Common = require('./common'); | ||
var SMS = require('./sms'); | ||
var USSD = require('./ussd'); | ||
var Airtime = require('./airtime'); | ||
validate.validators.isString = function(value, options, key, attributes) { | ||
if (validate.isString(value)) { | ||
return null; | ||
} else { | ||
return "must be a string"; | ||
function AfricasTalking(options) { | ||
this.options = _.cloneDeep(options); | ||
validate.validators.isString = function(value, options, key, attributes) { | ||
if (validate.isEmpty(value) || validate.isString(value)) { // String or null & undefined | ||
return null; | ||
} else { | ||
return "must be a string"; | ||
} | ||
}; | ||
var constraints = { | ||
format: { | ||
inclusion: ['json', 'xml'] | ||
}, | ||
username: { | ||
presence: true, | ||
isString: true | ||
}, | ||
apiKey: { | ||
presence: true, | ||
isString: true | ||
} | ||
}; | ||
var 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"; | ||
} | ||
function AfricasTalking(options) { | ||
this.options = options; | ||
this.SMS = new SMS(); | ||
this.SMS = new SMS(this.options); | ||
this.VOICE = null; | ||
this.USSD = null; | ||
this.AIRTIME = null; | ||
this.USSD = USSD; | ||
this.AIRTIME = new Airtime(this.options); | ||
} | ||
@@ -57,33 +89,3 @@ | ||
module.exports = function (options) { | ||
var constraints = { | ||
format: { | ||
inclusion: ['json', 'xml'] | ||
}, | ||
username: { | ||
presence: true, | ||
isString: true | ||
}, | ||
apiKey: { | ||
presence: true, | ||
isString: true | ||
} | ||
}; | ||
var invalid = validate(options, constraints); | ||
if (invalid) { | ||
console.error(invalid); | ||
return null; | ||
} | ||
switch (options.format) { | ||
case "xml": | ||
options.format = "application/xml"; | ||
break; | ||
case "json": // Get json by default | ||
default: | ||
options.format = "application/json"; | ||
} | ||
return new AfricasTalking(options); | ||
}; |
239
lib/sms.js
'use strict'; | ||
function SMS() { | ||
var Promise = require('bluebird'); | ||
var unirest = require('unirest'); | ||
var validate = require('validate.js'); | ||
var _ = require('lodash'); | ||
var Common = require('./common'); | ||
function SMS(options) { | ||
var _self = this; | ||
this.options = options; | ||
this._send = function (params, isBulk, isPremium) { | ||
// Validate params | ||
var _validateParams = function () { | ||
var 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)) { | ||
// TODO: Validate number format | ||
let isInvalid = false; | ||
if (isInvalid) { | ||
return { | ||
format: "must be a valid phone number" | ||
}; | ||
} | ||
} | ||
if (validate.isArray(value)) { | ||
let foundInvalid = false; | ||
value.forEach(function (phone) { | ||
// TODO: Validate number format | ||
}); | ||
if (foundInvalid) { | ||
return { | ||
format: "must NOT contain invalid phone number" | ||
} | ||
} | ||
} | ||
return null; | ||
}, | ||
from: { | ||
isString: true | ||
}, | ||
message: { | ||
presence: true | ||
} | ||
}; | ||
if (isBulk) { | ||
constraints.enqueue = { | ||
inclusion: [true] | ||
}; | ||
} | ||
if (isPremium) { | ||
constraints.keyword = { | ||
presence: true, | ||
isString: true | ||
}; | ||
constraints.linkId = { | ||
presence: true, | ||
isString: true | ||
}; | ||
constraints.retryDurationInHours = { | ||
numericality: true | ||
} | ||
} | ||
var error = validate(params, constraints); | ||
if (error) { | ||
throw error; | ||
} | ||
}; | ||
_validateParams(); | ||
// Multiple recipients? | ||
if (validate.isArray(params.to)) { | ||
if (params.to.length === 1) { | ||
params.to = params.to[0]; | ||
} else { | ||
params.to = params.to.join(); | ||
} | ||
} | ||
return new Promise(function (resolve, reject) { | ||
var 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 (isPremium) { | ||
body.keyword = params.keyword; | ||
body.linkId = params.linkId; | ||
if (params.retryDurationInHours) { | ||
body.retryDurationInHours = params.retryDurationInHours; | ||
} | ||
} | ||
var rq = unirest.post(Common.SMS_URL); | ||
rq.headers({ | ||
apiKey: _self.options.apiKey, | ||
Accept: _self.options.format | ||
}); | ||
rq.send(body); | ||
rq.end(function (resp) { | ||
if (resp.status === 201) { // API returns CREATED on success!? | ||
resolve(resp.body); | ||
} else { | ||
reject(resp.error || resp.body); | ||
} | ||
}); | ||
}); | ||
}; | ||
} | ||
SMS.prototype.send = function (options) { | ||
SMS.prototype.send = function (params) { | ||
var opts = _.cloneDeep(params); | ||
return this._send(opts, false, false); | ||
}; | ||
SMS.prototype.sendBulk = function (params) { | ||
var opts = _.cloneDeep(params); | ||
return this._send(opts, true, false); | ||
}; | ||
SMS.prototype.sendBulk = function (options) { | ||
SMS.prototype.sendPremium = function (params) { | ||
var opts = _.cloneDeep(params); | ||
return this._send(opts, false, true); | ||
}; | ||
SMS.prototype.fetchMessages = function (params) { | ||
SMS.prototype.sendPremium = function (options) { | ||
var _self = this; | ||
var opts = _.cloneDeep(params) || {}; | ||
opts.lastReceivedId = opts.lastReceivedId || 0; | ||
return new Promise(function (resolve, reject) { | ||
var rq = unirest.get(Common.SMS_URL); | ||
rq.headers({ | ||
apiKey: _self.options.apiKey, | ||
Accept: _self.options.format | ||
}); | ||
rq.query({ | ||
'username': _self.options.username, | ||
'lastReceivedId': opts.lastReceivedId | ||
}); | ||
rq.end(function (resp) { | ||
if (resp.status === 200) { | ||
resolve(resp.body); | ||
} else { | ||
reject(resp.error); | ||
} | ||
}); | ||
}); | ||
}; | ||
SMS.prototype.createSubscription = function (params) { | ||
var opts = _.cloneDeep(params); | ||
var constraints = { | ||
shortCode: { | ||
presence: true, | ||
isString: true | ||
}, | ||
keyword: { | ||
presence: true, | ||
isString: true | ||
}, | ||
phoneNumber: { | ||
presence: true, | ||
isString: true | ||
} | ||
}; | ||
var error = validate(opts, constraints); | ||
if (error) { | ||
throw error; | ||
} | ||
return new Promise(function (resolve, reject){ | ||
throw new Error("Not Implemented"); | ||
}); | ||
}; | ||
SMS.prototype.fetchSubscription = function (params) { | ||
var opts = _.cloneDeep(params); | ||
var constraints = { | ||
shortCode: { | ||
presence: true, | ||
isString: true | ||
}, | ||
keyword: { | ||
presence: true, | ||
isString: true | ||
}, | ||
lastReceivedId: { | ||
numericality: true | ||
} | ||
}; | ||
var error = validate(opts, constraints); | ||
if (error) { | ||
throw error; | ||
} | ||
opts.lastReceivedId = opts.lastReceivedId || 0; | ||
return new Promise(function (resolve, reject){ | ||
throw new Error("Not Implemented"); | ||
}); | ||
}; | ||
module.exports = SMS; | ||
{ | ||
"name": "africastalking", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Official AfricasTalking node.js API wrapper", | ||
@@ -27,2 +27,4 @@ "main": "lib/index.js", | ||
"bluebird": "^3.1.1", | ||
"body-parser": "^1.15.0", | ||
"lodash": "^4.0.0", | ||
"unirest": "^0.4.2", | ||
@@ -32,6 +34,8 @@ "validate.js": "^0.9.0" | ||
"devDependencies": { | ||
"express": "^4.13.4", | ||
"istanbul": "^0.4.2", | ||
"mocha": "^2.3.4", | ||
"should": "^8.1.1" | ||
"should": "^8.1.1", | ||
"supertest": "^1.2.0" | ||
} | ||
} |
106
README.md
# africastalking-node.js | ||
Official AfricasTalking node.js API wrapper | ||
**Goal**: Simple, easy to use (and to upgrade) API that covers all the features provided by [Africa's Talking](http://docs.africastalking.com/) | ||
### Install | ||
```shell | ||
$ npm install --save africastalking | ||
``` | ||
### Initialization | ||
@@ -10,4 +14,4 @@ | ||
var options = { | ||
apiKey: 'fb752d3417021812f0961y6c9464832dd1adb1e555c73f1e7c32bcc006488674', | ||
username: 'salama' | ||
apiKey: 'YOUR_API_KEY', | ||
username: 'YOUR_USERNAME', | ||
format: 'json' // or xml | ||
@@ -20,2 +24,3 @@ }; | ||
**`Important`: If you register a callback URL with the API, always remember to acknowledge the receipt of any data it sends by responding with an HTTP `200`; e.g. `res.status(200);` for express**. | ||
@@ -36,3 +41,3 @@ ### SMS | ||
- `to`: An array of recipients. `REQUIRED` | ||
- `to`: A single recipient or an array of recipients. `REQUIRED` | ||
- `from`: Shortcode or alphanumeric ID that is registered with Africa's Talking account. | ||
@@ -53,6 +58,6 @@ - `message`: SMS content. `REQUIRED` | ||
> You can register a callback URL with us and we will forward any messages that are sent to your account the moment they arrive. | ||
> You can register a callback URL with us and we will forward any messages that are sent to your account the moment they arrive. | ||
> [Read more](http://docs.africastalking.com/sms/callback) | ||
- `fetchMessages(options)`: | ||
- `fetchMessages(options)`: Manually retrieve your messages. | ||
@@ -80,5 +85,62 @@ - `lastReceivedId`: "This is the id of the message that you last processed". Defaults to `0`. `REQUIRED` | ||
### Voice | ||
### [USSD](http://docs.africastalking.com/ussd) | ||
> Processing USSD requests using our API is very easy once your account is set up. In particular, you will need to: | ||
> - Register a service code with us. | ||
> - Register a URL that we can call whenever we get a request from a client coming into our system. | ||
> | ||
> Once you register your callback URL, any requests that we receive belonging to you will trigger a callback that sends the request data to that page using HTTP POST. | ||
> [Read more.](http://docs.africastalking.com/ussd) | ||
If you are using connect-like frameworks (*express*), you could use the middleware `AfricasTalking.USSD(handler)`: | ||
`handler(params, next)`: Process USSD request and call `next()` when done. | ||
- `params`: contains the following user data sent by Africa's Talking servers: `sessionId`, `serviceCode`, `phoneNumber` and `text`. | ||
- `next(args)`: `args` must contain the following: | ||
- `response`: Text to display on user's device. `REQUIRED` | ||
- `endSession`: Boolean to decide whether to **END** session or to **CON**tinue it. `REQUIRED` | ||
```javascript | ||
// example (express) | ||
app.post('/natoil-ussd', new AfricasTalking.USSD((params, next) => { | ||
var endSession = false; | ||
var message = ''; | ||
var session = sessions.get(params.sessionId); | ||
var user = db.getUserByPhone(params.phoneNumber); | ||
if (params.text === '') { | ||
message = "Welcome to Nat Oil \n"; | ||
message += "1: For account info \n"; | ||
message += "2: For lost gas cylinder"; | ||
} else if (params.text === '1') { | ||
message = user.getInfo(); | ||
endSession = true; | ||
} else if (params.text === '2') { | ||
message = "Enter 1 for recovery \n"; | ||
message += "Enter 2 for lost and found"; | ||
endSession = true; | ||
} else { | ||
message = "Invalid option"; | ||
endSession = true; | ||
} | ||
next({ | ||
response: message, | ||
endSession: endSession | ||
}); | ||
})); | ||
``` | ||
## Voice **TODO** | ||
```javascript | ||
var voice = AfricasTalking.VOICE; | ||
@@ -91,11 +153,22 @@ ``` | ||
- Media upload | ||
- Remember to send back an HTTP 200. | ||
### USSD | ||
### Airtime | ||
```javascript | ||
var ussd = AfricasTalking.USSD; | ||
var airtime = AfricasTalking.AIRTIME; | ||
``` | ||
- Make helpers that will construct proper `text/plain` data to send back to Africa's Taking API when it comes calling. [Read more](http://docs.africastalking.com/ussd) | ||
- `airtime.send(options)`: Send airtime | ||
- `recipients`: An array of the following | ||
- `phoneNumber`: Receipient of airtime | ||
- `amount`: Amount sent. `>= 10 && <= 10K` | ||
```javascript | ||
airtime.send(options) | ||
.then(success) | ||
.catch(error); | ||
``` | ||
### Account | ||
@@ -109,13 +182,2 @@ ```javascript | ||
### Airtime | ||
```javascript | ||
var airtime = AfricasTalking.AIRTIME; | ||
``` | ||
- `send(options)`: Send airtime | ||
- `recipients`: An array of the following | ||
- `phoneNumber`: Receipient of airtime | ||
- `amount`: Amount sent. `>= 10 && <= 10K` | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
56328
9
427
177
5
5
1
+ Addedbody-parser@^1.15.0
+ Addedlodash@^4.0.0
+ Addedbody-parser@1.20.3(transitive)
+ Addedbytes@3.1.2(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedcontent-type@1.0.5(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddepd@2.0.0(transitive)
+ Addeddestroy@1.2.0(transitive)
+ Addedee-first@1.1.1(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhttp-errors@2.0.0(transitive)
+ Addediconv-lite@0.4.24(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedmedia-typer@0.3.0(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedms@2.0.0(transitive)
+ Addedobject-inspect@1.13.3(transitive)
+ Addedon-finished@2.4.1(transitive)
+ Addedqs@6.13.0(transitive)
+ Addedraw-body@2.5.2(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsetprototypeof@1.2.0(transitive)
+ Addedside-channel@1.0.6(transitive)
+ Addedstatuses@2.0.1(transitive)
+ Addedtoidentifier@1.0.1(transitive)
+ Addedtype-is@1.6.18(transitive)
+ Addedunpipe@1.0.0(transitive)