slack.realtime.Rtm.scala Maven / Gradle / Ivy
The newest version!
package slack.realtime
import io.circe.Json
import io.circe.parser._
import io.circe.syntax._
import slack.api.realtime
import slack.realtime.models.{ Hello, OutboundMessage, SlackEvent }
import slack.{ SlackError, SlackException }
import sttp.client.ws.WebSocket
import sttp.model.ws.WebSocketFrame
import zio._
import zio.stream.{ Take, ZStream }
trait Rtm {
val rtm: Rtm.Service[Any]
}
object Rtm {
type MessageStream = ZStream[Any, SlackError, SlackEvent]
def parseMessage(message: String): IO[io.circe.Error, SlackEvent] =
IO.fromEither(parse(message).flatMap { json =>
for {
message <- json.as[SlackEvent]
} yield message
})
trait Service[R] {
private def readMessage(ws: WebSocket[Task]): ZIO[Any, Throwable, Take[Nothing, SlackEvent]] =
ws.receiveText().flatMap(_.fold(_ => ZIO.succeed(Take.End), value => parseMessage(value).map(Take.Value(_))))
private val openAndHandshake: ZIO[SlackRealtimeEnv, SlackError, WebSocket[Task]] = for {
ws <- realtime.openWebsocket
// After the socket has been opened the first message we expect is the "hello" message
// Chew that off the front of the socket, if we don't receive it we should return an exception
_ <- readMessage(ws).filterOrFail {
case Take.Value(Hello(_)) => true
case _ => false
}(SlackException.ProtocolError("Protocol error did not receive hello as first message"))
} yield ws
def connect[R0 <: R, E1 >: SlackError](
outbound: ZStream[R0, E1, OutboundMessage]
): ZManaged[R0 with SlackRealtimeEnv, E1, MessageStream] =
for {
ws <- openAndHandshake.toManaged_
// Reads the messages being sent from the caller and buffers them while we wait to send them
// to slack
_ <- (for {
queue <- outbound.toQueueUnbounded[E1, OutboundMessage]
_ <- ZStream.fromQueue(queue).forever.unTake.zipWithIndex.foreachManaged {
case (event, idx) =>
ws.send(WebSocketFrame.text(event.asJson.deepMerge(Json.obj("id" -> idx.asJson)).noSpaces))
}
} yield ()).fork
// We set up the stream to begin receiving text messages
// We map each of the events into a Take to model the possible end of the stream
receive = ZStream
.fromEffect(readMessage(ws))
.forever
.unTake
} yield receive
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy