Creating a XMPP Chat Bot with Apache Camel

October 12th, 2014 by

Apache Camel not only is one of my favourite frameworks ever but it also allows the humble developer to create a full blown chat bot within a few lines of code and using the Camel XMPP component.

In the following tutorial, we’re going to create a simple chat bot and since Atlassian’s HipChat basic plan is now free for unlimited users, we’re using HipChat as our play- and testing ground for the bot.

XMPP Bot connected to HipChat

XMPP Bot connected to HipChat

 

Project Setup / Dependencies

Using Maven, we’re adding three dependencies to our pom.xml: Camel Core, Camel XMPP Components and Config-Builder for our configuration management.

<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-core</artifactId>
	<version>2.14.0</version>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-xmpp</artifactId>
	<version>2.14.0</version>
</dependency>
<dependency>
	<groupId>com.tngtech.java</groupId>
	<artifactId>config-builder</artifactId>
	<version>1.3</version>
</dependency>

We’re using Camel with Java in this tutorial – but if you prefer using Scala, porting the following examples shouldn’t be a problem – please feel free to have a look at my blog article “Using Apache Camel with Scala and the Camel Scala DSL” for further details about the Camel Scala DSL.

HipChat Setup (optional)

HipChat with the basic plan is free with unlimited users and no need to enter credit card information or stuff like this.

So basically to create a HipChat instance for our bot we need to do..

  • Register a new account at https://www.hipchat.com/sign_up
  • Create a new room
  • Create a user account for the bot
  • Make sure, that the bot-account may access the room

That’s all …

Creating the Bot

Now that the playground for our bot conversations is ready we’re ready to write the bot application with a few lines of code.

Managing Configuration

First of all, we’re using a property file to hold our configuration details. To access this information, I’m using the config-builder library here that allows us to bind fields of a POJO to the elements of the properties file using simple annotations.

If you’re interested in some more details here, please feel free to have a look at my blog article “Using Java Config-Builder to assemble your Application Configuration“.

This is our configuration class:

package com.hascode.app;
 
import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles;
import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue;
 
@PropertiesFiles("config")
public class Config {
	@PropertyValue("port")
	private final int port = 5222;
 
	@PropertyValue("host")
	private String host;
 
	@PropertyValue("user")
	private String user;
 
	@PropertyValue("password")
	private String password;
 
	@PropertyValue("room")
	private String room;
 
	@PropertyValue("nickname")
	private String nickname;
 
	// getter, setter ommitted ...
}

This is our config file src/main/resources/config.properties fill the valid properties according to your environment or HipChat account:

user=12345_67890
nickname=hasCode Bot
password=ohiamsoosecret
host=chat.hipchat.com
port=5222
room=12345_bot_test

*Update*: We may obtain all values for our property file from the HipChat configuration (being logged in as administrator):

  • user: Account Settings -> XMPP/Jabber Info -> Username
  • password: The user account password
  • host: Account Settings -> XMPP/Jabber Info -> Account info -> Connect host
  • port: Account Settings -> XMPP/Jabber Info -> Account info -> Connect port
  • room: Account Settings -> XMPP/Jabber Info -> Account info -> Connect host

Bot Logic and Camel Routes

Basically we’re doing all the work with a single route. We’re able to create an XMPP endpoint using an URL like this: “xmpp://user@host:port?room=chatroom&password=xxx&nickname=Bot“.

When a new message arrives from the XMPP endpoint we’re first filtering for messages containing a valid bot command – valid commands are:

  • bot date: displays the current date
  • bot help: displays a usage hint

Having filtered the chat messages for valid bot commands, we’re now using a content-based-router to delegate to the appropriate command handling.

Finally we’re sending some output to the XMPP endpoint so that the bot talks to the designated HipChat room.

package com.hascode.app;
 
import java.time.Instant;
 
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.xmpp.XmppMessage;
import org.apache.camel.main.Main;
import org.jivesoftware.smack.packet.Message;
 
import com.tngtech.configbuilder.ConfigBuilder;
 
public class Bot {
	public static void main(final String[] args) throws Exception {
		new Bot().run();
	}
 
	private void run() throws Exception {
		Config config = new ConfigBuilder<Config>(Config.class).build();
		String chatEndpoint = String.format("xmpp://%s@%s:%s?room=%s&password=%s&nickname=%s", config.getUser(), config.getHost(), config.getPort(), config.getRoom(), config.getPassword(),
				config.getNickname());
 
		Main main = new Main();
		main.addRouteBuilder(new RouteBuilder() {
			@Override
			public void configure() {
				from(chatEndpoint)
				.filter(exchange -> isValidBotCommand(exchange))
				.choice()
					.when(exchange -> getSmackMessage(exchange).getBody().contains("date"))
						.setBody(constant("It's " + Instant.now()))
						.to(chatEndpoint)
					.otherwise()
						.setBody(constant("Available commands: bot date, bot help"))
						.to(chatEndpoint);
			}
		});
		main.enableHangupSupport();
		main.run();
	}
 
	private boolean isValidBotCommand(final Exchange exchange) {
		return getSmackMessage(exchange).getBody().matches("bot (date|help)");
	}
 
	private Message getSmackMessage(final Exchange exchange) {
		XmppMessage xmppMessage = (XmppMessage) exchange.getIn();
		Message smackMessage = xmppMessage.getXmppMessage();
		return smackMessage;
	}
 
}

The Bot in Action

Running the bot should not be a problem, we simply need to fill the config.properties with our account information and should afterwards be able to do some bot-conversation as shown in the following screen-shot.

Run it from your IDE or using the following Maven command:

mvn compile exec:java -Dexec.mainClass=com.hascode.app.Bot
XMPP Bot connected to HipChat

XMPP Bot connected to HipChat

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/camel-hipchat-bot.git

Resources

Article Updates

  • 2014-11-04: Added Maven snippet to run the bot.
  • 2015-05-19: Detailed information about settings and configuration details added.
  • 2015-06-17: Details about running the bot with Maven added.

Tags: , , , , , , , , ,

13 Responses to “Creating a XMPP Chat Bot with Apache Camel”

  1. Chris Says:

    Sorry for the freshman question:

    I downloaded your git repo and compiled with: “mvn install”

    Now how to start the Bot?
    - “mvn compile”
    - “mvn exec:java”

    Thanks, Chris

  2. micha kops Says:

    Hi Chris,

    I’ve added some sample code to the article for running the bot. To answer your question – you may run the bot the following ways:

    - Using Maven: mvn exec:java -Dexec.mainClass=com.hascode.app.Bot
    - In your IDE: run Bot.java
    - Packaged: run mvn package and afterwards java -cp file.jar com.hascode.app.Bot

    Please don’t forget to modify the contents of the properties file first so that your bot is able to connect to the designated HipChat instance!

    Please feel free to ask if there is another question! :)

    Cheers,

    Micha

  3. Martin Says:

    It could use a bit more detail on the hipchat setup, I can’t see how to correlate the hipchat credentials with the config file, whenever I try it I get:

    Exception in thread “main” org.apache.camel.FailedToCreateProducerException: Failed to create Producer for endpoint: Endpoint[xmpp://botbot@chat.hipchat.com:5222?nickname=botbot&password=xxxxxx&room=botroom]. Reason: java.lang.RuntimeException: Could not connect to XMPP server: chat.hipchat.com:5222/null

    Caused by: SASL authentication PLAIN failed: invalid-authzid:

  4. micha kops Says:

    Hi Martin,

    I have updated the article to give some more detailed information where to obtain the information for the property file from the HipChat configuration panels.

    Please feel free to ask if you have got any further question!

    Cheers,

    Micha

  5. Kapil Says:

    I cloned your tutorial resources using git, modified the config.properties file with the appropriate information, and tried running the bot through the terminal using the command you specified, but the build fails with the error displayed as “java.lang.ClassNotFoundException: com.hascode.app.Bot”

    Do you have any idea how I can fix this? Is there anything I missed?

  6. micha kops Says:

    Hi Kapil,

    you need to compile the project before, so running mvn compile exec:java -Dexec.mainClass=com.hascode.app.Bot should do the work for you!

    I have updated the tutorial to reflect this fact.

    Please feel free to post further problems here! :)

  7. Kapil Says:

    Thanks for your help!

    This is not so much a problem, but a general question: Is there a way to end the session through a command to have the bot log out? Currently, every time i want to end the session with the bot, I disconnect by quitting from my IDE but I’m sure there’s a better way.

  8. micha kops Says:

    Hi Kapil,

    do you want to stop the Camel route or simply shutdown the bot application? One simple way could be to listen to a third bot command named “die” or sth like that and shutdown the application. Or to deactivate the corresponding Camel route (http://camel.apache.org/how-can-i-stop-a-route-from-a-route.html).

    Cheers,

    Micha

  9. Kapil Says:

    Do you think there’s a way to shutdown the bot through a command?

  10. micha kops Says:

    Hi Kapil,

    I haven’t tried it, but i think you could add the following snippet to the choice() block to implement a new bot command (“die”) to end the current route-processing:

    .when(exchange -> getSmackMessage(exchange).getBody().contains(“die”)).stop()

    To shutdown the application you could use a similar approach but use the context’s stop method http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html#stop()

  11. Achyut Says:

    Can we use this bot for all rooms inside one team?
    or let’s say how can we install this bot in a TEAM/COMPANY so, that all rooms inside that COMPANY can use “bot date” command?

  12. Vladimir Says:

    Thanks a lot for the article! It helped me much!

  13. Micha Kops Says:

    Thanks, you’re welcome! :)

Search
Categories