Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • May 28 08:01
    scala-steward closed #263
  • May 28 08:01
    scala-steward commented #263
  • May 28 08:01
    scala-steward opened #289
  • May 27 02:14
    scala-steward closed #283
  • May 27 02:14
    scala-steward commented #283
  • May 27 02:14
    scala-steward opened #288
  • May 26 22:11
    scala-steward closed #286
  • May 26 22:11
    scala-steward commented #286
  • May 26 22:11
    scala-steward opened #287
  • May 26 14:10
    scala-steward opened #286
  • May 26 08:08
    scala-steward opened #285
  • May 17 14:19
    scala-steward closed #264
  • May 17 14:19
    scala-steward commented #264
  • May 17 14:19
    scala-steward opened #284
  • May 17 08:10
    scala-steward closed #275
  • May 17 08:10
    scala-steward commented #275
  • May 17 08:10
    scala-steward opened #283
  • May 14 20:15
    scala-steward closed #277
  • May 14 20:15
    scala-steward commented #277
  • May 14 20:15
    scala-steward opened #282
Yura Slinkin
@augustjune
alternatively, you can make News.documents return result in abstract effect type with some constraints and propagate those constraints here
def documents[F[_]: HttpClient](request: PageRequest): F[List[Document]]=
  get[Document]("/news4You/news", request.parameters)


def provideNews[F[_]: TelegramClient: HttpClient](chat:Chat, link:String): Scenario[F, Unit] =
  for {
    docs <- Scenario.eval(News.documents(PageRequest(link)))
    _ <- Scenario.eval(chat.send("Your news:"))
    _ <- Scenario.eval(chat.send(docs.toString))
  } yield ()
aka tagless final approach
@nickdndev
Nikolai Diakin
@nickdndev
@augustjune thank you!
Aleksei Terekhin
@daron666
@augustjune hey! long time no see, hope you are fine :) can you please review and merge and release? augustjune/canoe#165
Aleksei Terekhin
@daron666
=) when release? =)
Yura Slinkin
@augustjune
@daron666 oh, there was something strange with travis earlier
gonna make it today
Aleksei Terekhin
@daron666
thanks a lot dude =)
Yura Slinkin
@augustjune
done. is going to be available on mvn in a while
Nikolai Diakin
@nickdndev
@augustjune How can we set expired time for the scenario? For instance, we have 1000 user and then they stop proceed on the middle scenario. These scenarios will be deleted auto or forever on RAM ?
Yura Slinkin
@augustjune
Hi @nickdndev! Currently, there's no time limit for the duration of a started scenario, so in a situation where doesn't follow up, it will be hanging in memory in case user wants to get back to the thread.
Although, scenarios are optimized to be very lightweight (you can easily have 10000+ of them), there's an issue that addresses this feature: augustjune/canoe#158
I think I'm able to find some time to work on this
Esenbaev Nursultan
@eanea
@augustjune, hi) when are you gonna release to update the last API?
Yura Slinkin
@augustjune
Hi @eanea I can do that later today. does that work for you?
Esenbaev Nursultan
@eanea
@augustjune, yes, would be great)
Lior Liviev
@liorl00
Hey, I have an inline button and I want it to trigger another scenario, how do I do that?
Lior Liviev
@liorl00
@augustjune
Yura Slinkin
@augustjune
Hi @liorl00
In order to achieve that you have to share the state between the callback handler and scenario that you want to be triggered.
Lior Liviev
@liorl00
can you give me an example?
Yura Slinkin
@augustjune
Here's an example
  def run(args: List[String]): IO[ExitCode] =
    Stream
      .resource(TelegramClient.global[IO](token))
      .flatMap { implicit client =>
        Stream.eval(Store.make[IO]).flatMap { store =>
          Bot
            .fromStream(Bot.polling[IO].updates.through(answerCallbacks(store)))
            .follow(createButton, trigger(store)(reaction))
        }
      }
      .compile
      .drain
      .as(ExitCode.Success)

  def createButton[F[_]: TelegramClient]: Scenario[F, Unit] =
    for {
      msg <- Scenario.expect(command("callback"))
      _ <- Scenario.eval(
        msg.chat.send(content = "pretty message", keyboard = setupMarkupWithSungleCallbackButton("button", "xx"))
      )
    } yield ()

  def trigger[F[_]: TelegramClient](lastCallback: Store[F])(main: Scenario[F, Unit]): Scenario[F, Unit] =
    Scenario.eval(lastCallback.check("xx")).ifM(main, Scenario.done)

  def reaction[F[_]: TelegramClient]: Scenario[F, Unit] = ???

  def answerCallbacks[F[_]: Monad: TelegramClient](lastCallback: Store[F]): Pipe[F, Update, Update] =
    _.evalTap {
      case CallbackButtonSelected(_, query) =>
        query.data match {
          case Some(cbd) => lastCallback.save(cbd) *> query.finish
          case _         => lastCallback.clean
        }
      case _ => lastCallback.clean
    }

  def setupMarkupWithSungleCallbackButton(text: String, data: String): Keyboard.Inline = {
    val inlineBtn = InlineKeyboardButton.callbackData(text = text, cbd = data)
    val inlineKeyboardMarkUp = InlineKeyboardMarkup.singleButton(inlineBtn)
    Keyboard.Inline(inlineKeyboardMarkUp)
  }


trait Store[F[_]] {
  def save(id: String): F[Unit]

  def clean: F[Unit]

  def check(id: String): F[Boolean]
}

object Store {
  def make[F[_]: Concurrent]: F[Store[F]] =
    Ref[F].of(none[String]).map { ref =>
      new Store[F] {
        def save(id: String): F[Unit] = ref.set(id.some)

        def clean: F[Unit] = ref.set(none)

        def check(id: String): F[Boolean] = ref.get.map(_.contains(id))
      }
    }
}
Basically, you set the state in the callback handler and read the state as the first step in your scenario.
Lior Liviev
@liorl00
Thank you :)
Yura Slinkin
@augustjune
You can share the state with Ref and you can see I used a small wrapper around it (Store) to simplify access to the data
Lior Liviev
@liorl00
The only problem is that I need to rewrite it to zio)
Yura Slinkin
@augustjune
Well, the effect type is abstracted, so you can as well use zio.Task specifying it just on the top level in run
Lior Liviev
@liorl00
can you show me the imports as well?
Yura Slinkin
@augustjune
Here you go :wink:
import canoe.api._
import canoe.api.models.Keyboard
import canoe.models.{CallbackButtonSelected, InlineKeyboardButton, InlineKeyboardMarkup, Update}
import canoe.syntax._
import cats.Monad
import cats.effect.concurrent.Ref
import cats.effect.{Concurrent, ExitCode, IO, IOApp}
import cats.implicits._
import fs2.{Pipe, Stream}
Lior Liviev
@liorl00
Thx)
Lior Liviev
@liorl00
Oh and what is query.finish
Can't find this method
Yura Slinkin
@augustjune
It's a method that stops processing circle in telegram when you hit a callback button
Are you using laterst version 0.5.0?
Lior Liviev
@liorl00
yes
Yura Slinkin
@augustjune
in that case finish should be available if you have imported canoe.api._
Lior Liviev
@liorl00
@augustjune Is it okay to use chat.send in answerCallbacks without Scenario.eval?
Yura Slinkin
@augustjune
@liorl00 yeah, totally cool. Scenario.eval just allows you to lift F[A] into Scenario context
Lior Liviev
@liorl00
thx)
Lior Liviev
@liorl00
@augustjune do you know if there any other way to trigger Scenario in callback than sharing the state?
Lior Liviev
@liorl00
@augustjune ?
Yura Slinkin
@augustjune
@liorl00 I don't think there is any other way as of now
that's something that could be added to Scenario though
if you're interested, you can raise an issue in the project and I'll find some time to work on it soon
:wink:
Lior Liviev
@liorl00
@augustjune Hey is it possible now to use this api?
https://core.telegram.org/bots/api#loginurl
Yura Slinkin
@augustjune
@liorl00 Hi
Yes, you can use it in combination with InlineKeyboardButton. There's an optional argument that takes this object LoginUrl that you can should construct yourself and create a button with it
Lior Liviev
@liorl00
@augustjune Could you give me an example pls?)
Cause I don't see any method with loginUrl
Yura Slinkin
@augustjune
yeah, you're right