Skip to content

Commit

Permalink
feat: initial draft of nodetifications
Browse files Browse the repository at this point in the history
need to add more providers and modes of notifications
  • Loading branch information
ParadoxInfinite committed Nov 17, 2021
1 parent b020e75 commit adf4e72
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 0 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
# nodetifications

Notifications made easy in NodeJS!

## Features
- Very (c)lean package! < 4kB zipped and < 8kB unzipped, and documented with JSDoc annotations.
- Multiple providers and notification modes in 1 package.
- Very lean dependency, which means less points of failure.
- Highly flexible to your needs, change provider/creds anywhere in between.
- Standardized response formats. All successful requests will return JSON responses.
- Promise based for asynchronous support and no [callback hell](http://callbackhell.com/).

## Installation

```zsh
npm install nodetifications
```

## Usage

Complete usage docs with sample code - COMING SOON.

## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

## License
[MIT](https://choosealicense.com/licenses/mit/)
77 changes: 77 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import fetch, { Headers } from 'node-fetch';

import { GenericError } from './utils/errors.js';

const errorCheck = (from, to, userId, password) => {
if (!userId || !password) {
throw new GenericError({
errType: 'INVALID_CREDENTIALS',
errMsg: 'Invalid userId and password provided, check the credentials again'
})
}
if (!from) throw new GenericError({
errType: 'INVALID_FROM',
errMsg: 'Invalid From Number'
})
if (!to) throw new GenericError({
errType: 'INVALID_TO',
errMsg: 'Invalid To Number'
})
}

const gupShupSMSBaseURL = 'https://enterprise.smsgupshup.com/GatewayAPI/rest';
const twilioSMSBaseURL = (userId) => `https://api.twilio.com/2010-04-01/Accounts/${userId}/Messages.json`;

export class SMS {
constructor({ userId, password, provider, from } = {}) {
this.userId = userId;
this.password = password;
this.provider = provider;
this.from = from;
}

sendSMS({
provider = this.provider,
from = this.from,
to,
message = [],
userId = this.userId,
password = this.password
} = {}) {
if (provider !== this.provider && (userId === this.userId || password === this.password)) {
throw new GenericError({
errType: 'INVALID_CREDENTIALS',
errMsg: 'Provider has changed but the credentials have not, if you\'re using the same creds in multiple places, please stop. Get some help.'
})
}
let encodedMessage;
errorCheck(from, to, userId, password);
if (typeof message !== 'string') {
if (message[0]) {
encodedMessage = message.reduce(
(interimMessage, line, index) => `${interimMessage}${encodeURIComponent(line)}${index === message.length - 1 ? '' : '%0A'}`
, '');
} else {
encodedMessage = ''
}
} else {
message = encodeURIComponent(message);
}
switch (provider) {
case 'gupshup': return fetch(`${gupShupSMSBaseURL}?method=SendMessage&send_to=${to}&msg=${encodedMessage}&msg_type=TEXT&userid=${userId}&auth_scheme=plain&password=${password}&v=1.1&format=json`).then(result => result.json());
case 'twilio': const headers = new Headers();
const auth = Buffer.from(`${userId}:${password}`);
headers.append('Authorization', 'Basic ' + auth.toString('base64'));
return fetch(twilioSMSBaseURL(userId), { method: 'POST', headers, body: { Body: encodedMessage, From: encodeURIComponent(from), To: encodeURIComponent(to) } }).then(response => response.json());
default: throw new GenericError({
errType: 'PROVIDER_ERR',
errMsg: 'Invalid provider',
additionalDetails: 'If you would like to see this provider integrated, please raise an issue on https://github.com/ParadoxInfinite/nodetifications/issues/.'
});
}
}
}

export default {
SMS
};
98 changes: 98 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "nodetifications",
"version": "0.0.1",
"description": "Notifications made easy in NodeJS",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/ParadoxInfinite/nodetifications.git"
},
"keywords": [
"notifications",
"node",
"gupshup"
],
"types": "./types/index.d.ts",
"files": [
"index.js",
"types/index.d.ts",
"utils/errors.js"
],
"author": "Samarth Kulkarni",
"license": "MIT",
"bugs": {
"url": "https://github.com/ParadoxInfinite/nodetifications/issues"
},
"homepage": "https://github.com/ParadoxInfinite/nodetifications#readme",
"dependencies": {
"node-fetch": "^3.0.0"
}
}
32 changes: 32 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export class SMS {
/**
* @param {string} userId - User ID to authenticate with the provider
* @param {string} password - Password to authenticate with the provider
* @param {string} provider - Provider to send messages from. Currently valid providers: `twilio` and `gupshup`
* @param {string} from - Number from which to send the SMS, can be changed per request too, @see `SMS.sendSMS`
*/
constructor({ userId, password, provider, from }: {
userId: string,
password: string,
provider: string,
from: string
})

/**
* @param {string} provider - Defaults to constructor value, but can give a different `provider` than before, however this makes `userId` and `password` mandatory.
* @param {string} from - Defaults to constructor value, but can be different than the `from` before.
* @param {string} to - Mandatory value, recipients's phone number.
* @param {Array} message - Array of strings, each element is a new line. Blank string(without whitespace) for a blank new line.
* @param {string} userId - Defaults to constructor value, but mandatory when `provider` is passed and different from constructor.
* @param {string} password - Defaults to constructor value, but mandatory when `provider` is passed and different from constructor.
* @returns {Promise} A response promise from the provider, if successful, the response will be in JSON format. Errors need to be handled separately.
*/
sendSMS({ provider, from, to, message, userId, password }: {
provider: string,
from: string,
to: string,
message: Array<string>,
userId: string,
password: string
}): Promise<any>
}
22 changes: 22 additions & 0 deletions utils/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const Reset = "\x1b[0m"
const Bright = "\x1b[1m"

const FgWhite = "\x1b[37m"

const BgRed = "\x1b[41m"

export class GenericError extends Error {
constructor({
errType = 'UNKNOWN_ERROR',
errMsg = 'Unknown error occured',
additionalDetails = "If you would like to report this to the author, please raise an issue here: https://github.com/ParadoxInfinite/nodetifications/issues/"
}) {
super();
this.name = `${Bright}${BgRed}${FgWhite}${errType}`,
this.message = `${errMsg}.${Reset}\n${additionalDetails}`
}
}

export default {
GenericError
}

0 comments on commit adf4e72

Please sign in to comment.