By Jason Weiss
January 16, 2008
The AOL Instant Messenger (AIM®) Java software developer kit (SDK) provides Java developers with relatively easy access to AOL instant messaging functionality. The true power of the SDK comes into focus when additional technologies are integrated with AIM, and that's precisely what I cover here. In this article, I show you how to create a personal translator bot, a mashup that combines the AIM Java SDK with a Java wrapper for Google Translate.
AIM bots can deliver advanced services directly to the user. These services range from simple to extremely complex, in which the service connects to multiple, disparate back-end services to provide value to the user. The personal translator bot embodies this idea, relying on the Google Translate service to provide real-time language translation. When you need to look up a phrase in another language, the bot stands at the ready in your Buddy List to do the translating for you. In fact, the bot you will be building in this article also supports translating an entire plain-text file sent via file transfer. The key concepts that I discuss here include creating an AIM bot by using the AIM Custom Client, session management, state management, and processing file transfers.
The bot I discuss in this article is based on the Java AIM Bot framework, which is open source, heavily commented, and is included with the sample code that accompanies this article.
Setting Up an AIM Bot Development Environment
You can quickly turn any AOL screen name into a bot at http://developer.aim.com and revert the bot back to a normal screen name just as easily. At least two screen names are required to develop an AIM Bot--one that will be flagged as the bot, and the other a normal screen name that will be used to communicate with and for testing the bot. For this reason, the logical first step in setting up an AIM bot development environment is to register a new screen name that will become the bot.
You can convert an existing screen name into a bot, or register a new screen name that can be converted to a bot, at http://developer.aim.com/bot. After creating the screen name, you need to complete the AIM Developer registration and agree to the terms of service.
After you convert the screen name to a bot, the next step is to obtain an AIM Custom Client key. AOL offers development and production keys; the only difference between them, essentially, is how many times a bot service is allowed to sign on to the AIM network and how many instant messages can be generated over a given period of time. Click Get a Custom Client Key to begin the process of obtaining the required license key. Without the key, the bot cannot sign on to the AOL network.
Next, click the registration process link in the What do I need to get started? section. The name of the bot is your choice--I named this one TranslatorBot. The URL is very important--it identifies the end point of the service and must map back to the IP address of the computer from which the bot will run. Both the bot name and the URL can be changed at any point quite easily; you can use a URL that points to the IP address of the development computer during development, then change the URL to its production domain name before going live with the bot. Following is a sample key that was generated during the development of the TranslatorBot:
Next, choose the OS flavor and download the SDK. The AIM Java SDK is located inconsistently and varies by download.
For Mac OS X, the AIM Java SDK .jar file can be found at: ./accsdk_macosx_univ_1_5/C++/lib/release
For Windows, the AIM Java SDK .jar file can be found at: .\accsdk_win32_1_5\dist\release
This mashup also requires the Google Translate Java wrapper, a single .jar file that you can download from http://code.google.com/p/google-api-translate-java/.
The final task in setting up a bot development environment isn't a task, per se. Java developers must be cognizant of the fact that the AIM Java SDK relies on the Java Native Interface (JNI) to communicate back to a C/C++ shared library; for example, a .dll in Windows or a .dylib in Mac OS X. That means that to be able to test any bot written in Java, both the Java class path and the Java library path must be properly configured; the latter ensures that the Java Virtual Machine (JVM) can find the native libraries at run time.
For Mac OS X, it is also necessary to export the following with an appropriate path:
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/path/to/lib/release
Bot Session Management
The primary interface for the AIM Custom Client is AccEvents. This interface defines over 60 methods that must be implemented! Conceptually, these methods can be thought of as event handlers because they are effectively call-back methods that the AIM Custom Client engine calls. In the bot framework that accompanies this article, the abstract Java class AbstractAccEventListener contains empty implementations of these methods. Another abstract class, AbstractBot, extends AbstractAccEventListener and implements a small and intuitive BotService interface. The AbstractBot class contains all of the routine code required to establish and run a bot. The very nature of the protected constructor ensures that any bots implemented with the framework provide the minimum set of configuration parameters required to run the bot service. With this object-oriented approach, bot developers can focus specifically on the events they are interested in and they can develop a series of bots in a consistent manner.
There are only four AccEvents methods you need to implement for the personal translator bot:
OnParticipantJoined( AccSession session, AccSecondarySession secondarySession, AccParticipant participant) OnImReceived( AccSession session, AccImSession imSession, AccParticipant participant, AccIm im) OnNewSecondarySession( AccSession session, AccSecondarySession secondarySession, nit serviced) OnFileXferComplete( AccSession session, AccFileXferSession xferSession, AccFileXfer xferFile, AccResult result)
By definition, a bot will likely deal with many different AIM buddies concurrently, so session management is critical. The OnParticipantJoined event is a strong choice for the bot to intercept a new participant and establish any initial state that might be required. In the case of translation, the state that needs to be managed is the buddy's target language for translation. The participant's preference is stored in a thread-safe ConcurrentHashMap instance, and its initial target language is set to Spanish.
The only other event that relates to session management is OnImReceived. Here, buddies could opt to change the target translation language by using a simple command:
language:<locale>
The list of supported locales is listed for buddies when they send an IM that simply reads "help." The instant message of the text can be retrieved using the AccIm instance passed to the OnImReceived method:
String msg = im.getConvertedText("text/plain");
From this point, developers are limited only by their imagination with respect to how they will process the text retrieved from the instant message the bot received. I discuss translation of the text in the following section.
Linking In Google Translate
The unofficial Google Translate Java API provides a single intuitive method for translating text from a source locale to a target locale. Earlier, I discussed how the OnParticipantJoined and OnImReceived events manage the buddy's target locale (for simplicity, the source locale is always assumed to be English). To retrieve a buddy's current target language, read the preference from the thread-safe Map:
String targetLanguage = participantLanguageMap.get(participant.getName());
At this point, both the original English message and the buddy's target language have been gathered. All that is left is to invoke the Google Translate API and to let it do the translation:
String xlated = Translate.translate(msg, Language.ENGLISH, targetLanguage);
The translated text is ready to be sent back to the buddy in response to the message. The OnImReceived method already has an AccIm instance ready to handle this:
im.setText(xlated); imSession.sendIm(im);
Within a split second, the translated phrase is returned to the buddy, as shown in the following image. The image demonstrates a sample translation session that shows the translation of "he eats" into French and Spanish.
Working with File Transfers: Translating Entire Documents
The ability to transfer a file to a buddy has proven to be an invaluable AIM service. It is easy to envision a scenario in which the text to be translated is stored in a document. Extending a bot to process file transfers involves coding two more distinct events, OnNewSecondarySession and OnFileXferComplete.
When AIM initiates a file transfer, it actually does so under what it defines as a secondary session. In other words, the main chat window operates in one session; the file transfer operates in another, subordinate session. This makes sense because while a large file is being transferred, you can continue exchanging instant messages with a buddy; secondary sessions enable this capability. When the buddy initiates a file transfer to the bot, the bot needs to formally accept the secondary session. Because there are many different types of secondary sessions (for example, AudioVideo, FileSharing, and FileXfer), serviceId identifies the type of secondary session that has been requested. For file transfer, a simple check can identify a file transfer request:
if (AccSecondarySessionServiceId.FileXfer.value() == serviceId)...
After the secondary session has been positively identified as a file transfer, accept the session and the file transfer will automatically begin in the background:
secondarySession.accept();
You can control the destination directory on the server where the files are transferred by using a preference. In fact, the AbstractBot class automatically configures this preference when the directory is passed to the protected constructor:
prefs.setValue("aimcc.folders.downloads", _downloadDirectory);
After the file transfer has completed, the OnFileXferComplete event is triggered, indicating that the file is ready for processing by the bot. The translation of the plain-text file is relatively straightforward. The file is read in the text using a FileReader instance, which then takes that text and the buddy's current target locale and passes it off to Google Translate. The size of the text is limited only by what Google Translate will handle, so a strategy of breaking the document into pages might be necessary.
You can communicate the file-translating status to your buddy while the translation is underway from within the OnFileXferComplete event.
AccIm im = session.createIm("File received...translating", "text/plain");
AccImSession imSession = session.createImSession(xferSession.getRemoteUserName(),
AccImSessionType.Im);
imSession.sendIm(im);
The master session object provides a method that creates an AccIm instance, passing the message in the first parameter and the Multipurpose Internet Mail Extensions (MIME) type in the second session. The session object is used again, this time to create an instant message session with the named buddy. The buddy's name is affiliated with the file transfer session (remember, this is a bot servicing many clients at once!) and can be accessed by using the AccFileXferSession instance passed to the method. And, of course, the session type is an instant message, AccImSessionType.Im. Finally, use the imSession instance to send the instant message.
After the file has been translated and written back to the server, it can be transferred back to the buddy who requested it. You can send a file programmatically by using a single line of code:
session.getFileXferManager().send( xferSession.getRemoteUserName(), translatedFile.getAbsolutePath(), "Here is your translated document", AccFileXferFlags.ForceNonSecure);
Using the parent session, get a file transfer manager instance and let its send() method do the heavy lifting. The first parameter is the name of the buddy who will receive the file, followed by the absolute path to the translated file, an optional message to display to the buddy, and a flag that indicates whether the file must be sent securely or not.
Running the Personal Translator Bot
The bot code does not include either a preconfigured bot screen name or a license key. To use the code, you first need to obtain a bot-enabled screen name and a license key, as I described earlier in this article. The directory structure of the .zip file that contains the code is as follows:
- The
./distdirectory contains a pre-compiled version of the bot and a bot.properties file that you must configure with your bot-enabled screen name, password, and license key. - The
./libdirectory contains the Mac OS X libraries (drop in the aforementioned .dll files if running Windows). - The
./srcdirectory contains the Java source, including the full Java AIM Bot framework inside the package com.beadlefox.bot.framework. - The
./rootdirectory contains an Apache Ant build script.
You can execute the bot as-is after unzipping it by opening up a shell window in the ./lib directory. The Main class expects a single argument that is the path to a .properties file that contains the configuration data. Run the following commands on a single line to start the bot:
For Mac OS X:
java -Djava.library.path=. -cp ./accjwrap.jar:./google-api-translate-java-0.26.jar:../dist/ com.beadlefox.bot.Main ../dist/bot.properties
For Windows:
java -Djava.library.path=. -cp .\accjwrap.jar;.\google-api-translate-java-0.26.jar;..\dist\ com.beadlefox.bot.Main ..\dist\bot.properties
As with any mashup, there are a number of moving parts involved and it is possible that something could be improperly configured. Here are some suggestions to work around common problems.
- If there is an error about the native library not being found, then the underlying C/C++ library that the AIM Custom Client requires cannot be found in the Java library path. If you are on a Mac, double-check that the DYLD_LIBRARY_PATH has been properly configured.
- If there is a problem logging into the AIM service, ensure that the screen name, password, and the URL that you entered at http://developer.aim.com are accurate. If the URL you entered there does not map to the computer on which you are running the bot, client computers will not see the bot.
- Make sure there is a valid license key in the .properties file, and that the path to the .properties file passed into the program is accurate.
- The locales are restricted to those that Google supports; Google translation pairs might be at fault. For example, translating from English to English doesn't make sense, and Google Translate will report an error. The bot code in this sample is not production-ready, meaning that there is no check to ensure that the user chooses a language pair that makes sense.
Conclusion
In this article, I've demonstrated just how easy it is to use the AIM Java SDK as part of a mashup. By combining the virtues of instant messaging with powerful third-party technologies, the possibilities are endless. You can write powerful bots that deliver true value to users. For example, you could write a bot that delivers tracking status updates of a package over AIM. Or, you could write a bot that initiates a conversation with a manager when a critical event occurs (or doesn't occur) in a back-office setup. Only by creating a mashup of the AIM Java SDK with one or more of the plethora of libraries that exist today will the true value of bots become self-evident. Happy coding!
