ZIO client

A ZIO-based ShowdownClient. Each method returns a ZIO program and is not effectful in itself.

Connecting to the server

You can create a ZIOShowdownClient from a ZIO-HTTP Client then use ZIOShowdownClient#openConnection to connect to the Pokemon Showdown server.

val zHttpClient: Client = ???
val connectionProgram: ZIO[Scope, ProtocolError, ZIOShowdownConnection] = ZIOShowdownClient(zHttpClient).openConnection()

Note that openConnection only returns a program. It does not perform any effect and need to be run by the ZIO runtime (e.g using a ZIOApp).

Receiving messages

Server-sent messages can be treated as a ZStream using ZIOShowdownConnection#serverMessages.

val connection: ZIOShowdownConnection = ???
val consumeUntilStop: Task[Unit] = connection
  .serverMessages
  .runForeachWhile:
    case Right(GlobalMessage.PrivateMessage(User(sender, _), _, content)) if content.value.equalsIgnoreCase("ping") =>
      for
        _ <- Console.printLine(s"< $sender: Ping!")
        _ <- connection.sendPrivateMessage(sender, ChatContent("Pong!"))
        _ <- Console.printLine(s"> $sender: Pong!")
      yield
        true

    case Right(GlobalMessage.PrivateMessage(User(sender, _), _, content)) if content.value.equalsIgnoreCase("stop") =>
      for
        _ <- Console.printLine(s"< $sender: Received stop command")
        _ <- connection.sendPrivateMessage(sender, ChatContent("Goodbye!"))
      yield
        false

    case Left(error) =>
      for
        _ <- Console.printLine(s"An error occurred: $sender")
      yield
        true

Using ZIO's service pattern

Both ZIOShowdownClient and ZIOShowdownConnection's companion object provide service accessors to be used with ZIO's service pattern.

Here is the example above rewritten using the service pattern:

val connectionProgram: ZIO[ZIOShowdownConnection, Throwable, Unit] =
  ZIOShowdownConnection
    .serverMessages
    .runForeachWhile:
      case Right(GlobalMessage.PrivateMessage(User(sender, _), _, content)) if content.value.equalsIgnoreCase("ping") =>
        for
          _ <- Console.printLine(s"< $sender: Ping!")
          _ <- connection.sendPrivateMessage(sender, ChatContent("Pong!"))
          _ <- Console.printLine(s"> $sender: Pong!")
        yield
          true

      case Right(GlobalMessage.PrivateMessage(User(sender, _), _, content)) if content.value.equalsIgnoreCase("stop") =>
        for
          _ <- Console.printLine(s"< $sender: Received stop command")
          _ <- connection.sendPrivateMessage(sender, ChatContent("Goodbye!"))
        yield
          false

      case Left(error) =>
        for
          _ <- Console.printLine(s"An error occurred: $sender")
        yield
          true

val clientProgram: ZIO[Scope & ZIOShowdownClient, Throwable, Unit] =
  ZIOShowdownClient
    .openConnection()
    .flatMap(connection => connectionProgram.provideSome[ZIOShowdownClient](ZLayer.succeed(connection)))

//Can be used in a ZIOApp
val program: Task[Unit] =
  ZIO.scoped(clientProgram).provide(
    Client.default,
    ZIOShowdownClient.layer
  )

Note: authentication process has been truncated for readability reasons. Check this page instead.