mirror of https://github.com/waku-org/js-waku.git
Implement chat message protobuf to support nick and time handles
This commit is contained in:
parent
0f694cf8e1
commit
cca1d685dc
|
@ -3,4 +3,4 @@ version: v1beta1
|
||||||
plugins:
|
plugins:
|
||||||
- name: ts_proto
|
- name: ts_proto
|
||||||
out: ./src/proto
|
out: ./src/proto
|
||||||
opt: grpc_js
|
opt: grpc_js,esModuleInterop=true
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package chat.v2;
|
||||||
|
|
||||||
|
message ChatMessageProto {
|
||||||
|
uint64 timestamp = 1;
|
||||||
|
string nick = 2;
|
||||||
|
bytes payload = 3;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import { Message } from '../lib/waku_message';
|
||||||
import { TOPIC } from '../lib/waku_relay';
|
import { TOPIC } from '../lib/waku_relay';
|
||||||
import { delay } from '../test_utils/delay';
|
import { delay } from '../test_utils/delay';
|
||||||
|
|
||||||
|
import { ChatMessage } from './chat_message';
|
||||||
|
|
||||||
(async function () {
|
(async function () {
|
||||||
const opts = processArguments();
|
const opts = processArguments();
|
||||||
|
|
||||||
|
@ -12,8 +14,18 @@ import { delay } from '../test_utils/delay';
|
||||||
|
|
||||||
// TODO: Bubble event to waku, infer topic, decode msg
|
// TODO: Bubble event to waku, infer topic, decode msg
|
||||||
waku.libp2p.pubsub.on(TOPIC, (event) => {
|
waku.libp2p.pubsub.on(TOPIC, (event) => {
|
||||||
const msg = Message.fromBinary(event.data);
|
const wakuMsg = Message.decode(event.data);
|
||||||
console.log(msg.utf8Payload());
|
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');
|
console.log('Waku started');
|
||||||
|
@ -47,7 +59,9 @@ import { delay } from '../test_utils/delay';
|
||||||
rl.prompt();
|
rl.prompt();
|
||||||
rl.on('line', async (line) => {
|
rl.on('line', async (line) => {
|
||||||
rl.prompt();
|
rl.prompt();
|
||||||
const msg = Message.fromUtf8String('(js-chat) ' + line);
|
const chatMessage = new ChatMessage(new Date(), 'js-chat', line);
|
||||||
|
|
||||||
|
const msg = Message.fromBytes(chatMessage.encode());
|
||||||
await waku.relay.publish(msg);
|
await waku.relay.publish(msg);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('Waku Message', function () {
|
||||||
fc.property(fc.string(), (s) => {
|
fc.property(fc.string(), (s) => {
|
||||||
const msg = Message.fromUtf8String(s);
|
const msg = Message.fromUtf8String(s);
|
||||||
const binary = msg.toBinary();
|
const binary = msg.toBinary();
|
||||||
const actual = Message.fromBinary(binary);
|
const actual = Message.decode(binary);
|
||||||
|
|
||||||
return actual.isEqualTo(msg);
|
return actual.isEqualTo(msg);
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,16 +15,25 @@ export class Message {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Message from utf-8 string
|
* Create Message with a utf-8 string as payload
|
||||||
* @param message
|
* @param payload
|
||||||
* @returns {Message}
|
* @returns {Message}
|
||||||
*/
|
*/
|
||||||
static fromUtf8String(message: string): Message {
|
static fromUtf8String(payload: string): Message {
|
||||||
const payload = Buffer.from(message, 'utf-8');
|
const buf = Buffer.from(payload, 'utf-8');
|
||||||
|
return new Message(buf, DEFAULT_CONTENT_TOPIC, DEFAULT_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Message with a byte array as payload
|
||||||
|
* @param payload
|
||||||
|
* @returns {Message}
|
||||||
|
*/
|
||||||
|
static fromBytes(payload: Uint8Array): Message {
|
||||||
return new Message(payload, DEFAULT_CONTENT_TOPIC, DEFAULT_VERSION);
|
return new Message(payload, DEFAULT_CONTENT_TOPIC, DEFAULT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBinary(bytes: Uint8Array): Message {
|
static decode(bytes: Uint8Array): Message {
|
||||||
const wakuMsg = WakuMessage.decode(Reader.create(bytes));
|
const wakuMsg = WakuMessage.decode(Reader.create(bytes));
|
||||||
return new Message(wakuMsg.payload, wakuMsg.contentTopic, wakuMsg.version);
|
return new Message(wakuMsg.payload, wakuMsg.contentTopic, wakuMsg.version);
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,6 +392,6 @@ function waitForNextData(pubsub: Pubsub): Promise<Message> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
pubsub.once(TOPIC, resolve);
|
pubsub.once(TOPIC, resolve);
|
||||||
}).then((msg: any) => {
|
}).then((msg: any) => {
|
||||||
return Message.fromBinary(msg.data);
|
return Message.decode(msg.data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue