diff --git a/poc.js b/poc.js deleted file mode 100644 index 82e5e61..0000000 --- a/poc.js +++ /dev/null @@ -1,76 +0,0 @@ -const Web3 = require('web3'); -const { utils: { asciiToHex, hexToAscii, sha3 } } = Web3; - -const POW_TIME = 1; -const TTL = 10; -const POW_TARGET = 0.002; - -const CHANNEL_NAME ="mytest" -const CHANNEL = Web3.utils.sha3(CHANNEL_NAME).slice(0, 10); - -function createStatusPayload() { - let tag = '~#c4'; - let content = 'Hello everyone, it\s status js'; - let messageType = '~:public-group-user-message'; - let clockValue = (new Date().getTime()) * 100; - let contentType = 'text/plain'; - let timestamp = new Date().getTime(); - return asciiToHex( - JSON.stringify([ - tag, - [ - content, - contentType, - messageType, - clockValue, - timestamp, - ], - ]), - ); -} - -(async () => { - - let web3 = new Web3(); - web3.setProvider(new Web3.providers.WebsocketProvider('ws://localhost:8546', {headers: {Origin: "statusjs"}})); - - await web3.shh.setMinPoW(POW_TARGET); - - let keys = {}; - - // keys.symKeyID = await web3.shh.newSymKey(); - // keys.sig = await web3.shh.newKeyPair(); - keys.symKeyID = await web3.shh.generateSymKeyFromPassword(CHANNEL_NAME); - keys.sig = await web3.shh.newKeyPair(); - - console.dir("keys generated"); - console.dir(keys); - - subscription = web3.shh.subscribe("messages", { - minPow: POW_TARGET, - symKeyID: keys.symKeyID, - topics: [CHANNEL] - }).on('data', (data) => { - console.dir("message received!"); - console.dir(data); - console.dir(JSON.parse(hexToAscii(data.payload))); - }).on('error', () => { - console.dir("error receiving message"); - }); - - web3.shh.post({ - symKeyID: keys.symKeyID, // encrypts using the sym key ID - sig: keys.sig, // signs the message using the keyPair ID - ttl: TTL, - topic: CHANNEL, - payload: createStatusPayload(), - powTime: POW_TIME, - powTarget: POW_TARGET - }).then(() => { - console.dir('message sent!'); - }).catch((e) => { - console.dir("error sending message"); - console.dir(e); - }); - -})() diff --git a/src/index.js b/src/index.ts similarity index 55% rename from src/index.js rename to src/index.ts index 3522ec6..a464dfc 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,30 +1,38 @@ -const Web3 = require('web3'); -import utils from './utils.js'; -import mailservers from './mailservers.js'; -const constants = require('./constants'); +const Web3 = require("web3"); +import utils from "./utils.js"; +import mailservers from "./mailservers.js"; +const constants = require("./constants"); const { utils: { asciiToHex, hexToAscii } } = Web3; -function createStatusPayload(content, messageType, clockValue, isJson) { - const tag = constants.messageTags.message; - const oneMonthInMs = 60 * 60 * 24 * 31 * 1000; - if(clockValue < (new Date().getTime())){ +function createStatusPayload(content: string, messageType: string, clockValue: number, isJson = false) { + const tag: string = constants.messageTags.message; + const oneMonthInMs: number = 60 * 60 * 24 * 31 * 1000; + if (clockValue < (new Date().getTime())) { clockValue = (new Date().getTime() + oneMonthInMs) * 100; } - const contentType = (isJson ? 'content/json' : 'text/plain'); + const contentType = (isJson ? "content/json" : "text/plain"); const timestamp = new Date().getTime(); return asciiToHex( JSON.stringify([ tag, - [content, contentType, messageType, clockValue, timestamp, ["^ ","~:text", content]], + [content, contentType, messageType, clockValue, timestamp, ["^ ", "~:text", content]], ]), ); } -const _sig = new WeakMap(); +const sig = new WeakMap(); + class StatusJS { + private channels: any; + private contacts: any; + private userMessagesSubscription: any; + private mailservers: any; + private isHttpProvider: boolean; + private shh: any; + private chatRequestCb: any; constructor() { this.channels = {}; @@ -34,16 +42,16 @@ class StatusJS { this.isHttpProvider = false; } - async connect(url, privateKey) { - let web3 = new Web3(); - if(url.startsWith("ws://")){ + public async connect(url: string, privateKey?: string) { + const web3: any = new Web3(); + if (url.startsWith("ws://")) { web3.setProvider(new Web3.providers.WebsocketProvider(url, {headers: {Origin: "statusjs"}})); - } else if(url.startsWith("http://") || url.startsWith("https://")) { + } else if (url.startsWith("http://") || url.startsWith("https://")) { // Deprecated but required for statusd web3.setProvider(new Web3.providers.HttpProvider(url)); this.isHttpProvider = true; } else { - const net = require('net'); + const net = require("net"); web3.setProvider(new Web3.providers.IpcProvider(url, net)); } @@ -51,59 +59,63 @@ class StatusJS { this.mailservers = new mailservers(web3); await web3.shh.setMinPoW(constants.post.POW_TARGET); - _sig.set( + sig.set( this, - privateKey ? await this.generateWhisperKeyFromWallet(privateKey) : await web3.shh.newKeyPair() + privateKey ? await this.generateWhisperKeyFromWallet(privateKey) : await web3.shh.newKeyPair(), ); } - isConnected() { + public isConnected() { return this.shh.isListening(); } - async generateWhisperKeyFromWallet(key){ - await this.shh.addPrivateKey(key); - return; + private async generateWhisperKeyFromWallet(key: string) { + const keyId = await this.shh.addPrivateKey(key); + return keyId; } - async getPublicKey(){ - const pubKey = await this.shh.getPublicKey(_sig.get(this)); + public async getPublicKey() { + const pubKey = await this.shh.getPublicKey(sig.get(this)); return pubKey; } - async getUserName(pubKey){ - if(!pubKey) { + public async getUserName(pubKey?: any) { + if (!pubKey) { pubKey = await this.getPublicKey(); } return utils.generateUsernameFromSeed(pubKey); } - async joinChat(channelName, cb) { - let channelKey = await this.shh.generateSymKeyFromPassword(channelName); + public async joinChat(channelName: string, cb?: any) { + const channelKey = await this.shh.generateSymKeyFromPassword(channelName); this.channels[channelName] = { - channelName, + channelCode: Web3.utils.sha3(channelName).slice(0, 10), channelKey, + channelName, lastClockValue: 0, - channelCode: Web3.utils.sha3(channelName).slice(0, 10) }; - if (cb) cb(); + if (cb) { + cb(); + } } - async addContact(contactCode, cb) { + public async addContact(contactCode: string, cb?: any) { this.contacts[contactCode] = { + lastClockValue: 0, username: utils.generateUsernameFromSeed(contactCode), - lastClockValue: 0 }; - if (cb) cb(); + if (cb) { + cb(); + } } - leaveChat(channelName) { - if(!this.isHttpProvider) { + public leaveChat(channelName: string) { + if (!this.isHttpProvider) { this.channels[channelName].subscription.unsubscribe(); } else { // TODO: fix me - //web3.shh.deleteMessageFilter(this.channels[channelName].filterId) + // web3.shh.deleteMessageFilter(this.channels[channelName].filterId) // .then(result => { // clearInterval(this.channels[channelName].interval); // }); @@ -111,217 +123,233 @@ class StatusJS { delete this.channels[channelName]; } - async removeContact(contactCode, _cb) { + public async removeContact(contactCode: string) { delete this.contacts[contactCode]; } - isSubscribedTo(channelName) { + public isSubscribedTo(channelName: string) { return !!this.channels[channelName]; } - onMessage(par1, par2) { - if(typeof par1 === "function"){ + public onMessage(par1: any, par2: any) { + if (typeof par1 === "function") { this.onUserMessage(par1); } else { this.onChannelMessage(par1, par2); } } - onChatRequest(cb){ + public onChatRequest(cb: any) { this.chatRequestCb = cb; } - onChannelMessage(channelName, cb) { + public onChannelMessage(channelName: string, cb: any) { if (!this.channels[channelName]) { return cb("unknown channel: " + channelName); } const filters = { + allowP2P: true, symKeyID: this.channels[channelName].channelKey, topics: [this.channels[channelName].channelCode], - allowP2P: true }; - const messageHandler = (data) => { - let username = utils.generateUsernameFromSeed(data.sig); + const messageHandler = (data: any) => { + const username = utils.generateUsernameFromSeed(data.sig); const payloadArray = JSON.parse(hexToAscii(data.payload)); - if(this.channels[channelName].lastClockValue < payloadArray[1][3]){ + if (this.channels[channelName].lastClockValue < payloadArray[1][3]) { this.channels[channelName].lastClockValue = payloadArray[1][3]; } - cb(null, {payload: hexToAscii(data.payload), data: data, username: username}); + cb(null, {payload: hexToAscii(data.payload), data, username}); }; - if(this.isHttpProvider){ + if (this.isHttpProvider) { this.shh.newMessageFilter(filters) - .then(filterId => { + .then((filterId: any) => { this.channels[channelName].filterId = filterId; this.channels[channelName].interval = setInterval(() => { this.shh.getFilterMessages(filterId) - .then(data => { - data.map(d => { + .then((data: any) => { + data.map((d: any) => { messageHandler(d); }); }) - .catch((err) => { cb(err); }); + .catch((err: any) => { cb(err); }); }, 250); }); } else { this.channels[channelName].subscription = this.shh.subscribe("messages", filters) - .on('data', messageHandler) - .on('error', (err) => { cb(err); }); + .on("data", messageHandler) + .on("error", (err: any) => { cb(err); }); } } - onUserMessage(cb) { - + public onUserMessage(cb: any) { const filters = { + allowP2P: true, minPow: constants.post.POW_TARGET, - privateKeyID: _sig.get(this), + privateKeyID: sig.get(this), topics: [constants.topics.CONTACT_DISCOVERY_TOPIC], - allowP2P: true }; - const messageHandler = (data) => { - if(!this.contacts[data.sig]){ + const messageHandler = (data: any) => { + if (!this.contacts[data.sig]) { this.addContact(data.sig); } const payloadArray = JSON.parse(hexToAscii(data.payload)); - if(this.contacts[data.sig].lastClockValue < payloadArray[1][3]){ + if (this.contacts[data.sig].lastClockValue < payloadArray[1][3]) { this.contacts[data.sig].lastClockValue = payloadArray[1][3]; } - if(payloadArray[0] === constants.messageTags.message){ - cb(null, {payload: hexToAscii(data.payload), data: data, username: this.contacts[data.sig].username}); - } else if(payloadArray[0] === constants.messageTags.chatRequest) { + if (payloadArray[0] === constants.messageTags.message) { + cb(null, {payload: hexToAscii(data.payload), data, username: this.contacts[data.sig].username}); + } else if (payloadArray[0] === constants.messageTags.chatRequest) { this.contacts[data.sig].displayName = payloadArray[1][0]; this.contacts[data.sig].profilePic = payloadArray[1][1]; - if(this.chatRequestCb){ + if (this.chatRequestCb) { this.chatRequestCb(null, { - 'username': this.contacts[data.sig].username, - 'displayName': this.contacts[data.sig].displayName, - 'profilePic': this.contacts[data.sig].profilePic, + displayName: this.contacts[data.sig].displayName, + profilePic: this.contacts[data.sig].profilePic, + username: this.contacts[data.sig].username, }); } } }; - - if(this.isHttpProvider){ + if (this.isHttpProvider) { this.shh.newMessageFilter(filters) - .then(filterId => { + .then((filterId: any) => { this.userMessagesSubscription = {}; this.userMessagesSubscription.filterId = filterId; this.userMessagesSubscription.interval = setInterval(() => { this.shh.getFilterMessages(filterId) - .then(data => { - data.map(d => { + .then((data: any) => { + data.map((d: any) => { messageHandler(d); }); }) - .catch((err) => { cb(err); }); + .catch((err: any) => { cb(err); }); }, 250); }); } else { this.userMessagesSubscription = this.shh.subscribe("messages", filters) - .on('data', (data) => { messageHandler(data); }) - .on('error', (err) => { cb(err); }); + .on("data", (data: any) => { messageHandler(data); }) + .on("error", (err: any) => { cb(err); }); } } - sendUserMessage(contactCode, msg, cb) { - if(!this.contacts[contactCode]){ + public sendUserMessage(contactCode: string, msg: string, cb?: any) { + if (!this.contacts[contactCode]) { this.addContact(contactCode); } this.contacts[contactCode].lastClockValue++; this.shh.post({ - pubKey: contactCode, - sig: _sig.get(this), - ttl: constants.post.TTL, - topic: constants.topics.CONTACT_DISCOVERY_TOPIC, payload: createStatusPayload(msg, constants.messageTypes.USER_MESSAGE, this.contacts[contactCode].lastClockValue), + powTarget: constants.post.POW_TARGET, powTime: constants.post.POW_TIME, - powTarget: constants.post.POW_TARGET + pubKey: contactCode, + sig: sig.get(this), + topic: constants.topics.CONTACT_DISCOVERY_TOPIC, + ttl: constants.post.TTL, }).then(() => { - if (!cb) return; + if (!cb) { + return; + } cb(null, true); - }).catch((e) => { - if (!cb) return; + }).catch((e: any) => { + if (!cb) { + return; + } cb(e, false); }); } - sendGroupMessage(channelName, msg, cb) { + public sendGroupMessage(channelName: string, msg: string, cb?: any) { if (!this.channels[channelName]) { - if(!cb) return; + if (!cb) { + return; + } return cb("unknown channel: " + channelName); } this.channels[channelName].lastClockValue++; this.shh.post({ - symKeyID: this.channels[channelName].channelKey, - sig: _sig.get(this), - ttl: constants.post.TTL, - topic: this.channels[channelName].channelCode, payload: createStatusPayload(msg, constants.messageTypes.GROUP_MESSAGE, this.channels[channelName].lastClockValue), + powTarget: constants.post.POW_TARGET, powTime: constants.post.POW_TIME, - powTarget: constants.post.POW_TARGET + sig: sig.get(this), + symKeyID: this.channels[channelName].channelKey, + topic: this.channels[channelName].channelCode, + ttl: constants.post.TTL, }).then(() => { - if (!cb) return; + if (!cb) { + return; + } cb(null, true); - }).catch((e) => { - if (!cb) return; + }).catch((e: any) => { + if (!cb) { + return; + } cb(e, false); }); } - sendJsonMessage(destination, msg, cb) { + public sendJsonMessage(destination: string, msg: string, cb?: any) { if (constants.regExp.CONTACT_CODE_REGEXP.test(destination)) { - if(!this.contacts[destination]){ + if (!this.contacts[destination]) { this.addContact(destination); } this.contacts[destination].lastClockValue++; this.shh.post({ - pubKey: destination, - sig: _sig.get(this), - ttl: constants.post.TTL, - topic: constants.topics.CONTACT_DISCOVERY_TOPIC, payload: createStatusPayload(msg, constants.messageTypes.USER_MESSAGE, this.contacts[destination].lastClockValue, true), + powTarget: constants.post.POW_TARGET, powTime: constants.post.POW_TIME, - powTarget: constants.post.POW_TARGET + pubKey: destination, + sig: sig.get(this), + topic: constants.topics.CONTACT_DISCOVERY_TOPIC, + ttl: constants.post.TTL, }).then(() => { - if (!cb) return; + if (!cb) { + return; + } cb(null, true); - }).catch((e) => { - if (!cb) return; + }).catch((e: any) => { + if (!cb) { + return; + } cb(e, false); }); } else { this.channels[destination].lastClockValue++; this.shh.post({ - symKeyID: this.channels[destination].channelKey, - sig: _sig.get(this), - ttl: constants.post.TTL, - topic: this.channels[destination].channelCode, payload: createStatusPayload(JSON.stringify(msg), constants.messageTypes.GROUP_MESSAGE, this.channels[destination].lastClockValue, true), + powTarget: constants.post.POW_TARGET, powTime: constants.post.POW_TIME, - powTarget: constants.post.POW_TARGET + sig: sig.get(this), + symKeyID: this.channels[destination].channelKey, + topic: this.channels[destination].channelCode, + ttl: constants.post.TTL, }).then(() => { - if (!cb) return; + if (!cb) { + return; + } cb(null, true); - }).catch((e) => { - if (!cb) return; + }).catch((e: any) => { + if (!cb) { + return; + } cb(e, false); }); } } - sendMessage(destination, msg, cb){ + public sendMessage(destination: string, msg: string, cb?: any) { if (constants.regExp.CONTACT_CODE_REGEXP.test(destination)) { this.sendUserMessage(destination, msg, cb); } else { diff --git a/src/mailservers.ts b/src/mailservers.ts index a2cff79..3a74b0c 100644 --- a/src/mailservers.ts +++ b/src/mailservers.ts @@ -9,7 +9,7 @@ class MailServers { this.web3 = web3; } - public async useMailserver(mailserver: string, cb?: Function) { + public async useMailserver(mailserver: string, cb?: any) { const enode: string = mailserverList[mailserver]; if (!enode) { diff --git a/src/types.d.ts b/src/types.d.ts index e44bcb0..7635f08 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1 +1 @@ -declare module 'chance' +declare module "chance"; diff --git a/tslint.json b/tslint.json index be16668..82d65b7 100644 --- a/tslint.json +++ b/tslint.json @@ -9,6 +9,7 @@ "interface-name": [true, "never-prefix"], "member-ordering": [false], "no-var-requires": false, + "ordered-imports": false, "no-empty": false, "no-console": false },