Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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}"
Expand All @@ -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}"
Expand Down
8 changes: 4 additions & 4 deletions core/src/com/bot4s/telegram/cats/TelegramBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
4 changes: 2 additions & 2 deletions core/src/com/bot4s/telegram/clients/FutureSttpClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
21 changes: 10 additions & 11 deletions core/src/com/bot4s/telegram/clients/SttpClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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/"
Expand All @@ -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) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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])
Expand All @@ -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] {
Expand Down
6 changes: 4 additions & 2 deletions examples/src-cats/CommandsBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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] {
Expand Down
6 changes: 5 additions & 1 deletion examples/src-cats/EchoBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
7 changes: 0 additions & 7 deletions examples/src-cats/ExampleBot.scala

This file was deleted.

28 changes: 19 additions & 9 deletions examples/src-cats/Launcher.scala
Original file line number Diff line number Diff line change
@@ -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"))
}
}
}
6 changes: 4 additions & 2 deletions examples/src-cats3/CommandsBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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] {
Expand Down
4 changes: 3 additions & 1 deletion examples/src-cats3/EchoBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
7 changes: 0 additions & 7 deletions examples/src-cats3/ExampleBot.scala

This file was deleted.

5 changes: 3 additions & 2 deletions examples/src-cats3/Launcher.scala
Original file line number Diff line number Diff line change
@@ -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 _ =>
Expand Down
6 changes: 3 additions & 3 deletions examples/src-jvm-2/SttpBackends.scala
Original file line number Diff line number Diff line change
@@ -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()
}
4 changes: 3 additions & 1 deletion examples/src-monix/EchoBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
6 changes: 0 additions & 6 deletions examples/src-monix/ExampleBot.scala

This file was deleted.

3 changes: 2 additions & 1 deletion examples/src-monix/Launcher.scala
Original file line number Diff line number Diff line change
@@ -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 _ =>
Expand Down
6 changes: 4 additions & 2 deletions examples/src-zio/CommandsBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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] {
Expand Down
7 changes: 5 additions & 2 deletions examples/src-zio/CommandsWebhookBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ 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
*
* @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] {

Expand Down
5 changes: 4 additions & 1 deletion examples/src-zio/EchoBot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
Loading