mirror of
https://github.com/logos-messaging/nim-chat-poc.git
synced 2026-01-07 00:23:08 +00:00
Add dynamic contentFrame
This commit is contained in:
parent
87adb3b5d0
commit
f595ff554d
@ -51,7 +51,7 @@ type Client* = ref object
|
||||
inboundQueue: QueueRef
|
||||
isRunning: bool
|
||||
|
||||
newMessageCallbacks: seq[MessageCallback[string]]
|
||||
newMessageCallbacks: seq[MessageCallback[ContentFrame]]
|
||||
newConvoCallbacks: seq[NewConvoCallback]
|
||||
|
||||
#################################################
|
||||
@ -115,12 +115,13 @@ proc listConversations*(client: Client): seq[Conversation] =
|
||||
# Callback Handling
|
||||
#################################################
|
||||
|
||||
proc onNewMessage*(client: Client, callback: MessageCallback[string]) =
|
||||
proc onNewMessage*(client: Client, callback: MessageCallback[ContentFrame]) =
|
||||
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:
|
||||
discard cb(convo, msg)
|
||||
discard cb(convo, content)
|
||||
|
||||
proc onNewConversation*(client: Client, callback: NewConvoCallback) =
|
||||
client.newConvoCallbacks.add(callback)
|
||||
|
||||
@ -3,6 +3,7 @@ import std/[options, times]
|
||||
import ./conversations/convo_type
|
||||
import crypto
|
||||
import identity
|
||||
import proto_types
|
||||
|
||||
type ConvoId = string
|
||||
|
||||
@ -13,4 +14,5 @@ type
|
||||
proc identity(self: Self): Identity
|
||||
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 strutils
|
||||
|
||||
import ../proto_types
|
||||
import ../delivery/waku_client
|
||||
import ../utils
|
||||
|
||||
@ -24,6 +25,6 @@ method id*(self: Conversation): string {.raises: [Defect, ValueError].} =
|
||||
panic("ProgramError: Missing concrete implementation")
|
||||
|
||||
method sendMessage*(convo: Conversation, ds: WakuClient,
|
||||
text: string) {.async, base, gcsafe.} =
|
||||
content_frame: ContentFrame) {.async, base, gcsafe.} =
|
||||
# TODO: make this a compile time check
|
||||
panic("ProgramError: Missing concrete implementation")
|
||||
|
||||
@ -91,17 +91,18 @@ proc handleFrame*[T: ConversationStore](convo: PrivateV1, client: T,
|
||||
case frame.getKind():
|
||||
of typeContentFrame:
|
||||
# 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:
|
||||
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:
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
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"
|
||||
|
||||
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/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() =
|
||||
when defined(chronicles_runtime_filtering):
|
||||
setLogLevel(LogLevel.Debug)
|
||||
@ -14,6 +32,25 @@ proc initLogging() =
|
||||
discard setTopicState("waku relay", chronicles.Normal, LogLevel.Error)
|
||||
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.} =
|
||||
|
||||
# Create Configurations
|
||||
@ -29,22 +66,24 @@ proc main() {.async.} =
|
||||
|
||||
# Start Clients
|
||||
var saro = newClient("Saro", cfg_saro)
|
||||
saro.onNewMessage(proc(convo: Conversation, msg: string) {.async.} =
|
||||
echo " Saro <------ :: " & msg
|
||||
saro.onNewMessage(proc(convo: Conversation, msg: ContentFrame) {.async.} =
|
||||
echo " Saro <------ :: " & getContent(msg)
|
||||
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()
|
||||
|
||||
var raya = newClient("Raya", cfg_raya)
|
||||
raya.onNewMessage(proc(convo: Conversation, msg: string) {.async.} =
|
||||
echo " ------> Raya :: " & msg
|
||||
raya.onNewMessage(proc(convo: Conversation, msg: ContentFrame) {.async.} =
|
||||
echo " ------> Raya :: " & getContent(msg)
|
||||
await sleepAsync(10000)
|
||||
await convo.sendMessage(raya.ds, "Pong")
|
||||
await convo.sendMessage(raya.ds, initTextFrame("Pong").toContentFrame())
|
||||
)
|
||||
|
||||
raya.onNewConversation(proc(convo: Conversation) {.async.} =
|
||||
echo " ------> Raya :: New Conversation: " & convo.id()
|
||||
await convo.sendMessage(raya.ds, "Hello")
|
||||
await convo.sendMessage(raya.ds, initTextFrame("Hello").toContentFrame())
|
||||
)
|
||||
await raya.start()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user