Skip to content

Instantly share code, notes, and snippets.

Created May 10, 2024 17:01
Show Gist options
  • Save adamw/8eba6ae90dd497bcddccc55e7083c50d to your computer and use it in GitHub Desktop.
Save adamw/8eba6ae90dd497bcddccc55e7083c50d to your computer and use it in GitHub Desktop.
//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.10.7
import ox.channels.{Actor, ActorRef, Channel, ChannelClosed, Default, DefaultResult, selectOrClosed}
import ox.{fork, releaseAfterScope, supervised}
import sttp.tapir.*
import sttp.tapir.CodecFormat.*
import sttp.tapir.server.netty.sync.{Id, NettySyncServer, OxStreams}
import java.util.UUID
type ChatMemberId = UUID
case class ChatMember(id: ChatMemberId, channel: Channel[Message])
object ChatMember:
def create: ChatMember = ChatMember(UUID.randomUUID(), Channel.bufferedDefault[Message])
class ChatRoom:
private var members: Map[ChatMemberId, ChatMember] = Map()
def connected(m: ChatMember): Unit =
members = members + ( -> m)
println(s"Connected: ${}, number of members: ${members.size}")
def disconnected(m: ChatMember): Unit =
members = members -
println(s"Disconnected: ${}, number of members: ${members.size}")
def incoming(message: Message): Unit =
println(s"Broadcasting: ${message.v}")
members = members.flatMap { (id, member) =>
selectOrClosed(, Default(())) match
case => Some((id, member))
case _: ChannelClosed =>
println(s"Channel of member $id closed, removing from members")
case DefaultResult(_) =>
println(s"Buffer for member $id full, not sending message")
Some((id, member))
case class Message(v: String) // could be more complex, e.g. JSON including nickname + message
given Codec[String, Message, TextPlain] =
val chatEndpoint = endpoint.get
.out(webSocketBody[Message, TextPlain, Message, TextPlain](OxStreams))
def chatProcessor(a: ActorRef[ChatRoom]): OxStreams.Pipe[Message, Message] =
incoming => {
val member = ChatMember.create
fork {
incoming.foreach { msg =>
releaseAfterScope {
@main def chatWsServer(): Unit =
supervised {
val chatActor = Actor.create(new ChatRoom)
val chatServerEndpoint = chatEndpoint.serverLogicSuccess[Id](_ => chatProcessor(chatActor))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment