Creating a XMPP Chat Bot with Apache Camel
October 12th, 2014 by Micha KopsApache 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.
Contents
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
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
- Apache Camel Website
- Camel XMPP Component Documentation
- Claus Ibsen, Jonathan Anstey, Hadrian Zbarcea: Camel in Action
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: Apache, Atlassian, camel, chat, config, eip, hipchat, Java, maven, xmpp
November 3rd, 2014 at 2:02 pm
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
November 4th, 2014 at 8:00 am
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
May 19th, 2015 at 3:43 pm
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:
May 19th, 2015 at 6:37 pm
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
June 16th, 2015 at 9:11 pm
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?
June 17th, 2015 at 8:46 pm
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! :)
July 1st, 2015 at 6:03 pm
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.
July 1st, 2015 at 6:39 pm
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
July 2nd, 2015 at 5:44 pm
Do you think there’s a way to shutdown the bot through a command?
July 2nd, 2015 at 6:11 pm
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()
October 10th, 2015 at 12:25 pm
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?
September 21st, 2016 at 6:31 pm
Thanks a lot for the article! It helped me much!
September 22nd, 2016 at 5:52 am
Thanks, you’re welcome! :)