Wit.ai platform Java Client

Recently, I decided to build a smarter bot and use wit.ai platform for it. This tool integrates NLP and allows you to extract meaningful data from user messages. In other words, to make your bot more human-like. The tool looks powerful, the only problem is an absence of Java client library. So, I decided to build one. It’s built for my specific needs and tried to generalise. It covers two main API methods now, so it’s a long way to go. You could support the development with issues and pull requests. The source code could be found on github and when it will be stable, publish it to maven central.

I won’t tell about building the library, I’ll describe how to use it on example bot for Telegram.

First of all, you need to make a story on wit.ai. I made a simple flow, when user asks for a joke, bot executes getJoke() method and returns text of the joke. To understand the principle of wit.ai, I encourage you to look through the documentation, it is pretty demonstrative.

screenshot

So, let’s start to code. First of all we need to init the client library.

client = new WitClientBuilder("YOUR_WIT_AI_TOKEN")
        .addActionHandler("getJoke", new JokeAction())
        .setMessageHandler(new JokeMessage(this)).build();
context = new ChatContext();

We have two possible bot actions, which are “send text” and execute getJoke. To send a message, you should implement com.featurefactory.witjava.MessageHandler. The implementation varies depending on messenger. In my case, I need to know chat ID and the instance of bot class to send the message, so it looks like this.

public class JokeMessage implements MessageHandler{
    private  SampleBot bot; //Bot instance

    public JokeMessage(SampleBot bot) {
        this.bot = bot;
    }

    @Override
    public void sendMessage(String message, Map<String, Object> chatMetadata) {
        SendMessage req = new SendMessage();
        req.setChatId(chatMetadata.get("chat_id").toString());
        req.setText(message);

        try {
            bot.sendMessage(req);
        } catch (TelegramApiException e) {
            throw new RuntimeException(e);
        }
    }
}

The ActionHandler it’s part of API which actually gets some data to pass it to user and for each method we need a separate class which implements com.featurefactory.witjava.ActionHandler. Context is a key object to transfer data between your bot and wit.ai service. When you get data, write it to context with key, specified in wit.ai story. In my case it’s joke. Run method takes a tricky type for the intent parameter, that is because of equally tricky JSON structure, received from wit.ai.

public class JokeAction implements ActionHandler{

    private List<String> jokes;
    private Random randomGenerator;

    public JokeAction() {
        jokes = Arrays.asList(
                "What is a robot’s favorite type of music? \n Heavy metal!",
                " Why did the robot go back to robot school? \n Because his skills were getting a little rusty!",
                "What do you get when you cross a robot and a tractor? \n A trans-farmer!");
        randomGenerator = new Random();
    }

    @Override
    public ChatContext run(Map<String, List<Map<String, Object>>> entities, ChatContext context) {
        if(entities.containsKey("intent") && entities.get("intent").get(0).get("value").equals("joke")){
            context.setValue("joke", getRandomJoke());
        }
        return context;
    }

    private String getRandomJoke() {
        int index = randomGenerator.nextInt(jokes.size());
        return jokes.get(index);
    }
}

Now we need to react on incoming messages from a user. Method WitClientBuilder#converse() executes the story flow, it stops either when the story ends or when you’re waiting for user input. It takes user message, session id, context and chat metadata, which we use in the message handler. According to documentation, each conversation even with the same user should have a unique session id. I generate it on each user message but in case of a complex story, converse() returns boolean flag which is false when the dialogue is finished and the session should be expired.

else if(message.hasText()){
    Map<String, Object> chatMetadata = new HashMap<>();
    chatMetadata.put("chat_id", message.getChatId().toString());
    client.converse(message.getText(), UUID.randomUUID().toString(), context, chatMetadata);
}

That is it. Now you could write more clever bots using java library. I didn’t cover Telegram messenger platform which I used, you could read more about building bot’s on it in this post.