Skip to content
This repository has been archived by the owner on Feb 8, 2020. It is now read-only.

Commit

Permalink
Welcome Nexmo to Broid family. (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
killix authored Feb 7, 2017
1 parent 2b77c34 commit 350fb65
Show file tree
Hide file tree
Showing 23 changed files with 5,201 additions and 0 deletions.
661 changes: 661 additions & 0 deletions integrations/broid-nexmo/LICENCE.md

Large diffs are not rendered by default.

177 changes: 177 additions & 0 deletions integrations/broid-nexmo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![deps][deps]][deps-url]
[![tests][tests]][tests-url]
[![bithound][bithound]][bithound-url]
[![bithoundscore][bithoundscore]][bithoundscore-url]
[![nsp-checked][nsp-checked]][nsp-checked-url]

# Broid Nexmo Integration

Broid Integrations is an open source project providing a suite of Activity Streams 2 libraries for unified communications among a vast number of communication platforms.

> Connect your App to Multiple Messaging Channels with One OpenSource Language.
[![gitter](https://badges.gitter.im/broidHQ/broid.svg)](https://t.broid.ai/c/Blwjlw?utm_source=github&utm_medium=readme&utm_campaign=top&link=gitter)

## Getting started

### Install

```bash
npm install --save broid-nexmo
```

### Connect to Nexmo

```javascript
import broidNexmo from 'broid-nexmo'

const nexmo = new broidNexmo({
username: '<your_sender_number>',
token: '<your_nexmo_token>',
tokenSecret: '<your_nexmo_token_secret>',
http: {
port: 8080,
host: "0.0.0.0"
}
})

nexmo.connect()
.subscribe({
next: data => console.log(data),
error: err => console.error(`Something went wrong: ${err.message}`),
complete: () => console.log('complete'),
})
```

**Options availables**

| name | Type | default | Description |
| --------------- |:--------:| :--------: | --------------------------|
| serviceID | string | random | Arbitrary identifier of the running instance |
| logLevel | string | `info` | Can be : `fatal`, `error`, `warn`, `info`, `debug`, `trace` |
| username | string | | Your sender phone number |
| token | string | | Your nexmo token |
| tokenSecret | string | | Your nexmo token secret |
| http | object | `{ "port": 8080, "http": "0.0.0.0" }` | WebServer options (`host`, `port`) |

### Receive a message

```javascript
nexmo.listen()
.subscribe({
next: data => console.log(`Received message: ${data}`),
error: err => console.error(`Something went wrong: ${err.message}`),
complete: () => console.log('complete'),
})
```

### Post a message

To send a message, the format should use the [broid-schemas](https://github.com/broidhq/broid-schemas).

```javascript
const message_formated = '...'

nexmo.send(message_formated)
.then(() => console.log("ok"))
.catch(err => console.error(err))
```

## Examples of messages

### Message received

- A message received from Sally

```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"published": 1483677146,
"type": "Create",
"generator": {
"id": "67c9cb10-8a74-42c8-ba55-294d0447cdf9",
"type": "Service",
"name": "nexmo"
},
"actor": {
"id": "15146345659",
"name": "15146345659",
"type": "Person"
},
"target": {
"id": "12262101490",
"name": "12262101490",
"type": "Person"
},
"object": {
"type": "Note",
"id": "0B0000003186F6EB",
"content": "Hello world"
}
}
```

### Send a message

- Send a simple message

```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"generator": {
"id": "f6e92eb6-f69e-4eae-8158-06613461cf3a",
"type": "Service",
"name": "nexmo"
},
"object": {
"type": "Note",
"content": "hello world"
},
"to": {
"id": "15146345659",
"type": "Person"
}
}
```

# Contributing to Broid

Broid is an open source project. Broid wouldn't be where it is now without contributions by the community. Please consider forking Broid to improve, enhance or fix issues. If you feel like the community will benefit from your fork, please open a pull request.

And because we want to do the better for you. Help us improving Broid by
sharing your feedback on our [Integrations GitHub Repo](https://github.com/broidhq/integrations) and let's build Broid together!

## Code of Conduct

Make sure that you're read and understand the [Code of Conduct](http://contributor-covenant.org/version/1/2/0/).

## Copyright & License

Copyright (c) 2016-2017 Broid.ai

This project is licensed under the AGPL 3, which can be
[found here](https://www.gnu.org/licenses/agpl-3.0.en.html).

[npm]: https://img.shields.io/badge/npm-broid-green.svg?style=flat
[npm-url]: https://www.npmjs.com/~broid

[node]: https://img.shields.io/node/v/broid-slack.svg
[node-url]: https://nodejs.org

[deps]: https://img.shields.io/badge/dependencies-checked-green.svg?style=flat
[deps-url]: #integrations

[tests]: https://img.shields.io/travis/broidHQ/integrations/master.svg
[tests-url]: https://travis-ci.org/broidHQ/integrations

[bithound]: https://img.shields.io/bithound/code/github/broidHQ/integrations.svg
[bithound-url]: https://www.bithound.io/github/broidHQ/integrations

[bithoundscore]: https://www.bithound.io/github/broidHQ/integrations/badges/score.svg
[bithoundscore-url]: https://www.bithound.io/github/broidHQ/integrations

[nsp-checked]: https://img.shields.io/badge/nsp-checked-green.svg?style=flat
[nsp-checked-url]: https://nodesecurity.io
94 changes: 94 additions & 0 deletions integrations/broid-nexmo/lib/core/adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use strict";
const Promise = require("bluebird");
const broid_schemas_1 = require("broid-schemas");
const broid_utils_1 = require("broid-utils");
const Nexmo = require("nexmo");
const uuid = require("node-uuid");
const R = require("ramda");
const Rx_1 = require("rxjs/Rx");
const parser_1 = require("./parser");
const webHookServer_1 = require("./webHookServer");
class Adapter {
constructor(obj) {
this.serviceID = obj && obj.serviceID || uuid.v4();
this.logLevel = obj && obj.logLevel || "info";
this.token = obj && obj.token || null;
this.tokenSecret = obj && obj.tokenSecret || null;
this.username = obj && obj.username || null;
const HTTPOptions = {
host: "127.0.0.1",
port: 8080,
};
this.HTTPOptions = obj && obj.http || HTTPOptions;
this.HTTPOptions.host = this.HTTPOptions.host || HTTPOptions.host;
this.HTTPOptions.port = this.HTTPOptions.port || HTTPOptions.port;
this.parser = new parser_1.default(this.serviceID, this.logLevel);
this.logger = new broid_utils_1.Logger("adapter", this.logLevel);
}
users() {
return Promise.reject(new Error("Not supported"));
}
channels() {
return Promise.reject(new Error("Not supported"));
}
serviceId() {
return this.serviceID;
}
connect() {
if (this.connected) {
return Rx_1.Observable.of({ type: "connected", serviceID: this.serviceId() });
}
this.connected = true;
if (!this.token || this.token === "") {
return Rx_1.Observable.throw(new Error("Token should exist."));
}
if (!this.tokenSecret || this.tokenSecret === "") {
return Rx_1.Observable.throw(new Error("TokenSecret should exist."));
}
this.session = new Nexmo({
apiKey: this.token,
apiSecret: this.tokenSecret,
});
this.webhookServer = new webHookServer_1.default(this.HTTPOptions, this.logLevel);
this.webhookServer.listen();
return Rx_1.Observable.of(({ type: "connected", serviceID: this.serviceId() }));
}
disconnect() {
return Promise.reject(new Error("Not supported"));
}
listen() {
if (!this.webhookServer) {
return Rx_1.Observable.throw(new Error("No webhookServer found."));
}
return Rx_1.Observable.fromEvent(this.webhookServer, "message")
.mergeMap((normalized) => this.parser.parse(normalized))
.mergeMap((parsed) => this.parser.validate(parsed))
.mergeMap((validated) => {
if (!validated) {
return Rx_1.Observable.empty();
}
return Promise.resolve(validated);
});
}
send(data) {
this.logger.debug("sending", { message: data });
return broid_schemas_1.default(data, "send")
.then(() => {
if (data.object.type !== "Note") {
return Promise.reject(new Error("Only Note is supported."));
}
return Promise.resolve(data)
.then((result) => {
const toNumber = R.path(["to", "id"], result);
const content = R.path(["object", "content"], result);
return Promise.fromCallback((cb) => this.session.message.sendSms(this.username, toNumber, content, {}, cb));
})
.then((result) => {
const ids = R.map((message) => message["message-id"], result.messages);
return { type: "sent", serviceID: this.serviceId(), ids };
});
});
}
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Adapter;
3 changes: 3 additions & 0 deletions integrations/broid-nexmo/lib/core/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use strict";
const adapter_1 = require("./adapter");
module.exports = adapter_1.default;
1 change: 1 addition & 0 deletions integrations/broid-nexmo/lib/core/interfaces.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"use strict";
77 changes: 77 additions & 0 deletions integrations/broid-nexmo/lib/core/parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use strict";
const Promise = require("bluebird");
const broid_schemas_1 = require("broid-schemas");
const broid_utils_1 = require("broid-utils");
const moment = require("moment");
const uuid = require("node-uuid");
const R = require("ramda");
class Parser {
constructor(serviceID, logLevel) {
this.serviceID = serviceID;
this.generatorName = "nexmo";
this.logger = new broid_utils_1.Logger("parser", logLevel);
}
validate(event) {
this.logger.debug("Validation process", { event });
const parsed = broid_utils_1.cleanNulls(event);
if (!parsed || R.isEmpty(parsed)) {
return Promise.resolve(null);
}
if (!parsed.type) {
this.logger.debug("Type not found.", { parsed });
return Promise.resolve(null);
}
return broid_schemas_1.default(parsed, "activity")
.then(() => parsed)
.catch((err) => {
this.logger.error(err);
return null;
});
}
parse(event) {
this.logger.debug("Normalize process", { event });
const normalized = broid_utils_1.cleanNulls(event);
if (!normalized || R.isEmpty(normalized)) {
return Promise.resolve(null);
}
const activitystreams = this.createActivityStream(event);
activitystreams.actor = {
id: normalized.msisdn,
name: normalized.msisdn,
type: "Person",
};
activitystreams.target = {
id: normalized.to,
name: normalized.to,
type: "Person",
};
activitystreams.object = {
content: normalized.text,
id: normalized.messageId || this.createIdentifier(),
type: "Note",
};
return Promise.resolve(activitystreams);
}
createIdentifier() {
return uuid.v4();
}
createActivityStream(event) {
let timestamp = moment().unix();
if (event.timestamp) {
timestamp = moment.utc(event.timestamp, "YYYY-MM-DD HH:mm:ss", true)
.unix();
}
return {
"@context": "https://www.w3.org/ns/activitystreams",
"generator": {
id: this.serviceID,
name: this.generatorName,
type: "Service",
},
"published": timestamp,
"type": "Create",
};
}
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Parser;
Loading

0 comments on commit 350fb65

Please sign in to comment.