From 4117e91900fd5468ee1bdd5668959efacf3eb60f Mon Sep 17 00:00:00 2001 From: Mike Limansky Date: Sat, 6 Dec 2025 16:40:02 +0300 Subject: [PATCH 1/3] Migrate to sttp 4 --- build.sc | 17 +++++------ .../com/bot4s/telegram/cats/TelegramBot.scala | 8 +++--- .../telegram/clients/FutureSttpClient.scala | 4 +-- .../bot4s/telegram/clients/SttpClient.scala | 21 +++++++------- .../telegram/clients/SttpClientSuite.scala | 8 +++--- examples/src-cats/CommandsBot.scala | 6 ++-- examples/src-cats/EchoBot.scala | 6 +++- examples/src-cats/ExampleBot.scala | 7 ----- examples/src-cats/Launcher.scala | 28 +++++++++++++------ examples/src-cats3/CommandsBot.scala | 6 ++-- examples/src-cats3/EchoBot.scala | 4 ++- examples/src-cats3/ExampleBot.scala | 7 ----- examples/src-cats3/Launcher.scala | 5 ++-- examples/src-monix/EchoBot.scala | 4 ++- examples/src-monix/ExampleBot.scala | 6 ---- examples/src-monix/Launcher.scala | 3 +- examples/src-zio/CommandsBot.scala | 6 ++-- examples/src-zio/CommandsWebhookBot.scala | 7 +++-- examples/src-zio/EchoBot.scala | 5 +++- examples/src-zio/ExampleBot.scala | 8 ------ examples/src-zio/Launcher.scala | 10 ++++--- examples/src/CovfefeBot.scala | 2 +- examples/src/ExampleBot.scala | 8 +++--- 23 files changed, 96 insertions(+), 90 deletions(-) delete mode 100644 examples/src-cats/ExampleBot.scala delete mode 100644 examples/src-cats3/ExampleBot.scala delete mode 100644 examples/src-monix/ExampleBot.scala delete mode 100644 examples/src-zio/ExampleBot.scala diff --git a/build.sc b/build.sc index d8d1a7a4..c80cb098 100644 --- a/build.sc +++ b/build.sc @@ -16,7 +16,7 @@ object library { val catsEffect3 = "3.6.2" val zhttp = "2.0.0-RC10" val zioInteropCats = "23.1.0.5" - val sttp = "3.11.0" + val sttp = "4.0.13" val scalaTest = "3.2.19" val scalaMockScalaTest = "7.4.0" val scalaLogging = "3.9.5" @@ -38,14 +38,15 @@ object library { val akkaTestkit = mvn"com.typesafe.akka::akka-testkit::${Version.akkaTestkit}" val akkaActor = mvn"com.typesafe.akka::akka-actor::${Version.akkaActor}" val akkaStream = mvn"com.typesafe.akka::akka-stream::${Version.akkaStream}" + val asyncHttpClientBackendCats = - mvn"com.softwaremill.sttp.client3::async-http-client-backend-cats-ce2::${Version.sttp}" + mvn"com.softwaremill.sttp.client4::fs2ce2::${Version.sttp}" val asyncHttpClientBackendCats3 = - mvn"com.softwaremill.sttp.client3::async-http-client-backend-cats::${Version.sttp}" + mvn"com.softwaremill.sttp.client4::cats::${Version.sttp}" val asyncHttpClientBackendZio = - mvn"com.softwaremill.sttp.client3::async-http-client-backend-zio::${Version.sttp}" + mvn"com.softwaremill.sttp.client4::zio::${Version.sttp}" - val asyncHttpClientBackendMonix = mvn"com.softwaremill.sttp.client3::async-http-client-backend-monix::${Version.sttp}" + val asyncHttpClientBackendMonix = mvn"com.softwaremill.sttp.client4::monix::${Version.sttp}" val scalajHttp = mvn"org.scalaj::scalaj-http::${Version.scalajHttp}" val scalaLogging = mvn"com.typesafe.scala-logging::scala-logging::${Version.scalaLogging}" val scalaMockScalaTest = mvn"org.scalamock::scalamock::${Version.scalaMockScalaTest}" @@ -64,9 +65,9 @@ object library { val catsEffect = mvn"org.typelevel::cats-effect::${Version.catsEffect}" val catsEffect3 = mvn"org.typelevel::cats-effect::${Version.catsEffect3}" val monix = mvn"io.monix::monix::${Version.monix}" - val sttpCore = mvn"com.softwaremill.sttp.client3::core::${Version.sttp}" - val sttpCirce = mvn"com.softwaremill.sttp.client3::circe::${Version.sttp}" - val sttpOkHttp = mvn"com.softwaremill.sttp.client3::okhttp-backend::${Version.sttp}" + val sttpCore = mvn"com.softwaremill.sttp.client4::core::${Version.sttp}" + val sttpCirce = mvn"com.softwaremill.sttp.client4::circe::${Version.sttp}" + val sttpOkHttp = mvn"com.softwaremill.sttp.client4::okhttp-backend::${Version.sttp}" val hammock = mvn"com.pepegar::hammock-core::${Version.hammock}" val zio = mvn"dev.zio::zio::${Version.zio}" val zhttp = mvn"io.d11::zhttp::${Version.zhttp}" diff --git a/core/src/com/bot4s/telegram/cats/TelegramBot.scala b/core/src/com/bot4s/telegram/cats/TelegramBot.scala index 8593511c..526df307 100644 --- a/core/src/com/bot4s/telegram/cats/TelegramBot.scala +++ b/core/src/com/bot4s/telegram/cats/TelegramBot.scala @@ -3,17 +3,17 @@ package com.bot4s.telegram.cats import cats.MonadError import com.bot4s.telegram.api.BotBase import com.bot4s.telegram.clients.SttpClient -import sttp.client3.SttpBackend +import sttp.client4.Backend class TelegramBot[F[_]]( token: String, - backend: SttpBackend[F, Any], + backend: Backend[F], telegramHost: String = "api.telegram.org" )(implicit monadError: MonadError[F, Throwable]) extends BotBase[F] { override val monad = monadError - implicit private val b: SttpBackend[F, Any] = backend - val client = new SttpClient[F](token, telegramHost) + implicit private val b: Backend[F] = backend + val client = new SttpClient[F](token, telegramHost) } diff --git a/core/src/com/bot4s/telegram/clients/FutureSttpClient.scala b/core/src/com/bot4s/telegram/clients/FutureSttpClient.scala index a9bf39be..0b297f52 100644 --- a/core/src/com/bot4s/telegram/clients/FutureSttpClient.scala +++ b/core/src/com/bot4s/telegram/clients/FutureSttpClient.scala @@ -4,9 +4,9 @@ import cats.instances.future._ import scala.concurrent.ExecutionContext import scala.concurrent.Future -import sttp.client3.SttpBackend +import sttp.client4.Backend class FutureSttpClient(token: String, telegramHost: String = "api.telegram.org")(implicit - backend: SttpBackend[Future, Any], + backend: Backend[Future], ec: ExecutionContext ) extends SttpClient[Future](token, telegramHost) diff --git a/core/src/com/bot4s/telegram/clients/SttpClient.scala b/core/src/com/bot4s/telegram/clients/SttpClient.scala index 84e47ba2..fbdfe3eb 100644 --- a/core/src/com/bot4s/telegram/clients/SttpClient.scala +++ b/core/src/com/bot4s/telegram/clients/SttpClient.scala @@ -13,13 +13,12 @@ import io.circe.{ Decoder, Encoder } import com.typesafe.scalalogging.StrictLogging import scala.concurrent.duration._ -import sttp.client3._ +import sttp.client4._ -import sttp.client3.ResponseAs +import sttp.client4.ResponseAs import sttp.model.MediaType -import sttp.client3.BodySerializer -import sttp.client3.StringBody -import sttp.client3.{ Request => SttpRequest } +import sttp.client4.StringBody +import sttp.client4.{ Request => SttpRequest } /** * Sttp HTTP client. @@ -28,17 +27,17 @@ import sttp.client3.{ Request => SttpRequest } * @param token Bot token */ class SttpClient[F[_]](token: String, telegramHost: String = "api.telegram.org")(implicit - backend: SttpBackend[F, Any], + backend: Backend[F], monadError: MonadError[F, Throwable] ) extends RequestHandler[F]()(using monadError) with StrictLogging { val readTimeout: Duration = 50.seconds - private implicit def circeBodySerializer[B: Encoder]: BodySerializer[B] = - b => StringBody(marshalling.toJson[B](b), "utf-8", MediaType.ApplicationJson) + private def asJson[B: Encoder](b: B): StringBody = + StringBody(marshalling.toJson[B](b), "utf-8", MediaType.ApplicationJson) - private def asJson[B: Decoder]: ResponseAs[B, Any] = + private def asJson[B: Decoder]: ResponseAs[B] = asStringAlways("utf-8").map(s => marshalling.fromJson[B](s)) private val apiBaseUrl = s"https://$telegramHost/bot$token/" @@ -48,9 +47,9 @@ class SttpClient[F[_]](token: String, telegramHost: String = "api.telegram.org") )(implicit d: Decoder[request.Response]): F[request.Response] = { val url = apiBaseUrl + request.methodName - val sttpRequest: Either[IllegalArgumentException, SttpRequest[String, Any]] = request match { + val sttpRequest: Either[IllegalArgumentException, SttpRequest[String]] = request match { case r: JsonRequest => - Right(quickRequest.post(uri"$url").body(request)) + Right(quickRequest.post(uri"$url").body(asJson(request))) case r: MultipartRequest => val parts = r.getFiles.flatMap { case (camelKey, inputFile) => diff --git a/core/test/src/com/bot4s/telegram/clients/SttpClientSuite.scala b/core/test/src/com/bot4s/telegram/clients/SttpClientSuite.scala index 280e916e..4c2241c7 100644 --- a/core/test/src/com/bot4s/telegram/clients/SttpClientSuite.scala +++ b/core/test/src/com/bot4s/telegram/clients/SttpClientSuite.scala @@ -1,7 +1,7 @@ package com.bot4s.telegram.clients import org.scalatest.flatspec.AsyncFlatSpec -import sttp.client3.testing.SttpBackendStub +import sttp.client4.testing.BackendStub import com.bot4s.telegram.methods.GetMe import scala.concurrent.ExecutionContext import io.circe.ParsingFailure @@ -12,7 +12,7 @@ class SttpClientSuite extends AsyncFlatSpec { behavior of "STTP client" it should "fail with a ParsingFailure in case of server error" in { - val backend = SttpBackendStub.asynchronousFuture + val backend = BackendStub.asynchronousFuture .whenRequestMatches(_.uri.path.contains("GetMe")) .thenRespondServerError() val client = new FutureSttpClient("")(backend, implicitly[ExecutionContext]) @@ -23,9 +23,9 @@ class SttpClientSuite extends AsyncFlatSpec { } it should "fail with a TelegramApiException the API returned an error" in { - val backend = SttpBackendStub.asynchronousFuture + val backend = BackendStub.asynchronousFuture .whenRequestMatches(_.uri.path.contains("GetMe")) - .thenRespond("""{"ok":false,"error_code":401,"description":"Unauthorized"}""") + .thenRespondAdjust("""{"ok":false,"error_code":401,"description":"Unauthorized"}""") val client = new FutureSttpClient("")(backend, implicitly[ExecutionContext]) recoverToSucceededIf[TelegramApiException] { diff --git a/examples/src-cats/CommandsBot.scala b/examples/src-cats/CommandsBot.scala index 318125be..9b233ee9 100644 --- a/examples/src-cats/CommandsBot.scala +++ b/examples/src-cats/CommandsBot.scala @@ -10,6 +10,8 @@ import com.bot4s.telegram.cats.Polling import scala.concurrent.duration._ import scala.util.Try +import sttp.client4.Backend +import com.bot4s.telegram.cats.TelegramBot /** * Showcases different ways to declare commands (Commands + RegexCommands). @@ -18,8 +20,8 @@ import scala.util.Try * * @param token Bot's token. */ -class CommandsBot[F[_]: Concurrent: Timer: ContextShift](token: String) - extends ExampleBot[F](token) +class CommandsBot[F[_]: Concurrent: Timer: ContextShift](token: String, backend: Backend[F]) + extends TelegramBot[F](token, backend) with Polling[F] with Commands[F] with RegexCommands[F] { diff --git a/examples/src-cats/EchoBot.scala b/examples/src-cats/EchoBot.scala index e51a0c90..439e2ade 100644 --- a/examples/src-cats/EchoBot.scala +++ b/examples/src-cats/EchoBot.scala @@ -4,8 +4,12 @@ import cats.syntax.functor._ import com.bot4s.telegram.cats.Polling import com.bot4s.telegram.methods._ import com.bot4s.telegram.models._ +import com.bot4s.telegram.cats.TelegramBot +import sttp.client4.Backend -class EchoBot[F[_]: Concurrent: ContextShift](token: String) extends ExampleBot[F](token) with Polling[F] { +class EchoBot[F[_]: Concurrent: ContextShift](token: String, backend: Backend[F]) + extends TelegramBot[F](token, backend) + with Polling[F] { override def receiveMessage(msg: Message): F[Unit] = msg.text.fold(unit) { text => diff --git a/examples/src-cats/ExampleBot.scala b/examples/src-cats/ExampleBot.scala deleted file mode 100644 index 592300be..00000000 --- a/examples/src-cats/ExampleBot.scala +++ /dev/null @@ -1,7 +0,0 @@ -import cats.effect.{ Concurrent, ContextShift } -import com.bot4s.telegram.cats.TelegramBot -import org.asynchttpclient.Dsl.asyncHttpClient -import sttp.client3.asynchttpclient.cats.AsyncHttpClientCatsBackend - -abstract class ExampleBot[F[_]: ContextShift: Concurrent](val token: String) - extends TelegramBot[F](token, AsyncHttpClientCatsBackend.usingClient[F](asyncHttpClient())) diff --git a/examples/src-cats/Launcher.scala b/examples/src-cats/Launcher.scala index 8e6df602..f131b021 100644 --- a/examples/src-cats/Launcher.scala +++ b/examples/src-cats/Launcher.scala @@ -1,18 +1,28 @@ import cats.effect.ExitCode import cats.effect.IO import cats.effect.IOApp +import sttp.client4.httpclient.fs2.HttpClientFs2Backend +import cats.effect.Blocker object Launcher extends IOApp { def run(args: List[String]): IO[ExitCode] = - args match { - case List("EchoBot", token) => - new EchoBot[IO](token).startPolling().map(_ => ExitCode.Success) - case List("CommandsBot", token) => - new CommandsBot[IO](token).startPolling().map(_ => ExitCode.Success) - case List(name, _) => - IO.raiseError(new Exception(s"Unknown bot $name")) - case _ => - IO.raiseError(new Exception("Usage:\nLauncher $botName $token")) + Blocker[IO].use { blocker => + args match { + case List("EchoBot", token) => + HttpClientFs2Backend + .resource[IO](blocker) + .use(backend => new EchoBot[IO](token, backend).startPolling()) + .as(ExitCode.Success) + case List("CommandsBot", token) => + HttpClientFs2Backend + .resource[IO](blocker) + .use(backend => new CommandsBot[IO](token, backend).startPolling()) + .as(ExitCode.Success) + case List(name, _) => + IO.raiseError(new Exception(s"Unknown bot $name")) + case _ => + IO.raiseError(new Exception("Usage:\nLauncher $botName $token")) + } } } diff --git a/examples/src-cats3/CommandsBot.scala b/examples/src-cats3/CommandsBot.scala index 53eede44..ba5bf111 100644 --- a/examples/src-cats3/CommandsBot.scala +++ b/examples/src-cats3/CommandsBot.scala @@ -10,6 +10,8 @@ import com.bot4s.telegram.cats.Polling import scala.concurrent.duration._ import scala.util.Try +import com.bot4s.telegram.cats.TelegramBot +import sttp.client4.Backend /** * Showcases different ways to declare commands (Commands + RegexCommands). @@ -18,8 +20,8 @@ import scala.util.Try * * @param token Bot's token. */ -class CommandsBot[F[_]: Async: Temporal](token: String) - extends ExampleBot[F](token) +class CommandsBot[F[_]: Async: Temporal](token: String, backend: Backend[F]) + extends TelegramBot[F](token, backend) with Polling[F] with Commands[F] with RegexCommands[F] { diff --git a/examples/src-cats3/EchoBot.scala b/examples/src-cats3/EchoBot.scala index 2c2e47aa..7ad28407 100644 --- a/examples/src-cats3/EchoBot.scala +++ b/examples/src-cats3/EchoBot.scala @@ -4,8 +4,10 @@ import cats.syntax.functor._ import com.bot4s.telegram.cats.Polling import com.bot4s.telegram.methods._ import com.bot4s.telegram.models._ +import com.bot4s.telegram.cats.TelegramBot +import sttp.client4.Backend -class EchoBot[F[_]: Async](token: String) extends ExampleBot[F](token) with Polling[F] { +class EchoBot[F[_]: Async](token: String, backend: Backend[F]) extends TelegramBot[F](token, backend) with Polling[F] { override def receiveMessage(msg: Message): F[Unit] = msg.text.fold(unit) { text => diff --git a/examples/src-cats3/ExampleBot.scala b/examples/src-cats3/ExampleBot.scala deleted file mode 100644 index 66d7eb1a..00000000 --- a/examples/src-cats3/ExampleBot.scala +++ /dev/null @@ -1,7 +0,0 @@ -import cats.effect.Async -import com.bot4s.telegram.cats.TelegramBot -import org.asynchttpclient.Dsl.asyncHttpClient -import sttp.client3.asynchttpclient.cats.AsyncHttpClientCatsBackend - -abstract class ExampleBot[F[_]: Async](val token: String) - extends TelegramBot[F](token, AsyncHttpClientCatsBackend.usingClient[F](asyncHttpClient())) diff --git a/examples/src-cats3/Launcher.scala b/examples/src-cats3/Launcher.scala index 8e6df602..25808f9e 100644 --- a/examples/src-cats3/Launcher.scala +++ b/examples/src-cats3/Launcher.scala @@ -1,15 +1,16 @@ import cats.effect.ExitCode import cats.effect.IO import cats.effect.IOApp +import sttp.client4.httpclient.cats.HttpClientCatsBackend object Launcher extends IOApp { def run(args: List[String]): IO[ExitCode] = args match { case List("EchoBot", token) => - new EchoBot[IO](token).startPolling().map(_ => ExitCode.Success) + HttpClientCatsBackend.resource[IO]().use(c => new EchoBot[IO](token, c).startPolling()).as(ExitCode.Success) case List("CommandsBot", token) => - new CommandsBot[IO](token).startPolling().map(_ => ExitCode.Success) + HttpClientCatsBackend.resource[IO]().use(c => new CommandsBot[IO](token, c).startPolling()).as(ExitCode.Success) case List(name, _) => IO.raiseError(new Exception(s"Unknown bot $name")) case _ => diff --git a/examples/src-monix/EchoBot.scala b/examples/src-monix/EchoBot.scala index 99ab73e9..558b58d3 100644 --- a/examples/src-monix/EchoBot.scala +++ b/examples/src-monix/EchoBot.scala @@ -3,8 +3,10 @@ import monix.eval.Task import com.bot4s.telegram.cats.Polling import com.bot4s.telegram.methods._ import com.bot4s.telegram.models._ +import com.bot4s.telegram.cats.TelegramBot +import sttp.client4.Backend -class EchoBot(token: String) extends ExampleBot(token) with Polling[Task] { +class EchoBot(token: String, backend: Backend[Task]) extends TelegramBot(token, backend) with Polling[Task] { override def receiveMessage(msg: Message): Task[Unit] = msg.text.fold(Task.pure(())) { text => diff --git a/examples/src-monix/ExampleBot.scala b/examples/src-monix/ExampleBot.scala deleted file mode 100644 index caecb3ab..00000000 --- a/examples/src-monix/ExampleBot.scala +++ /dev/null @@ -1,6 +0,0 @@ -import com.bot4s.telegram.cats.TelegramBot -import org.asynchttpclient.Dsl.asyncHttpClient -import sttp.client3.asynchttpclient.monix.AsyncHttpClientMonixBackend - -abstract class ExampleBot(val token: String) - extends TelegramBot(token, AsyncHttpClientMonixBackend.usingClient(asyncHttpClient())) diff --git a/examples/src-monix/Launcher.scala b/examples/src-monix/Launcher.scala index 1673216a..6cd7030f 100644 --- a/examples/src-monix/Launcher.scala +++ b/examples/src-monix/Launcher.scala @@ -1,12 +1,13 @@ import cats.effect._ import monix.eval._ +import sttp.client4.httpclient.monix.HttpClientMonixBackend object Launcher extends TaskApp { def run(args: List[String]): Task[ExitCode] = args.toList match { case List("EchoBot", token) => - new EchoBot(token).run().as(ExitCode.Success) + HttpClientMonixBackend().flatMap(b => new EchoBot(token, b).run()).as(ExitCode.Success) case List(name, _) => Task.raiseError(new Exception(s"Unknown bot $name")) case _ => diff --git a/examples/src-zio/CommandsBot.scala b/examples/src-zio/CommandsBot.scala index b81285c0..252923ec 100644 --- a/examples/src-zio/CommandsBot.scala +++ b/examples/src-zio/CommandsBot.scala @@ -7,6 +7,8 @@ import com.bot4s.telegram.api.declarative.{ Commands, RegexCommands } import com.bot4s.telegram.cats.Polling import scala.util.Try +import sttp.client4.Backend +import com.bot4s.telegram.cats.TelegramBot /** * Showcases different ways to declare commands (Commands + RegexCommands). @@ -15,8 +17,8 @@ import scala.util.Try * * @param token Bot's token. */ -class CommandsBot(token: String) - extends ExampleBot(token) +class CommandsBot(token: String, backend: Backend[Task]) + extends TelegramBot(token, backend) with Polling[Task] with Commands[Task] with RegexCommands[Task] { diff --git a/examples/src-zio/CommandsWebhookBot.scala b/examples/src-zio/CommandsWebhookBot.scala index 3d595bd0..2331fc38 100644 --- a/examples/src-zio/CommandsWebhookBot.scala +++ b/examples/src-zio/CommandsWebhookBot.scala @@ -6,6 +6,9 @@ import zhttp.service.Server import zhttp.service.server.ServerChannelFactory import zhttp.service.EventLoopGroup import com.bot4s.telegram.models.Update +import com.bot4s.telegram.cats.TelegramBot +import sttp.client4.Backend +import zio.interop.catz._ /** * Example on the usage of webhook with a custom webserver @@ -13,8 +16,8 @@ import com.bot4s.telegram.models.Update * @param token Bot's token. * @param started Bot's current state (started or not) */ -class CommandsWebhookBot(token: String, private val started: Ref.Synchronized[Boolean]) - extends ExampleBot(token) +class CommandsWebhookBot(token: String, backend: Backend[Task], private val started: Ref.Synchronized[Boolean]) + extends TelegramBot(token, backend) with Commands[Task] with RegexCommands[Task] { diff --git a/examples/src-zio/EchoBot.scala b/examples/src-zio/EchoBot.scala index a2c498d3..6bf29ac9 100644 --- a/examples/src-zio/EchoBot.scala +++ b/examples/src-zio/EchoBot.scala @@ -3,8 +3,11 @@ import zio.Task import com.bot4s.telegram.cats.Polling import com.bot4s.telegram.methods._ import com.bot4s.telegram.models._ +import sttp.client4.Backend +import com.bot4s.telegram.cats.TelegramBot +import zio.interop.catz._ -class EchoBot(token: String) extends ExampleBot(token) with Polling[Task] { +class EchoBot(token: String, backend: Backend[Task]) extends TelegramBot(token, backend) with Polling[Task] { override def receiveMessage(msg: Message): Task[Unit] = msg.text.fold(unit) { text => diff --git a/examples/src-zio/ExampleBot.scala b/examples/src-zio/ExampleBot.scala deleted file mode 100644 index b44c74e9..00000000 --- a/examples/src-zio/ExampleBot.scala +++ /dev/null @@ -1,8 +0,0 @@ -import com.bot4s.telegram.cats.TelegramBot -import org.asynchttpclient.Dsl.asyncHttpClient -import zio.Task -import zio.interop.catz._ -import sttp.client3.asynchttpclient.zio.AsyncHttpClientZioBackend - -abstract class ExampleBot(val token: String) - extends TelegramBot[Task](token, AsyncHttpClientZioBackend.usingClient(zio.Runtime.default, asyncHttpClient())) diff --git a/examples/src-zio/Launcher.scala b/examples/src-zio/Launcher.scala index 27977940..d976c3d7 100644 --- a/examples/src-zio/Launcher.scala +++ b/examples/src-zio/Launcher.scala @@ -1,19 +1,21 @@ import zio._ import zio.{ ZIO, ZIOAppArgs } +import sttp.client4.httpclient.zio.HttpClientZioBackend object Launcher extends zio.ZIOAppDefault { override def run = for { args <- ZIO.service[ZIOAppArgs] + b <- HttpClientZioBackend() _ <- args.getArgs.toList match { case List("EchoBot", token) => - new EchoBot(token).startPolling().map(_ => ExitCode.success) - case List("CommandsBot", token) => - new CommandsBot(token).startPolling().map(_ => ExitCode.success) + new EchoBot(token, b).startPolling().map(_ => ExitCode.success) + case List("CommandsBot", token, _) => + new CommandsBot(token, b).startPolling().map(_ => ExitCode.success) case List("CommandsWebhookBot", token) => Ref.Synchronized.make(false).flatMap { started => - new CommandsWebhookBot(token, started).run() + new CommandsWebhookBot(token, b, started).run() } case List(name, _) => diff --git a/examples/src/CovfefeBot.scala b/examples/src/CovfefeBot.scala index 13def7f5..71ec7a8f 100644 --- a/examples/src/CovfefeBot.scala +++ b/examples/src/CovfefeBot.scala @@ -2,7 +2,7 @@ import cats.instances.future._ import cats.syntax.functor._ import com.bot4s.telegram.future.Polling import com.bot4s.telegram.api.declarative.Commands -import sttp.client3._ +import sttp.client4._ import scala.concurrent.Future diff --git a/examples/src/ExampleBot.scala b/examples/src/ExampleBot.scala index 90005aa9..827ed2e0 100644 --- a/examples/src/ExampleBot.scala +++ b/examples/src/ExampleBot.scala @@ -3,8 +3,8 @@ import com.bot4s.telegram.clients.FutureSttpClient import com.bot4s.telegram.future.TelegramBot import scala.concurrent.Future -import sttp.client3.SttpBackend -import sttp.client3.okhttp.OkHttpFutureBackend +import sttp.client4.Backend +import sttp.client4.okhttp.OkHttpFutureBackend /** * Quick helper to spawn example bots. @@ -18,6 +18,6 @@ import sttp.client3.okhttp.OkHttpFutureBackend */ abstract class ExampleBot(val token: String) extends TelegramBot { - implicit val backend: SttpBackend[Future, Any] = OkHttpFutureBackend() - override val client: RequestHandler[Future] = new FutureSttpClient(token) + implicit val backend: Backend[Future] = OkHttpFutureBackend() + override val client: RequestHandler[Future] = new FutureSttpClient(token) } From 25bc4d485035927f4158e09328264ade5f6b5dc5 Mon Sep 17 00:00:00 2001 From: Mike Limansky Date: Sun, 7 Dec 2025 14:48:39 +0300 Subject: [PATCH 2/3] Fix examples compilation --- examples/src-jvm-2/SttpBackends.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/src-jvm-2/SttpBackends.scala b/examples/src-jvm-2/SttpBackends.scala index 37424de9..eee259bc 100644 --- a/examples/src-jvm-2/SttpBackends.scala +++ b/examples/src-jvm-2/SttpBackends.scala @@ -1,7 +1,7 @@ -import sttp.client3.okhttp.OkHttpFutureBackend -import sttp.client3.SttpBackend +import sttp.client4.okhttp.OkHttpFutureBackend +import sttp.client4.Backend import scala.concurrent.Future object SttpBackends { - val default: SttpBackend[Future, Any] = OkHttpFutureBackend() + val default: Backend[Future] = OkHttpFutureBackend() } From 3ba76e17d06072c867943ef105af1fd736990378 Mon Sep 17 00:00:00 2001 From: Mike Limansky Date: Sun, 7 Dec 2025 15:18:29 +0300 Subject: [PATCH 3/3] Fix zio example command line parsing issue --- examples/src-zio/Launcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src-zio/Launcher.scala b/examples/src-zio/Launcher.scala index d976c3d7..93de5547 100644 --- a/examples/src-zio/Launcher.scala +++ b/examples/src-zio/Launcher.scala @@ -11,7 +11,7 @@ object Launcher extends zio.ZIOAppDefault { _ <- args.getArgs.toList match { case List("EchoBot", token) => new EchoBot(token, b).startPolling().map(_ => ExitCode.success) - case List("CommandsBot", token, _) => + case List("CommandsBot", token) => new CommandsBot(token, b).startPolling().map(_ => ExitCode.success) case List("CommandsWebhookBot", token) => Ref.Synchronized.make(false).flatMap { started =>