import chronos, nimcrypto, strutils import ../../libp2p/daemon/daemonapi ## nim c -r --threads:on chat.nim when not(compileOption("threads")): {.fatal: "Please, compile this program with the --threads:on option!".} const ServerProtocols = @["/test-chat-stream"] type CustomData = ref object api: DaemonAPI remotes: seq[StreamTransport] consoleFd: AsyncFD serveFut: Future[void] proc threadMain(wfd: AsyncFD) {.thread.} = ## This procedure performs reading from `stdin` and sends data over ## pipe to main thread. var transp = fromPipe(wfd) while true: var line = stdin.readLine() let res = waitFor transp.write(line & "\r\n") proc serveThread(udata: CustomData) {.async.} = ## This procedure perform reading on pipe and sends data to remote clients. var transp = fromPipe(udata.consoleFd) proc remoteReader(transp: StreamTransport) {.async.} = while true: var line = await transp.readLine() if len(line) == 0: break echo ">> ", line while true: try: var line = await transp.readLine() if line.startsWith("/connect"): var parts = line.split(" ") if len(parts) == 2: var peerId = PeerID.init(parts[1]) var address = MultiAddress.init(multiCodec("p2p-circuit")) address &= MultiAddress.init(multiCodec("p2p"), peerId) echo "= Searching for peer ", peerId.pretty() var id = await udata.api.dhtFindPeer(peerId) echo "= Peer " & parts[1] & " found at addresses:" for item in id.addresses: echo $item echo "= Connecting to peer ", $address await udata.api.connect(peerId, @[address], 30) echo "= Opening stream to peer chat ", parts[1] var stream = await udata.api.openStream(peerId, ServerProtocols) udata.remotes.add(stream.transp) echo "= Connected to peer chat ", parts[1] asyncCheck remoteReader(stream.transp) elif line.startsWith("/search"): var parts = line.split(" ") if len(parts) == 2: var peerId = PeerID.init(parts[1]) echo "= Searching for peer ", peerId.pretty() var id = await udata.api.dhtFindPeer(peerId) echo "= Peer " & parts[1] & " found at addresses:" for item in id.addresses: echo $item elif line.startsWith("/consearch"): var parts = line.split(" ") if len(parts) == 2: var peerId = PeerID.init(parts[1]) echo "= Searching for peers connected to peer ", parts[1] var peers = await udata.api.dhtFindPeersConnectedToPeer(peerId) echo "= Found ", len(peers), " connected to peer ", parts[1] for item in peers: var peer = item.peer var addresses = newSeq[string]() var relay = false for a in item.addresses: addresses.add($a) if a.protoName() == "/p2p-circuit": relay = true break if relay: echo peer.pretty(), " * ", " [", addresses.join(", "), "]" else: echo peer.pretty(), " [", addresses.join(", "), "]" elif line.startsWith("/exit"): break else: var msg = line & "\r\n" echo "<< ", line var pending = newSeq[Future[int]]() for item in udata.remotes: pending.add(item.write(msg)) if len(pending) > 0: var results = await all(pending) except: echo getCurrentException().msg proc main() {.async.} = var data = new CustomData data.remotes = newSeq[StreamTransport]() var (rfd, wfd) = createAsyncPipe() if rfd == asyncInvalidPipe or wfd == asyncInvalidPipe: raise newException(ValueError, "Could not initialize pipe!") data.consoleFd = rfd data.serveFut = serveThread(data) var thread: Thread[AsyncFD] thread.createThread(threadMain, wfd) echo "= Starting P2P node" data.api = await newDaemonApi({DHTFull, Bootstrap}) await sleepAsync(3000) var id = await data.api.identity() proc streamHandler(api: DaemonAPI, stream: P2PStream) {.async.} = echo "= Peer ", stream.peer.pretty(), " joined chat" data.remotes.add(stream.transp) while true: var line = await stream.transp.readLine() if len(line) == 0: break echo ">> ", line await data.api.addHandler(ServerProtocols, streamHandler) echo "= Your PeerID is ", id.peer.pretty() await data.serveFut when isMainModule: waitFor(main())