-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
9: chat example r=D4nte a=D4nte Resolves #15 Co-authored-by: Franck Royer <franck@royer.one>
- Loading branch information
Showing
15 changed files
with
452 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,4 @@ version: v1beta1 | |
plugins: | ||
- name: ts_proto | ||
out: ./src/proto | ||
opt: grpc_js | ||
opt: grpc_js,esModuleInterop=true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
syntax = "proto3"; | ||
|
||
package chat.v2; | ||
|
||
message ChatMessageProto { | ||
uint64 timestamp = 1; | ||
string nick = 2; | ||
bytes payload = 3; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { expect } from 'chai'; | ||
import fc from 'fast-check'; | ||
|
||
import { ChatMessage } from './chat_message'; | ||
|
||
describe('Chat Message', function () { | ||
it('Chat message round trip binary serialization', function () { | ||
fc.assert( | ||
fc.property( | ||
fc.date({ min: new Date(0) }), | ||
fc.string(), | ||
fc.string(), | ||
(timestamp, nick, message) => { | ||
const msg = new ChatMessage(timestamp, nick, message); | ||
const buf = msg.encode(); | ||
const actual = ChatMessage.decode(buf); | ||
|
||
// Date.toString does not include ms, as we loose this precision by design | ||
expect(actual.timestamp.toString()).to.eq(timestamp.toString()); | ||
expect(actual.nick).to.eq(nick); | ||
expect(actual.message).to.eq(message); | ||
} | ||
) | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Reader } from 'protobufjs/minimal'; | ||
|
||
import { ChatMessageProto } from '../proto/chat/v2/chat_message'; | ||
|
||
export class ChatMessage { | ||
public constructor( | ||
public timestamp: Date, | ||
public nick: string, | ||
public message: string | ||
) {} | ||
|
||
static decode(bytes: Uint8Array): ChatMessage { | ||
const protoMsg = ChatMessageProto.decode(Reader.create(bytes)); | ||
const timestamp = new Date(protoMsg.timestamp * 1000); | ||
const message = protoMsg.payload | ||
? Array.from(protoMsg.payload) | ||
.map((char) => { | ||
return String.fromCharCode(char); | ||
}) | ||
.join('') | ||
: ''; | ||
return new ChatMessage(timestamp, protoMsg.nick, message); | ||
} | ||
|
||
encode(): Uint8Array { | ||
const timestamp = Math.floor(this.timestamp.valueOf() / 1000); | ||
const payload = Buffer.from(this.message, 'utf-8'); | ||
|
||
return ChatMessageProto.encode({ | ||
timestamp, | ||
nick: this.nick, | ||
payload, | ||
}).finish(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import readline from 'readline'; | ||
import util from 'util'; | ||
|
||
import Waku from '../lib/waku'; | ||
import { WakuMessage } from '../lib/waku_message'; | ||
import { TOPIC } from '../lib/waku_relay'; | ||
import { delay } from '../test_utils/delay'; | ||
|
||
import { ChatMessage } from './chat_message'; | ||
|
||
(async function () { | ||
const opts = processArguments(); | ||
|
||
const rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout, | ||
}); | ||
|
||
const question = util.promisify(rl.question).bind(rl); | ||
|
||
// Looks like wrong type definition of promisify is picked. | ||
// May be related to https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20497 | ||
const nick = ((await question( | ||
'Please choose a nickname: ' | ||
)) as unknown) as string; | ||
console.log(`Hi ${nick}!`); | ||
|
||
const waku = await Waku.create({ listenAddresses: [opts.listenAddr] }); | ||
|
||
// TODO: Bubble event to waku, infer topic, decode msg | ||
// Tracked with https://github.com/status-im/js-waku/issues/19 | ||
waku.libp2p.pubsub.on(TOPIC, (event) => { | ||
const wakuMsg = WakuMessage.decode(event.data); | ||
if (wakuMsg.payload) { | ||
const chatMsg = ChatMessage.decode(wakuMsg.payload); | ||
const timestamp = chatMsg.timestamp.toLocaleString([], { | ||
month: 'short', | ||
day: 'numeric', | ||
hour: 'numeric', | ||
minute: '2-digit', | ||
hour12: false, | ||
}); | ||
console.log(`<${timestamp}> ${chatMsg.nick}: ${chatMsg.message}`); | ||
} | ||
}); | ||
|
||
console.log('Waku started'); | ||
|
||
if (opts.staticNode) { | ||
console.log(`dialing ${opts.staticNode}`); | ||
await waku.dial(opts.staticNode); | ||
} | ||
|
||
await new Promise((resolve) => | ||
waku.libp2p.pubsub.once('gossipsub:heartbeat', resolve) | ||
); | ||
|
||
// TODO: identify if it is possible to listen to an event to confirm dial | ||
// finished instead of an arbitrary delay. Tracked with | ||
// https://github.com/status-im/js-waku/issues/18 | ||
await delay(2000); | ||
// TODO: Automatically subscribe, tracked with | ||
// https://github.com/status-im/js-waku/issues/17 | ||
await waku.relay.subscribe(); | ||
console.log('Subscribed to waku relay'); | ||
|
||
await new Promise((resolve) => | ||
waku.libp2p.pubsub.once('gossipsub:heartbeat', resolve) | ||
); | ||
|
||
console.log('Ready to chat!'); | ||
rl.prompt(); | ||
for await (const line of rl) { | ||
rl.prompt(); | ||
const chatMessage = new ChatMessage(new Date(), nick, line); | ||
|
||
const msg = WakuMessage.fromBytes(chatMessage.encode()); | ||
await waku.relay.publish(msg); | ||
} | ||
})(); | ||
|
||
interface Options { | ||
staticNode?: string; | ||
listenAddr: string; | ||
} | ||
|
||
function processArguments(): Options { | ||
const passedArgs = process.argv.slice(2); | ||
|
||
let opts: Options = { listenAddr: '/ip4/0.0.0.0/tcp/0' }; | ||
|
||
while (passedArgs.length) { | ||
const arg = passedArgs.shift(); | ||
switch (arg) { | ||
case '--staticNode': | ||
opts = Object.assign(opts, { staticNode: passedArgs.shift() }); | ||
break; | ||
case '--listenAddr': | ||
opts = Object.assign(opts, { listenAddr: passedArgs.shift() }); | ||
break; | ||
default: | ||
console.log(`Unsupported argument: ${arg}`); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
return opts; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,28 @@ | ||
import fc from 'fast-check'; | ||
|
||
import { Message } from './waku_message'; | ||
import { WakuMessage } from './waku_message'; | ||
|
||
describe('Waku Message', function () { | ||
it('Waku message round trip binary serialization', function () { | ||
fc.assert( | ||
fc.property(fc.string(), (s) => { | ||
const msg = Message.fromUtf8String(s); | ||
const msg = WakuMessage.fromUtf8String(s); | ||
const binary = msg.toBinary(); | ||
const actual = Message.fromBinary(binary); | ||
const actual = WakuMessage.decode(binary); | ||
|
||
return actual.isEqualTo(msg); | ||
}) | ||
); | ||
}); | ||
|
||
it('Payload to utf-8', function () { | ||
fc.assert( | ||
fc.property(fc.string(), (s) => { | ||
const msg = WakuMessage.fromUtf8String(s); | ||
const utf8 = msg.utf8Payload(); | ||
|
||
return utf8 === s; | ||
}) | ||
); | ||
}); | ||
}); |
Oops, something went wrong.