Creating a C# .NET Jabber Chatbot that Talks on Gmail with XMPP and AIML

modified

Introduction

User interfaces for software applications can come in a variety of formats, ranging from command-line, graphical, web application, and even voice. While the most popular user interfaces include graphical and web-based applications, occasionally the need arises for an alternative interface. Whether due to multi-threaded complexity, concurrent connectivity, or details surrounding execution of the service, a chat bot based interface may suit the need.

Chat bots typically provide a text-based user interface, allowing the user to type commands and receive text responses. Chat bots are usually stateful services, remembering previous commands (and perhaps even conversation) in order to provide functionality. A popular example from recent Internet history is the usage of IRC chat bots, often used for monitoring chat rooms, playing games, and providing help. The same technology can be used today, but to provide even more powerful functionality, such as serving business processing requests, multi-threaded processing, or any multi-threaded task that may not be ideal within a web application. When chat bot technology is integrated with popular web services, such as Gmail / GTalk, the chatbot can be utilized securely by an even larger audience.

Objective

In this tutorial, we’ll create a C# .NET Jabber chat bot, using the XMPP Jabber protocol. Our chatbot will be compatible with Gmail and run on the Gmail / GTalk XMPP server, allowing users to access the chat bot interface directly from within their Gmail account. Our C# .NET Jabber chat bot will execute commands to list the file size on its hard drive. To keep things interesting (for both the developer and the user), our chatbot will also utilize AIML to start a conversation with the user.

Example Chatbot Log via Gmail

Gmail GTalk Jabber Chatbot C# .NET XMPP AIML

Old School - Gmail Jabber chat bot C# .NET AIML XMPP Artificial Intelligence

Some Text for the Non-Humans

1
2
3
4
5
6
7
me:  hi
GmailChatBot: Hello there.
me: what is your disk size?
GmailChatBot: The current disk size on C:\ is 249546768384.
me: cool
GmailChatBot: Who is the best autonomous computer program?
me: You, of course!

Also available as a YouTube video.

Gmail is the World

An often overlooked feature of Gmail is the GTalk feature, which allows users to instant message IM other Gmail users, directly from within their Gmail account. By default, Google’s instant messaging uses the XMPP Jabber protocol for communication. In this manner, our chat bot can login to Gmail’s XMPP server and listen for incoming requests. Any instant messages sent to the Gmail user will be forwarded to our chat bot software, allowing us to process and return a result.

Of course, we’ll first need a Gmail account to host our C# .NET Jabber chat bot. You can create one by signing up to Google Gmail. After signing up, the account can be used by the C# .NET Jabber chat bot to communicate. A user who wishes to communicate with the chat bot will need to enter the name of the chat bot under the “Chat” section in Gmail, and request an invite to talk. Once connected, the user can directly communicate with the C# .NET jabber chatbot.

Summary of the Challenge at Hand

There are a couple of technologies that need to be defined before stepping into creating the C# .NET chat bot:

Jabber is an open-source technology for communicating.

Jabber uses the XMPP protocol to communicate.

AIML is an artificial intelligence markup language.

AIML provides a system of XML files for requests and responses to communicate with a human.

We can combine Jabber XMPP with AIML and execute within Gmail to provide a fully functional and secure chatbot software application.

Speaking the Jabber Language

With the technologies defined, we can now step into creating the C# .NET Jabber chatbot. For the XMPP protocol, we’ll be using the agsXMPP SDK. For our brain, AIML, we’ll be using the popular ALICE SDK from the Artificial Intelligence Foundation. AIML will provide a human conversation feature for our Jabber chat bot (while business chat bots may be designed primarily to perform business tasks, it doesn’t hurt to communicate like a human). To get started, we’ll need to setup a connection to Gmail’s XMPP service. We can do this by initializing and connecting to Gmail at talk.google.com with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
// Open connection to Gmail/GTalk.
_xmppConnection.Server = "gmail.com";
_xmppConnection.ConnectServer = "talk.google.com";
_xmppConnection.Username = "your_gmail_username";
_xmppConnection.Password = "your_gmail_password";

_xmppConnection.Open();

// Setup event handlers.
_xmppConnection.OnLogin += new ObjectHandler(xmpp_OnLogin);
_xmppConnection.OnError += new ErrorHandler(xmpp_OnError);
_xmppConnection.OnMessage += new agsXMPP.protocol.client.MessageHandler(xmpp_OnMessage);

Improving Configuration for the Human

Rather than providing hard-coded values for the XMPP server information, we can move this into an App.config file for easier management. This will change our code as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Open connection to Gmail/GTalk.
_xmppConnection.Server = _botConfiguration.XMPP.Server;
_xmppConnection.ConnectServer = _botConfiguration.XMPP.ConnectServer;
_xmppConnection.Username = _botConfiguration.XMPP.Username;

// If a password was not present in the app.config, ask the console user for it.
if (_xmppConnection.Password.Length == 0)
{
_xmppConnection.Password =
CommonManager.GetPasswordFromConsole(_botConfiguration.XMPP.Username);
}

_xmppConnection.Open();

Notice in the above code, we’ve provided a means for allowing the console user to enter a password when running the C# .NET chatbot. Since our program is a Windows console application, we have the option to enter the password within the App.config, or to provide a small amount of security, and have the user manually enter the password within the client application at runtime.

We can actually hide the password being typed in the C# .NET console application by using the following helper method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static string GetPasswordFromConsole(string username)
{
Console.Write("Please enter the Gmail password for " + username + ":");

// Hide typing.
ConsoleColor oldFore = Console.ForegroundColor;
Console.ForegroundColor = Console.BackgroundColor;

string password = Console.ReadLine();

// Restore typing.
Console.ForegroundColor = oldFore;

return password;
}

The above method provides a small amount of security while entering the Gmail account password, allowing the C# .NET jabber chatbot to login to the XMPP service.

We’ve also refactored the XMPP configuration to take out the hard-coded server and login parameters, and have replaced them with a C# .NET custom configuration section.

Come On, You’re Not Hard-Coding Login Details.. Are You?

By removing the hard-coded login details, we can implement a custom configuration section, allowing our App.config to appear as follows:

1
2
3
4
5
6
7
8
9
<configuration>
<configSections>
<section name="botConfig" type="GmailChatBot.Configuration.BotConfigSection, GmailChatBot" allowLocation="true" allowDefinition="Everywhere" />
</configSections>

<botConfig>
<XMPP server="gmail.com" connectServer="talk.google.com" username="YOUR_GMAIL_USERNAME" password="" />
</botConfig>
</configuration>

The botConfig Custom Configuration Section

We can implement the C# .NET custom configuration section with the following two classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/// <summary>
/// Custom configuration section for Chat bot configuration. Example:
/// <botConfig>
/// <XMPP server="gmail.com" connectServer="talk.google.com"
/// username="your_gmail_username" />
/// </botConfig>
/// </summary>
public class BotConfigSection : ConfigurationSection
{
[ConfigurationProperty("XMPP")]
public XMPPElement XMPP
{
get
{
return (XMPPElement)base["XMPP"];
}
}
}

/// <summary>
/// Custom configuration element XMPP. Example:
/// </summary>
public class XMPPElement : ConfigurationElement
{
[ConfigurationProperty("server", IsRequired=true)]
public string Server
{
get
{
return (string)this["server"];
}
}

[ConfigurationProperty("connectServer", IsRequired = true)]
public string ConnectServer
{
get
{
return (string)this["connectServer"];
}
}

/// <summary>
/// Gmail Username for bot to connect with.
/// </summary>
[ConfigurationProperty("username", IsRequired = true)]
public string Username
{
get
{
return (string)this["username"];
}
}

/// <summary>
/// Gmail Password for bot to connect with.
/// </summary>
[ConfigurationProperty("password", IsRequired = false)]
public string Password
{
get
{
return (string)this["password"];
}
}
}

With the above defined, we can access our C# .NET Jabber Gmail chatbot configuration with the following line of code:

1
private static BotConfigSection _botConfiguration = (BotConfigSection)ConfigurationManager.GetSection("botConfig");

Running Forever

Once the C# .NET Jabber chatbot is logged into the Gmail / GTalk service, we’ll enter a while loop within our console application. Since we’ve setup event handlers to receive incoming requests and respond, we can simple leave the program running. While the ideal solution would be to create a Windows service application to host our chatbot, for simplicity, we’ll leave a basic console application running.

1
2
3
4
while (true)
{
System.Threading.Thread.Sleep(100);
}

While running, our chatbot will respond to the supplied events: OnLogin, OnError, and OnMessage. Of course, the most important event is OnMessage, indicating we’ve receiving an incoming instant message from a user to our chatbot.

I Hear You Loud and Clear

Upon receiving an instant message on Gmail or the Jabber service, our chatbot will execute the OnMessage event handler. Within this event, we’ll process the message, to perform any service or functionality, and return an output to send back as an instant message. We can implement the OnMessage event handler as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private static void xmpp_OnMessage(object sender, agsXMPP.protocol.client.Message msg)
{
// This is called for any message received.
Console.WriteLine("Received: " + msg.Body);

// Get the Gmail username.
agsXMPP.Jid JID = new Jid(msg.From.Bare);

// Get the context User from the Gmail username
// (allows our bot to track conversations per user).
User user = CommonManager.GetUser(msg.From.Bare, _bot);

// Let our chat bot respond to the message.
string response = HandleMessage(msg.Body, user);

// Setup a response event.
_xmppConnection.MessageGrabber.Add(JID, new agsXMPP.Collections.BareJidComparer(), new MessageCB(ChatResponseReceived), null);

// Create a new message.
agsXMPP.protocol.client.Message newmsg = new agsXMPP.protocol.client.Message();
newmsg.Type = agsXMPP.protocol.client.MessageType.chat;
newmsg.To = JID;
newmsg.Body = response;

// Send response.
_xmppConnection.Send(newmsg);
}

Note, in the above code we get the Gmail username from the incoming instant message. We’ll use this to provide context to our chatbot. This allows our chatbot to remember conversations and state, responding accordingly to subsequent commands or conversation. AIML has built-in support for User context, although we’ll still need to maintain a hashtable of users (identified by Gmail username) on our end.

Processing an Instant Message

Once we’ve received the instant message, we’ll need to process the text for any commands that we recognize. We handle this processing within the HandleMessage() method, defined as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static string HandleMessage(string message, User user)
{
string output = "";

// Provide custom commands for our chat bot, such as disk space,
// utility functions, typical IRC bot features, etc.
if (message.ToUpper().IndexOf("DISK SPACE") != -1)
{
DriveInfo driveInfo = new DriveInfo("C");
output = "Available disk space on " + driveInfo.Name+ " is " + driveInfo.AvailableFreeSpace + ".";
}
else if (message.ToUpper().IndexOf("DISK SIZE") != -1)
{
DriveInfo driveInfo = new DriveInfo("C");
output = "The current disk size on " + driveInfo.Name + " is " + driveInfo.TotalSize + ".";
}
else
{
// No recognized command. Let our chat bot respond.
output = _bot.getOutput(message, user);
}

return output;
}

In the above code, we simply take the incoming instant message and check for any keywords or commands that we recognize. You can use your imagination to expand this functionality to any number of tasks, such as IRC bot functionality, script execution, or any number of business process features. For enhanced architecture, you could even abstract the if-then decision into a Strategy design pattern, and with the use of reflection, allow your chatbot to respond dynamically at runtime to new commands and requests (simply by dropping a new DLL into the executable folder).

In our example, we’ll simply recognize the command “DISK SPACE” in any sentence, as well as the command “DISK SIZE”. If found, we’ll perform some processing, such as accessing the C# .NET DriveInfo object, and return the output.

If a command is not recognized, we’ll pass the incoming instant message to our AIML chatbot instance to process and return a result. The result will then be sent back to the end user as a response. Of course, to process the instant message with AIML, we’ll need a brain within our C# .NET Jabber chatbot.

Giving our Chatbot a Brain

Our chatbot will directly handle recognized commands and return a response manually. We’ve provided this functionality directly within the HandleMessage method. However, for unrecognized commands, we’ll pass the instant message to our AIML bot to process. AIML will return a response in conversation format. We can create an AIML bot as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using AIMLbot;

public class AIMLChatBot : Bot
{
public AIMLChatBot()
{
Initialize();
}

/// <summary>
/// Loads AIML files located in the AIML folder.
/// </summary>
private void Initialize()
{
loadSettings();
isAcceptingUserInput = false;
loadAIMLFromFiles();
isAcceptingUserInput = true;
}

/// <summary>
/// Given an input string, return an output from the bot.
/// </summary>
/// <param name="input">string</param>
/// <param name="user">User (so conversations can be in context, per user)</param>
/// <returns>string</returns>
public String GetOutput(String input, User user)
{
Request r = new Request(input, user, this);
Result res = Chat(r);

return (res.Output);
}
}

In the above code, we’ve created a new class which wraps the AIMLbot.Bot class, provided by the AIML SDK. We initialize the C# .NET AIML bot and provide it with a list of AIML XML files (located within the /bin directory of our C# .NET console application).

Some Fancy Post-Build Steps for our Brain Files

Since AIML requires a series of XML files in order to process responses, we’ve included them within the Visual Studio project solution. We’ll need to copy these files to the executable directory each time. We can do this automatically with a post-build event step as follows:

1
2
xcopy /I /Y "$(ProjectDir)AIML\aiml" "$(TargetDir)\aiml"
xcopy /I /Y "$(ProjectDir)AIML\config" "$(TargetDir)\config"

The above command simply copies the two folders “aiml” and “config” into the solution’s /bin/Debug or /bin/Release folder.

Talking Back

The last method in our AIMLChatBot wrapper class is GetOutput, which simply calls the Chat() method of the AIML SDK to process and return a response. Since we pass in the User object for context, our AIML chat bot is able to provide context and specific conversation based upon each unique user. This allows our chatbot to remember the user’s name, identity, and other conversation features. We can also leverage this context for business functionality and stateful service within our application.

Giving our Chatbot Personality

The default implementation of our C# .NET Jabber Gmail chatbot uses AIML for its “brain”, in order to provide conversation. However, the chatbot can actually be customized to provide different personality, simply by editing the AIML XML files. You can experiment by editing the files in config/ such as config/DefaultPredicates.xml to provide names and background details, etc.

Into the Future and Beyond

It may be debatable whether a particular business problem can be best solved with the use of a C# .NET Jabber chatbot, as opposed to a C# ASP .NET web application. However, particular cases for a chatbot being more feasible over a web application may involve issues regarding multi-threaded and blocking functionality. Consider the execution of a server-side script or long-running process. Within a web application, the web page would need to serve the user a waiting page and notify the user upon completion. A chatbot, however waits by nature. You send an instant message to the chatbot. Process execution begins. The chat bot has the option to reply back with status and completion. While execution in a multi-threaded environment and the associated complexity is similar to a web application’s, a chatbot may offer a more streamlined solution, depending on business needs.

Another aspect of chatbot solutions is flexibility in the command-line driven user interface. The interface offers a more flexible and expressive means of executing a variety commands, often provided with less underlying framework on the developer’s part.

An additional added benefit to utilizing a chatbot, particular when combined with a popular and secure service such as Gmail, is the built-in security and access-point from the 3rd-party service. This allows more users to easily access the software and execute business services without the need for hosting concerns. Of course, the chatbot itself, will still need to be hosted on a server (whether central or decentralized).

## See It In Action

Download @ GitHub

You can download the project source code on GitHub by visiting the project home page.

Conclusion

A C# .NET Jabber chat bot application, running within the context of the popular Gmail service, can provide a variety of business functionality, including execution of tasks, information gathering, and even simulated human conversation. By integrating the Jabber XMPP protocol with the AIML chatbot SDK, an application can provide an experessive user interface and interaction, possibly providing a greater human touch to traditional user interfaces.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.

Share