The library also supports the FSM mechanism, which is a mechanism for progressive processing of user input with incorrect input handling.

In theory

Let's imagine a situation where you need to collect a user survey, you can ask for all the data of a person at one step, but with incorrect input of one of the parameters, it will be difficult both for the user and for us, and each step may have a difference depending on certain input data.

Now let's imagine step-by-step input of data, where the bot enters dialogue mode with the user.

Handling process diagram

Green arrows indicate the process of transitioning through steps without errors, blue arrows mean saving the current state and waiting for re-input (for example, if the user indicated that he is -100 years old, it should ask for age again), and red ones show exit from the entire process due to any command or any other meaning cancellation.

In practice

Such a mechanism can be implemented in the library through a simple class that implements a certain interface and marked with a specific annotation @InputChain.

@InputChain
object ConversationChain {
    object Name : BaseStatefulLink() {
        override val breakCondition = BreakCondition { _, update, _ -> update.text.isBlank() }
        override suspend fun breakAction(user: User, update: ProcessedUpdate, bot: TelegramBot) {
            message {
                "Please say your name, because that's what well-mannered people do :)"
            }.send(user, bot)
        }

        override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot): String {
            message { "Oh, ${update.text}, hey there" }.send(user, bot)
            message { "How old are you?" }.send(user, bot)

            return update.text
        }
    }

    object Age : BaseStatefulLink() {
        override val breakCondition = BreakCondition { _, update, _ -> update.text.toIntOrNull() == null }
        override suspend fun breakAction(user: User, update: ProcessedUpdate, bot: TelegramBot) {
            message {
                "Perhaps it's not nice to ask your age, but maybe you can tell me anyway."
            }.send(user, bot)
        }

        override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot): String {
            message { "Pleased to meet you!" }.send(user, bot)

            return update.text
        }
    }

    object Final : ChainLink() {
        override suspend fun action(user: User, update: ProcessedUpdate, bot: TelegramBot) {
            val state = user.getAllState(ConversationChain)

            message {
                "I'm not good at remembering, but I remembered you! " +
                        "You're ${state.Name} and you're ${state.Age} years old."
            }.send(user, bot)
        }
    }
}

And after we described the mechanism to start the processing, we just need to call the method and specify the initial step, then the library itself will follow the sequence.

bot.inputListener.setChain(user, Conversation.Name)

Links details

All links have the same foundation and implementing Link<T> interface, which have such properties:

Key Properties:

Key Functions:

There are two types of links they are differentiated by state, stateless and stateful:

Stateless links are represented by the abstract class ChainLink. This class serves as the foundation for creating links that do not maintain any state information between user interactions.

The InputChain mechanism, particularly when employing StatefulLink and its various implementations, offers a sophisticated approach to managing conversational states within applications, such as chatbots. This system automatically stores the result of the action function associated with each state, linking it directly to the user involved in the interaction (or other selected key).

Key Features

Automatic State Storage

Customizable Keys

Data Typing and Key Utilization

Unified Access to States

Usage Examples

Retrieving All States

To access all states associated with a particular chain for a given user, the following syntax can be employed:

user.getAllState(MyChain).LinkName

This command retrieves the data linked to the specified LinkName within MyChain, providing a comprehensive overview of the user's interaction history.

Direct State Access

Alternatively, for more granular control, states can be accessed directly through the links themselves:

Chain.LinkName.state.get(key)

Or, if querying the state within the current chain, Chain.LinkName may be omitted, simplifying the call to:

state.get(key)

Benefits

This methodology enables stricter data management protocols, offering rapid and convenient access to stored states. It enhances the efficiency of state retrieval and manipulation, contributing to a more seamless user experience.

Default BaseStatefulLink implementation uses ConcurrentHashMap, but for serious projects it is recommended to use other solutions 😃

Summarizing

It is possible to use the proposed tools with different variations to create quite flexible interaction, if you have any questions contact us in chat, we will be glad to help 😃