mirror of
https://github.com/logos-messaging/logos-chat.git
synced 2026-03-03 06:13:24 +00:00
Remove WrappedConvo
This commit is contained in:
parent
85718fefdc
commit
776b726249
162
src/client.nim
162
src/client.nim
@ -12,7 +12,9 @@ import # Foreign
|
||||
tables
|
||||
|
||||
import #local
|
||||
conversations/private_v1,
|
||||
conversation_store,
|
||||
conversation,
|
||||
convo_impl,
|
||||
crypto,
|
||||
identity,
|
||||
inbox,
|
||||
@ -21,6 +23,8 @@ import #local
|
||||
utils,
|
||||
waku_client
|
||||
|
||||
import #to be removed
|
||||
conversations/private_v1
|
||||
|
||||
logScope:
|
||||
topics = "chat client"
|
||||
@ -29,32 +33,16 @@ logScope:
|
||||
# Definitions
|
||||
#################################################
|
||||
|
||||
|
||||
type KeyEntry* = object
|
||||
keyType: string
|
||||
privateKey: PrivateKey
|
||||
timestamp: int64
|
||||
|
||||
|
||||
type
|
||||
SupportedConvoTypes* = Inbox | PrivateV1
|
||||
|
||||
ConvoType* = enum
|
||||
InboxV1Type, PrivateV1Type
|
||||
|
||||
ConvoWrapper* = object
|
||||
case convoType*: ConvoType
|
||||
of InboxV1Type:
|
||||
inboxV1*: Inbox
|
||||
of PrivateV1Type:
|
||||
privateV1*: PrivateV1
|
||||
|
||||
|
||||
type Client* = ref object
|
||||
ident: Identity
|
||||
ds*: WakuClient
|
||||
keyStore: Table[string, KeyEntry] # Keyed by HexEncoded Public Key
|
||||
conversations: Table[string, ConvoWrapper] # Keyed by conversation ID
|
||||
conversations: Table[string, Conversation] # Keyed by conversation ID
|
||||
inboundQueue: QueueRef
|
||||
isRunning: bool
|
||||
|
||||
@ -62,27 +50,29 @@ type Client* = ref object
|
||||
# Constructors
|
||||
#################################################
|
||||
|
||||
proc newClient*(name: string, cfg: WakuConfig): Client =
|
||||
proc newClient*(name: string, cfg: WakuConfig): Client {.raises: [IOError,
|
||||
ValueError, SerializationError].} =
|
||||
## Creates new instance of a `Client` with a given `WakuConfig`
|
||||
let waku = initWakuClient(cfg)
|
||||
|
||||
var q = QueueRef(queue: newAsyncQueue[ChatPayload](10))
|
||||
var c = Client(ident: createIdentity(name),
|
||||
ds: waku,
|
||||
keyStore: initTable[string, KeyEntry](),
|
||||
conversations: initTable[string, ConvoWrapper](),
|
||||
inboundQueue: q,
|
||||
isRunning: false)
|
||||
|
||||
let defaultInbox = initInbox(c.ident.getAddr())
|
||||
c.conversations[conversationIdFor(c.ident.getPubkey(
|
||||
))] = ConvoWrapper(convoType: InboxV1Type, inboxV1: defaultInbox)
|
||||
try:
|
||||
let waku = initWakuClient(cfg)
|
||||
|
||||
|
||||
notice "Client started", client = c.ident.getId(),
|
||||
defaultInbox = defaultInbox
|
||||
result = c
|
||||
var q = QueueRef(queue: newAsyncQueue[ChatPayload](10))
|
||||
var c = Client(ident: createIdentity(name),
|
||||
ds: waku,
|
||||
keyStore: initTable[string, KeyEntry](),
|
||||
conversations: initTable[string, Conversation](),
|
||||
inboundQueue: q,
|
||||
isRunning: false)
|
||||
|
||||
let defaultInbox = initInbox(c.ident.getPubkey())
|
||||
c.conversations[defaultInbox.id()] = defaultInbox
|
||||
|
||||
notice "Client started", client = c.ident.getId(),
|
||||
defaultInbox = defaultInbox
|
||||
result = c
|
||||
except Exception as e:
|
||||
error "newCLient", err = e.msg
|
||||
#################################################
|
||||
# Parameter Access
|
||||
#################################################
|
||||
@ -90,16 +80,19 @@ proc newClient*(name: string, cfg: WakuConfig): Client =
|
||||
proc getId(client: Client): string =
|
||||
result = client.ident.getId()
|
||||
|
||||
proc identity*(client: Client): Identity =
|
||||
result = client.ident
|
||||
|
||||
proc defaultInboxConversationId*(self: Client): string =
|
||||
## Returns the default inbox address for the client.
|
||||
result = conversationIdFor(self.ident.getPubkey())
|
||||
|
||||
proc getConversationFromHint(self: Client,
|
||||
conversationHint: string): Result[Option[ConvoWrapper], string] =
|
||||
conversationHint: string): Result[Option[Conversation], string] =
|
||||
|
||||
# TODO: Implementing Hinting
|
||||
if not self.conversations.hasKey(conversationHint):
|
||||
ok(none(ConvoWrapper))
|
||||
ok(none(Conversation))
|
||||
else:
|
||||
ok(some(self.conversations[conversationHint]))
|
||||
|
||||
@ -134,21 +127,13 @@ proc createIntroBundle*(self: var Client): IntroBundle =
|
||||
# Conversation Initiation
|
||||
#################################################
|
||||
|
||||
proc createPrivateConversation(client: Client, participant: PublicKey,
|
||||
discriminator: string = "default"): Option[ChatError] =
|
||||
## Creates a private conversation with the given participant and discriminator.
|
||||
## Discriminator allows multiple conversations to exist between the same
|
||||
## participants.
|
||||
proc addConversation*(client: Client, convo: Conversation) =
|
||||
notice "Creating conversation", topic = convo.id()
|
||||
client.conversations[convo.id()] = convo
|
||||
|
||||
let convo = initPrivateV1(client.ident, participant, discriminator)
|
||||
|
||||
notice "Creating PrivateV1 conversation", topic = convo.getConvoId()
|
||||
client.conversations[convo.getConvoId()] = ConvoWrapper(
|
||||
convoType: PrivateV1Type,
|
||||
privateV1: convo
|
||||
)
|
||||
|
||||
return some(convo.getConvoId())
|
||||
proc getConversation*(client: Client, convoId: string): Conversation =
|
||||
notice "Get conversation", convoId = convoId
|
||||
result = client.conversations[convoId]
|
||||
|
||||
proc newPrivateConversation*(client: Client,
|
||||
introBundle: IntroBundle): Future[Option[ChatError]] {.async.} =
|
||||
@ -174,68 +159,21 @@ proc newPrivateConversation*(client: Client,
|
||||
let env = wrapEnv(encrypt(InboxV1Frame(invitePrivateV1: invite,
|
||||
recipient: "")), convoId)
|
||||
|
||||
discard createPrivateConversation(client, destPubkey)
|
||||
let convo = initPrivateV1(client.identity(), destPubkey, "default")
|
||||
client.addConversation(convo)
|
||||
|
||||
# TODO: Subscribe to new content topic
|
||||
|
||||
await client.ds.sendPayload(destConvoTopic, env)
|
||||
return none(ChatError)
|
||||
|
||||
proc acceptPrivateInvite(client: Client,
|
||||
invite: InvitePrivateV1): Option[ChatError] =
|
||||
## Allows Recipients to join the conversation.
|
||||
|
||||
notice "ACCEPT PRIVATE Convo ", clientId = client.getId(),
|
||||
fromm = invite.initiator.mapIt(it.toHex(2)).join("")
|
||||
|
||||
let destPubkey = loadPublicKeyFromBytes(invite.initiator).valueOr:
|
||||
raise newException(ValueError, "Invalid public key in intro bundle.")
|
||||
|
||||
discard createPrivateConversation(client, destPubkey)
|
||||
# TODO: Subscribe to new content topic
|
||||
|
||||
result = none(ChatError)
|
||||
|
||||
|
||||
#################################################
|
||||
# Payload Handling
|
||||
#################################################
|
||||
|
||||
proc handleInboxFrame(client: Client, convo: Inbox, bytes: seq[byte]) =
|
||||
## Dispatcher for Incoming `InboxV1Frames`.
|
||||
## Calls further processing depending on the kind of frame.
|
||||
|
||||
let enc = decode(bytes, EncryptedPayload).valueOr:
|
||||
raise newException(ValueError, "Failed to decode payload")
|
||||
|
||||
let frame = convo.decrypt(enc).valueOr:
|
||||
error "Decrypt failed", error = error
|
||||
raise newException(ValueError, "Failed to Decrypt MEssage: " &
|
||||
error)
|
||||
|
||||
case getKind(frame):
|
||||
of typeInvitePrivateV1:
|
||||
notice "Receive PrivateInvite", client = client.getId(),
|
||||
frame = frame.invitePrivateV1
|
||||
discard client.acceptPrivateInvite(frame.invitePrivateV1)
|
||||
|
||||
of typeNote:
|
||||
notice "Receive Note", text = frame.note.text
|
||||
|
||||
proc handlePrivateFrame(client: Client, convo: PrivateV1, bytes: seq[byte]) =
|
||||
## Dispatcher for Incoming `PrivateV1Frames`.
|
||||
## Calls further processing depending on the kind of frame.
|
||||
let enc = decode(bytes, EncryptedPayload).get() # TODO: handle result
|
||||
let frame = convo.decrypt(enc) # TODO: handle result
|
||||
|
||||
case frame.getKind():
|
||||
of typeContentFrame:
|
||||
notice "Got Mail", client = client.getId(),
|
||||
text = frame.content.bytes.toUtfString()
|
||||
of typePlaceholder:
|
||||
notice "Got Placeholder", client = client.getId(),
|
||||
text = frame.placeholder.counter
|
||||
|
||||
proc parseMessage(client: Client, msg: ChatPayload) =
|
||||
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
|
||||
@ -243,7 +181,7 @@ proc parseMessage(client: Client, msg: ChatPayload) =
|
||||
let envelope = decode(msg.bytes, WapEnvelopeV1).valueOr:
|
||||
raise newException(ValueError, "Failed to decode WapEnvelopeV1: " & error)
|
||||
|
||||
let wrappedConvo = block:
|
||||
let convo = block:
|
||||
let opt = client.getConversationFromHint(envelope.conversationHint).valueOr:
|
||||
raise newException(ValueError, "Failed to get conversation: " & error)
|
||||
|
||||
@ -255,12 +193,11 @@ proc parseMessage(client: Client, msg: ChatPayload) =
|
||||
hint = envelope.conversationHint, knownIds = k
|
||||
return
|
||||
|
||||
case wrappedConvo.convoType:
|
||||
of InboxV1Type:
|
||||
client.handleInboxFrame(wrappedConvo.inboxV1, envelope.payload)
|
||||
try:
|
||||
convo.handleFrame(client, envelope.payload)
|
||||
except Exception as e:
|
||||
error "HandleFrame Failed", error = e.msg
|
||||
|
||||
of PrivateV1Type:
|
||||
client.handlePrivateFrame(wrappedConvo.privateV1, envelope.payload)
|
||||
|
||||
proc addMessage*(client: Client, convo: PrivateV1,
|
||||
text: string = "") {.async.} =
|
||||
@ -303,9 +240,10 @@ proc simulateMessages(client: Client){.async.} =
|
||||
for a in 1..5:
|
||||
await sleepAsync(4.seconds)
|
||||
|
||||
for convoWrapper in client.conversations.values():
|
||||
if convoWrapper.convoType == PrivateV1Type:
|
||||
await client.addMessage(convoWrapper.privateV1, fmt"message: {a}")
|
||||
|
||||
for conversation in client.conversations.values():
|
||||
if conversation of PrivateV1:
|
||||
await client.addMessage(PrivateV1(conversation), fmt"message: {a}")
|
||||
|
||||
#################################################
|
||||
# Control Functions
|
||||
|
||||
17
src/conversation.nim
Normal file
17
src/conversation.nim
Normal file
@ -0,0 +1,17 @@
|
||||
# import conversations/private_v1
|
||||
import strformat
|
||||
|
||||
type
|
||||
ConvoTypes* = enum
|
||||
InboxV1Type, PrivateV1Type
|
||||
|
||||
type
|
||||
Conversation* = ref object of RootObj
|
||||
name: string
|
||||
|
||||
proc `$`(conv: Conversation): string =
|
||||
fmt"Convo: {conv.name}"
|
||||
|
||||
method id*(self: Conversation): string {.raises: [Defect].} =
|
||||
raise newException(Defect, "Abstract function")
|
||||
|
||||
16
src/conversation_store.nim
Normal file
16
src/conversation_store.nim
Normal file
@ -0,0 +1,16 @@
|
||||
import std/[options, times]
|
||||
import conversation
|
||||
import crypto
|
||||
import identity
|
||||
|
||||
type ConvoId = string
|
||||
|
||||
|
||||
|
||||
|
||||
type
|
||||
ConversationStore* = concept
|
||||
proc addConversation(self: Self, convo: Conversation)
|
||||
proc getConversation(self: Self, convoId: string): Conversation
|
||||
proc identity(self: Self): Identity
|
||||
proc getId(self: Self): string
|
||||
@ -7,6 +7,8 @@ import std/algorithm
|
||||
import sugar
|
||||
|
||||
import ../[
|
||||
conversation,
|
||||
conversation_store,
|
||||
crypto,
|
||||
identity,
|
||||
proto_types,
|
||||
@ -14,8 +16,10 @@ import ../[
|
||||
waku_client
|
||||
]
|
||||
|
||||
|
||||
|
||||
type
|
||||
PrivateV1* = object
|
||||
PrivateV1* = ref object of Conversation
|
||||
# Placeholder for PrivateV1 conversation type
|
||||
owner: Identity
|
||||
topic: string
|
||||
@ -70,3 +74,20 @@ proc sendMessage*(self: PrivateV1, ds: WakuClient,
|
||||
|
||||
discard ds.sendPayload(self.getTopic(), encryptedBytes.toEnvelope(
|
||||
self.getConvoId()))
|
||||
|
||||
method id*(self: PrivateV1): string =
|
||||
return getConvoIdRaw(self.participants, self.discriminator)
|
||||
|
||||
proc handleFrame*[T: ConversationStore](convo: PrivateV1, client: T,
|
||||
bytes: seq[byte]) =
|
||||
## Dispatcher for Incoming `PrivateV1Frames`.
|
||||
## Calls further processing depending on the kind of frame.
|
||||
|
||||
let enc = decode(bytes, EncryptedPayload).get() # TODO: handle result
|
||||
let frame = convo.decrypt(enc) # TODO: handle result
|
||||
|
||||
case frame.getKind():
|
||||
of typeContentFrame:
|
||||
notice "Got Mail", text = frame.content.bytes.toUtfString()
|
||||
of typePlaceholder:
|
||||
notice "Got Placeholder", text = frame.placeholder.counter
|
||||
|
||||
30
src/convo_impl.nim
Normal file
30
src/convo_impl.nim
Normal file
@ -0,0 +1,30 @@
|
||||
import conversation_store
|
||||
import conversation
|
||||
|
||||
import inbox
|
||||
import conversations/private_v1
|
||||
|
||||
|
||||
proc getType(convo: Conversation): ConvoTypes =
|
||||
if convo of Inbox:
|
||||
return InboxV1Type
|
||||
|
||||
elif convo of PrivateV1:
|
||||
return PrivateV1Type
|
||||
|
||||
else:
|
||||
raise newException(Defect, "Conversation Type not processed")
|
||||
|
||||
proc handleFrame*[T: ConversationStore](convo: Conversation, client: T,
|
||||
bytes: seq[byte]) =
|
||||
|
||||
case convo.getType():
|
||||
of InboxV1Type:
|
||||
let inbox = Inbox(convo)
|
||||
inbox.handleFrame(client, bytes)
|
||||
|
||||
of PrivateV1Type:
|
||||
let priv = PrivateV1(convo)
|
||||
priv.handleFrame(client, bytes)
|
||||
|
||||
|
||||
@ -1,20 +1,30 @@
|
||||
import
|
||||
chronicles,
|
||||
chronos,
|
||||
results
|
||||
results,
|
||||
strformat
|
||||
|
||||
import
|
||||
conversation,
|
||||
conversations/private_v1,
|
||||
conversation_store,
|
||||
crypto,
|
||||
proto_types,
|
||||
utils
|
||||
|
||||
type
|
||||
Inbox* = object
|
||||
Inbox* = ref object of Conversation
|
||||
pubkey: PublicKey
|
||||
inbox_addr: string
|
||||
|
||||
proc initInbox*(inbox_addr: string): Inbox =
|
||||
|
||||
proc `$`*(conv: Inbox): string =
|
||||
fmt"Inbox: addr->{conv.inbox_addr}"
|
||||
|
||||
|
||||
proc initInbox*(pubkey: PublicKey): Inbox =
|
||||
## Initializes an Inbox object with the given address and invite callback.
|
||||
return Inbox(inbox_addr: inbox_addr)
|
||||
return Inbox(pubkey: pubkey)
|
||||
|
||||
proc encrypt*(frame: InboxV1Frame): EncryptedPayload =
|
||||
return encrypt_plain(frame)
|
||||
@ -43,3 +53,41 @@ proc conversation_id_for*(pubkey: PublicKey): string =
|
||||
# TODO derive this from instance of Inbox
|
||||
proc topic_inbox*(client_addr: string): string =
|
||||
return "/inbox/" & client_addr
|
||||
|
||||
method id*(convo: Inbox): string =
|
||||
return conversation_id_for(convo.pubkey)
|
||||
|
||||
|
||||
#################################################
|
||||
# Conversation Creation
|
||||
#################################################
|
||||
|
||||
proc createPrivateV1FromInvite*[T: ConversationStore](client: T,
|
||||
invite: InvitePrivateV1) =
|
||||
|
||||
let destPubkey = loadPublicKeyFromBytes(invite.initiator).valueOr:
|
||||
raise newException(ValueError, "Invalid public key in intro bundle.")
|
||||
|
||||
let convo = initPrivateV1(client.identity(), destPubkey, "default")
|
||||
|
||||
notice "Creating PrivateV1 conversation", topic = convo.getConvoId()
|
||||
client.addConversation(convo)
|
||||
|
||||
proc handleFrame*[T: ConversationStore](convo: Inbox, client: T, bytes: seq[byte]) =
|
||||
## Dispatcher for Incoming `InboxV1Frames`.
|
||||
## Calls further processing depending on the kind of frame.
|
||||
|
||||
let enc = decode(bytes, EncryptedPayload).valueOr:
|
||||
raise newException(ValueError, "Failed to decode payload")
|
||||
|
||||
let frame = convo.decrypt(enc).valueOr:
|
||||
error "Decrypt failed", error = error
|
||||
raise newException(ValueError, "Failed to Decrypt MEssage: " &
|
||||
error)
|
||||
|
||||
case getKind(frame):
|
||||
of typeInvitePrivateV1:
|
||||
createPrivateV1FromInvite(client, frame.invitePrivateV1)
|
||||
|
||||
of typeNote:
|
||||
notice "Receive Note", text = frame.note.text
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user