-
Notifications
You must be signed in to change notification settings - Fork 160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create InputChainHandler #290
base: main
Are you sure you want to change the base?
Create InputChainHandler #290
Conversation
9be06cb
to
d01c257
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea that you are proposing here, just leaving some questions and comments about the implementation.
private val inputChainIdGen: (previousInputChainId: T?, Update) -> T?, | ||
private val handleInputChain: HandleInputChain<T>, | ||
) : Handler { | ||
private val chainMap = mutableMapOf<Long, T?>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User id sounds right as discriminator for deciding when to chain updates, but I think it might be possible for the same bot to be in 2 different chats with the same user and thus we could run into an incorrect chain in that situation. Should we be using chat id too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!!!
Should we have:
Map<Long<Map<Long, T?>>
(ChatId, UserId, Identifier)
or:
Map<String, T?>
("$chatId$userId, Identifier)
First one should be easier to access.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First sounds good to me. And if we are always going to access a single identifier for a given (ChatId,UserId) pair another option would be to have our own chain discrimator data class with both that we use as the key (like the second option but without having to concatenate the ids, etc...).
class InputChainHandler<T>( | ||
private val filter: Filter, | ||
private val initialHandler: Handler, | ||
private val inputChainIdGen: (previousInputChainId: T?, Update) -> T?, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this supposed to be used? It's not obvious to me by reading the internal code of library, so I think it will be even more difficult for consumers of the library to understand how to (correctly) use this API. Is it possible to encapsulate this detail in the library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add some showcases to help other devs to use it and understand how to implement their own chain
fun <T> Handler.inputChain( | ||
filter: Filter = Filter.Text, | ||
inputChainIdGen: (previousInputChainId: T?, Update) -> T?, | ||
handleInputChain: HandleInputChain<T>, | ||
): Handler { | ||
return InputChainHandler(filter, this, inputChainIdGen, handleInputChain) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to also have an example usage of this in the doc and/or one of the sample modules.
private val inputChainIdGen: (previousInputChainId: T?, Update) -> T?, | ||
private val handleInputChain: HandleInputChain<T>, | ||
) : Handler { | ||
private val chainMap = mutableMapOf<Long, T?>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also it looks like the value of the map is not really relevant, should this be a hash set of the visited "userId,chatId" instead? That way we can also get rid of inputChainIdGen
. Or is there another reason why we'd like to have that in the environment object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The T
value is the identifier of the "Input Question".
When the "chain action" is executed, it needs to know "which id it comes from".
Imagine you are creating a form with 3 questions like the one I posted in the PR description, but whenever the question is responded we want to store them on a DB. We need to know which question the user responded, and the value of the T
is the id used. I used a generic type to allow devs to include their own data type (Maybe more than an int/String value is needed to continue the chain).
telegram/src/main/kotlin/com/github/kotlintelegrambot/dispatcher/handlers/InputChainHandler.kt
Show resolved
Hide resolved
} | ||
} | ||
} ?: initialHandler.handleUpdate(bot, update).also { | ||
update.takeIf { it.consumed } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only chaining if the update is consumed? I feel that having to mark the first update as consumed for the chain to work is going to make the API hard to use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about it...
I assumed whenever "a question" is processed, they (App devs) should consume it to avoid wrong behavior in other commands that match the "initialHandler" checks (And maybe override the ID stored on this chain), but maybe is a good idea removing this check and rely on the "good implementation" of or users
val message: Message, | ||
) | ||
|
||
class InputChainHandler<T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be great to have some tests for this. It would be good as documentation for other maintainers of the library, it would give us more confindence to update this class in the future, it could help us find some edge cases that we may not have considered, etc...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I will add some tests for this new feature
9685456
to
25a21f5
Compare
25a21f5
to
64f3d9b
Compare
64f3d9b
to
9fec96b
Compare
@JcMinarro do you find this PR still valid? Do you have any last changes to push? Otherwise I would be happy to accept it |
This new
InputChainHandler
helps us to collect inputs after a previous handler has been processed.Let show some examples:
-. A button is pressed to change the name of the device:
-. The user send a command to create a new form