From 7b13a751706e1977c717c7054a91e3f6cd8987f7 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 26 Sep 2025 13:03:36 -0700 Subject: [PATCH] Added tui --- Makefile | 5 +- src/demo.nim => examples/tui.nim | 0 {src => examples}/tui/layout.nim | 0 {src => examples}/tui/persistence.nim | 8 +- {src => examples}/tui/tui.nim | 15 ++- {src => examples}/tui/utils.nim | 0 nim_chat_poc.nimble | 6 +- src/tui2.nim | 185 ++++++++++++++++++++++++++ 8 files changed, 207 insertions(+), 12 deletions(-) rename src/demo.nim => examples/tui.nim (100%) rename {src => examples}/tui/layout.nim (100%) rename {src => examples}/tui/persistence.nim (95%) rename {src => examples}/tui/tui.nim (99%) rename {src => examples}/tui/utils.nim (100%) create mode 100644 src/tui2.nim diff --git a/Makefile b/Makefile index 7446bde..482c89b 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ else # "variables.mk" was included. Business as usual until the end of this file .PHONY: all update clean # default target, because it's the first one that doesn't start with '.' -all: | waku_example nim_chat_poc +all: | waku_example nim_chat_poc tui test_file := $(word 2,$(MAKECMDGOALS)) define test_name @@ -87,6 +87,9 @@ nim_chat_poc: | build-waku-librln build-waku-nat nim_chat_poc.nims \ $(ENV_SCRIPT) nim nim_chat_poc $(NIM_PARAMS) nim_chat_poc.nims +tui: | build-waku-librln build-waku-nat nim_chat_poc.nims + echo -e $(BUILD_MSG) "build/$@" && \ + $(ENV_SCRIPT) nim tui $(NIM_PARAMS) --path:src nim_chat_poc.nims endif diff --git a/src/demo.nim b/examples/tui.nim similarity index 100% rename from src/demo.nim rename to examples/tui.nim diff --git a/src/tui/layout.nim b/examples/tui/layout.nim similarity index 100% rename from src/tui/layout.nim rename to examples/tui/layout.nim diff --git a/src/tui/persistence.nim b/examples/tui/persistence.nim similarity index 95% rename from src/tui/persistence.nim rename to examples/tui/persistence.nim index dd6db9f..606f856 100644 --- a/src/tui/persistence.nim +++ b/examples/tui/persistence.nim @@ -12,9 +12,9 @@ import strformat import strutils import tables -import ../chat_sdk/crypto/ecdh -import ../chat_sdk/delivery/waku_client -import ../chat_sdk/identity +import chat_sdk/crypto/ecdh +import chat_sdk/delivery/waku_client +import chat_sdk/identity const REGISTRATION_DIR = ".registry" @@ -103,7 +103,7 @@ proc saveCfg(name:string, cfg: Config) = staticPeers: cfg.waku.staticPeers ) - let json = s.toJson() + let json = jsonutils.toJson(s) try: writeFile(joinPath(KEY_DIR, fmt"{name.toLower()}.cfg"), $json) diff --git a/src/tui/tui.nim b/examples/tui/tui.nim similarity index 99% rename from src/tui/tui.nim rename to examples/tui/tui.nim index 4c30278..624440e 100644 --- a/src/tui/tui.nim +++ b/examples/tui/tui.nim @@ -10,12 +10,15 @@ import strutils import sugar import tables -import ../chat_sdk/client -import ../chat_sdk/conversations -import ../chat_sdk/delivery/waku_client -import ../chat_sdk/links -import ../chat_sdk/proto_types -import ../content_types/all +import chat_sdk/[ + client, + conversations, + delivery/waku_client, + links + # proto_types +] + +import ../../src/content_types/all import layout import persistence diff --git a/src/tui/utils.nim b/examples/tui/utils.nim similarity index 100% rename from src/tui/utils.nim rename to examples/tui/utils.nim diff --git a/nim_chat_poc.nimble b/nim_chat_poc.nimble index bfaf071..98df81c 100644 --- a/nim_chat_poc.nimble +++ b/nim_chat_poc.nimble @@ -40,4 +40,8 @@ task waku_example, "Build Waku based simple example": task nim_chat_poc, "Build Waku based simple example": let name = "nim_chat_poc" - buildBinary name, "examples/", " -d:chronicles_log_level='TRACE' " + buildBinary name, "examples/", " -d:chronicles_log_level='INFO' " + +task tui, "Build Waku based simple example": + let name = "tui" + buildBinary name, "examples/", " -d:chronicles_log_level='INFO' -d:chronicles_sinks=textlines[file]" diff --git a/src/tui2.nim b/src/tui2.nim new file mode 100644 index 0000000..6fa112f --- /dev/null +++ b/src/tui2.nim @@ -0,0 +1,185 @@ +import chronos +import chronicles +import strformat + +import chat_sdk/client +import chat_sdk/identity +import chat_sdk/conversations +import chat_sdk/delivery/waku_client +import chat_sdk/utils +import chat_sdk/proto_types + +import content_types/all +import libp2p/crypto/crypto +# import libp2p/crypto/secp256k1 +import os +import std/sequtils +import strutils +import tables +import std/parseopt + +import std/json +import std/jsonutils except distinctBase +import std/marshal +import std/streams + +import std/base64 + + + +import tui/persistence + + +const SELF_DEFINED = 99 + + +################################################# +# Command Line Args +################################################# + +type CmdArgs = object + username*: string + invite*: string + +proc getCmdArgs(): CmdArgs = + var username = "" + var invite = "" + for kind, key, val in getopt(): + case kind + of cmdArgument: + discard + of cmdLongOption, cmdShortOption: + case key + of "name", "n": + username = val + of "invite", "i": + invite = val + of cmdEnd: + break + if username == "": + username = "" + + result = CmdArgs(username: username, invite: invite) + + + +################################################# +# Link Generation +################################################# + +proc toBundle*(link: string): Result[IntroBundle, string] = + # Check scheme + if not link.startsWith("wap://"): + return err("InvalidScheme") + + # Remove scheme + let path = link[6..^1] # Remove "waku://" + + # Split by '/' + let parts = path.split('/') + + # Expected format: ident/{ident}/ephemeral/{ephemeral}/eid/{eid} + if parts.len != 6: + return err("InvalidFormat") + + # Validate structure + if parts[0] != "ident" or parts[2] != "ephemeral" or parts[4] != "eid": + return err("InvalidFormat") + + # Extract values + let ident = decode(parts[1]).toBytes() + let ephemeral = decode(parts[3]).toBytes() + let eid = int32(parseInt(parts[5])) # TODO: catch parse error + + # Validate non-empty + if ident.len == 0: + return err("MissingIdent") + if ephemeral.len == 0: + return err("MissingEphemeral") + + return ok(IntroBundle( + ident: ident, + ephemeral: ephemeral, + ephemeral_id: eid + )) + + +proc toLink(intro: IntroBundle): string = + let ident = encode(intro.ident, safe = true) + let ephemeral = intro.ephemeral.toHex() + result = fmt"wap://ident/{ident}/ephemeral/{ephemeral}/eid/{intro.ephemeral_id}" + + +proc createChatClient(name: string): Future[Client] {.async.} = + try: + var cfg = await getCfg(name) + for key, val in fetchRegistrations(): + if key != name: + cfg.waku.staticPeers.add(val) + + result = newClient(name, cfg.waku, cfg.ident) + except Exception as e: + echo "Failed to create client: ", e.msg + +proc main() {.async.} = + + let args = getCmdArgs() + var client = await createChatClient(args.username) + + echo "Identity: ", client.getId() + echo "Key: ", client.identity.getAddr() + echo "NodeAaddr:", client.ds.cfg.getMultiAddr() + + client.onNewMessage(proc(convo: Conversation, msg: ContentFrame) {.async.} = + echo " ------> Raya :: " + await sleepAsync(10000) + await convo.sendMessage(client.ds, initTextFrame("Pong").toContentFrame()) + ) + + client.onNewConversation(proc(convo: Conversation) {.async.} = + echo " ------> Raya :: New Conversation: " & convo.id() + await convo.sendMessage(client.ds, initTextFrame("Hello").toContentFrame()) + ) + + client.onDeliveryAck(proc(convo: Conversation, msgId: string) {.async.} = + echo " raya -- Read Receipt for " & msgId + ) + + await client.start() + + + + + # Perform OOB Introduction: Raya -> Saro + let x = proc() {.async.} = + while true: + await sleepAsync(5000) + notice "Heartbeat - PeerCount: ", peerCount = client.ds.getConnectedPeerCount() + asyncSpawn x() + + + warn "==== arg Invite ", arg= args.invite, peers = client.ds.getConnectedPeerCount() + if args.invite != "": + let invite = toBundle(args.invite).get() + await sleepAsync(20000) + discard await client.newPrivateConversation(invite) + else: + let bundle = client.createIntroBundle() + echo "Link: ", toLink(bundle) +# + + + # let link = toLink(raya_bundle) + # echo "Raya Intro Link: ", link + + # let parsed = toBundle(link).get() + # echo repr(parsed) + + await sleepAsync(400000) + + client.stop() + +when isMainModule: + setLogLevel(LogLevel.Debug) + waitFor main() + notice "Shutdown"