simplex_bot_actor/src/simplex_bot_actor/websockets.nim

74 lines
2.5 KiB
Nim

# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, json]
import preserves, preserves/jsonhooks
import syndicate, syndicate/actors
import ws
type JsonWebsocketAssertion* {.preservesRecord: "json-websocket".} = object
url: string
dataspace: Cap
type
SendJson* {.preservesRecord: "send".} = object
data: JsonNode
RecvJson* {.preservesRecord: "recv".} = object
data: JsonNode
proc spawnWebsocketJsonActor*(turn: var Turn; ds: Cap): Actor {.discardable.} =
## Spawn an actor that responds to observations of
## `<json-websocket @url string @dataspace #!Ref>`
## by connecting to Websocket urls and publishing dataspaces
## that carry messages to and from the Websocket endpoint.
spawn("json-websocket-actor", turn) do (turn: var Turn):
during(turn, ds, ?Observe(pattern: !JsonWebsocketAssertion) ?? {0: grabLit()}) do (url: string):
var ws: WebSocket
newWebSocket(url).addCallback(turn) do (turn: var Turn; sock: WebSocket):
ws = sock
let
facet = turn.facet
messageSpace = newDataspace(turn)
handle = publish(turn, ds,
JsonWebsocketAssertion(url: url, dataspace: messageSpace))
onStop(facet) do (turn: var Turn):
close(ws)
var fut: Future[(Opcode, string)]
proc recvMessage() {.gcsafe.} =
fut = receivePacket ws
addCallback(fut, facet) do (turn: var Turn):
let (opcode, data) = read fut
case opcode
of Text:
message(turn, messageSpace,
RecvJson(data: data.parseJson))
of Binary:
message(turn, messageSpace,
initRecord("recv", cast[seq[byte]](data).toPreserve))
of Ping:
asyncCheck(turn, ws.send(data, Pong))
of Pong, Cont:
discard
of Close:
stderr.writeLine "closed connection with ", url
retract(turn, handle)
stop(turn)
return
recvMessage()
recvMessage()
onMessage(turn, messageSpace, ?SendJson) do (data: JsonNode):
asyncCheck(turn, ws.send($data, Text))
do:
close(ws)
when isMainModule:
# Run as an independent component.
type Args {.preservesDictionary.} = object
dataspace: Cap
url: string
runActor("websocket-json-actor") do (root: Cap; turn: var Turn):
connectStdio(root, turn)
spawnWebsocketJsonActor(turn, root)