mirror of
https://github.com/logos-messaging/nim-chat-poc.git
synced 2026-05-29 22:09:29 +00:00
Add dynamic contentFrame
This commit is contained in:
parent
3ed1321f07
commit
26fd2bbf7f
@ -51,7 +51,7 @@ type Client* = ref object
|
|||||||
inboundQueue: QueueRef
|
inboundQueue: QueueRef
|
||||||
isRunning: bool
|
isRunning: bool
|
||||||
|
|
||||||
newMessageCallbacks: seq[MessageCallback[string]]
|
newMessageCallbacks: seq[MessageCallback[ContentFrame]]
|
||||||
newConvoCallbacks: seq[NewConvoCallback]
|
newConvoCallbacks: seq[NewConvoCallback]
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
@ -115,12 +115,13 @@ proc listConversations*(client: Client): seq[Conversation] =
|
|||||||
# Callback Handling
|
# Callback Handling
|
||||||
#################################################
|
#################################################
|
||||||
|
|
||||||
proc onNewMessage*(client: Client, callback: MessageCallback[string]) =
|
proc onNewMessage*(client: Client, callback: MessageCallback[ContentFrame]) =
|
||||||
client.newMessageCallbacks.add(callback)
|
client.newMessageCallbacks.add(callback)
|
||||||
|
|
||||||
proc notifyNewMessage(client: Client, convo: Conversation, msg: string) =
|
proc notifyNewMessage(client: Client, convo: Conversation,
|
||||||
|
content: ContentFrame) =
|
||||||
for cb in client.newMessageCallbacks:
|
for cb in client.newMessageCallbacks:
|
||||||
discard cb(convo, msg)
|
discard cb(convo, content)
|
||||||
|
|
||||||
proc onNewConversation*(client: Client, callback: NewConvoCallback) =
|
proc onNewConversation*(client: Client, callback: NewConvoCallback) =
|
||||||
client.newConvoCallbacks.add(callback)
|
client.newConvoCallbacks.add(callback)
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import std/[options, times]
|
|||||||
import ./conversations/convo_type
|
import ./conversations/convo_type
|
||||||
import crypto
|
import crypto
|
||||||
import identity
|
import identity
|
||||||
|
import proto_types
|
||||||
|
|
||||||
type ConvoId = string
|
type ConvoId = string
|
||||||
|
|
||||||
@ -13,4 +14,5 @@ type
|
|||||||
proc identity(self: Self): Identity
|
proc identity(self: Self): Identity
|
||||||
proc getId(self: Self): string
|
proc getId(self: Self): string
|
||||||
|
|
||||||
proc notifyNewMessage(self: Self, convo: Conversation, msg: string)
|
proc notifyNewMessage(self: Self, convo: Conversation,
|
||||||
|
content: ContentFrame)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import chronos
|
|||||||
import strformat
|
import strformat
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
|
import ../proto_types
|
||||||
import ../delivery/waku_client
|
import ../delivery/waku_client
|
||||||
import ../utils
|
import ../utils
|
||||||
|
|
||||||
@ -24,6 +25,6 @@ method id*(self: Conversation): string {.raises: [Defect, ValueError].} =
|
|||||||
panic("ProgramError: Missing concrete implementation")
|
panic("ProgramError: Missing concrete implementation")
|
||||||
|
|
||||||
method sendMessage*(convo: Conversation, ds: WakuClient,
|
method sendMessage*(convo: Conversation, ds: WakuClient,
|
||||||
text: string) {.async, base, gcsafe.} =
|
content_frame: ContentFrame) {.async, base, gcsafe.} =
|
||||||
# TODO: make this a compile time check
|
# TODO: make this a compile time check
|
||||||
panic("ProgramError: Missing concrete implementation")
|
panic("ProgramError: Missing concrete implementation")
|
||||||
|
|||||||
@ -91,17 +91,18 @@ proc handleFrame*[T: ConversationStore](convo: PrivateV1, client: T,
|
|||||||
case frame.getKind():
|
case frame.getKind():
|
||||||
of typeContentFrame:
|
of typeContentFrame:
|
||||||
# TODO: Using client.getId() results in an error in this context
|
# TODO: Using client.getId() results in an error in this context
|
||||||
client.notifyNewMessage(convo, toUtfString(frame.content.bytes))
|
client.notifyNewMessage(convo, frame.content)
|
||||||
|
|
||||||
of typePlaceholder:
|
of typePlaceholder:
|
||||||
notice "Got Placeholder", text = frame.placeholder.counter
|
notice "Got Placeholder", text = frame.placeholder.counter
|
||||||
|
|
||||||
|
|
||||||
method sendMessage*(convo: PrivateV1, ds: WakuClient, text: string) {.async.} =
|
method sendMessage*(convo: PrivateV1, ds: WakuClient,
|
||||||
|
content_frame: ContentFrame) {.async.} =
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let frame = PrivateV1Frame(sender: @(convo.owner.getPubkey().bytes()),
|
let frame = PrivateV1Frame(sender: @(convo.owner.getPubkey().bytes()),
|
||||||
content: ContentFrame(domain: 0, tag: 1, bytes: text.toBytes()))
|
content: content_frame)
|
||||||
|
|
||||||
await convo.sendFrame(ds, frame)
|
await convo.sendFrame(ds, frame)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -99,5 +99,6 @@ proc handleFrame*[T: ConversationStore](convo: Inbox, client: T, bytes: seq[
|
|||||||
notice "Receive Note", client = client.getId(), text = frame.note.text
|
notice "Receive Note", client = client.getId(), text = frame.note.text
|
||||||
|
|
||||||
|
|
||||||
method sendMessage*(convo: Inbox, ds: WakuClient, text: string) {.async.} =
|
method sendMessage*(convo: Inbox, ds: WakuClient,
|
||||||
|
content_frame: ContentFrame) {.async.} =
|
||||||
warn "Cannot send message to Inbox"
|
warn "Cannot send message to Inbox"
|
||||||
|
|||||||
55
src/content_types/all.nim
Normal file
55
src/content_types/all.nim
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Can this be an external package? It would be preferable to have these types
|
||||||
|
# easy to import and use.
|
||||||
|
|
||||||
|
import protobuf_serialization # This import is needed or th macro will not work
|
||||||
|
import protobuf_serialization/proto_parser
|
||||||
|
import results
|
||||||
|
import std/random
|
||||||
|
import strformat
|
||||||
|
|
||||||
|
import ../chat_sdk/proto_types
|
||||||
|
|
||||||
|
|
||||||
|
export protobuf_serialization
|
||||||
|
|
||||||
|
import_proto3 "protos/text_frame.proto"
|
||||||
|
# import_proto3 "../../protos/common_frames.proto"
|
||||||
|
|
||||||
|
export ContentFrame, TextFrame
|
||||||
|
|
||||||
|
type ContentTypes = TextFrame
|
||||||
|
|
||||||
|
# protobuf_serialization does not support enums, so it needs to be manually implemented
|
||||||
|
type
|
||||||
|
TextEncoding* = enum
|
||||||
|
Utf8 = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc encode*(frame: object): seq[byte] =
|
||||||
|
## Encodes the frame into a byte sequence using Protobuf serialization.
|
||||||
|
result = Protobuf.encode(frame)
|
||||||
|
|
||||||
|
|
||||||
|
proc decode*[T: object] (bytes: seq[byte], proto: typedesc[
|
||||||
|
T]): Result[T, string] =
|
||||||
|
## Encodes the frame into a byte sequence using Protobuf serialization.
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = ok(Protobuf.decode(bytes, proto))
|
||||||
|
except ProtobufError as e:
|
||||||
|
result = err("Failed to decode payload: " & e.msg)
|
||||||
|
|
||||||
|
|
||||||
|
proc toContentFrame*(frame: TextFrame): ContentFrame =
|
||||||
|
result = ContentFrame(domain: 0, tag: 0, bytes: encode(frame))
|
||||||
|
|
||||||
|
|
||||||
|
proc initTextFrame*(text: string): TextFrame =
|
||||||
|
result = TextFrame(encoding: ord(Utf8), text: text)
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(frame: TextFrame): string =
|
||||||
|
|
||||||
|
result = fmt"TextFrame(encoding:{TextEncoding(frame.encoding)} text:{frame.text})"
|
||||||
|
|
||||||
9
src/content_types/protos/text_frame.proto
Normal file
9
src/content_types/protos/text_frame.proto
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package wap.content_types;
|
||||||
|
|
||||||
|
|
||||||
|
message TextFrame {
|
||||||
|
uint32 encoding = 1;
|
||||||
|
string text = 2;
|
||||||
|
}
|
||||||
@ -7,6 +7,24 @@ import chat_sdk/conversations
|
|||||||
import chat_sdk/delivery/waku_client
|
import chat_sdk/delivery/waku_client
|
||||||
import chat_sdk/utils
|
import chat_sdk/utils
|
||||||
|
|
||||||
|
import content_types/all
|
||||||
|
|
||||||
|
|
||||||
|
const SELF_DEFINED = 99
|
||||||
|
|
||||||
|
type ImageFrame {.proto3.} = object
|
||||||
|
url {.fieldNumber: 1.}: string
|
||||||
|
altText {.fieldNumber: 2.}: string
|
||||||
|
|
||||||
|
|
||||||
|
proc initImage(url: string): ContentFrame =
|
||||||
|
result = ContentFrame(domain: SELF_DEFINED, tag: 0, bytes: encode(ImageFrame(
|
||||||
|
url: url, altText: "This is an image")))
|
||||||
|
|
||||||
|
proc `$`*(frame: ImageFrame): string =
|
||||||
|
result = fmt"ImageFrame(url:{frame.url} alt_text:{frame.altText})"
|
||||||
|
|
||||||
|
|
||||||
proc initLogging() =
|
proc initLogging() =
|
||||||
when defined(chronicles_runtime_filtering):
|
when defined(chronicles_runtime_filtering):
|
||||||
setLogLevel(LogLevel.Debug)
|
setLogLevel(LogLevel.Debug)
|
||||||
@ -14,6 +32,25 @@ proc initLogging() =
|
|||||||
discard setTopicState("waku relay", chronicles.Normal, LogLevel.Error)
|
discard setTopicState("waku relay", chronicles.Normal, LogLevel.Error)
|
||||||
discard setTopicState("chat client", chronicles.Enabled, LogLevel.Debug)
|
discard setTopicState("chat client", chronicles.Enabled, LogLevel.Debug)
|
||||||
|
|
||||||
|
proc getContent(content: ContentFrame): string =
|
||||||
|
notice "GetContent", domain = content.domain, tag = content.tag
|
||||||
|
|
||||||
|
# TODO: Hide this complexity from developers
|
||||||
|
if content.domain == 0:
|
||||||
|
if content.tag == 0:
|
||||||
|
let m = decode(content.bytes, TextFrame).valueOr:
|
||||||
|
raise newException(ValueError, fmt"Badly formed Content (domain:{content.domain} tag:{content.tag})")
|
||||||
|
return fmt"{m}"
|
||||||
|
|
||||||
|
if content.domain == SELF_DEFINED:
|
||||||
|
if content.tag == 0:
|
||||||
|
let m = decode(content.bytes, ImageFrame).valueOr:
|
||||||
|
raise newException(ValueError, fmt"Badly formed Content (domain:{content.domain} tag:{content.tag})")
|
||||||
|
return fmt"{m}"
|
||||||
|
|
||||||
|
raise newException(ValueError, fmt"Unhandled content (domain:{content.domain} tag:{content.tag})")
|
||||||
|
|
||||||
|
|
||||||
proc main() {.async.} =
|
proc main() {.async.} =
|
||||||
|
|
||||||
# Create Configurations
|
# Create Configurations
|
||||||
@ -29,22 +66,24 @@ proc main() {.async.} =
|
|||||||
|
|
||||||
# Start Clients
|
# Start Clients
|
||||||
var saro = newClient("Saro", cfg_saro)
|
var saro = newClient("Saro", cfg_saro)
|
||||||
saro.onNewMessage(proc(convo: Conversation, msg: string) {.async.} =
|
saro.onNewMessage(proc(convo: Conversation, msg: ContentFrame) {.async.} =
|
||||||
echo " Saro <------ :: " & msg
|
echo " Saro <------ :: " & getContent(msg)
|
||||||
await sleepAsync(10000)
|
await sleepAsync(10000)
|
||||||
await convo.sendMessage(saro.ds, "Ping"))
|
await convo.sendMessage(saro.ds, initImage(
|
||||||
|
"https://waku.org/theme/image/logo-black.svg"))
|
||||||
|
)
|
||||||
await saro.start()
|
await saro.start()
|
||||||
|
|
||||||
var raya = newClient("Raya", cfg_raya)
|
var raya = newClient("Raya", cfg_raya)
|
||||||
raya.onNewMessage(proc(convo: Conversation, msg: string) {.async.} =
|
raya.onNewMessage(proc(convo: Conversation, msg: ContentFrame) {.async.} =
|
||||||
echo " ------> Raya :: " & msg
|
echo " ------> Raya :: " & getContent(msg)
|
||||||
await sleepAsync(10000)
|
await sleepAsync(10000)
|
||||||
await convo.sendMessage(raya.ds, "Pong")
|
await convo.sendMessage(raya.ds, initTextFrame("Pong").toContentFrame())
|
||||||
)
|
)
|
||||||
|
|
||||||
raya.onNewConversation(proc(convo: Conversation) {.async.} =
|
raya.onNewConversation(proc(convo: Conversation) {.async.} =
|
||||||
echo " ------> Raya :: New Conversation: " & convo.id()
|
echo " ------> Raya :: New Conversation: " & convo.id()
|
||||||
await convo.sendMessage(raya.ds, "Hello")
|
await convo.sendMessage(raya.ds, initTextFrame("Hello").toContentFrame())
|
||||||
)
|
)
|
||||||
await raya.start()
|
await raya.start()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user