SmackBot - XMPP Bot using the Smack Library
This bot is currently called SmackBot (I think I'll change it to XMPPBot) The SmackBot is developed using the
Smack Library Its currently running in my personal SnipSnap at
http://senikk.com I apriciate suggestions, just post a comment.
Ideas
Todo
Add connection listener
Improve connection listener add degrading polling interval while trying to reconnect.
CHANGE This functionality will soon be included in the Smack Library
- Need to fix the roster handling and groups
- Add an url to the snip in sendHeadline
Config
app.xmpp.server=
app.xmpp.user=
app.xmpp.password=
app.xmpp.resource=
The Code
package org.snipsnap.notification.xmpp;// SnipSnap + Radeox import
import org.snipsnap.app.Application;
import org.snipsnap.config.AppConfiguration;
import org.snipsnap.snip.SnipSpace;
import org.snipsnap.snip.SnipSpaceFactory;
import org.snipsnap.user.UserManager;
import org.radeox.util.logging.Logger;// Smack import
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.filter.FromContainsFilter;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;public class SmackBot
{
private static SmackBot instance;
private XMPPConnection xmpp;
private String xmppServer;
private String xmppUser;
private String xmppPassword;
private String xmppResource; public static synchronized SmackBot getInstance() {
if (null == instance) {
instance = new SmackBot();
}
return instance;
} //
// Listeners
// private ConnectionListener conlistener =
new ConnectionListener() {
public void connectionClosed() {
Logger.warn("XMPP Connection Closed");
connect();
} public void connectionClosedOnError(Exception e) {
Logger.warn("XMPP Connection Error: "+e.toString());
connect();
}
}; private PacketListener listener =
new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message)packet;
postBlogItem(message.getSubject(),message.getBody());
}
}; //
// Connect
// private void connect()
{
try {
xmpp = new XMPPConnection(xmppServer); if(xmpp.isConnected()) {
Logger.warn("SmackBot: connected");
xmpp.login(xmppUser,xmppPassword,xmppResource);
}
else Logger.warn("SmackBot: not connected");
} catch(Exception e) { Logger.warn("SmackBot unable to connect",e); }
} //
// The Bot
// public SmackBot() {
try {
// Read configuration
AppConfiguration conf = Application.get().getConfiguration();
xmppServer = conf.getProperty(conf.APP_XMPP_SERVER);
xmppUser = conf.getProperty(conf.APP_XMPP_USER);
xmppPassword = conf.getProperty(conf.APP_XMPP_PASSWORD);
xmppResource = conf.getProperty(conf.APP_XMPP_RESOURCE); // Login into xmpp server
connect(); // Add connection listener
xmpp.addConnectionListener(conlistener); // Add packet filter - needs to be changed
PacketFilter postfilter = new AndFilter(new PacketTypeFilter(Message.class),new FromContainsFilter("senikk@jabber.org/Psi"));
PacketCollector myCollector = xmpp.createPacketCollector(postfilter);
xmpp.addPacketListener(listener,postfilter); // Add group to Roster
Roster roster =xmpp.getRoster();
roster.setSubscriptionMode(Roster.SUBCRIPTION_ACCEPT_ALL);
roster.createGroup("registered"); Logger.debug("Registered Groups: "+roster.getGroupCount()+" Entries: "+roster.getEntryCount());
} catch (Exception e) { Logger.warn(e.toString()); }
} public void postBlogItem(String title, String body) {
// Get blog instance
String user = Application.get().getConfiguration().getAdminLogin();
Application.get().setUser(UserManager.getInstance().load(user));
SnipSpaceFactory.getInstance().post(body,title);
} //
// Roster
// public void addToRoster(String jid, String group) {
Roster roster = xmpp.getRoster();
String[] groups = {group};
try {
roster.createEntry(jid,jid,groups);
} catch(XMPPException e) { Logger.warn(e.toString()); }
} //
// Presence
// public String onlineStatus(String jid) {
Roster roster = xmpp.getRoster();
Presence presence = roster.getPresence(jid);
if(presence!=null) return presence.getStatus();
else return "";
} public String onlineMode(String jid) {
Roster roster = xmpp.getRoster();
Presence presence = roster.getPresence(jid);
if(presence!=null) return presence.getMode().toString();
else return "";
} //
// Headline
// public void sendHeadline(String user, String message) {
try {
Logger.debug("Sending '" + message + "' to '" + user + "' ..."); Message msg = new Message(user,Message.Type.HEADLINE);
msg.setSubject(Application.get().getConfiguration().getName());
msg.setBody(message);
xmpp.sendPacket(msg);
Logger.debug("Sent.");
} catch (Exception e) {
Logger.warn("Unable to send message to " + user, e);
}
}}
The Smack LibraryA simple example of sending a message to me with smack:
XMPPConnection con = new XMPPConnection("jabber.org");
con.login("user","password");Chat chat = con.createChat("senikk@jabber.org");
chat.sendMessage("Hey, how's it going?"); Group Chat
When you register yourself at SnipSnap site or maybe when you add yourself a {jabberID} label I think of sending a jabber invite request to this jid telling that there exists a chat room for this site. I don't think the users would like to have such request every time they login and they are not in the chat room, but this could be an option.
With smack you can write such request as:
Packet invite = new Packet() {
public String toXML() {
return
"<message to='senikk@jabber.org/Psi' from='senikk@jabber.org/qexo-weblog'>" +
"<body>SnipSnap invite you to chat</body>" +
"<x xmlns='jabber:x:conference' jid='qexo-weblog@conference.jabber.org' />" +
"</message>";
}
};
con.sendPacket(invite); Icons
I read somewhere that someone had made some sort of jabber: url to lets say open the user jabber client chat window. But I don't think it has become a standard yet.
draft-ietf-xmpp-uriAnother solution I think of is to use the invite message above to send an invite message when you click on the SnipSnap users jabber icon, this demands that you have to be registered and logged into the SnipSnap site and have the {@jabberID} label in your snip. When the user receive the invite message he/she can initiate the chat with you.
Another possibility I think could be clicking on an "add to your Roster" icon sending a Roster message to your jid with the clicked SnipSnap user jid. Maybe this could be display for the {@jabberID} label in the users snip.
Online Status
By having a jabber bot adding every SnipSnap user with a {@jabberID} label to its Roster, I think it should be easy to extend lets say the {logins} macro to have the possibility to show the correct jabber icon symbol telling if you are online or not.
This makes it also possible I think to send anouncement messages to all registered users.
Affected macros / label
{logins}
{list-of-users}
{@jabberID:senikk@jabber.org}