Open B-R-P opened 4 months ago
I have been trying to do this as well. Here is some code I have been working with:
pub fn start(
req: Request(Connection),
selector: Option(Selector(CustomWebsocketMessage)),
queue_subject: Subject(QueueActorMessage)
) -> Response(ResponseData) {
mist.websocket(
request: req,
on_init: fn(_) {
io.println("New connection initialized")
let state = WebsocketActorState(
name: None,
subject: process.new_subject(),
room_subject: None,
queue_subject: queue_subject
)
#(state, selector)
},
// continued
I want to pass around a subject to other actors so that they will be able to send messages back to the websocket actor through Custom(CustomWebsocketMessage)
. However, everytime I do this, I get an error that looks like:
Actor discarding unexpected message: #(//erl(#Ref<0.1228312467.3287285767.83889>), Custom(Disconnect))
Hey @B-R-P I just wanted to let you know I figured out how to use the Custom
variant of WebsocketMessage
to receive messages from other actors in your application. I will share below:
My main file:
pub fn main() {
let queue_actor = queue_actor.start() // Ignore this its for my own applicaiton
let selector = process.new_selector() // Create a selector in the main process. This will be used to select custom messages (in my case I have a CustomWebsocketMessage type defined elsewhere
let assert Ok(_) = fn(req: Request(Connection)) -> Response(ResponseData) {
case request.path_segments(req) {
["api", "connect"] -> websocket_actor.start(req, selector, queue_actor)
_ -> response.new(404) |> response.set_body(Bytes(bytes_builder.new()))
}
}
|> mist.new
|> mist.port(port)
|> mist.start_http
process.sleep_forever()
}
The /api/connect
route calls the function below to upgrade to ws connection.
pub fn start(
req: Request(Connection),
selector: Selector(CustomWebsocketMessage),
queue_subject: Subject(QueueActorMessage) // Ignore this
) -> Response(ResponseData) {
mist.websocket(
request: req,
on_init: fn(_) {
io.println("New connection initialized")
// Create a new subject for the current websocket process that other actors will be able to send messages to
let ws_subject = process.new_subject()
// Register it to the CustomWebsocketMessage selector
let new_selector = selector
|> process.selecting(ws_subject, function.identity) // Use function.identity since this is the only type I am selecting
let state = WebsocketActorState(
name: None, // Ignore
ws_subject: ws_subject, // Save the subject in the state
room_subject: None, // Ignore
queue_subject: queue_subject // Ignore
)
#(state, Some(new_selector))
},
on_close: fn(state) {
io.println("A connection was closed")
state |> on_shutdown
Nil
},
handler: handle_message
)
}
fn handle_message(
state: WebsocketActorState,
connection: WebsocketConnection,
message: WebsocketMessage(CustomWebsocketMessage)
) -> Next(CustomWebsocketMessage, WebsocketActorState) {
case message {
Custom(message) -> case message {
JoinRoom(room_subject) -> {
// Custom implementation
}
SendMessage(message) -> //...
_ -> state |> actor.continue
}
// ....
}
Hopefully this helps. The mist documentation should be updated to show an example as this was not trivial to figure out.
To answer your main question, if you wanted every client to send and receive messages like in a chat room, you could implement an actor that keeps a List(Subject(CustomwebSocketMessage))
and when the websocket actor receives a message from the client that they want to send a global message, the websocket actor will relay it to the chat room actor which will send the Custom SendMessage
message to every Subject(CustomwebsocketMessage)
in the List.
I think #53 would address the usage of the Custom
message type in an example. Covering things like "topics" or "broadcasting" I think are probably a bit beyond the scope of this library. You could approach them in many different ways, potentially with other libraries (pub-sub, registries, etc).
You don't need to pass in a Selector
to your start
function there. It looks like your code is pretty standard gleam/otp/actor
structure, which is good. I think this is probably just a general lack of documentation / discovery, rather than just a mist
issue.
But I will work on a more thorough example with some basic use-cases around this type of thing.
Yes, this was certainly an issue with lack of documentation (seems to be the only hole in otherwise well documented library). I have managed to implement a chat application that I believe is something like what @B-R-P was thinking of only using mist
.
The repo is: here
People are welcome to reference it to get a better understanding of how to use this library and Gleam in general. Hopefully it helps.
When
handle_ws_message
getmist.Custom
as message. I want to broadcast message to all clients. How can I do it? I want to create public chat (Every client can send and receive)