The goal of this project is to provide an open source framework for creating conversational bots using only a simple descriptive and human-readable configuration
- Download or build the jar
- Have a telegram bot token and name (telegram docs)
- Have Java 8+ installed
Under the config dir, create two files:
- application.properties
- conversation.json
The config/application.properties contains bot telegram definition and the relative path to the conversation json descriptor
kobot.conversation.path=config/conversation.json
kobot.telegram.bot.name=<your bot name>
kobot.telegram.bot.token=<your bot token>
The conversation.json file contains your actual configuration. Try with this:
{
"states": [
{"id": "start", "type": "start"},
{"id": "greet", "type": "send-mex", "message": "Welcome! Pick an option"},
{
"id": "wait-input",
"type": "wait-for-input",
"expected-type": "string",
"on-mismatch": "Choice not recognized. Please, pick one of the following only",
"expected-values": {
"type": "static",
"values": ["hello", "world"]
},
"session-field": "customer-key"
},
{"id": "ciao", "type": "send-mex", "message": "You wrote: !{customer-key}. Hello to you!"},
{"id": "mondo", "type": "send-mex", "message": "You wrote: !{customer-key}. Did you say word? Hello world!!"},
{"id": "question", "type": "send-mex", "message": "Do you like me?"},
{
"id": "get-answer",
"type": "wait-for-input",
"expected-type": "string",
"on-mismatch": "nah, that wasn't a valid option",
"expected-values": {
"type": "static",
"values": ["yes", "no"]
}
},
{"id": "sorry", "type": "send-mex", "message": "oh, I'm sorry... I'll try to do better"},
{"id": "wow", "type": "send-mex", "message": "WOW thanks! You are awesome too! <3"},
{"id": "bye", "type": "send-mex", "message": "Bye! See ya"},
{"id": "end", "type": "end"}
],
"relationships": [
{"from": "start", "to": "greet"},
{"from": "greet", "to": "wait-input"},
{"from" : "wait-input", "to": "ciao", "on-input": ["hello"]},
{"from" : "wait-input", "to": "mondo", "on-input": ["world"]},
{"from": "ciao", "to": "question"},
{"from": "mondo", "to": "question"},
{"from": "question", "to": "get-answer"},
{"from": "get-answer", "to": "sorry", "on-input": ["no"]},
{"from": "get-answer", "to": "wow", "on-input": ["yes"]},
{"from": "sorry", "to": "bye"},
{"from": "wow", "to": "bye"},
{"from": "bye", "to": "end"}
]
}
Run the bot with
java -jar kobot.jar
Now go to telegram and enjoy your first conversation with the bot!
A kobot conversation is represented as a simple json with two keys: states and relationships.
A state defines an action the bot will perform, while the relationships connect the actions together, forming the actual conversation. You can see some examples of conversation under the conversations folder.
In order to use Kobot, you have to create a:
application.properties
conversation.json
inside a config folder where you run the jar.
The application.properties contains application specific properties, such as database connection, logging level, etc In order to configure it, use the following as example:
logging.level.cloud.bernardinello.kobot=INFO
kobot.conversation.path=config/conversation.json
kobot.telegram.bot.name=<telegram bot name>
kobot.telegram.bot.token=<telegram bot token>
kobot.http.client.enabled=true
kobot.database.url=jdbc:h2:mem:store
kobot.database.username=h2
kobot.database.password=h2
kobot.database.driverClassName=org.h2.Driver
You can find some examples of conversations in the conversations folder
Every state must have a unique custom id property and a type properties.
There are different types, more will be developed:
- start or end, which indicate the beginning and the end of the conversation
- send-mex
- wait-for-input
- jdbc-read
- jdbc-write
All states must be connected in a path from the start node to the end node.
Each state can access session data, which are user-specific data saved during the conversation. For example, you can save the input from user in the session and use it later to query a db or invoking an API. Session data referring to key session-key can be accessed using
!{session-key}
You will find examples in each state on how to access this information.
A special session key provided by kobot is the !{chatId}, which indicates the user chat unique identifier.
{"id":"start", "type": "start"}
{"id":"end", "type": "end"}
These states indicates the beginning and the end of a conversation. Every conversation must have them and no other states can be before the start state or after the end state.
{"id":"send", "type": "send-mex", "message": "Welcome! Pick an option"}
{"id":"send", "type": "send-mex", "message": "Hi! Your chat is !{chatId}"}
This state makes kobot send the message to the user. You can use session-data for dynamic message: you can use the !{key}, but the key must be present in session. chatId is a special key, provided by the framework and indicates the unique user chat identifier.
{
"id": "wait-input",
"type": "wait-for-input",
"expected-type": "string",
"on-mismatch": "Choice not recognized. Please, pick one of the following only",
"expected-values": {
"type": "static",
"values": [ "hello", "world"]
}
}
{
"id": "show-retrieved",
"type": "wait-for-input",
"expected-type": "string",
"on-mismatch": "Not a valid option! Try again",
"expected-values": {
"type": "session",
"key": "db-data"
},
"session-field": "choice"
}
wait-for-input requires a expected-type field: this can be
- string
- number
on-mismatch is the message provided when the input doesn't match the expected values.
session-field is an optional key. If provided, the input will be stored in session at the provided variable name
expected-values type can be :
- static
- requires an array of values, which will be provided as input choices to the user and matched against given input
- session
- requires a key, which session-values will be provided as input choices to the user
{
"id": "get-content",
"type": "jdbc-read",
"query": "select bar from foobar where foo=!{choice}",
"session-field": "db-data"
}
this state requires a mandatory query and session-field. In order to perform queries, you have to configure the database access
The query is a SQL query, which can use session data in order to parametrize the query. The query must be a plain select and must fetch only a single column.
If multiple results will be fetched, data will be stored in a list. If a single value is fetched, it will provided as a scalar value.
session-field indicates the key of the session where data will be stored.
{
"id": "insert",
"type": "jdbc-write",
"query": "insert into foobar(foo, bar) values(!{foo}, '!{bar}')"
}
{
"id": "insert",
"type": "jdbc-write",
"query": "update foobar set foo=!{foo} where bar='!{bar}'"
}
This state runs an insert or update against the configured db.
You can use Session parameters, which are automatically decoded.
The only required field (except for id) is the query, which is a SQL valid query.
Be careful that only insert or update operations are accepted. If you use a select, an exception will be thrown at startup.
{
"id": "get-quote",
"type": "http",
"request": {
"method": "get",
"url": "<your url>",
"query-params": [],
"body-params": [],
"headers": {
"content-type": "application/json",
"accept": "application/json"
}
},
"extraction-key": "x.y.z",
"session-field": "session-field"
}
This state execute a HTTP get/post/put/delete request against a target endpoint.
Result is parsed using the "extraction-key" field, which can be in the form "x.y.z": this is usefull for parsing nested json object.
Result is stored in "session-field"
Remember to set
kobot.http.client.enabled=true
in your configuration file, in order to enable the HTTP client
Bot relationships defines transitions between states. A relationships defines the link between two states
{"from": "s1", "to": "s2"}
In a wait-for-input state, you can go to different states based on the input from user: imagine a Yes/No situation.
For relationsips from a wait-for-input states with static expected-values, you have to specify the message on the relationship. For example:
{"from" : "wait-input", "to": "ciao", "on-input": ["hello"]}
{"from" : "wait-input", "to": "mondo", "on-input": ["world, foo"]}
In this case, when in wait-input state and after receiving input hello, kobot will go to ciao state.
If world or foo is received, the mondo state will be reached. Please note that on-input values must exactly match the static expected-values provided in the state definition.
You will receive an error on startup in case
git clone https://github.com/besil/kobot.git
cd kobot
mvn clean package
Put your telegram.json and conversation.json inside the config folder
A tipical application.properties could be
logging.level.cloud.bernardinello.kobot=INFO
kobot.conversation.path=config/conversation.json
kobot.telegram.bot.name=<bot name>
kobot.telegram.bot.token=<bot token>
kobot.http.client.enabled=true
kobot.database.url=jdbc:h2:mem:store
kobot.database.username=h2
kobot.database.password=h2
kobot.database.driverClassName=org.h2.Driver
Then run
bash scripts/build-docker.sh
bash scripts/run-docker.sh
You can access H2 database on http://localhost:8080/h2-console
Apache Open Source