-
Notifications
You must be signed in to change notification settings - Fork 7
Conversations
A conversation represents a structured interaction between a bot and a user. It typically follows a sequence of exchanges (messages) where the bot prompts the user for input, processes the response, and decides on the next step.
Imagine this example: a bot asks the user for their name, then asks for their age, and finally asks for their favorite color. Each of these steps is a conversation turn, and the entire sequence is a conversation.
Normally, you would have to listen for each message and keep track of the conversation state manually. However, with the Conversation API, you can define a conversation as workflow and let Teleight handle the conversation flow for you.
To create a conversation, you need to implement the ConversationExecutor interface.
public class TestConversation implements ConversationExecutor {
@Override
public void execute(@NotNull ConversationContext context) {
final Chat chat = context.chat();
final String chatId = String.valueOf(chat.id());
// Let's start the conversation by sending a message to the user
SendMessage startMessage = SendMessage.ofBuilder(chatId, "Send me a message with the text \"hello\"").build();
context.bot().execute(startMessage);
// Now, let's wait for an update. We give the user 10 seconds to reply.
// If the result is null, that means the bot did not receive an update in time
final Update update = context.waitForUpdate(10, TimeUnit.SECONDS);
if (update == null) {
// And this is the case, so let's leave the conversation and send an error message to the user
SendMessage resultToUser = SendMessage.ofBuilder(chatId, "You didn't send the message in time..").build();
context.bot().execute(resultToUser);
return;
}
// Let's get a message from the update
final Message message = update.message();
if (message == null) {
// The message is null, and it's not what we want
// This happens when the bot receives an update which does not contain a message.
SendMessage resultToUser = SendMessage.ofBuilder(chatId, "You didn't send a message..").build();
context.bot().execute(resultToUser);
return;
}
// Now, check if the text equals hello or not and act appropriately
String resultToUser;
if (message.text() == null || !message.text().equals("hello")) {
resultToUser = "You didn't send \"hello\"!";
} else {
resultToUser = "Good job!";
}
final SendMessage result = SendMessage.ofBuilder(chatId, resultToUser).build();
context.bot().execute(result);
}
}The main method you will use for continuing the flow is the waitForUpdate method inside the ConversationContext class.
This method will wait for an update from the user for a specified amount of time.
If the user does not respond in time, the method will return null.
To register a conversation, call the registerConversation method.
Required parameters are the conversation name (which will be used as identifier) and the conversation instance.
final Conversation testConversation = Conversation.of("test", new TestConversation());
bot.getConversationManager().registerConversation(testConversation);To start a conversation, you need to call the startConversation method.
final JoinResult result = bot.getConversationManager().startConversation(user, chat, "test");This will return a JoinResult object that contains the result of the operation.
if (result instanceof ConversationManager.JoinResult.AlreadyInConversation) {
System.out.println("User is already in conversation");
} else if (result instanceof ConversationManager.JoinResult.ConversationNotFound) {
System.out.println("Conversation not found");
} else {
System.out.println("Conversation started");
}In TeleightBots, custom properties allow you to define and manage additional data within a conversation. These properties can be used to store and retrieve information that is relevant to the conversation flow.
Each property can be defined as required or optional. Required properties must be set when the conversation is created, while optional properties can be set in the join conversation operation.
Custom properties are defined using the Property class.
Each property has a name, a value, and a flag indicating whether it is required or optional.
If you create a property with a default value, it will be considered optional:
Property<String> testProperty = Property.of("testProperty", "defaultValue");If you instead create a property without a default value, it will be considered required:
Property<Integer> testProperty = Property.of("testProperty");The Property class provides several methods to retrieve the value in different types:
-
asString(): Returns the value as a string. -
asInt(): Returns the value as an integer. -
asBool(): Returns the value as a boolean. -
asLong(): Returns the value as a long. -
asFloat(): Returns the value as a float. -
as(Class<? extends A> type): Returns the value as a custom type.
To register a conversation with custom properties, you can use the ofBuilder method of the Conversation class and the property or properties method to add custom properties:
Conversation testConversation = Conversation.ofBuilder("testConversation", new TestConversation())
.property(Property.of("isThisAProperty", true))
.property(Property.of("isUserAdmin", false))
.allowUnknownProperties(true)
.build();
bot.getConversationManager().registerConversation(testConversation);The allowUnknownProperties method allows you to specify whether unknown properties should be allowed in the conversation.
Unknown properties are properties that are not explicitly defined when the conversation is registered, and can be applied later when you use the joinConversation method.
If this flag is set to false (default behavior), an exception will be thrown if an unknown property is passed to the conversation.
Here's an example of how to use custom properties in a conversation:
public class TestConversation implements ConversationExecutor {
@Override
public void execute(@NotNull ConversationContext context) {
final Chat chat = context.chat();
final String chatId = String.valueOf(chat.id());
// Retrieve the custom property.
// Be careful: getProperty() can return a null value if the property is not found
// In this case, we can omit the null check because we know the property is defined directly in the conversation
boolean testProperty = context.getProperty("isThisAProperty").asBool();
// Use the property in the conversation logic
if (testProperty) {
SendMessage message = SendMessage.ofBuilder(chatId, "Property is true").build();
context.bot().execute(message);
} else {
SendMessage message = SendMessage.ofBuilder(chatId, "Property is false").build();
context.bot().execute(message);
}
}
}When a user joins a conversation, you can pass a map of properties to initialize the conversation with specific values:
Map<String, Object> properties = Map.of(
"name", "John",
"age", 25
);
ConversationManager.JoinResult result = context.bot().getConversationManager().joinConversation(
sender, context.message().chat(), "testConversation", properties);
if (result instanceof ConversationManager.JoinResult.AlreadyInConversation) {
System.out.println("User is already in conversation");
} else if (result instanceof ConversationManager.JoinResult.ConversationNotFound) {
System.out.println("Conversation not found");
} else {
System.out.println("Conversation started");
}Warning
In this example, name and age are considered unknown properties because they were not defined when the conversation was registered.
If allowUnknownProperties is set to false, an exception will be thrown when the user tries to join the conversation with unknown properties.