Blog

15Sep
2005
Building An IM Bot - Part II

As should be clear by Part I of this post, creating a Google Talk IM bot requires the following three items:

  • An installed ColdFusion gateway type that is compatible with the Google Talk network. As Google Talk is built on top of Jabber which is XMPP based, the XMPP gateway that comes with ColdFusion fits the bill.
  • A configuration file specifying the Google Talk account name and password to be used by the bot.
  • A CFC file to actually process and respond to inbound IM messages.

Obviously, any IM bot needs an IM user account, so the first thing you'll need to do is obtain a Google Talk account. Google Talk uses GMail addresses and passwords, so obtain a new GMail account if needed, or use any unused account (don't use an account that you yourself will be using with Google Talk, the same account cannot be logged in twice of course, so the bot needs its own account).

Next you'll need to save that account information into a configuration file. Gateway config files are typically stored in /gateway/config under the ColdFusion root (c:\cfusionmx7\gateway\config on a Windows standalone installation, c:\jrun4\servers\cfusion\cfusion-ear\cfusion-war\web-inf\cfusion\gateway\config on the default ColdFusion instance on a Windows multi-server installation), although they could be stored elsewhere too. That folder may contain a sample XMPP configuration file named xmpp.cfg which you may copy and modify. Your configuration file should contain (at a minimum) the following entries (obviously setting userid and password to your GMail account information):

view plain print about
1userid=account@gmail.com
2password=password
3resourceName=ColdFusion MX 7
4secureprotocol=TSL
5securerequirement=true
6serverip=talk.google.com
7serverport=5222

Next you need the CFC that will respond to inbound requests. At a minimum you need two methods in your CFC, onIncomingMessage and onAddBuddyRequest.

All instant messages sent to your bot are routed to a method named onIncomingMessage. This method is the one that actually receives all inbound messages, and this is where you'd process user requests, generating responses if needed.

Here is a simple example:

view plain print about
1<cffunction name="onIncomingMessage">
2 <cfargument name="CFEvent" type="struct" required="yes">
3
4 <!--- Get the message --->
5 <cfset var message=CFEvent.data.message>
6 <cfset var originatorID=CFEvent.originatorID>
7
8 <!--- Result structure --->
9 <cfset var retValue=structNew()>
10 <cfset retValue.BuddyID= originatorID >
11 <cfset retValue.Message="You said: #message#">
12
13 <!--- send the return message back --->
14 <cfreturn retValue>
15</cffunction>

All methods invoked by gateways receive a single argument, a CFEvent structure (the contents of which vary based on the gateway type). In this example, two local variables are created containing values extracted from the CFEvent structure, message is the actual text sent by the user, and originatorID is the sender name.

Typically your bot would do some processing based on the contents of message, but this simple example just echoes the content back to the sender. A return structure is created, and two values are set, BuddyID is set to originatorID (so that the message is sent back to the sender), and Message contains a string echoing the received message.

And that is all that is needed. The return structure is returned by the <cfreturn> tag, and the echo will be sent back to the sender.

That's onIncomingMessage. Next comes onAddBuddyRequest. When you add a user to your own IM buddy list, the network sends a message to that user asking them to accept or deny the request. If accepted you'll be able to see when that user is online and will be able to send him or her messages, and if denied then you'll not be able to see online status, and you may not even be able to send messages. When a user adds your bot as a buddy, the server will ask it to accept or decline the request. Obviously, these requests must be handled programmatically (as there is no actual user), and so these are routed to a CFC method named onAddBuddyRequest. This method can always return an accept response, or can conditionally accept or decline based on some code (maybe a database lookup, or some password or code provided, and so on).

Here is an example:

view plain print about
1<cffunction name="onAddBuddyRequest">
2 <cfargument name="CFEvent" type="struct" required="true">
3 <cfset var retValue = structNew()>
4
5 <cfset retValue.Command="accept">
6 <cfset retValue.BuddyID=CFEvent.data.sender>
7<cfset retValue.Reason="Welcome!">
8
9 <cfreturn retValue>
10</cffunction>

Once again, onAddBuddyRequest receives a CFEvent structure. This method must respond to the network either accepting or denying the request. Here a return structure is created. Command is set to accept, accepting the request (set it to decline to deny the request). BuddyID is the name of the user being accepted, and Reason is a welcome message that may be sent to the user (depending on the network and IM client being used). And like before, the return structure is then returned using the <cfreturn> tag.

Other methods are supported too. And best practices dictate that every method be present in your CFC, even if they are not all used. So every IM bot CFC you create should probably contain the following (even if the methods are all empty):

view plain print about
1<cffunction name="onAddBuddyResponse">
2 <cfargument name="CFEvent" type="struct" required="YES">
3</cffunction>
4
5<cffunction name="onBuddyStatus">
6 <cfargument name="CFEvent" type="struct" required="YES">
7</cffunction>
8
9<cffunction name="onIMServerMessage">
10 <cfargument name="CFEvent" type="struct" required="YES">
11</cffunction>
12
13<cffunction name="onAdminMessage">
14 <cfargument name="CFEvent" type="struct" required="YES">
15</cffunction>

The CFC can be saved anywhere on your server. The default path for gateway CFC files is /gateway/cfc under the ColdFusion root (c:\cfusionmx7\gateway\cfc on a Windows standalone installation, c:\jrun4\servers\cfusion\cfusion-ear\cfusion-war\web-inf\cfusion\gateway\cfc on the default ColdFusion instance on a Windows multi-server installation).

With the config file and CFC saved, you can now register the new gateway instance. Here are the steps needed:

  1. Open the ColdFusion Administrator.
  2. Go to the Event Gateways, Settings screen, and make sure that Enable ColdFusion Event Gateway Services checkbox is checked.
  3. Next, go to the Event Gateways, Gateway Instances screen.
  4. Use the form at the top of the screen to add your new gateway instance. Start by specifying a unique Gateway ID.
  5. In the Gateway Type field select XMPP – XMPP Gateway.
  6. In the CFC Path field specify the path to your gateway CFC file.
  7. In the Configuration File field specify the path to your gateway configuration file.
  8. To have your gateway start automatically upon ColdFusion server start, set Startup Mode to Automatic, otherwise set this field to Manual.
  9. And finally, click the Add Gateway Instance button to save your new gateway instance.

To start the gateway, click the green Start button in the gateway Action column. The status will change to Starting, and then Running. And once running, your Google Talk IM bot will be ready to receive and respond to requests.

But what if the bot won't start? The Debugging & Logging, Log Files screen contains a file named eventgateway.log (this file will be created the first time event gateways are used). You can inspect this log file to determine what your gateway is doing and what errors it may have thrown.

And with that, you are all set to create a Google Talk IM bot. In a subsequent post we'll look at other IM networks, and advanced IM bot functionality.

Related Blog Entries

Comments (14)



  • Raymond Camden

    Why not share the CFC for your googlebot?

    #1Posted by Raymond Camden | Sep 15, 2005, 05:26 PM
  • Raymond Camden

    You have a bug in your code. It isn't:

    <cfset var originatorID=CFEvent.data.originatorID>

    but

    <cfset var originatorID=CFEvent.originatorID>

    #2Posted by Raymond Camden | Sep 15, 2005, 05:42 PM
  • Ben Forta

    Thanks Ray, I fixed that.

    And yes, I will post that code, I have quite a bit more I want to post on the subject, starting with other IM networks and IM helper functions. More to follow.

    #3Posted by Ben Forta | Sep 15, 2005, 09:31 PM
  • Michael Dawson

    I am sure that, by now, many people have added the cfdocs "buddy" to their Google Talk buddy list. Is there anyway you can programmatically retrieve a list of those buddies?

    For example, could you write a function that returns a count of buddies if I send "show buddy count" to cfdocs?

    Also, does Google Talk have a "history" of interactions (not necessarily chat logs) between cfdocs and its buddies, or do you need to log the interactions within your gateway?

    In other words, can Google Talk tell you how many times I sent an IM to cfdocs, or when I added cfdocs to my buddy list?

    M!ke

    #4Posted by Michael Dawson | Sep 16, 2005, 09:15 PM
  • Michael Dawson

    Never mind. I found part III of the IM bot discussion. It answered all of my questions.

    #5Posted by Michael Dawson | Sep 16, 2005, 09:27 PM
  • Anthony

    I keep all my ports locked down on my server, is 5222 the only one i would need to have opened?

    #6Posted by Anthony | Oct 20, 2005, 08:41 AM
  • David Droddy

    I am working on CFMX7 but could not find the Event Gateways page in the Administrator--so, I went into the Administrator directories to see if I could force my browser to open the pages. I found the admin pages and pointed my browser to /administrator/eventgateway/index.cfm. I got the following error:

    Element ENABLEEVENTGATEWAYSERVICE is undefined in GLOBALS.

    Next, I tried /administrator/eventgateway/gateways.cfm

    This page loads, but there are no predefined Gateway Types.

    Can anyone help me figure out what's going on here?

    #7Posted by David Droddy | Jan 23, 2006, 04:21 PM
  • since1968

    All three articles in the series were excellent; thanks for posting.

    One thing I haven't grasped: how do I "push" data to an event gateway? For example, if I have a component "Orders" with the method "add", I'd like to invoke a method in my gmail CFC every time an order is added. That way I could send an alert to google talk/ichat when someone places an order.

    Is this feasible?

    #8Posted by since1968 | Mar 7, 2006, 03:09 PM
  • since1968

    OK, answering my own question you can use sendGatewayMessage, called from any CFC, to broadcast.

    #9Posted by since1968 | Mar 8, 2006, 12:34 AM
  • Zimbie

    Every heard of zimbie...

    #10Posted by Zimbie | Apr 19, 2007, 11:39 AM
  • Niall

    @David Droddy

    Just thought I'd add a response to your question about the error:
    Element ENABLEEVENTGATEWAYSERVICE is undefined in GLOBALS.

    I know it's not timely but it happened to me also, and when 'googled', the only result was Ben's Blog.

    Anyway, to overcome this issue all you simply have to do is: Restart your CF services.

    This resolved the issue for me.

    HTH

    #11Posted by Niall | Aug 9, 2007, 04:43 AM
  • Phillip Gagnon

    Ben-

    I followed your tutorial ver batum several times to no avail. I copied your code, and replaced the nessicary values (username, password, etc) and I cannot get the service to start. I do not get any error message, it just says "starting." It never gets past that.

    Any thoughts?

    Thanks!!!

  • Phillip Gagnon

    --Follow up to my above post
    I copied and pasted the exact same code to another computer, running CF8 and had no issue. The test server is running CF7

    Thanks
    -PG

    #13Posted by Phillip Gagnon | Aug 21, 2008, 06:16 PM
  • Ben Forta

    Phillip,

    There is a .jar file in CF7 that was updated at one point, I believe it is smack.jar, and the newer one solved that problem. I am not sure where the updated .jar file is anymore though (you may be able to use the one from CF8, but I am not suggesting you do that without a goo backup and thorough testing).

    --- Ben

    #14Posted by Ben Forta | Aug 22, 2008, 11:32 AM