Wire delivery acknowledgements

This commit is contained in:
Jazz Turner-Baggs 2025-09-26 17:26:11 -07:00
parent f52150ba83
commit aefacba0e4
No known key found for this signature in database
6 changed files with 43 additions and 68 deletions

View File

@ -35,6 +35,8 @@ type
sender: string
content: string
timestamp: DateTime
id: string
isAcknowledged: bool
ConvoInfo = object
name: string
@ -71,13 +73,15 @@ proc `==`(a,b: ConvoInfo):bool =
# Data Management
#################################################
proc addMessage(conv: var ConvoInfo, sender: string, content: string) =
proc addMessage(conv: var ConvoInfo, messageId: MessageId, sender: string, content: string) =
let now = now()
conv.messages.add(Message(
sender: sender,
id: messageId,
content: content,
timestamp: now
timestamp: now,
isAcknowledged: false
))
conv.lastMsgTime = now
@ -117,10 +121,13 @@ proc createConvo(app: ChatApp) {.async.} =
discard await app.client.newPrivateConversation(toBundle(app.inputInviteBuffer.strip()).get())
proc sendMessage(app: ChatApp, convoInfo: ptr ConvoInfo, msg: string) {.async.} =
convoInfo[].addMessage("You", app.inputBuffer)
var msgId = ""
if convoInfo.convo != nil:
await convoInfo.convo.sendMessage(app.client.ds, initTextFrame(msg).toContentFrame())
msgId = await convoInfo.convo.sendMessage(app.client.ds, initTextFrame(msg).toContentFrame())
convoInfo[].addMessage(msgId, "You", app.inputBuffer)
proc setupChatSdk(app: ChatApp) =
@ -149,6 +156,16 @@ proc setupChatSdk(app: ChatApp) =
app.client.onDeliveryAck(proc(convo: Conversation, msgId: string) {.async.} =
info "DeliveryAck", msgId=msgId
app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt"Ack:{msgId}"))
var s = ""
var msgs = addr app.conversations[convo.id()].messages
for i in countdown(msgs[].high, 0):
s = fmt"{s},{msgs[i].id}"
var m = addr msgs[i]
if m.id == msgId:
m.isAcknowledged = true
break # Stop after
)
@ -265,11 +282,12 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
let m = convo.messages[i]
let timeStr = m.timestamp.format("HH:mm:ss")
var deliveryIcon = " "
var remainingText = m.content
if m.sender == "You":
tb.setForegroundColor(fgYellow)
deliveryIcon = if m.isAcknowledged: "" else: ""
else:
tb.setForegroundColor(fgGreen)
@ -278,7 +296,7 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt" TOO LONG: {convo.name}"))
return
tb.write(x, y, fmt"[{timeStr}] {m.sender}")
tb.write(x, y, fmt"[{timeStr}] {deliveryIcon} {m.sender}")
y = y + 1
while remainingText.len > 0:
@ -300,11 +318,12 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
let m = convo.messages[i]
let timeStr = m.timestamp.format("HH:mm:ss")
var deliveryIcon = " "
var remainingText = m.content
if m.sender == "You":
tb.setForegroundColor(fgYellow)
deliveryIcon = if m.isAcknowledged: "" else: ""
else:
tb.setForegroundColor(fgGreen)
@ -322,7 +341,7 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
tb.write(x+3, y, line)
y = y - 1
tb.write(x, y, fmt"[{timeStr}] {m.sender}")
tb.write(x, y, fmt"[{timeStr}] {deliveryIcon} {m.sender}")
y = y - 2
proc drawMsgInput( app: ChatApp, layout: Pane) =
@ -528,59 +547,10 @@ proc initChatApp(client: Client): Future[ChatApp] {.async.} =
# Add some sample conversations with messages
var conv1 = ConvoInfo(name: "Alice", messages: @[])
conv1.addMessage("36Alice", "Hey there! How are you doing?")
conv1.addMessage("35You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("34Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("33You", "A terminal-based chat application in Nim!")
conv1.addMessage("32Alice", "Hey there! How are you doing?")
conv1.addMessage("31You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("30Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("29You", "A terminal-based chat application in Nim!")
conv1.addMessage("28Alice", "Hey there! How are you doing?")
conv1.addMessage("27You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("26Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("25You", "A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim!")
conv1.addMessage("24Alice", "Hey there! How are you doing?")
conv1.addMessage("23You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("22Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("21You", "A terminal-based chat application in Nim!")
conv1.addMessage("20Alice", "Hey there! How are you doing?")
conv1.addMessage("19You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("18Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("17You", "A terminal-based chat application in Nim!")
conv1.addMessage("16Alice", "Hey there! How are you doing?")
conv1.addMessage("15You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("14Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("13You", "A terminal-based chat application in Nim!")
conv1.addMessage("12Alice", "Hey there! How are you doing?")
conv1.addMessage("11You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("10Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("9You", "The system architecture consists of three main components: the client interface, the processing engine, and the data storage layer. Each component communicates through well-defined APIs that ensure scalability and maintainability. The client interface handles user interactions and validates input data before forwarding requests to the processing engine.")
conv1.addMessage("8Alice", "Hey there! How are you doing?")
conv1.addMessage("7You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("6Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("5You", "A terminal-based chat application in Nim!")
conv1.addMessage("4Alice", "Hey there! How are you doing?")
conv1.addMessage("3You", "I'm doing well, thanks! Working on a new project.")
conv1.addMessage("2Alice", "That sounds exciting! What kind of project?")
conv1.addMessage("1You", "A terminal-based chat application in Nim!")
var conv1 = ConvoInfo(name: "ReadMe", messages: @[])
conv1.addMessage("","Bob", "TODO")
app.conversations[conv1.name] = conv1
var conv2 = ConvoInfo(name: "Bob", messages: @[])
conv2.addMessage("Bob", "Did you see the game last night?")
conv2.addMessage("You", "The system architecture consists of three main components: the client interface, the processing engine, and the data storage layer. Each component communicates through well-defined APIs that ensure scalability and maintainability. The client interface handles user interactions and validates input data before forwarding requests to the processing engine")
conv2.addMessage("Bob", "The home team crushed it! 3-0")
app.conversations[conv2.name] = conv2
var conv3 = ConvoInfo(name: "Development Team", messages: @[])
conv3.addMessage("Sarah", "Morning standup in 10 minutes")
conv3.addMessage("Mike", "I'll be there. Just finishing up the last bug fix.")
conv3.addMessage("You", "On my way!")
conv3.addMessage("Sarah", "Great! Let's review the sprint progress today.")
app.conversations[conv3.name] = conv3
return app

View File

@ -4,11 +4,12 @@ import chat_sdk/[
delivery/waku_client,
identity,
links,
proto_types
proto_types,
types
]
export client, conversations, waku_client, identity, links
#export specific frames need by applications
export ContentFrame
export ContentFrame, MessageId

View File

@ -231,8 +231,6 @@ proc newPrivateConversation*(client: Client,
proc parseMessage(client: Client, msg: ChatPayload) {.raises: [ValueError,
SerializationError].} =
## Receives a incoming payload, decodes it, and processes it.
info "Parse", clientId = client.getId(), msg = msg,
contentTopic = msg.contentTopic
let envelope = decode(msg.bytes, WapEnvelopeV1).valueOr:
raise newException(ValueError, "Failed to decode WapEnvelopeV1: " & error)

View File

@ -5,6 +5,7 @@ import strutils
import ../proto_types
import ../delivery/waku_client
import ../utils
import ../types
type
ConvoTypes* = enum
@ -25,6 +26,6 @@ method id*(self: Conversation): string {.raises: [Defect, ValueError].} =
panic("ProgramError: Missing concrete implementation")
method sendMessage*(convo: Conversation, ds: WakuClient,
content_frame: ContentFrame) {.async, base, gcsafe.} =
content_frame: ContentFrame) : Future[MessageId] {.async, base, gcsafe.} =
# TODO: make this a compile time check
panic("ProgramError: Missing concrete implementation")

View File

@ -116,7 +116,7 @@ proc initPrivateV1*(owner: Identity, participant: PublicKey,
raise newException(ValueError, "bad sds channel")
proc sendFrame(self: PrivateV1, ds: WakuClient,
msg: PrivateV1Frame): Future[void]{.async.} =
msg: PrivateV1Frame): Future[MessageId]{.async.} =
let frameBytes = encode(msg)
let msgId = self.calcMsgId(frameBytes)
@ -130,6 +130,9 @@ proc sendFrame(self: PrivateV1, ds: WakuClient,
discard ds.sendPayload(self.getTopic(), encryptedBytes.toEnvelope(
self.getConvoId()))
result = msgId
method id*(self: PrivateV1): string =
return getConvoIdRaw(self.participants, self.discriminator)
@ -166,13 +169,13 @@ proc handleFrame*[T: ConversationStore](convo: PrivateV1, client: T,
method sendMessage*(convo: PrivateV1, ds: WakuClient,
content_frame: ContentFrame) {.async.} =
content_frame: ContentFrame) : Future[MessageId] {.async.} =
try:
let frame = PrivateV1Frame(sender: @(convo.owner.getPubkey().bytes()),
timestamp: getCurrentTimestamp(), content: content_frame)
await convo.sendFrame(ds, frame)
result = await convo.sendFrame(ds, frame)
except Exception as e:
error "Unknown error in PrivateV1:SendMessage"

View File

@ -11,6 +11,7 @@ import
crypto,
delivery/waku_client,
proto_types,
types,
utils
logScope:
@ -104,5 +105,6 @@ proc handleFrame*[T: ConversationStore](convo: Inbox, client: T, bytes: seq[
method sendMessage*(convo: Inbox, ds: WakuClient,
content_frame: ContentFrame) {.async.} =
content_frame: ContentFrame) : Future[MessageId] {.async.} =
warn "Cannot send message to Inbox"
result = "program_error"