Closed spaiman closed 5 months ago
Hey, thank you for choosing this library as your first time experience :)
Sounds like you looking for something like FSM.
Or If you want more controll over process you can mark function with both CommandHandler + InputHandler so function can be triggered each way if this case more suits you :)
Hey, thank you for choosing this library as your first time experience :)
Thanks to you too for creating this awesome telegram bot library for Kotlin. The main differentiator of the library is the usage of KSP which makes it easier to structure the code :)
Sounds like you looking for something like FSM.
Or If you want more controll over process you can mark function with both CommandHandler + InputHandler so function can be triggered each way if this case more suits you :)
Yeah, I've used FSM for chaining input and it works well. If I understand correctly, marking the function as InputHandler
would wait for a user input before the function is triggered right? What I wanted is executing the function right away without waiting for user input.
Here's an example. Let's say I have 2 commands, (1) for setting the first name and last name, (2) for setting date of birth
@CommandHandler(["/start"])
suspend fun start(user: User, bot: TelegramBot) {
message { "Hello, tell me more about yourself. What's your first name?" }.send(user, bot)
bot.inputListener.setChain(user, MainInfo.FirstName)
}
@CommandHandler(["/set_dob"])
suspend fun setDob(user: User, bot: TelegramBot) {
message { "What's your date of birth?" }.send(user, bot)
bot.inputListener.setChain(user, DetailInfo.DOB)
}
And here's the implementation of the InputChain:
@InputChain
class MainInfo {
object FirstName : ChainLink() {
override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot) {
bot.userData[user, "first_name"] = update.text
message { "What's your last name?" }.send(user, bot)
}
}
object LastName : ChainLink() {
override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot) {
bot.userData[user, "last_name"] = update.text
message { "Hello, ${bot.userData[user, "first_name"]} bot.userData[user, "last_name"]" }.send(user, bot)
// Now here, how can I navigate to command `/set_dob` without waiting for user input?
}
}
}
Now after entering last name, I want to continue to command /set_dob
, so the user will get the DOB prompt and continue with another InputChain. I'm expecting something similar like how we can redirect user to a specific InputHandler
e.g.:
bot.inputListener[user] = "your-input-handler-keyword-here"
Surely I can call setDob(...)
function from MainInfo.LastName.action
to achieve that, but asking here if something built-in from the library exists.
I was thinking about your case, I was thinking to add hooks on chaines, I think it would work for this kind of problem.
added hooks for chainlink in 5.5.0, I hope it will help in similar cases!
That was quick!
I just tried 5.5.0 and it looks like that beforeAction
behaves differently than what I expected. It still waits for user input before continuing, doesn't it? I overrode beforeAction
like this:
override val beforeAction: Action = Action { user, _, bot ->
message { "Hello, what's your first name" }.send(user, bot)
}
And beforeAction
is never invoked until it receives an input. After receiving an input, beforeAction
is invoked followed by action
right away. Is that the intended behavior?
As I saw your previous examples you can set afterAction in your MainInfo.LastName, doesn't help in your case?
Could you give me an example on how afterAction helps for my case? I might be missing an important piece here.
Something like this:
@InputChain
class MainInfo {
object FirstName : ChainLink() {
override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot) {
bot.userData[user, "first_name"] = update.text
message { "What's your last name?" }.send(user, bot)
}
}
object LastName : ChainLink() {
override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot) {
bot.userData[user, "last_name"] = update.text
message { "Hello, ${bot.userData[user, "first_name"]} bot.userData[user, "last_name"]" }.send(user, bot)
// Now here, how can I navigate to command `/set_dob` without waiting for user input?
}
override val afterAction: Action = Action { user, _, bot ->
message { "What's your date of birth?" }.send(user, bot)
bot.inputListener.setChain(user, DetailInfo.DOB)
}
}
}
Yeah, your example would work. Even without beforeAction
and afterAction
, I can actually put that inside action
. But, my main objective was avoiding code duplication. If you notice, I need to duplicate this code:
message { "What's your date of birth?" }.send(user, bot)
bot.inputListener.setChain(user, DetailInfo.DOB)
in 2 places i.e. one in the CommandHandler and one in the ChainLink. I wanted to have a single place where I can invoke and reuse them. Hence, the initial question on whether I can redirect the user to the CommandHandler from the ChainLink.
Yeah as you said in such case best option is to manually call setDob
function.
Alright. Thank you for looking into this!
Is there a way to navigate to a specific
CommandHandler
from anInputChain
? I have several steps that the user needs to go through. Each step can be triggered by its own command, so each step is handled by its ownCommandHandler
. Each step will trigger its ownInputChain
. When the user sends/start
, I'd like to make the user journey seamless by going through the steps without waiting the user to send the command. I've tried to go through the wiki, but nothing mentions something similar like this.This is my first time building a Telegram bot, so please bear with me if this is such a basic question.
Thanks in advance!