diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6c50812..4fd1c0c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,7 +22,7 @@ jobs: arguments: "-c .scalafmt.conf --test" - uses: jodersky/setup-mill@v0.2.3 with: - mill-version: 0.10.2 + mill-version: 0.10.5 - name: Compile run: mill '__.compile' - name: Test diff --git a/.github/workflows/publish_snapshot.yaml b/.github/workflows/publish_snapshot.yaml index 376f269..e288910 100644 --- a/.github/workflows/publish_snapshot.yaml +++ b/.github/workflows/publish_snapshot.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v2 - uses: jodersky/setup-mill@v0.2.3 with: - mill-version: 0.10.2 + mill-version: 0.10.5 - name: Import GPG key id: import_gpg uses: crazy-max/ghaction-import-gpg@v3 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 351a2ab..0087361 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 - uses: jodersky/setup-mill@v0.2.3 with: - mill-version: 0.10.2 + mill-version: 0.10.5 - name: Import GPG key id: import_gpg uses: crazy-max/ghaction-import-gpg@v3 diff --git a/.mill-version b/.mill-version index 5eef0f1..9028ec6 100644 --- a/.mill-version +++ b/.mill-version @@ -1 +1 @@ -0.10.2 +0.10.5 diff --git a/build.sc b/build.sc index f61093e..81240a1 100644 --- a/build.sc +++ b/build.sc @@ -4,20 +4,18 @@ import publish._ import mill.scalalib._ object Versions { - val zioMagicVersion = "0.3.12" - val zioLoggingVersion = "0.5.14" - val sttpVersion = "3.6.2" - val zioVersion = "1.0.15" - val sttpVersion = "3.6.2" + val zioLoggingVersion = "2.0.1" + val zioVersion = "2.0.0" + val sttpVersion = "3.7.1" val circeVersion = "0.14.2" val pureConfigVersion = "0.17.1" } -val scalaVersions = List("2.12.15", "2.13.8") +val scalaVersions = List("2.12.16", "2.13.8") trait Publishable extends PublishModule { override def artifactName = s"zmatrix" - override def publishVersion = "0.1.2" + override def publishVersion = "0.2.0" override def pomSettings = PomSettings( description = "Matrix.org API client written using ZIO", @@ -39,14 +37,15 @@ class CoreModule(val crossScalaVersion: String) extends CrossScalaModule with Pu override def ivyDeps = Agg( ivy"dev.zio::zio:${zioVersion}", - ivy"io.github.kitlangton::zio-magic:${zioMagicVersion}", ivy"dev.zio::zio-logging:${zioLoggingVersion}", ivy"com.softwaremill.sttp.client3::core:${sttpVersion}", ivy"com.softwaremill.sttp.client3::circe:${sttpVersion}", - ivy"com.softwaremill.sttp.client3::async-http-client-backend-zio1:${sttpVersion}", + ivy"com.softwaremill.sttp.client3::async-http-client-backend-zio:${sttpVersion}", ivy"com.github.pureconfig::pureconfig:${pureConfigVersion}", ivy"io.circe::circe-generic:${circeVersion}", - ivy"io.circe::circe-generic-extras:${circeVersion}" + ivy"io.circe::circe-generic-extras:${circeVersion}", + // https://github.com/com-lihaoyi/mill/issues/1797 + ivy"org.scala-lang:scala-reflect:${crossScalaVersion}" ) override def scalacOptions = Seq( @@ -67,8 +66,8 @@ class CoreModule(val crossScalaVersion: String) extends CrossScalaModule with Pu ivy"dev.zio::zio-test-sbt:${zioVersion}" ) - def testOne(args: String*) = T.command { - super.runMain("org.scalatest.run", args: _*) + def testOne(spec: String, args: String*) = T.command { + super.runMain(spec, args: _*) } def testFramework = "zio.test.sbt.ZTestFramework" @@ -79,6 +78,17 @@ class CoreModule(val crossScalaVersion: String) extends CrossScalaModule with Pu object examples extends Cross[ExamplesModule](scalaVersions: _*) class ExamplesModule(val crossScalaVersion: String) extends CrossScalaModule { val moduleDeps = Seq(core(crossScalaVersion)) + override def scalacOptions = Seq( + "-unchecked", + "-deprecation", + "-language:_", + "-Ywarn-unused", + "-encoding", + "UTF-8", + "-feature", + "-unchecked", + "-Ywarn-dead-code" + ) def mainClass = Some("com.bot4s.zmatrix.Runner") } diff --git a/core/resources/bot.conf b/core/resources/bot.conf index be45d70..48a6427 100644 --- a/core/resources/bot.conf +++ b/core/resources/bot.conf @@ -1,4 +1,5 @@ matrix { api-prefix="/_matrix/client/r0" home-server="https://matrix.org" + user-id="ziobot" } diff --git a/core/src/com/bot4s/zmatrix/MatrixConfiguration.scala b/core/src/com/bot4s/zmatrix/MatrixConfiguration.scala index 4e36c32..d4d5a16 100644 --- a/core/src/com/bot4s/zmatrix/MatrixConfiguration.scala +++ b/core/src/com/bot4s/zmatrix/MatrixConfiguration.scala @@ -1,9 +1,10 @@ package com.bot4s.zmatrix -import zio.{ Has, IO, Layer, Ref, UIO, URIO, ZIO } +import zio.{ IO, Layer, Ref, UIO, URIO, ZIO } import pureconfig.ConfigSource import pureconfig.generic.auto._ import pureconfig.error.ConfigReaderFailures +import zio.ZLayer final case class Config( matrix: MatrixConfigurationContent @@ -29,19 +30,19 @@ object MatrixConfiguration { val DEFAULT_API_PREFIX = "/_matrix/client/r0" val DEFAULT_CONFIG_FILE = "bot.conf" - def get: URIO[Has[MatrixConfiguration], Config] = ZIO.accessM(_.get.get) + def get: URIO[MatrixConfiguration, Config] = ZIO.environmentWithZIO(_.get.get) private def refFromFile(filename: String): IO[ConfigReaderFailures, Ref[Config]] = - ZIO.fromEither(ConfigSource.resources(filename).load[Config]) >>= Ref.make + ZIO.fromEither(ConfigSource.resources(filename).load[Config]).flatMap(e => Ref.make(e)) /** * Create an in-memory configuration that is not persistent. */ - def live(filename: String = DEFAULT_CONFIG_FILE): Layer[ConfigReaderFailures, Has[MatrixConfiguration]] = - refFromFile(filename).map { configRef => + def live(filename: String = DEFAULT_CONFIG_FILE): Layer[ConfigReaderFailures, MatrixConfiguration] = + ZLayer.fromZIO(refFromFile(filename).map { configRef => new MatrixConfiguration { override def get: UIO[Config] = configRef.get } - }.toLayer + }) } diff --git a/core/src/com/bot4s/zmatrix/SyncTokenConfiguration.scala b/core/src/com/bot4s/zmatrix/SyncTokenConfiguration.scala index ee090b2..b53b13b 100644 --- a/core/src/com/bot4s/zmatrix/SyncTokenConfiguration.scala +++ b/core/src/com/bot4s/zmatrix/SyncTokenConfiguration.scala @@ -1,11 +1,12 @@ package com.bot4s.zmatrix -import zio.{ Has, IO, Layer, Managed, Ref, UIO, URIO, ZIO } +import zio.{ IO, Layer, Ref, UIO, URIO, ZIO } import pureconfig.{ ConfigSource, ConfigWriter } import pureconfig.generic.auto._ import pureconfig.error.ConfigReaderFailures import java.io.{ BufferedWriter, File, FileWriter } import com.typesafe.config.ConfigRenderOptions +import zio.ZLayer final case class SyncToken( since: Option[String] = None @@ -19,26 +20,27 @@ trait SyncTokenConfiguration { object SyncTokenConfiguration { val DEFAULT_TOKEN_FILE = "token.conf" - def get: URIO[Has[SyncTokenConfiguration], SyncToken] = ZIO.accessM(_.get.get) - def set(config: SyncToken): URIO[Has[SyncTokenConfiguration], Unit] = ZIO.accessM(_.get.set(config)) + def get: URIO[SyncTokenConfiguration, SyncToken] = ZIO.environmentWithZIO(_.get.get) + def set(config: SyncToken): URIO[SyncTokenConfiguration, Unit] = ZIO.environmentWithZIO(_.get.set(config)) private def refFromFile(filename: String): IO[ConfigReaderFailures, Ref[SyncToken]] = ZIO .fromEither(ConfigSource.file(filename).load[SyncToken]) - .orElse(ZIO.succeed(SyncToken(None))) >>= Ref.make + .orElse(ZIO.succeed(SyncToken(None))) + .flatMap(token => Ref.make(token)) /** * Create an in-memory configuration that is not persistent. * It's not recommended to use this layer as the token will not be persisted * between runs */ - def live(filename: String = DEFAULT_TOKEN_FILE): Layer[ConfigReaderFailures, Has[SyncTokenConfiguration]] = - refFromFile(filename).map { tokenRef => + def live(filename: String = DEFAULT_TOKEN_FILE): Layer[ConfigReaderFailures, SyncTokenConfiguration] = + ZLayer.fromZIO(refFromFile(filename).map { tokenRef => new SyncTokenConfiguration { def get: UIO[SyncToken] = tokenRef.get def set(config: SyncToken): UIO[Unit] = tokenRef.set(config) } - }.toLayer + }) /** * Create a persistent (on disk storage) configuration from the given file. @@ -46,8 +48,8 @@ object SyncTokenConfiguration { */ def persistent( filename: String = DEFAULT_TOKEN_FILE - ): Layer[ConfigReaderFailures, Has[SyncTokenConfiguration]] = - refFromFile(filename).map { configRef => + ): Layer[ConfigReaderFailures, SyncTokenConfiguration] = + ZLayer.fromZIO(refFromFile(filename).map { configRef => new SyncTokenConfiguration { override def get: UIO[SyncToken] = configRef.get override def set(config: SyncToken): UIO[Unit] = { @@ -55,15 +57,17 @@ object SyncTokenConfiguration { val toWrite = ConfigWriter[SyncToken].to(config).render(renderOptions) val updateConf = for { - file <- ZIO.effect(new File(filename)) - managed = Managed.make(ZIO.effect(new BufferedWriter(new FileWriter(file))))(bw => IO.succeed(bw.close)) - _ <- configRef.set(config) - _ <- managed.use(c => IO.effect(c.write(toWrite))) + file <- ZIO.attempt(new File(filename)) + _ <- configRef.set(config) + _ <- + ZIO.acquireReleaseWith(ZIO.attempt(new BufferedWriter(new FileWriter(file))))(bw => + ZIO.succeed(bw.close) + )(c => ZIO.attempt(c.write(toWrite))) } yield () - updateConf.catchAll(_ => IO.succeed(())) + updateConf.catchAll(_ => ZIO.succeed(())) } } - }.toLayer + }) } diff --git a/core/src/com/bot4s/zmatrix/WithAccess.scala b/core/src/com/bot4s/zmatrix/WithAccess.scala index 63eed50..6a3cba5 100644 --- a/core/src/com/bot4s/zmatrix/WithAccess.scala +++ b/core/src/com/bot4s/zmatrix/WithAccess.scala @@ -1,12 +1,12 @@ package com.bot4s.zmatrix -import zio.{ Has, URIO } +import zio.URIO import sttp.client3.RequestT import com.bot4s.zmatrix.services.Authentication trait WithAccess { - def authenticate[U[_], T](request: RequestT[U, T, Any]): URIO[Has[Authentication], RequestT[U, T, Any]] = + def authenticate[U[_], T](request: RequestT[U, T, Any]): URIO[Authentication, RequestT[U, T, Any]] = Authentication.accessToken.flatMap(_.authenticateM(request)) } diff --git a/core/src/com/bot4s/zmatrix/api/Account.scala b/core/src/com/bot4s/zmatrix/api/Account.scala index cdd9168..4b1982b 100644 --- a/core/src/com/bot4s/zmatrix/api/Account.scala +++ b/core/src/com/bot4s/zmatrix/api/Account.scala @@ -6,7 +6,7 @@ import com.bot4s.zmatrix.{ AuthMatrixEnv, MatrixError } trait Account { def whoAmI: ZIO[AuthMatrixEnv, MatrixError, UserResponse] = - (get(Seq("account", "whoami")) >>= authenticate >>= send) >>= as[UserResponse] + sendWithAuth[UserResponse](get(Seq("account", "whoami"))) } object accounts extends Account diff --git a/core/src/com/bot4s/zmatrix/api/DeviceManagement.scala b/core/src/com/bot4s/zmatrix/api/DeviceManagement.scala index d58b1e6..1ce18eb 100644 --- a/core/src/com/bot4s/zmatrix/api/DeviceManagement.scala +++ b/core/src/com/bot4s/zmatrix/api/DeviceManagement.scala @@ -11,7 +11,7 @@ trait DeviceManagement { * Documentation: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-devices */ def getDevices(): ZIO[AuthMatrixEnv, MatrixError, List[Device]] = - (get(Seq("devices")) >>= authenticate >>= send).flatMap(json => as(json)(_.downField("devices").as[List[Device]])) + sendWithAuth(get(Seq("devices")))(_.downField("devices").as[List[Device]]) } object devices extends DeviceManagement; diff --git a/core/src/com/bot4s/zmatrix/api/Login.scala b/core/src/com/bot4s/zmatrix/api/Login.scala index 508a2a9..7c8f40c 100644 --- a/core/src/com/bot4s/zmatrix/api/Login.scala +++ b/core/src/com/bot4s/zmatrix/api/Login.scala @@ -22,8 +22,7 @@ trait Login { ) .deepDropNullValues - val request = postJson(Seq("login"), json) - (request >>= send) >>= as[LoginResponse] + send[LoginResponse](postJson(Seq("login"), json)) } def tokenLogin( @@ -36,8 +35,7 @@ trait Login { "device_id" -> deviceId.map(Json.fromString(_)).getOrElse(Json.Null) ) - val request = postJson(Seq("login"), json) - (request >>= send) >>= as[LoginResponse] + send[LoginResponse](postJson(Seq("login"), json)) } } diff --git a/core/src/com/bot4s/zmatrix/api/RoomCreation.scala b/core/src/com/bot4s/zmatrix/api/RoomCreation.scala index 2c0fd17..5c92d80 100644 --- a/core/src/com/bot4s/zmatrix/api/RoomCreation.scala +++ b/core/src/com/bot4s/zmatrix/api/RoomCreation.scala @@ -13,9 +13,7 @@ trait RoomCreation { * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-createroom */ def createRoom(roomCreation: RoomCreationData): ZIO[AuthMatrixEnv, MatrixError, RoomId] = - (postJson(Seq("createRoom"), roomCreation.asJson.dropNullValues) >>= authenticate >>= send).flatMap(json => - as(json)(_.downField("room_id").as[RoomId]) - ) + sendWithAuth(postJson(Seq("createRoom"), roomCreation.asJson.dropNullValues))(_.downField("room_id").as[RoomId]) } diff --git a/core/src/com/bot4s/zmatrix/api/RoomMembership.scala b/core/src/com/bot4s/zmatrix/api/RoomMembership.scala index fa1adb0..219fd76 100644 --- a/core/src/com/bot4s/zmatrix/api/RoomMembership.scala +++ b/core/src/com/bot4s/zmatrix/api/RoomMembership.scala @@ -5,7 +5,6 @@ import com.bot4s.zmatrix.{ AuthMatrixEnv, MatrixError } import com.bot4s.zmatrix.models.responses._ import com.bot4s.zmatrix.models.RoomId import io.circe.Json -import zio.logging.log import com.bot4s.zmatrix.models.StateDecoder._ trait RoomMembership { @@ -15,82 +14,87 @@ trait RoomMembership { * Documentation:https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-join */ def join(roomId: RoomId): ZIO[AuthMatrixEnv, MatrixError, JoinResponse] = - post(Seq("join", roomId.id)) >>= authenticate >>= send >>= as[JoinResponse] + sendWithAuth[JoinResponse](post(Seq("join", roomId.id))) /** * Get a list of joined rooms * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-joined-rooms */ def joinedRooms(): ZIO[AuthMatrixEnv, MatrixError, List[RoomId]] = - (get(Seq("joined_rooms")) >>= authenticate >>= send).flatMap(json => - as(json)(_.downField("joined_rooms").as[List[RoomId]]) - ) + sendWithAuth[List[RoomId]](get(Seq("joined_rooms")))(_.downField("joined_rooms").as[List[RoomId]]) /** * Invite a user in the given room * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-invite */ def invite(roomId: RoomId, user: String): ZIO[AuthMatrixEnv, MatrixError, Unit] = - postJson( - Seq("rooms", roomId.id, "invite"), - Json.obj("user_id" -> Json.fromString(user)) - ).flatMap(x => authenticate(x).tap(x => log.info(x.toCurl))) >>= send >>= as[Unit] + sendWithAuth[Unit](postJson(Seq("rooms", roomId.id, "invite"), Json.obj("user_id" -> Json.fromString(user)))) /** * Forget the given room * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-forget */ def forget(roomId: RoomId): ZIO[AuthMatrixEnv, MatrixError, Unit] = - post( - Seq("rooms", roomId.id, "forget") - ) >>= authenticate >>= send >>= as[Unit] + sendWithAuth[Unit]( + post( + Seq("rooms", roomId.id, "forget") + ) + ) /** * Leave the given room * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-leave */ def leave(roomId: RoomId): ZIO[AuthMatrixEnv, MatrixError, Unit] = - post( - Seq("rooms", roomId.id, "leave") - ) >>= authenticate >>= send >>= as[Unit] + sendWithAuth[Unit]( + post( + Seq("rooms", roomId.id, "leave") + ) + ) /** * Ban the user from a given room * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-ban */ def ban(roomId: RoomId, user: String, reason: Option[String] = None): ZIO[AuthMatrixEnv, MatrixError, Unit] = - postJson( - Seq("rooms", roomId.id, "ban"), - Json.obj( - "user_id" -> Json.fromString(user), - "reason" -> Json.fromString(reason.getOrElse("")) + sendWithAuth[Unit]( + postJson( + Seq("rooms", roomId.id, "ban"), + Json.obj( + "user_id" -> Json.fromString(user), + "reason" -> Json.fromString(reason.getOrElse("")) + ) ) - ) >>= authenticate >>= send >>= as[Unit] + ) /** * Uban the user from a given room * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-unban */ def unban(roomId: RoomId, user: String): ZIO[AuthMatrixEnv, MatrixError, Unit] = - postJson( - Seq("rooms", roomId.id, "unban"), - Json.obj( - "user_id" -> Json.fromString(user) + sendWithAuth[Unit]( + postJson( + Seq("rooms", roomId.id, "unban"), + Json.obj( + "user_id" -> Json.fromString(user) + ) ) - ) >>= authenticate >>= send >>= as[Unit] + ) /** * Kick the user from a given room * Documentation: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-kick */ def kick(roomId: RoomId, user: String, reason: Option[String] = None): ZIO[AuthMatrixEnv, MatrixError, Unit] = - postJson( - Seq("rooms", roomId.id, "kick"), - Json.obj( - "user_id" -> Json.fromString(user), - "reason" -> Json.fromString(reason.getOrElse("")) + sendWithAuth[Unit]( + postJson( + Seq("rooms", roomId.id, "kick"), + Json.obj( + "user_id" -> Json.fromString(user), + "reason" -> Json.fromString(reason.getOrElse("")) + ) ) - ) >>= authenticate >>= send >>= as[Unit] + ) } diff --git a/core/src/com/bot4s/zmatrix/api/Rooms.scala b/core/src/com/bot4s/zmatrix/api/Rooms.scala index 4373126..5ed9247 100644 --- a/core/src/com/bot4s/zmatrix/api/Rooms.scala +++ b/core/src/com/bot4s/zmatrix/api/Rooms.scala @@ -13,13 +13,15 @@ trait Rooms { * Documentation: https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid */ def sendMsg(roomId: RoomId, eventType: EventType, text: String) = - (putJson( - Seq("rooms", roomId.id, "send", eventType.toString(), UUID.randomUUID().toString()), - Json.obj( - "msgtype" -> Json.fromString("m.text"), - "body" -> Json.fromString(text) + sendWithAuth[EventResponse]( + putJson( + Seq("rooms", roomId.id, "send", eventType.toString(), UUID.randomUUID().toString()), + Json.obj( + "msgtype" -> Json.fromString("m.text"), + "body" -> Json.fromString(text) + ) ) - ) >>= authenticate >>= send) >>= as[EventResponse] + ) } diff --git a/core/src/com/bot4s/zmatrix/api/SessionManagement.scala b/core/src/com/bot4s/zmatrix/api/SessionManagement.scala index 9215c25..0772126 100644 --- a/core/src/com/bot4s/zmatrix/api/SessionManagement.scala +++ b/core/src/com/bot4s/zmatrix/api/SessionManagement.scala @@ -11,14 +11,14 @@ trait SessionManagement { * Documentation: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-logout */ def logout(): ZIO[AuthMatrixEnv, MatrixError, Unit] = - post(Seq("logout")) >>= authenticate >>= send >>= as[Unit] + sendWithAuth[Unit](post(Seq("logout"))) /* * Delete all existing tokens for the user. The current token will be invalidated * Documentation: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-logout-all */ def logoutAll(): ZIO[AuthMatrixEnv, MatrixError, Unit] = - post(Seq("logout", "all")) >>= authenticate >>= send >>= as[Unit] + sendWithAuth[Unit](post(Seq("logout", "all"))) } object sessions extends SessionManagement diff --git a/core/src/com/bot4s/zmatrix/api/Sync.scala b/core/src/com/bot4s/zmatrix/api/Sync.scala index cb322cf..4c4ceeb 100644 --- a/core/src/com/bot4s/zmatrix/api/Sync.scala +++ b/core/src/com/bot4s/zmatrix/api/Sync.scala @@ -8,7 +8,13 @@ import com.bot4s.zmatrix.{ AuthMatrixEnv, MatrixError } trait Sync { def sync: ZIO[AuthMatrixEnv, MatrixError, SyncState] = - (get(Seq("sync")) >>= withSince >>= authenticate >>= send) >>= as[SyncState] + for { + request <- get(Seq("sync")) + request <- withSince(request) + request <- authenticate(request) + result <- send(request) + decoded <- as[SyncState](result) + } yield decoded } diff --git a/core/src/com/bot4s/zmatrix/api/package.scala b/core/src/com/bot4s/zmatrix/api/package.scala index bd0a291..eabea4d 100644 --- a/core/src/com/bot4s/zmatrix/api/package.scala +++ b/core/src/com/bot4s/zmatrix/api/package.scala @@ -1,13 +1,29 @@ package com.bot4s.zmatrix import sttp.client3.Request -import zio.{ Has, ZIO } +import zio.ZIO import com.bot4s.zmatrix.client.{ MatrixClient, MatrixParser, MatrixRequests } +import io.circe.Decoder package object api extends MatrixRequests with WithAccess with MatrixParser { def send[T]( request: Request[MatrixResponse[T], Any] - ): ZIO[Has[MatrixClient], MatrixError, T] = - ZIO.accessM[Has[MatrixClient]](_.get.send(request)) + ): ZIO[MatrixClient, MatrixError, T] = + ZIO.environmentWithZIO[MatrixClient](_.get.send(request)) + + def send[T](action: MatrixAction)(implicit decoder: Decoder[T]): ZIO[MatrixEnv, MatrixError, T] = + for { + req <- action + response <- send(req) + decoded <- as(response)(decoder) + } yield decoded + + def sendWithAuth[T](action: MatrixAction)(implicit decoder: Decoder[T]): ZIO[AuthMatrixEnv, MatrixError, T] = + for { + req <- action + auth <- authenticate(req) + response <- send(auth) + decoded <- as(response)(decoder) + } yield decoded } diff --git a/core/src/com/bot4s/zmatrix/client/MatrixClient.scala b/core/src/com/bot4s/zmatrix/client/MatrixClient.scala index 82776ea..aa735b8 100644 --- a/core/src/com/bot4s/zmatrix/client/MatrixClient.scala +++ b/core/src/com/bot4s/zmatrix/client/MatrixClient.scala @@ -1,12 +1,11 @@ package com.bot4s.zmatrix.client -import zio.logging._ -import sttp.client3.asynchttpclient.zio.{ send => sendRequest, SttpClient } import sttp.client3.{ asBoth, asStringAlways, Request } -import zio.{ Has, IO, URLayer, ZIO, ZLayer } +import zio.{ IO, Task, URLayer, ZIO, ZLayer } import com.bot4s.zmatrix.MatrixError.{ NetworkError, SerializationError } import com.bot4s.zmatrix.{ MatrixError, MatrixResponse } import sttp.client3.{ DeserializationException, HttpError } +import sttp.client3.SttpBackend /** * This service provide the low lever interface with Matrix. @@ -21,27 +20,29 @@ trait MatrixClient { object MatrixClient { - def send[T](request: Request[MatrixResponse[T], Any]): ZIO[Has[MatrixClient], MatrixError, T] = - ZIO.accessM(_.get.send(request)) + def send[T](request: Request[MatrixResponse[T], Any]): ZIO[MatrixClient, MatrixError, T] = + ZIO.environmentWithZIO(_.get.send(request)) - def live: URLayer[SttpClient with Logging, Has[MatrixClient]] = - ZLayer.fromFunction[SttpClient with Logging, MatrixClient] { client => - new MatrixClient { - override def send[T]( - request: Request[MatrixResponse[T], Any] - ): IO[MatrixError, T] = - (for { - _ <- log.debug(request.toCurl) - result <- sendRequest(request.response(asBoth(request.response, asStringAlways))) - .mapError(t => NetworkError(f"Error contacting matrix server: ${t.toString()}", t)) - _ <- log.trace(result.body._2) - json <- ZIO.fromEither(result.body._1).mapError { - case httpError: HttpError[MatrixError] => httpError.body - case deserialisationError: DeserializationException[_] => - SerializationError(deserialisationError.body, deserialisationError.error) - case x => NetworkError(f"Unknown error: ${x.toString()}", x) - } - } yield json).provide(client) - } - } + def live: URLayer[SttpBackend[Task, Any], MatrixClient] = + ZLayer.fromFunction(LiveMatrixClient.apply _) +} + +final case class LiveMatrixClient(backend: SttpBackend[Task, Any]) extends MatrixClient { + + override def send[T]( + request: Request[MatrixResponse[T], Any] + ): IO[MatrixError, T] = + for { + _ <- ZIO.logDebug(request.toCurl) + result <- backend + .send(request.response(asBoth(request.response, asStringAlways))) + .mapError(t => NetworkError(f"Error contacting matrix server: ${t.toString()}", t)) + _ <- ZIO.logTrace(result.body._2) + json <- ZIO.fromEither(result.body._1).mapError { + case httpError: HttpError[MatrixError] => httpError.body + case deserialisationError: DeserializationException[_] => + SerializationError(deserialisationError.body, deserialisationError.error) + case x => NetworkError(f"Unknown error: ${x.toString()}", x) + } + } yield json } diff --git a/core/src/com/bot4s/zmatrix/client/MatrixParser.scala b/core/src/com/bot4s/zmatrix/client/MatrixParser.scala index f517a63..7c0cae5 100644 --- a/core/src/com/bot4s/zmatrix/client/MatrixParser.scala +++ b/core/src/com/bot4s/zmatrix/client/MatrixParser.scala @@ -1,12 +1,12 @@ package com.bot4s.zmatrix.client -import zio.IO +import zio.{ IO, ZIO } import io.circe.{ Decoder, Json } import com.bot4s.zmatrix.MatrixError._ trait MatrixParser { def as[T](json: Json)(implicit decoder: Decoder[T]): IO[SerializationError, T] = - IO.fromEither(json.as[T]).mapError { decoding => + ZIO.fromEither(json.as[T]).mapError { decoding => SerializationError(json.toString, decoding) } } diff --git a/core/src/com/bot4s/zmatrix/client/MatrixRequests.scala b/core/src/com/bot4s/zmatrix/client/MatrixRequests.scala index eb75725..52895e5 100644 --- a/core/src/com/bot4s/zmatrix/client/MatrixRequests.scala +++ b/core/src/com/bot4s/zmatrix/client/MatrixRequests.scala @@ -9,7 +9,7 @@ import sttp.client3._ import com.bot4s.zmatrix._ import com.bot4s.zmatrix.MatrixConfiguration import com.bot4s.zmatrix.MatrixError.ResponseError -import zio.{ Has, URIO, ZIO } +import zio.{ URIO, ZIO } /** * This trait provides all the helper methods related to the queries that must @@ -19,7 +19,7 @@ import zio.{ Has, URIO, ZIO } trait MatrixRequests { type MatrixResponseError = ResponseException[ResponseError, Error] type MatrixResponse[T] = Either[MatrixResponseError, T] - type MatrixAction = URIO[Has[MatrixConfiguration], Request[MatrixResponse[Json], Any]] + type MatrixAction = URIO[MatrixConfiguration, Request[MatrixResponse[Json], Any]] def get(path: Seq[String]): MatrixAction = requestWithPath(Method.GET, path).map(_.response(asJsonEither[ResponseError, Json])) @@ -35,7 +35,7 @@ trait MatrixRequests { def withSince( request: Request[MatrixResponse[Json], Any] - ) = ZIO.accessM[AuthMatrixEnv] { env => + ): URIO[AuthMatrixEnv, Request[MatrixResponse[Json], Any]] = ZIO.environmentWithZIO[AuthMatrixEnv] { env => val config = env.get[SyncTokenConfiguration] config.get.map { config => val uriWithParam = request.uri.addParam("since", config.since) @@ -49,7 +49,7 @@ trait MatrixRequests { */ private def requestWithPath(method: Method, path: Seq[String]) = - ZIO.accessM[Has[MatrixConfiguration]] { config => + ZIO.environmentWithZIO[MatrixConfiguration] { config => config.get.get.map { config => basicRequest .method(method, uri"${config.matrix.apiPath}/$path") diff --git a/core/src/com/bot4s/zmatrix/models/AccessToken.scala b/core/src/com/bot4s/zmatrix/models/AccessToken.scala index f7fcc4e..4d95dbf 100644 --- a/core/src/com/bot4s/zmatrix/models/AccessToken.scala +++ b/core/src/com/bot4s/zmatrix/models/AccessToken.scala @@ -1,13 +1,13 @@ package com.bot4s.zmatrix.models -import zio.UIO +import zio.{ UIO, ZIO } import io.circe.Decoder import io.circe.generic.extras.semiauto.deriveConfiguredDecoder import sttp.client3.RequestT final case class AccessToken(token: String) extends AnyVal { def authenticateM[U[_], T, S](request: RequestT[U, T, S]): UIO[RequestT[U, T, S]] = - UIO.succeed(request.auth.bearer(token)) + ZIO.succeed(request.auth.bearer(token)) } object AccessToken { diff --git a/core/src/com/bot4s/zmatrix/models/RoomCreation.scala b/core/src/com/bot4s/zmatrix/models/RoomCreation.scala index db67d9d..56ed00f 100644 --- a/core/src/com/bot4s/zmatrix/models/RoomCreation.scala +++ b/core/src/com/bot4s/zmatrix/models/RoomCreation.scala @@ -39,6 +39,6 @@ final case class RoomCreationData( object RoomCreationData { implicit val customConfig: Configuration = Configuration.default.withSnakeCaseMemberNames - implicit val fooEncoder: Encoder[RoomCreationData] = deriveConfiguredEncoder - implicit val fooDecoder: Decoder[RoomCreationData] = deriveConfiguredDecoder + implicit val roomCreationDataEncoder: Encoder[RoomCreationData] = deriveConfiguredEncoder + implicit val roomCreationDataDecoder: Decoder[RoomCreationData] = deriveConfiguredDecoder } diff --git a/core/src/com/bot4s/zmatrix/package.scala b/core/src/com/bot4s/zmatrix/package.scala index 2062009..5551f69 100644 --- a/core/src/com/bot4s/zmatrix/package.scala +++ b/core/src/com/bot4s/zmatrix/package.scala @@ -2,22 +2,17 @@ package com.bot4s import com.bot4s.zmatrix.client.{ MatrixClient, MatrixRequests } import com.bot4s.zmatrix.models._ -import zio.{ Has, ZEnv, ZIO } -import zio.logging._ +import zio.ZIO import com.bot4s.zmatrix.services.Authentication package object zmatrix extends MatrixRequests { - type MatrixEnv = ZEnv - with Has[MatrixClient] - with Has[MatrixConfiguration] - with Has[SyncTokenConfiguration] - with Logging - type AuthMatrixEnv = MatrixEnv with Has[Authentication] + type MatrixEnv = MatrixClient with MatrixConfiguration with SyncTokenConfiguration + type AuthMatrixEnv = MatrixEnv with Authentication implicit class ExtendedZIOState[R, E](x: ZIO[R, E, SyncState]) { - def updateState[R1 <: R with Has[SyncTokenConfiguration], E1 >: E]() - : ZIO[R1 with Has[SyncTokenConfiguration], E1, SyncState] = x.tap[R1, E1] { syncState => + def updateState[R1 <: R with SyncTokenConfiguration, E1 >: E]() + : ZIO[R1 with SyncTokenConfiguration, E1, SyncState] = x.tap[R1, E1] { syncState => SyncTokenConfiguration.get.flatMap { config => SyncTokenConfiguration.set(config.copy(since = Some(syncState.nextBatch))) } diff --git a/core/src/com/bot4s/zmatrix/services/Authentication.scala b/core/src/com/bot4s/zmatrix/services/Authentication.scala index 308b30d..08b103f 100644 --- a/core/src/com/bot4s/zmatrix/services/Authentication.scala +++ b/core/src/com/bot4s/zmatrix/services/Authentication.scala @@ -1,10 +1,11 @@ package com.bot4s.zmatrix.services import com.bot4s.zmatrix._ -import zio.{ Has, IO, Ref, UIO, URIO, ZIO } +import zio.{ IO, Ref, UIO, URIO, ZIO } import com.bot4s.zmatrix.models.AccessToken import com.bot4s.zmatrix.api.login import com.bot4s.zmatrix.MatrixError +import zio.ZLayer /** * Authentication service responsible to store and refresh the access token @@ -17,8 +18,8 @@ trait Authentication { object Authentication { - def accessToken: URIO[Has[Authentication], AccessToken] = ZIO.accessM(_.get.accessToken) - def refresh: ZIO[Has[Authentication], MatrixError, AccessToken] = ZIO.accessM(_.get.refresh) + def accessToken: URIO[Authentication, AccessToken] = ZIO.environmentWithZIO(_.get.accessToken) + def refresh: ZIO[Authentication, MatrixError, AccessToken] = ZIO.environmentWithZIO(_.get.refresh) /** * Default implementation for the Authentiction service, it will use the MATRIX_BOT_ACCESS and MATRIX_BOT_PASSWORD @@ -26,34 +27,35 @@ object Authentication { * The refresh method can be used to re-create a token from a password, this can be useful for the first login but * it is not a good idea to create a new access token at each restart, it should be store somewhere safe between runs. */ - val live = ZIO - .accessM[MatrixEnv] { env => - env.get[MatrixConfiguration].get.flatMap { config => - Ref.make(AccessToken(sys.env.getOrElse("MATRIX_BOT_ACCESS", ""))).map { tokenRef => - new Authentication { - def accessToken: UIO[AccessToken] = tokenRef.get + val live = ZLayer.fromZIO( + ZIO + .environmentWithZIO[MatrixEnv] { env => + env.get[MatrixConfiguration].get.flatMap { config => + Ref.make(AccessToken(sys.env.getOrElse("MATRIX_BOT_ACCESS", ""))).map { tokenRef => + new Authentication { + def accessToken: UIO[AccessToken] = tokenRef.get - def refresh: IO[MatrixError, AccessToken] = - (config.matrix.userId, sys.env.get("MATRIX_BOT_PASSWORD")) match { - case (Some(userId), Some(password)) => - login - .passwordLogin( - user = userId, - password = password, - deviceId = config.matrix.deviceId + def refresh: IO[MatrixError, AccessToken] = + (config.matrix.userId, sys.env.get("MATRIX_BOT_PASSWORD")) match { + case (Some(userId), Some(password)) => + login + .passwordLogin( + user = userId, + password = password, + deviceId = config.matrix.deviceId + ) + .flatMap(response => tokenRef.updateAndGet(_ => response.accessToken)) + .provideEnvironment(env) + case (Some(_), _) => + ZIO.fail( + MatrixError.InvalidParameterError("password", "Missing password, please set MATRIX_BOT_PASSWORD") ) - .flatMap(response => tokenRef.updateAndGet(_ => response.accessToken)) - .provide(env) - case (Some(_), _) => - IO.fail( - MatrixError.InvalidParameterError("password", "Missing password, please set MATRIX_BOT_PASSWORD") - ) - case (None, _) => - IO.fail(MatrixError.InvalidParameterError("userId", "user-id is not defined in configuration")) - } + case (None, _) => + ZIO.fail(MatrixError.InvalidParameterError("userId", "user-id is not defined in configuration")) + } + } } } } - } - .toLayer + ) } diff --git a/core/src/com/bot4s/zmatrix/services/Logger.scala b/core/src/com/bot4s/zmatrix/services/Logger.scala deleted file mode 100644 index dd0a61f..0000000 --- a/core/src/com/bot4s/zmatrix/services/Logger.scala +++ /dev/null @@ -1,11 +0,0 @@ -package com.bot4s.zmatrix.services - -import zio.logging.{ LogFormat, LogLevel, Logging } - -object Logger { - - def live(name: String) = Logging.console( - logLevel = LogLevel.Info, - format = LogFormat.ColoredLogFormat() - ) >>> Logging.withRootLoggerName(name) -} diff --git a/core/test/src/com/bot4s/zmatrix/SerializationSpec.scala b/core/test/src/com/bot4s/zmatrix/SerializationSpec.scala index 6745f8f..45fed2e 100644 --- a/core/test/src/com/bot4s/zmatrix/SerializationSpec.scala +++ b/core/test/src/com/bot4s/zmatrix/SerializationSpec.scala @@ -3,11 +3,11 @@ package com.bot4s.zmatrix import zio.test._ import io.circe.syntax._ import zio.test.Assertion._ -import zio.test.DefaultRunnableSpec -import com.bot4s.zmatrix.models.{ Preset, RoomCreationData } import io.circe.Json +import com.bot4s.zmatrix.models.{ Preset, RoomCreationData } + +object SerializationSpec extends ZIOSpecDefault { -object SerializationSpec extends DefaultRunnableSpec { def spec = suite("Serialization")( test("Room Creation") { val json = RoomCreationData( diff --git a/examples/src/com/bot4s/zmatrix/CreateRoom.scala b/examples/src/com/bot4s/zmatrix/CreateRoom.scala index ea7342f..13802b2 100644 --- a/examples/src/com/bot4s/zmatrix/CreateRoom.scala +++ b/examples/src/com/bot4s/zmatrix/CreateRoom.scala @@ -1,19 +1,13 @@ package com.bot4s.zmatrix -import com.bot4s.zmatrix.api.{ accounts, roomCreation, roomMembership, rooms } -import zio.console._ -import zio.{ ExitCode, URIO } -import com.bot4s.zmatrix.models.RoomCreationData -import com.bot4s.zmatrix.models.Preset -import com.bot4s.zmatrix.models.Visibility -import com.bot4s.zmatrix.models.RoomId -import com.bot4s.zmatrix.models.EventType -import zio.logging.log import zio._ +import zio.Console._ +import com.bot4s.zmatrix.api.{ roomCreation, rooms } +import com.bot4s.zmatrix.models.{ EventType, Preset, RoomCreationData, Visibility } -object CreateRoom extends ExampleApp { +object CreateRoom extends ExampleApp[ExitCode] { - override def runExample(args: List[String]): URIO[AuthMatrixEnv, ExitCode] = + override def runExample: URIO[AuthMatrixEnv, ExitCode] = roomCreation .createRoom( RoomCreationData( @@ -26,12 +20,12 @@ object CreateRoom extends ExampleApp { ) ) .flatMap { roomId => - log.info(s"Created room with id $roomId") *> + ZIO.logInfo(s"Created room with id $roomId") *> // roomMembership.invite(roomId, "@exampleUser:matrix.org") *> rooms.sendMsg(roomId, EventType.roomMessages, "Welcome to my room") } - .tapError(e => putStrLn(e.toString())) - .flatMap(x => putStrLn(x.toString())) + .tapError(e => printLineError(e.toString())) + .flatMap(x => printLine(x.toString())) .exitCode } diff --git a/examples/src/com/bot4s/zmatrix/ExampleApp.scala b/examples/src/com/bot4s/zmatrix/ExampleApp.scala index 30d9fca..5fb20cd 100644 --- a/examples/src/com/bot4s/zmatrix/ExampleApp.scala +++ b/examples/src/com/bot4s/zmatrix/ExampleApp.scala @@ -1,32 +1,42 @@ package com.bot4s.zmatrix -import zio.magic._ -import zio.console._ -import com.bot4s.zmatrix.api.{ accounts, roomMembership } -import com.bot4s.zmatrix.models.responses._ +import zio._ import com.bot4s.zmatrix.client.MatrixClient -import zio.{ ExitCode, URIO, ZEnv, ZIO } +import com.bot4s.zmatrix.MatrixError.{ NetworkError, ResponseError } import sttp.client3.asynchttpclient.zio.AsyncHttpClientZioBackend import com.bot4s.zmatrix.services.Authentication -import com.bot4s.zmatrix.services.Logger +import zio.Schedule -trait ExampleApp extends zio.App { +trait ExampleApp[T] extends zio.ZIOAppDefault { - def runExample(args: List[String]): URIO[AuthMatrixEnv, ExitCode] + def runExample: ZIO[AuthMatrixEnv, MatrixError, T] - override def run(args: List[String]): URIO[ZEnv, ExitCode] = runExample(args) - .inject( - ZEnv.live, - Logger.live("matrix-zio-main"), - SyncTokenConfiguration - .persistent() - .mapError(x => new Exception(s"Unable to read token configuration $x")) - .orDie, - MatrixConfiguration.live().mapError(x => new Exception(s"Unable to read configuration $x")).orDie, - Authentication.live, - AsyncHttpClientZioBackend.layer().orDie, - MatrixClient.live - ) + override def run: ZIO[Environment, Any, ExitCode] = + runExample.catchSome { case ResponseError("M_MISSING_TOKEN", _, _) | ResponseError("M_UNKNOWN_TOKEN", _, _) => + for { + _ <- ZIO.logError("Invalid or empty token provided, trying password authentication") + // We want to retry authentication only in case of network error, any other error should terminate the fiber instead + _ <- Authentication.refresh + .tapError(x => ZIO.logError(x.toString())) + .refineOrDie { case x: NetworkError => x } + .retry(Schedule.exponential(1.seconds)) + .tap(token => ZIO.logInfo(token.token)) + program <- runExample + } yield program + } + .tapError(error => ZIO.logError(error.toString())) + .retry(Schedule.forever) + .exitCode + .provide( + SyncTokenConfiguration + .persistent() + .mapError(x => new Exception(s"Unable to read token configuration $x")) + .orDie, + MatrixConfiguration.live().mapError(x => new Exception(s"Unable to read configuration $x")).orDie, + Authentication.live, + AsyncHttpClientZioBackend.layer().orDie, + MatrixClient.live + ) } diff --git a/examples/src/com/bot4s/zmatrix/Runner.scala b/examples/src/com/bot4s/zmatrix/Runner.scala index 9daba20..a1304aa 100644 --- a/examples/src/com/bot4s/zmatrix/Runner.scala +++ b/examples/src/com/bot4s/zmatrix/Runner.scala @@ -1,9 +1,11 @@ package com.bot4s.zmatrix -import zio.console._ -import zio.{ ExitCode, URIO, ZEnv, ZIO } +import zio.Console._ +import zio.{ ExitCode, ZIO } +import zio.Schedule +import zio.ZIOAppArgs -object Runner extends zio.App { +object Runner extends zio.ZIOAppDefault { private def examples = Map( "Simple" -> Simple, @@ -11,14 +13,17 @@ object Runner extends zio.App { "CreateRoom" -> CreateRoom ) - def run(args: List[String]): URIO[ZEnv, ExitCode] = { + override def run: ZIO[Environment with ZIOAppArgs, Any, ExitCode] = { val examplesStr = examples.keySet.mkString(start = "Available examples:\n\t", sep = "\n\t", end = "\n> ") (for { - _ <- putStr(examplesStr) - input <- getStrLn - example <- ZIO.fromOption(examples.get(input)).mapError(_ => new Exception(s"$input does not exist")) - runnable <- example.run(List()) - } yield runnable).orDie + _ <- print(examplesStr) + input <- readLine + example <- ZIO + .fromOption(examples.get(input)) + .mapError(_ => new Exception(s"Example '$input' does not exist")) + .tapError(e => printLineError(e.getMessage())) + runnable <- example.run + } yield runnable).retry(Schedule.forever) } } diff --git a/examples/src/com/bot4s/zmatrix/Simple.scala b/examples/src/com/bot4s/zmatrix/Simple.scala index 7bea488..6e095b8 100644 --- a/examples/src/com/bot4s/zmatrix/Simple.scala +++ b/examples/src/com/bot4s/zmatrix/Simple.scala @@ -1,15 +1,15 @@ package com.bot4s.zmatrix +import zio._ +import zio.Console._ import com.bot4s.zmatrix.api.{ accounts, roomMembership } -import zio.console._ -import zio.{ ExitCode, URIO } -object Simple extends ExampleApp { +object Simple extends ExampleApp[Unit] { - override def runExample(args: List[String]): URIO[AuthMatrixEnv, ExitCode] = - (accounts.whoAmI <*> roomMembership.joinedRooms()) - .tapError(e => putStrLn(e.toString())) - .flatMap(x => putStrLn(x.toString())) - .exitCode + override def runExample: ZIO[AuthMatrixEnv, MatrixError, Unit] = + (accounts.whoAmI *> roomMembership.joinedRooms()) + .tapError(e => printLineError(e.toString())) + .flatMap(x => printLine(x.toString())) + .refineOrDie { case x: MatrixError => x } } diff --git a/examples/src/com/bot4s/zmatrix/SimpleSync.scala b/examples/src/com/bot4s/zmatrix/SimpleSync.scala index dc2706c..50c93ee 100644 --- a/examples/src/com/bot4s/zmatrix/SimpleSync.scala +++ b/examples/src/com/bot4s/zmatrix/SimpleSync.scala @@ -1,48 +1,34 @@ package com.bot4s.zmatrix +import zio._ import com.bot4s.zmatrix.api.{ roomMembership, rooms, sync } -import com.bot4s.zmatrix.MatrixError._ import com.bot4s.zmatrix.models._ import com.bot4s.zmatrix.models.InviteEvent._ import com.bot4s.zmatrix.models.RoomEvent._ -import com.bot4s.zmatrix.services.{ Authentication, Logger } -import com.bot4s.zmatrix.client.MatrixClient -import zio.{ ExitCode, Schedule, URIO, ZEnv, ZIO } -import sttp.client3.asynchttpclient.zio.AsyncHttpClientZioBackend -import zio.magic._ -import zio.duration._ -import zio.logging.log +object SimpleSync extends ExampleApp[Long] { -object SimpleSync extends ExampleApp { - - override def runExample(args: List[String]): URIO[AuthMatrixEnv, ExitCode] = { - lazy val method: ZIO[AuthMatrixEnv, MatrixError, SyncState] = + override def runExample: ZIO[AuthMatrixEnv, MatrixError, Long] = { + val method: ZIO[AuthMatrixEnv, MatrixError, SyncState] = sync.sync.tapInviteEvent { case (roomId, invite: InviteMemberEvent) if invite.content.membership == "invite" => - log.info(f"Joining $roomId") *> roomMembership.join(roomId) - }.tapRoomEvent { case (roomId, x: RoomMessageText) => - log.info(f"${roomId} got: ${x.content.body}") *> ZIO.when(!x.sender.contains("ziobot"))( - rooms.sendMsg(roomId, EventType.roomMessages, "welcome back") + ZIO.logInfo(f"Joining $roomId") *> roomMembership.join(roomId) + }.tapRoomEvent { case (roomId, msg: RoomMessageText) => + val notFromBot = + MatrixConfiguration.get.map(!_.matrix.userId.exists(botName => msg.sender.startsWith(s"@$botName"))) + ZIO.whenZIO(notFromBot)( + ZIO.logInfo(f"${roomId} Message received: ${msg.content.body}") *> + rooms.sendMsg( + roomId, + EventType.roomMessages, + "welcome back" + ) ) }.updateState() - val mainLoop = method + method .repeat(Schedule.spaced(10.seconds)) - mainLoop.catchSome { case ResponseError("M_MISSING_TOKEN", _, _) | ResponseError("M_UNKNOWN_TOKEN", _, _) => - for { - _ <- log.error("Invalid or empty token provided, trying password authentication") - // We want to retry authentication only in case of network error, any other error should terminate the fiber instead - _ <- Authentication.refresh.refineOrDie { case x: NetworkError => x } - .retry(Schedule.exponential(1.seconds)) - .tap(token => log.info(token.token)) - x <- mainLoop - } yield x - } - .tapError(error => log.error(error.toString())) - .retry(Schedule.forever) - .exitCode } }