Merge pull request #260 from status-im/remove-chat-message

This commit is contained in:
Franck Royer 2021-08-09 12:35:15 +10:00 committed by GitHub
commit 55a36f2263
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 49 additions and 11177 deletions

View File

@ -12,7 +12,7 @@ jobs:
examples_build_and_test:
strategy:
matrix:
example: [ cli-chat, web-chat, eth-dm, min-react-js-chat ]
example: [ web-chat, eth-dm, min-react-js-chat ]
runs-on: ubuntu-latest
steps:

View File

@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Removed
- Examples (cli-chat): The focus of this library is Web environment;
Several examples now cover usage of Waku Relay and Waku Store making cli-chat example obsolete;
web-chat POC should be preferred to use the [TOY-CHAT](https://rfc.vac.dev/spec/22/) protocol.
- `ChatMessage` has been moved from js-waku to web-chat example;
it is a type used for the [TOY-CHAT](https://rfc.vac.dev/spec/22/) protocol;
js-waku users should not build on top if this toy protocol and instead design message data structures appropriate to their use case.
## [0.10.0] - 2021-08-06
### Added

View File

@ -1,35 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": { "project": "./tsconfig.json" },
"env": { "es6": true },
"ignorePatterns": ["node_modules"],
"plugins": ["import", "eslint-comments", "functional"],
"extends": [
"eslint:recommended",
"plugin:eslint-comments/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/typescript",
"prettier",
"prettier/@typescript-eslint"
],
"globals": { "BigInt": true, "console": true, "WebAssembly": true },
"rules": {
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"eslint-comments/disable-enable-pair": [
"error",
{ "allowWholeFile": true }
],
"eslint-comments/no-unused-disable": "error",
"import/order": [
"error",
{ "newlines-between": "always", "alphabetize": { "order": "asc" } }
],
"no-constant-condition": ["error", { "checkLoops": false }],
"sort-imports": [
"error",
{ "ignoreDeclarationSort": true, "ignoreCase": true }
]
}
}

View File

@ -1,4 +0,0 @@
# Generated directories:
.nyc_output/
node_modules/
/tsconfig.tsbuildinfo

View File

@ -1,5 +0,0 @@
{
"extension": ["ts"],
"spec": "src/**/*.spec.ts",
"require": "ts-node/register"
}

View File

@ -1,35 +0,0 @@
# CLI Chat App
**Demonstrates**:
- Group chat
- Node JS/TypeScript
- Waku Relay
- Waku Light Push
- Waku Store
A node chat app is provided as a working example of the library.
It implements [Waku v2 Toy Chat](https://rfc.vac.dev/spec/22/) protocol.
Find the code in the [examples folder](https://github.com/status-im/js-waku/tree/main/examples/cli-chat).
To run the chat app, first ensure you have [Node.js](https://nodejs.org/en/) v14 or above:
```shell
node --version
```
Then, install and run:
```shell
git clone https://github.com/status-im/js-waku/ ; cd js-waku
npm install # Install dependencies for js-waku
npm run build # Build js-waku
cd examples/cli-chat
npm install # Install dependencies for the cli app
npm run start -- --autoDial
```
You can also specify an optional `listenAddr` parameter (.e.g `--listenAddr /ip4/0.0.0.0/tcp/7777/ws`).
This is only useful if you want a remote node to dial to your chat app,
it is not necessary in normal usage when you just connect to the fleet.

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +0,0 @@
{
"name": "js-waku-cli-chat",
"version": "0.1.0",
"description": "A NodeJS CLI Chat App powered by js-waku",
"main": "./index.ts",
"repository": "https://github.com/status-im/js-waku",
"license": "MIT OR Apache-2.0",
"keywords": [
"waku",
"decentralised",
"communication"
],
"scripts": {
"build": "run-s build:*",
"build:main": "tsc -p tsconfig.json",
"fix": "run-s fix:*",
"fix:prettier": "prettier \"src/**/*.ts\" \"./*.json\" --write",
"fix:lint": "eslint src --ext .ts --fix",
"start": "ts-node src/index.ts",
"test": "run-s build test:*",
"test:lint": "eslint src --ext .ts",
"test:prettier": "prettier \"src/**/*.ts\" \"./*.json\" --list-different",
"test:spelling": "cspell \"{README.md,src/**/*.ts}\" -c ../../.cspell.json",
"test:unit": "nyc --silent mocha",
"watch:build": "tsc -p tsconfig.json -w",
"watch:test": "nyc --silent mocha --watch",
"version": "standard-version",
"reset-hard": "git clean -dfx && git reset --hard && npm i"
},
"engines": {
"node": ">=14"
},
"dependencies": {
"js-waku": "../../build/main",
"libp2p-tcp": "^0.17.0",
"prompt-sync": "^4.2.0"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/app-root-path": "^1.2.4",
"@types/chai": "^4.2.15",
"@types/mocha": "^8.2.2",
"@types/node": "^14.14.31",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"chai": "^4.3.4",
"cspell": "^4.1.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"mocha": "^8.3.2",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"prettier": "^2.1.1",
"ts-node": "^9.1.1",
"typedoc": "^0.20.29",
"typescript": "^4.0.2"
},
"prettier": {
"singleQuote": true
},
"files": [
"!**/*.spec.*",
"!**/*.json",
"README.md"
],
"nyc": {
"extends": "@istanbuljs/nyc-config-typescript",
"exclude": [
"**/*.spec.js"
]
}
}

View File

@ -1,17 +0,0 @@
import { expect } from 'chai';
import { ChatMessage } from 'js-waku';
import { formatMessage } from './chat';
describe('CLI Chat app', () => {
it('Format message', () => {
const date = new Date(234325324);
const chatMessage = ChatMessage.fromUtf8String(
date,
'alice',
'Hello world!'
);
expect(formatMessage(chatMessage)).to.match(/^<.*> alice: Hello world!$/);
});
});

View File

@ -1,204 +0,0 @@
import readline from 'readline';
import util from 'util';
import {
ChatMessage,
Direction,
Environment,
getStatusFleetNodes,
Protocol,
StoreCodec,
Waku,
WakuMessage,
} from 'js-waku';
import TCP from 'libp2p-tcp';
import { multiaddr, Multiaddr } from 'multiaddr';
const ChatContentTopic = '/toy-chat/2/huilong/proto';
export default async function startChat(): Promise<void> {
let opts = processArguments();
if (!opts) return;
if (opts.autoDial) {
opts = await addFleetNodes(opts);
}
const waku = await Waku.create({
libp2p: {
addresses: { listen: [opts.listenAddr] },
modules: { transport: [TCP] },
},
});
console.log('PeerId: ', waku.libp2p.peerId.toB58String());
console.log('Listening on ');
waku.libp2p.multiaddrs.forEach((address) => {
console.log(`\t- ${address}`);
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
let nick = 'js-waku';
try {
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
nick = ((await question(
'Please choose a nickname: '
)) as unknown) as string;
} catch (e) {
console.log('Using default nick. Due to ', e);
}
console.log(`Hi, ${nick}!`);
waku.relay.addObserver(
(message) => {
if (message.payload) {
const chatMsg = ChatMessage.decode(message.payload);
console.log(formatMessage(chatMsg));
}
},
[ChatContentTopic]
);
await Promise.all(
opts.staticNodes.map((addr) => {
console.log(`Dialing ${addr}`);
return waku.dial(addr);
})
);
// If we connect to a peer with WakuStore, we run the protocol
// TODO: Instead of doing it `once` it should always be done but
// only new messages should be printed
waku.libp2p.peerStore.once(
'change:protocols',
async ({ peerId, protocols }) => {
if (protocols.includes(StoreCodec)) {
console.log(
`Retrieving archived messages from ${peerId.toB58String()}`
);
const messages = await waku.store.queryHistory({
peerId,
contentTopics: [ChatContentTopic],
direction: Direction.FORWARD,
});
messages?.map((msg) => {
if (msg.payload) {
const chatMsg = ChatMessage.decode(msg.payload);
console.log(formatMessage(chatMsg));
}
});
}
}
);
console.log('Ready to chat!');
rl.prompt();
for await (const line of rl) {
rl.prompt();
const chatMessage = ChatMessage.fromUtf8String(new Date(), nick, line);
const msg = await WakuMessage.fromBytes(
chatMessage.encode(),
ChatContentTopic,
{
timestamp: new Date(),
}
);
if (opts.lightPush) {
await waku.lightPush.push(msg);
} else {
await waku.relay.send(msg);
}
}
}
interface Options {
staticNodes: Multiaddr[];
listenAddr: string;
autoDial: boolean;
prod: boolean;
lightPush: boolean;
}
function processArguments(): Options | null {
const passedArgs = process.argv.slice(2);
let opts: Options = {
listenAddr: '/ip4/0.0.0.0/tcp/0',
staticNodes: [],
autoDial: false,
prod: false,
lightPush: false,
};
while (passedArgs.length) {
const arg = passedArgs.shift();
switch (arg) {
case `--help`:
console.log('Usage:');
console.log(' --help This help message');
console.log(
' --staticNode {multiaddr} Connect to this static node, can be set multiple time'
);
console.log(' --listenAddr {addr} Listen on this address');
console.log(' --autoDial Automatically dial Status fleet nodes');
console.log(
' --prod With `autoDial`, connect ot Status prod fleet (test fleet is dialed if not set)'
);
console.log(
' --lightPush Use Waku v2 Light Push protocol to send messages, instead of Waku v2 relay'
);
return null;
case '--staticNode':
opts.staticNodes.push(multiaddr(passedArgs.shift()!));
break;
case '--listenAddr':
opts = Object.assign(opts, { listenAddr: passedArgs.shift() });
break;
case '--autoDial':
opts.autoDial = true;
break;
case '--prod':
opts.prod = true;
break;
case '--lightPush':
opts.lightPush = true;
break;
default:
console.log(`Unsupported argument: ${arg}`);
process.exit(1);
}
}
return opts;
}
export function formatMessage(chatMsg: ChatMessage): string {
const timestamp = chatMsg.timestamp.toLocaleString([], {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: false,
});
return `<${timestamp}> ${chatMsg.nick}: ${chatMsg.payloadAsUtf8}`;
}
async function addFleetNodes(opts: Options): Promise<Options> {
await getStatusFleetNodes(
opts.prod ? Environment.Prod : Environment.Test,
Protocol.tcp
).then((nodes) =>
nodes.map((addr) => {
opts.staticNodes.push(multiaddr(addr));
})
);
return opts;
}

View File

@ -1,5 +0,0 @@
import startChat from './chat';
(async () => {
await startChat();
})();

View File

@ -1,5 +0,0 @@
declare module 'libp2p-tcp' {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const TCP: any;
export = TCP;
}

View File

@ -1,54 +0,0 @@
{
"compilerOptions": {
"incremental": true,
"target": "es2017",
"rootDir": "src",
"noEmit": true,
"moduleResolution": "node",
"module": "commonjs",
"declaration": true,
"inlineSourceMap": true,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
"resolveJsonModule": true /* Include modules imported with .json extension. */,
"strict": true /* Enable all strict type-checking options. */,
/* Strict Type-Checking Options */
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"strictNullChecks": true /* Enable strict null checks. */,
"strictFunctionTypes": true /* Enable strict checking of function types. */,
"strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */,
"noUnusedParameters": true /* Report errors on unused parameters. */,
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
"forceConsistentCasingInFileNames": true,
/* Debugging Options */
"traceResolution": false /* Report module resolution log messages. */,
"listEmittedFiles": false /* Print names of generated files part of the compilation. */,
"listFiles": false /* Print names of files part of the compilation. */,
"pretty": true /* Stylize errors and messages using color and context. */,
// Due to broken types in indirect dependencies
"skipLibCheck": true,
/* Experimental Options */
// "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
// "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
"lib": ["es2017"],
"types": ["node", "mocha"],
"typeRoots": ["node_modules/@types", "src/types"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules/**"],
"compileOnSave": false,
"ts-node": {
"files": true
}
}

View File

@ -3,6 +3,5 @@
Here is the list of the code examples and the features they demonstrate:
- [Web Chat App](web-chat): Group chat, React/TypeScript, Relay, Store.
- [CLI Chat App](cli-chat): Group chat, Node JS/TypeScript, Relay, Light Push, Store.
- [Ethereum Direct Message Web App](eth-dm): Private Messaging, React/TypeScript, Light Push, Signature with Web3, Asymmetric Encryption.
- [Minimal ReactJS Chat App](min-react-js-chat): Group chat, React/JavaScript, Relay, Protobuf using `protons`.

View File

@ -0,0 +1,6 @@
version: v1beta1
plugins:
- name: ts_proto
out: ./src/proto
opt: grpc_js,esModuleInterop=true

View File

@ -0,0 +1,5 @@
version: v1beta1
build:
roots:
- ./src/proto

View File

@ -38,6 +38,8 @@
"test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.{ts,tsx},public/**/*.html}\" -c ../../.cspell.json",
"fix:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" --write",
"fix:lint": "eslint src --ext .ts --ext .tsx --fix",
"proto": "run-s proto:*",
"proto:build": "buf generate",
"js-waku:build": "cd ../; npm run build",
"predeploy": "run-s js-waku:build build",
"deploy": "gh-pages -d build"

View File

@ -1,4 +1,5 @@
import { ChatMessage, WakuMessage } from 'js-waku';
import { WakuMessage } from 'js-waku';
import { ChatMessage } from './chat_message';
export class Message {
public chatMessage: ChatMessage;

View File

@ -1,10 +1,11 @@
import { ChatMessage, WakuMessage } from 'js-waku';
import { WakuMessage } from 'js-waku';
import { ChatContentTopic } from './App';
import ChatList from './ChatList';
import MessageInput from './MessageInput';
import { useWaku } from './WakuContext';
import { TitleBar } from '@livechat/ui-kit';
import { Message } from './Message';
import { ChatMessage } from './chat_message';
interface Props {
messages: Message[];

View File

@ -1,6 +1,6 @@
import { Reader } from 'protobufjs/minimal';
import * as proto from '../../proto/chat/v2/chat_message';
import * as proto from './proto/chat_message';
/**
* ChatMessage is used by the various show case waku apps that demonstrates

View File

@ -1,7 +1,5 @@
syntax = "proto3";
package chat.v2;
message ChatMessage {
uint64 timestamp = 1;
string nick = 2;

View File

@ -2,7 +2,7 @@
import Long from 'long';
import _m0 from 'protobufjs/minimal';
export const protobufPackage = 'chat.v2';
export const protobufPackage = '';
export interface ChatMessage {
timestamp: number;

View File

@ -8,8 +8,6 @@ export { WakuMessage } from './lib/waku_message';
export { generatePrivateKey, getPublicKey } from './lib/waku_message/version_1';
export { ChatMessage } from './lib/chat_message';
export {
WakuLightPush,
LightPushCodec,

View File

@ -1,26 +0,0 @@
import { expect } from 'chai';
import fc from 'fast-check';
import { ChatMessage } from './index';
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 = ChatMessage.fromUtf8String(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.payloadAsUtf8).to.eq(message);
}
)
);
});
});

View File

@ -8,10 +8,21 @@ import { makeLogFileName, NimWaku, NOISE_KEY_1 } from '../test_utils/';
import { Waku } from './waku';
describe('Waku Dial', function () {
let waku: Waku;
let nimWaku: NimWaku;
afterEach(async function () {
this.timeout(10_000);
nimWaku ? nimWaku.stop() : null;
waku ? await waku.stop() : null;
});
describe('Interop: Nim', function () {
it('nim connects to js', async function () {
this.timeout(10_000);
const waku = await Waku.create({
waku = await Waku.create({
staticNoiseKey: NOISE_KEY_1,
libp2p: {
addresses: { listen: ['/ip4/0.0.0.0/tcp/0'] },
@ -21,7 +32,7 @@ describe('Waku Dial', function () {
const multiAddrWithId = waku.getLocalMultiaddrWithID();
const nimWaku = new NimWaku(makeLogFileName(this));
nimWaku = new NimWaku(makeLogFileName(this));
await nimWaku.start({ staticnode: multiAddrWithId });
const nimPeers = await nimWaku.peers();
@ -38,9 +49,6 @@ describe('Waku Dial', function () {
const jsPeers = waku.libp2p.peerStore.peers;
expect(jsPeers.has(nimPeerId.toB58String())).to.be.true;
nimWaku.stop();
await waku.stop();
});
});
});

View File

@ -274,6 +274,7 @@ describe('Waku Relay', () => {
});
afterEach(async function () {
this.timeout(5000);
nimWaku ? nimWaku.stop() : null;
waku ? await waku.stop() : null;
});

View File

@ -1,5 +1,3 @@
export { ChatMessage } from './chat/v2/chat_message';
export { WakuMessage } from './waku/v2/message';
export {

View File

@ -13,9 +13,10 @@ import { waitForFile } from './async_fs';
export default async function waitForLine(
filepath: string,
logLine: string
logLine: string,
timeout: number
): Promise<void> {
await pTimeout(waitForFile(filepath), 2000);
await pTimeout(waitForFile(filepath), timeout);
const options = {
fromBeginning: true,

View File

@ -134,8 +134,8 @@ export class NimWaku {
);
});
dbg("Waiting to see 'RPC Server started' in nim-waku logs");
await this.waitForLog('RPC Server started');
dbg("Waiting to see 'Node setup complete' in nim-waku logs");
await this.waitForLog('Node setup complete', 9000);
dbg('nim-waku node has been started');
}
@ -149,8 +149,8 @@ export class NimWaku {
this.process = undefined;
}
async waitForLog(msg: string): Promise<void> {
return waitForLine(this.logPath, msg);
async waitForLog(msg: string, timeout: number): Promise<void> {
return waitForLine(this.logPath, msg, timeout);
}
/** Calls nim-waku2 JSON-RPC API `get_waku_v2_admin_v1_peers` to check