Welcome to Dovetail Software Blogs : Sign in | Join | Help
Scenario Testing With Watin

We are starting to use WatiN to automate the testing of a web application. So far it is working pretty well. One of the things I found confusing was handling confirmation alerts like this:

image

Pop-ups like this need to be handled by Watin. The code for handling this looks like this.

   1: //setup a handler with Watin
   2: var confirmDialogHandler = new ConfirmDialogHandler();
   3: browser.DialogWatcher.Add(confirmDialogHandler);
   4:  
   5: //click the button that causes a confirmation pop-up
   6: browser.Element("buttonId").ClickNoWait();
   7:  
   8: //Clickon the ok button of the confirmation window. 
   9: confirmDialogHandler.WaitUntilExists();
  10: confirmDialogHandler.OKButton.Click();
  11: browser.WaitForComplete();
  12:  
  13: //You may want to test that the message is correct.
  14:  
  15: //cleanup
  16: browser.DialogWatcher.RemoveAll(confirmDialogHandler);

There is a lot of setup and teardown for the simply clicking a button. Using some .Net 3.5 goodness I created an extension method to make this a little more terse.

   1: //usage
   2: Browser.OkButtonClickedOnConfirmDialog(ClickOnUnMarkMasterButton);
   3:  
   4: //could use a lambda but this utilty method looks cleaner
   5: private void ClickOnUnMarkMasterButton()
   6: {
   7:     Browser.Element(Find.ById("unmarkMasterContact")).ClickNoWait();
   8: }
   9:  
  10: //implementation
  11: public static void OkButtonClickedOnConfirmDialog(this IE browser, Action actionCausingConfirmationDialog)
  12: {
  13:     var confirmDialogHandler = new ConfirmDialogHandler();
  14:     browser.DialogWatcher.Add(confirmDialogHandler);
  15:  
  16:     actionCausingConfirmationDialog();
  17:  
  18:     confirmDialogHandler.WaitUntilExists();
  19:     confirmDialogHandler.OKButton.Click();
  20:     browser.WaitForComplete();
  21:     browser.DialogWatcher.RemoveAll(confirmDialogHandler);
  22: }

Thinking about this more the difficulty in testing this dialog is a sign that we should be using a JQuery lightbox to accomplish the same functionality.

Exposing Knowledge Using Dovetail Seeker

Dovetail Seeker is our new search offering for Dovetail CRM. One of the primary design goals from a developer's point of view is to make it easy to integrate with Seeker. I know I am biased, but think we did pretty well. In this post I'll I share my experience integrating Seeker with our public web site exposing search access to Dovetail's Knowledge Base.

Why Is This A Good Idea?

For a long time we've exposed to customers the ability to browse our knowledge base. Here it is:

Dovetail's Browe KB Page

This is a pretty good solution for Dovetail administrators looking for common solutions to issues that may pop-up. A better solution would be to let users search the knowledge base for the exact issue they are running into and see what pops up. Being able to search for an problematic error message is pretty handy:

Searching Dovetail's KB

Raise your hand if you'd rather search than browsing around for a solution! If your hand happens to be up right now... Read on.

What've Got To Work With Here

The Dovetail public knowledge base is part of an ASP.Net 2.0 web application.

Our Seeker index resides in on a separate machine exposed by the Seeker search web service. Here is a look at a possible Seeker network topology ripped from the documentation:

Dovetail Seeker Topology

Disclaimer, I am not an ASP.Net expert. Likely there is a better way to get this done. Also, I leaned heavily on Chad Myers, for his ASP.Net experience. Thank you Chad for putting up with my silly questions, ASP.Net ignorance, and general complaints about ASP.Net Web Forms.

What Needs to Happen

To create a basic web search interface to Dovetail CRM we need to do the following:

  • Create a web page with a text box for search queries and a go button.
  • Write ASP.Net "code behind" for the page that relays the search query to Seeker.
  • Display the search results returned by Seeker to the web user as Html.
    • Show which search results are currently being displayed.
    • Show the total number of search results available for the query.
    • The interface needs to support paging. There can be a lot of search results.

Let's take this one step at a time working our way from an html form to search results.

Search User Input

Search input is pretty straight forward. You give the user a text box and a Go button. Here is the Html and some JavaScript I used to gather the search query from the user.

<script language="Javascript" type="text/javascript">
   1:  
   2:     function searchSeeker()
   3:     {
   4:         window.location.href= 'knowledgeBaseSearch.aspx?query=' + document.getElementById('searchQuery').value;
   5:     }
</script>
 
<div id="searchUi">
    <label for="searchQuery">Search :</label>
    <input id="searchQuery" type="text" name="query" size="57" value="<%=QueryString%>" 
        onkeypress="if(event.keyCode == 13) {searchSeeker(); return false; }"/>
    <input id="goSearch" type="button" value="Go" onclick="searchSeeker(); return false;"/>
</div>

I am using JavaScript to "submit" the search results due the nature of the ASP.Net application my application is living in. I was having trouble getting the page to behave using standard ASP.Net Web Forms techniques so I decided to trick ASP.Net into behaving like classic ASP. Sorry, I have no love for ASP.Net Web Forms I'd much rather have ASP.Net MVC next time around. You may also notice the query text box has an onkeypress event handler to capture the Enter key being pressed. Another crappy ASP.Net work around. In the end all that is happening is a GET request is returning to the same page with the query string as a parameter.

GET http://dovetailsoftware.com/.../knowledgeBaseSearch.aspx?query=ORA-065502

ASP.Net Code Behind

For searching Dovetail Seeker the knowledge base search code behind basically does the following:

  1. Get the search query and any paging information from the WebRequest
  2. Make a search WebRequest to the Dovetail Seeker web service
  3. Use a ASP.Net repeater control to data bind to the array of search results returned by Seeker.

I just want to hit the highlights if you are interested in the nitty gritty details. I have posted all the relevant html and code behind up on Pastie.

At a high level the code below is what's going on. When the page is loading: 1) Get the search query and paging information  2) execute a search against Seeker, and 3) bind the results to a repeater control on the page:

   1: protected override void OnLoad(EventArgs e)
   2: {
   3:     base.OnLoad(e);
   4:  
   5:     _queryString = Request["query"];
   6:     _resultCount = String.IsNullOrEmpty(Request["resultCount"]) ? "10" : Request["resultCount"];
   7:     _startResultIndex = String.IsNullOrEmpty(Request["startResultIndex"]) ? "0" : Request["startResultIndex"];
   8:  
   9:     Search();
  10: }
  11:  
  12: private void Search()
  13: {
  14:     if (String.IsNullOrEmpty(QueryString))
  15:         return;
  16:  
  17:     _searchResultInformation = SeekerClient.Query(QueryString, _resultCount, _startResultIndex);
  18:  
  19:     searchResultsRepeater.ItemDataBound += searchResultsRepeater_ItemDataBound;
  20:     searchResultsRepeater.DataSource = _searchResultInformation.SearchResults;
  21:     searchResultsRepeater.DataBind();
  22: }

You may have noticed the resultCount and startResultIndex parameters. They are optional Seeker parameters to support paging. To render "page 5" when there are 10 results per page you would say. Give me 10 search results starting at the 50th result.

An important task is 2) executing a search against Dovetail Seeker. The following code is a helper class that executes a search against Dovetail Seeker and returns a .Net object containing the search result:

   1: public static class SeekerClient
   2: {
   3:     public static readonly string SeekerUrlAppConfigKey = "seekerSearchWebServiceUrl";
   4:     public static readonly string SeekerUrl = System.Configuration.ConfigurationManager.AppSettings[SeekerUrlAppConfigKey];
   5:  
   6:     public static SearchResultInformation Query(string searchQuery, string resultCount, string startResultIndex)
   7:     {
   8:         string queryWithPrivateSolutionFilter = HttpContext.Current.Server.UrlEncode("(" + searchQuery + ") AND public:1 AND domain:solution");
   9:         string json = RunSearchQuery(queryWithPrivateSolutionFilter, resultCount, startResultIndex);
  10:         return AssembleSearchResultInformationFromJson(json);
  11:     }
  12:  
  13:     private static string RunSearchQuery(string searchQuery, string resultCount, string startResultIndex)
  14:     {
  15:         string searchQueryUrl = String.Format(SeekerUrl + "search/search.castle?query={0}&resultCount={1}&startResultIndex={2}", searchQuery, resultCount, startResultIndex);
  16:  
  17:         try
  18:         {
  19:             WebRequest webRequest = WebRequest.Create(searchQueryUrl);
  20:             WebResponse webResponse = webRequest.GetResponse();
  21:             StreamReader reader = new StreamReader(webResponse.GetResponseStream());
  22:             return reader.ReadToEnd();
  23:         }
  24:         catch(Exception ex)
  25:         {
  26:             string errorMessage = String.Format("Error using the Dovetail Seeker WebService. Please make sure the Dovetail Seeker web service application URL is set correctly in your Web.Config in the appSettings node using the key '{0}'.", SeekerUrlAppConfigKey);
  27:             throw new ApplicationException(errorMessage, ex);
  28:         }
  29:     }
  30:  
  31:     private static SearchResultInformation AssembleSearchResultInformationFromJson(string json)
  32:     {
  33:         return JavaScriptConvert.DeserializeObject<SearchResultInformation>(json);
  34:     }
  35: }

This code creates a web request to Dovetail Seeker and converts the JSON returned to a .Net object using the excellent Json.NET library. Dovetail Seeker includes an assembly with SearchResultInformation and SearchResult parameter classes (code) which are compatible with the JSON results returned by Dovetail Seeker.

The URI of the Dovetail Seeker web application is configured in the application configuration settings file (web.config)

Constraining the query on the server side

I only want to expose public solutions to the Dovetail knowledge base search interface. The trouble is our seeker index also contains other types of search results. You might have noticed that I added some additional magic to search query we are sending to Seeker:

This constraint to the query allows only Solution search results that are public to be returned from Dovetail Seeker.

string queryWithPrivateSolutionFilter = 
    HttpContext.Current.Server.UrlEncode("(" + searchQuery + ") AND public:1 AND domain:solution");

Pagination

For a paging interface we blatantly copied Digg's pagination. I'll be the first to admit that the code we wrote to accomplish this complicated and very much over the top. That said, I think it looks pretty cool.

Paging UI

Search Results - Exposed

With apologies to Sun Tzu. Know your Search Results and know your search integration.

The following information returned for every search:

  • The search query executed.
  • An array of search results.
  • The total number of search results available for this query. (paging)
  • The index of the first search result returned out of all potential search results. (paging)
  • When the search index was last updated.
  • Error result: If something goes wrong an error message is returned.

Each search result returned looks like this:

  • Score - how relevant is the result to the query.
  • Title - the title of the search result.
  • Summary - details about the search result.
  • Domain - what search domain does the result belong to. This allows you to mix results of different kind of data such as Cases and Solutions that match the search term.
  • Id - the unique Dovetail CRM identifier of the result

Conculsion / Wrap Up / Summary / El Fin

I appreciate that you are reading this conclusion. It means you are a diehard. It means you really want to know what pearls of wisdom that I may still have up my sleeve but sadly I'm out. Ok maybe a couple things. My best advice is to keep it simple. Borrow from the best (*cough* Google) . The code is out there if you want to use it as guidance. Comment below if you are confused about anything so that everyone can learn.

Dovetail Seeker Introduced

Stephen Lynn recently talked about our new search product in his post - Dovetail Seeker to the Rescue. Gary and I are working hard to get Seeker shipped to our customers. I wanted to post a follow up to Stephen's post to divulge some more details about Dovetail Seeker.

What is It?

Dovetail Seeker makes the data in your Dovetail CRM searchable just like Google makes the Internet searchable. All you need to do is type in a text box and click on Go.

We have worked very hard to deliver a great search solution for Dovetail CRM and I like to think we succeeded. Here is a summary of what we tried to accomplish with Seeker:

  • For Users: the ability to easily search for interesting data in Dovetail CRM.
  • For Administrators: the ability to control exactly what data users can search for.
  • For Developers: the ability to integrate Dovetail Seeker with their CRM applications.

Let's take a look at each of these points in more detail.

Users

Your users don't really care about search. They only want to find what they are looking for. We tried to make Seeker simple to use for basic searches while making it easy to discover how to unleash the power of advanced searches.

Here are a couple views of Dovetail Agent's new search interface:

Dovetail Agent 4.0 Search

Our Search Tips page teaches you how to use Seeker's more advanced capabilities.

image

Below is a screen shot of another Seeker implementation. Our Dovetail Software Knowledge Base is using Seeker to expose our Solutions on our public web site. This is a very simplistic integration with Seeker it only lets you search Solutions and does not expose any of Seeker's advanced capabilities to the user. But. It's public so you can take it for a spin if you like.

Dovetail Knowledge Base

If you are an existing customer and have a Dovetail Self Service Account you can play with a more advanced integration with Seeker at our Self Service web site.

Administrators

As an administrator you want your Dovetail CRM users to have quick access to your important CRM data. Dovetail Seeker leverages the excellent Lucene.Net text search engine to index Dovetail CRM. Seeker adds value by making it easy to define and index the Dovetail CRM data you want to make available to your users.

Seek Indexer

Before you can search you need an index. An index is created by the Seek Indexer using a configuration file setup by the Dovetail CRM administrator to describe what data you want to make searchable. Below is a screen shot of the Seek Indexer running. Not very exciting but you may notice that it is pretty fast indexing over 300 Solutions in 3 seconds.

Seeker Indexer 

To keep the index in-sync with your database it needs to be updated regularly. To accomplish this it is easy to setup a scheduled task in Windows to run the Seek Indexer and it will only update the records that have been modified since you last ran the indexer.

Seeker Search Web Service

Once you have an index setup your Dovetail CRM is searchable. Search capability is exposed by the Seeker web application which acts as a search web service. Applications can search Dovetail CRM by making requests against this web service application.

This screen shot shows details about the indexed Solutions.  

 image

The Dovetail Seeker web application does not include a HTML search user interface. The Seeker web application is a search service. It is only intended to get called by other Seeker enabled applications. When we release Dovetail Agent 4.0 it will include a search interface that consumes search results returned from Seeker.

Developers

image

To integrate an application with Seeker you need to be able to communicate with the Seeker web application using HTTP GET requests and be able to parse JSON. That's it.

Search results from the Seeker search web service are returned in a response encoded as JavaScript Object Notation (JSON). Using JSON is a handy way to serialize data between different applications. Please do not get scared by the JavaScript in the name there is a JSON implementation for many many languages.

Here is an example search request to Seeker's search service searching for the terms "create a case":

GET http://servername/seeker/search/search.castle?query=create%20a%20case

Here is some example JSON output from Seeker:

{"SearchResults":0[{"Score":1,"Domain":"case","Id":"7","Title":"Case with error in the title","Summary":"Case History"}], "TotalNumberOfResults":1, "StartResultIndex":0,"SearchQuery":"Error"}

There is of course a lot more to know from a developer's point of view. I will follow up with a more details post about my experience integrating Dovetail Seeker with Dovetail's Knowledge Base.

Cleanly Handling Ctrl-C In Console Applications

I find myself writing Console applications now and again. Command line ninjas know that if you want to stop a running console application you can hit Ctrl-C to  exit the application. In our Dovetail Seeker product, which we will be releasing with the next version of Dovetail Agent, when you cancel out of the indexer while it is runnLook out its a command line ninja.ing it would corrupt the search index. Not a big problem as the search indexes can be easily recreated but that can sometimes take a long time. Besides allowing scenario to exist where the user can corrupt application data is just plain wrong. Luckily it was easy to fill in this hole.

My Google-Fu took me to Curt Nichol's post on Ctrl-C and the .NET console application where he introduces the System.Console.CancelKeyPress event and demonstrates using an anonymous method for handling it. For what I need this is the perfect solution with a small wrinkle the CancelKeyPress event is invoked from a separate thread. To explain why that is a little scary I need to go into gory details. The main take away of this post is be careful you are entering the land of concurrency and you need to tread lightly.

The reason the index would get corrupted is that a Ctrl-C exit would not properly close the index being written to. Closing the index is easy. The trouble is that because the CancelKeyPress event is fired on a separate thread if the event handler simply closes the index, on the other thread the running indexer will throw an exception because it can't write to a closed index. The better solution is to tell the indexer to stop indexing. The last thing needed is to add the ability to tell when the indexing has successfully stopped. Here is my CancelKeyPress event handler:

   1: System.Console.CancelKeyPress += delegate
   2: {
   3:     _logger.WarnFormat("Indexing was canceled by user hitting Ctrl+C or Ctrl+Break. Attempting to cleanly cancel indexing.");
   4:     documentIndexer.CancelIndexing();
   5:     documentIndexer.IndexingCompleted.WaitOne();
   6: };

Within the documentIndexer's main loop each time around it checks to see if indexing is canceled. When indexing is all done it sets the IndexingCompleted ManualResetEvent which is there so that the application stays alive long enough for the indexer to finish up and properly close the index.

Have fun making your console applications more battle ready to fight those powerful command line ninjas.

Twitter Agent - Decoupling From Dependencies
Lately I have been posting about a Twitter/Dovetail CRM integration we have been working on. Recently I made some architectural changes that I will hopefully get to in a future post. This time around I wanted to talk about how a problem with the jabber-net XMPP library led to a better more testable design.

Connection Stability

Last post I talked about how we are using XMPP to send and receive direct messages with Twitter and when Twitter's XMPP presence is up it works great. Really well in fact. The main challenge has been keeping the connection to our Jabber server stable. The jabber-net library has an auto-reconnection issue when DNS cannot be resolved it gives up trying to reconnect. I did get some guidance from their Google Group on correcting this issue but unfortunately had already moved to a different library agsXmpp for which I was able to get a timed reconnect working.

Keeping your dependencies under control

One very useful side-effect of making the move to the agsXmpp library was a refactoring of how Twitter Agent communicates with the XMPP library.

public interface IXmppConnection { void Connect(); void Disconnect(); void SendMessage(string toJID, string messageToSend); bool IsConnected { get; } event Action<IMessage> OnMessageReceived; }

Before I would set a delegate that received a library specific Message object. Refactoring towards an abstract message IMessage completely decouples our code from the XMPP library. This also affords great testability. Interaction testing was previously difficult because it was hard to mock the library specific concrete message object.

Another side-effect of thinking about all messages that enter the system as an abstract IMessage led me to re-architect how messages are processed end-to-end but I will get to that next time.

Sneaky

I snuck in the fact that Gary and I stormed up a new name for the our Dovetail CRM/Twitter integration. Twitter Agent. What do you think?

Spring Alt.Net Pledge Drive

I just wanted to take a moment out of our regular blog-cast to tell you that Alt.Net is supported by listeners readers like you. Smart people work really hard volunteering all their free time and energy to put out great open source software for our community. We need to keep these people coding not wasting their time drumming up consulting gigs or looking for jobs.

Just wanted to read off a couple names of people that just contributed. Thank you Ray Houston for donating. You must have deep pockets with that sir name. Oh, and I see Chad Myers donated he is always a regular contributor. I see that Anders donated. Thanks Anders for your donation and for C# we all love it.

You TOO can make a difference and if you act right now we have a matching pledge from Bill Gates of 1 Million dollars*. Microsoft is putting their money where their USB port is and making a bet on the open source .Net community.

I myself called the pledge lines over at PayPal and donated to the two .Net open source contributors I use the most.

  • Castle Project - foundation for many of the tools I use including: the Windsor IoC Container, and MonoRail a MVC web application framework  - Donate
  • Oren Eini a.k.a Ayende is a one man powerhouse contributing to many excellent open source projects. - Donate

If you are a fan of these two open source contributors please pledge now. Or if you have a favorite project or leader you think I missed. Please comment below.

Thank you for your contribution. We now we return you to a re-run of Kevin's 24 hours of Twitter posts.

*If only this paragraph were only true. Think if they held a Microsoft Summer of Code.

Moving the Dovetail Twitter Integration To Jabber/XMPP

Quickly after creating a Twitter Integration with Dovetail I ran into rate limiting issues using Twitter's API. Twitter currently only allows 70 requests per hour. Here is their guidance...

If you are developing an application that requires more frequent requests to the Twitter API, please contact us and we'll see what we can do.  We maintain a white list of known-good screen names who have high-volume API needs.  We also suggest researching Jabber/XMPP if your application needs to deliver a near-realtime experience (for example, a bot that replies to direct messages).

I emailed Twitter support asking nicely for higher volume API usage but still haven't gotten a response. Then I realized that if we want to release this Dovetail/Twitter integration to our customers we it need to work within the rate limiting guidelines, so I listened to Twitter's guidance and researched using Jabber/XMPP.

The first thing I had to do was figure out how to use a Jabber client to send and receive messages with Twitter. For some reason my Google-Fu failed me and figuring out how to do that took a lot of time. Check out the post I created to get you through the task of hooking up Twitter with Jabber.

Update: It seems that there is some confusion that each Twitter user would need to pair their Twitter account with a Jabber ID. Not necessary. We only need to do this for the Dovetail Twitter Integration Bot's Twitter account. Twitter users, um... just use Twitter.

Next I wanted to switch my Twitter integration to use Jabber rather than the Twitter API. This turned out to be pretty easy and I am here to share what I learned along the way.

Add 1 cup of Jabber library

I went looking for .Net libraries that support Jabber/XMPP and found two: jabber-net and agsXMPP. I started off using jabber-net just because the license seemed more compatible with my goals (IANAL).

Using the jabber-net library is easy. Their examples are very drag-n-drop GUI driven but there is a console application example in their source. My main complaint is that I wish there were better testing seams in place. There are few interfaces and the JabberClient feels a bit like a god class. Enough complaining. On the upside for what we need to do it works. You basically use JabberClient to Connect to the Jabber server and add a OnMessage event handler that does the heavy lifting. 

Moving from polling to a push architecture

Moving to Jabber allows us to benefit from a push architecture. We are basically asking Twitter to push to us an instant message every time our account receives a direct message. This is much better than polling the Twitter API continually asking their API, "Any messages? ... How about now? ... Now?". A push is lower impact on the Twitter servers and we receive the message almost instantaneously. The main downside is that you need to maintain a constant network connection between Jabber client and server.

All my communication with Twitter goes through a TwitterAgent. To move from the HTTP APIs to Jabber I needed to rework my TwitterAgent class which looks like this:

    public interface IHttpTwitterAgent
{
IEnumerable<IMessage> GetMessages();
void SendDirectMessage(IMessage message);
void DeleteMessage(IMessage message);
}

To look a little something like this:

    public interface IJabberTwitterAgent
{
void Connect();
void Disconnect();
event Action<IMessage> OnDirectMessageReceived;
void SendDirectMessage(IMessage message);
}

I was able to quickly rework my Twitter Bot to use Jabber because I have a well defined interface between my application and Twitter. The main thing that changed was that getting direct messages now involves an event that is fired. In reality, this greatly simplified things as I no longer need code to poll the TwitterAgent for messages.

How it works

My implementation of the JabberTwitterAgent basically connects to the Jabber server using the credentials of a Jabber ID that is paired with the Dovetail Twitter account. When direct messages are received via Jabber a little bit of parsing needs to happen to parse out the sender's Twitter name and message body. Here is an example message:

Direct from KevM:
close case 1234 Called the customer and told them to reboot the server

Reply with 'd KevM hi.'

The message is parsed out and sent to the existing messaging pipeline which further takes the message apart to determine what to do with it. In the case above the Dovetail case 1234 would get closed with a note of 'Called the customer and told them to reboot the server'.

Responses are sent out using the JabberTwitterAgent with very little difference from the previous version. They just need to be formatted using the Twitter direct message syntax:

d <receiver> Message Text

Benefits

After the move to a Jabber based agent. I am seeing immediate reception of direct messages. Also, I no longer need to delete direct messages to prevent them from continually being returned by requests for direct messages further improving stability as the delete message Twitter API would sometimes not work.

Problems

Connection Stability

Currently I am trying to bullet proof maintenance of the connection between the Jabber client and server. Currently if the network connection goes down for some reason I do not always get reconnected to the server.

Server Certificates

The Jabber80 service supports secure communications but does not have a trusted certification authority. I needed to add a handler for the OnInvalidCertificate event.

    private bool jc_OnInvalidCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
_logger.DebugFormat("Invalid certificate ({0}):\n{1}", sslPolicyErrors, certificate.ToString(true));
return true;
}

What's in a Name

I am not excited about calling what I am working on Dovetail Twitter Integration. Calling it a Dovetail Twitter Mashup would be much cooler but this is technically not a mashup as I am not, yet, overlaying Twitter functionality in our Dovetail Agent web application. Anyone have a better suggestion for a project title?

Pairing Your Twitter Account With a Jabber Client

Twitter will send direct messages and updates for friends you follow who have notifications turned on to a device. Cool. Better yet a device can be a mobile phone (SMS) or an Instant Messaging account. The goal of this post is to show how to sending and receive messages to Twitter using a Jabber device. To do this we will be using the excellent IM client Digsby which it turns out has excellent Jabber support.

Here is what we will be doing.

  • Creating a Jabber ID in Digsby.
  • Adding the Twitter Jabber ID as a contact for your new Jabber ID.
  • Pair your new Jabber ID with your Twitter account.
  • Testing it out.

Creating a Jabber ID

To create a Jabber ID using Digsby click on Add IM Account in your preferences. Fill in the Jabber Account form and one will be created for you. I had connection issues sitting behind our firewall so I used jabber80 Jabber server. There many more open Jabber servers available.

Sign up for a Jabber account

Adding Twitter as a Jabber Contact

Click on the IM Account's checkbox to login.

Add twitter@twitter.com as a contact of your newly created Jabber ID. 

Add Twitter as a contact.

You sure? 

Pairing Your Jabber ID with your Twitter Account

Visit your twitter account's devices page and add a Jabber Instant Messaging device. You will be presented with bit of text to send Twitter using your Jabber account. You have to prove you are you somehow.

The result of pairing your account with Twitter.

Does It Work?

Ok let's talk to my Twitter Integration using direct messages from Jabber.

A jabber client talking to the Dovetail/Twitter integration.

Creating A Twitter Integration

Twhirl screenshot of a case notification and acceptance.I have been doing some work using Twitter as a notification mechanism for case events taking place in Dovetail CRM. I also wanted to expose an API of sorts driven by Twitter direct messages which would allow support agents to work on cases via Twitter. I created a twitter account for Dovetail and went crazy. To the right is a screen shot of my favorite twitter client showing me being notified of a case created by a customer and accepting it via Twitter.

This goal of this post is a brain dump what I have learned so far on my journey into Twitterland.

Twitter? - what is this Twitter you speak of

Twitter is a micro-blogging service on which you can post short messages called tweets. People usually post what they are up to, thinking about, and sadly what clothing they are putting on their cats. You can also follow other Twitter users and see their tweets as a timeline. When two Twitter users are following each other they are considered friends and can send direct messages to each other. It seems that most people currently use twitter as a mix of social networking and instant messaging.

I have been a Twitter user for almost a year and seen my usage grow quickly as many people I know are embracing the medium. Personally, I use Twitter to keep up-to-date with what my friends and colleagues are doing to while I am at the computer (using Twhirl) and away from the computer (using SMS). These days I am instant messaging less and less and twittering more and more.

It's a messaging platform

The beauty of Twitter is its flexibility as a messaging bus. You can easily get data into and out of Twitter. You can start out using their Web site. Add your phone as a SMS device or an Instant Messenger client to receive notifications when certain special twitterers tweet. I like to catch up with my missed Tweets via RSS feed. If you are a geek like me you could write a program that talks to the Twitter HTTP API.

A great example of a company integrating with Twitter is Remember The Milk (RTM). I use RTM to keep track stuff I need to get done. Send their Twitter user a direct message and they create a task for you.

Sending Notifications

As an Agent I want to be notified via Twitter when a case is dispatched to the support queue to improve customer response time by being constantly aware of new support cases.

Notification of new case in the support queue

Doing this was easy as I was able to leverage our integration with Campfire. Dovetail CRM has a windows service called RuleManager that can invoke actions based on application events. We already have a rule that fires when a case is dispatched to our support queue so all I had to do was added a new action on that rule that invokes a script. Sam Tyson wrote a script that basically sends a direct message to the twitter account of each queue member. We map their Dovetail CRM account to a twitter screen name using a custom field on the user table.

The bit of code that sends the direct message is actually shelling out to a handy command line utility for automating HTTP interactions called cURL. I just followed the instructions on the Twitter API Documentation Google Group. Here is a command line that sends a direct message using cURL:

curl -u <sender>:<password> -d text="<message>" -d user="<recipient>" http://twitter.com/direct_messages/new.xml

Essentially we ended up with a script that sends a direct message to all queue members that are twitter users.

We are not quite done though. It