First commit
This commit is contained in:
commit
56d6ee6897
|
@ -0,0 +1 @@
|
|||
node_modules/**
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "airbnb",
|
||||
"globals": {
|
||||
"artifacts": false,
|
||||
"document": false,
|
||||
"window": false
|
||||
},
|
||||
"rules": {
|
||||
"arrow-body-style": [2, "as-needed"],
|
||||
"arrow-parens": [2, "as-needed"],
|
||||
"import/prefer-default-export": [2, "never"],
|
||||
"space-before-function-paren": [2, "never"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
.idea
|
||||
dist
|
||||
.env
|
|
@ -0,0 +1,47 @@
|
|||
## matrix-whisper-bridge
|
||||
|
||||
A two-way bridge between the matrix protocol and the whisper protocol
|
||||
|
||||
### Development
|
||||
|
||||
1. Clone repository and enter project folder
|
||||
|
||||
```
|
||||
git clone ...
|
||||
cd matrix-whisper-bridge
|
||||
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
|
||||
```
|
||||
npm install
|
||||
|
||||
```
|
||||
|
||||
3. Install geth
|
||||
|
||||
4. Launch lightweight testnet with proper whisper settings for Status chat protocol
|
||||
|
||||
```
|
||||
geth --testnet --light --shh.pow=0.002
|
||||
|
||||
```
|
||||
|
||||
5. You need to add Status peers so you can join the network.
|
||||
|
||||
First connect to the running geth instance
|
||||
|
||||
```
|
||||
geth --testnet attach
|
||||
|
||||
```
|
||||
|
||||
Then add the peers from this list: https://github.com/status-im/status-go/blob/develop/geth/params/cluster.go
|
||||
|
||||
```
|
||||
var peers = ['enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404','enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404','enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404','enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404','enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.108.68:30304','enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304','enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404','enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404','enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404','enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404','enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.108.68:30304','enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304'];
|
||||
peers.forEach(function addPeer(peer) { admin.addPeer(peer); });
|
||||
exit
|
||||
|
||||
```
|
|
@ -0,0 +1,22 @@
|
|||
const WhisperUtils = require('../utils/whisperUtils');
|
||||
|
||||
class MatrixRiotBridge {
|
||||
init() {
|
||||
return Promise.all([
|
||||
this.connectToWhisper(),
|
||||
this.connectToMatrix(),
|
||||
]);
|
||||
}
|
||||
|
||||
connectToWhisper() {
|
||||
this.whisperUtils = new WhisperUtils();
|
||||
return this.whisperUtils.init();
|
||||
}
|
||||
|
||||
connectToMatrix() {
|
||||
this.matrixUtils = {};
|
||||
return this.matrixUtils;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MatrixRiotBridge;
|
|
@ -0,0 +1,37 @@
|
|||
//const transit = require('transit-js');
|
||||
|
||||
const MatrixRiotBridge = require('./app/MatrixRiotBridge');
|
||||
const { decodeStatusPayload } = require('./utils/statusUtils');
|
||||
|
||||
const CHANNEL = 'noman-test';
|
||||
|
||||
const bridge = new MatrixRiotBridge();
|
||||
|
||||
bridge.init().then(([whisperUtils]) => {
|
||||
whisperUtils.getPublicKey().then(publicKey => {
|
||||
function isMe(sig) {
|
||||
return publicKey === sig;
|
||||
}
|
||||
|
||||
whisperUtils.send(CHANNEL, 'Bot is alive');
|
||||
|
||||
whisperUtils.listen(CHANNEL).then(
|
||||
subscription => {
|
||||
subscription.on('data', ({ payload, sig, timestamp, topic }) => {
|
||||
const [, [message]] = decodeStatusPayload(payload);
|
||||
const me = isMe(sig);
|
||||
const name = me ? 'Bot' : sig.slice(0, 10);
|
||||
|
||||
console.log('From:', name, 'at', timestamp);
|
||||
console.log('Topic:', topic);
|
||||
console.log('Message:', message, '\n');
|
||||
|
||||
if (!me) {
|
||||
whisperUtils.send(CHANNEL, `${message} back`);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
error => console.error(error));
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "matrix-whisper-bridge",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "A two-way bridge between the matrix protocol and the whisper protocol",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Noman <noman@noman.land>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"eslint": "^5.2.0",
|
||||
"eslint-config-airbnb": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.13.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"npm": "^6.4.1",
|
||||
"prompt": "^1.0.0",
|
||||
"transit-js": "^0.8.861",
|
||||
"web3": "^1.0.0-beta.35",
|
||||
"ws": "^6.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
const Web3 = require('web3');
|
||||
|
||||
const web3 = new Web3();
|
||||
|
||||
const { utils: { asciiToHex, hexToAscii, sha3 } } = web3;
|
||||
|
||||
function createStatusPayload(
|
||||
{
|
||||
tag = '~#c4',
|
||||
content = 'This is a whisper/slack test!',
|
||||
messageType = '~:public-group-user-message',
|
||||
clockValue = (new Date().getTime()) * 100,
|
||||
contentType = 'text/plain',
|
||||
timestamp = new Date().getTime(),
|
||||
} = {},
|
||||
) {
|
||||
return asciiToHex(
|
||||
JSON.stringify([
|
||||
tag,
|
||||
[
|
||||
content,
|
||||
contentType,
|
||||
messageType,
|
||||
clockValue,
|
||||
timestamp,
|
||||
],
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
function decodeStatusPayload(payload) {
|
||||
return JSON.parse(hexToAscii(payload));
|
||||
}
|
||||
|
||||
function topicFromChannelName(channelName) {
|
||||
return sha3(channelName).slice(0, 10);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createStatusPayload,
|
||||
decodeStatusPayload,
|
||||
topicFromChannelName,
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
const net = require('net');
|
||||
const Web3 = require('web3');
|
||||
|
||||
const {
|
||||
createStatusPayload,
|
||||
topicFromChannelName,
|
||||
} = require('./statusUtils');
|
||||
|
||||
const CHANNEL = 'noman-test';
|
||||
|
||||
class WhisperUtils {
|
||||
init() {
|
||||
this.web3 = new Web3(new Web3.providers.IpcProvider(
|
||||
process.env.GETH_IPC_PATH,
|
||||
net,
|
||||
));
|
||||
|
||||
this.shh = this.web3.shh;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.createWhisperIdentity().then(
|
||||
sig => {
|
||||
this.sig = sig;
|
||||
resolve(this);
|
||||
},
|
||||
error => reject(error),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
createWhisperIdentity() {
|
||||
return this.shh.newKeyPair();
|
||||
}
|
||||
|
||||
getPublicKey() {
|
||||
return this.shh.getPublicKey(this.sig);
|
||||
}
|
||||
|
||||
listen(channel) {
|
||||
const topic = topicFromChannelName(channel);
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.shh.generateSymKeyFromPassword(channel)
|
||||
.then(symKeyID => {
|
||||
const subscription = this.shh.subscribe('messages', {
|
||||
minPow: 0.002,
|
||||
symKeyID,
|
||||
topics: [topic],
|
||||
});
|
||||
resolve(subscription);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
send(channel, message) {
|
||||
const payload = createStatusPayload({ content: message });
|
||||
|
||||
return this.shh.generateSymKeyFromPassword(channel)
|
||||
.then(symKeyID => this.shh.post({
|
||||
payload,
|
||||
powTarget: 0.002,
|
||||
powTime: 1,
|
||||
sig: this.sig,
|
||||
symKeyID,
|
||||
topic: topicFromChannelName(channel),
|
||||
ttl: 10,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WhisperUtils;
|
Loading…
Reference in New Issue