Skip to content
/ kobot Public

A configurable conversation bot framework, written in Kotlin

License

Notifications You must be signed in to change notification settings

besil/kobot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kobot - Conversational bot

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

Quickstart guide

Prerequisites

Define the configuration

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!

User guide

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.

Configuration

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

Kobot conversation

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:

All states must be connected in a path from the start node to the end node.

Session data

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.

State types

start/end State

{"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.

send-mex

{"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.

wait-for-input

{
  "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

jdbc-read

{
  "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.

jdbc-write

{
  "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.

http

{
  "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

Relationships

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

Developer guide

Build locally

git clone https://github.com/besil/kobot.git
cd kobot
mvn clean package

Docker

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

License

Apache Open Source

Written in Kotlin with love

About

A configurable conversation bot framework, written in Kotlin

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages