Someone asked on the PowerShell Community Forum about a way to use google talk or jabber to send messages from PowerShell, and I got carried away …

The result is yet another CodePlex project: PoshXmpp. Basically I just took the event-based GPL library provided by AG Software (agsXMPP) and wrote a polling-based wrapper for it.

You can do pretty much anything instant-messaging related that you want to do with Jabber, from building bridges between different jabber transports, to notifying yourself of events on servers, etc. As an example, here’s the script for a Jabber bot that joins a chat (#PowerShell on irc.FreeNode.net by default) and then notifies us of new posts in an ATOM feed (by default, it monitors the PowerShell usenet newsgroup by using the Google groups ATOM feed).


param (
    $JabberId = $( Read-Host "Bot's Jabber ID" )
   ,$Password = $( Read-Host "Bot's Password" -asSecure)
   ,$AtomFeeds[] = @("http://groups.google.com/group/microsoft.public.windows.powershell/feed/atom_v1_0_topics.xml")
   ,$Chat = "PowerShell%irc.FreeNode.net@irc.im.flosoft.biz"     # An IRC channel to join!
   ,$ChatNick = $("PowerBot$((new-object Random).Next(0,9999))") # Your nickname in IRC
)
$ErrorActionPreference = "Stop"

$global:PoshXmppClient =
PoshXmpp\New-Client $JabberId $Password # http://im.flosoft.biz:5280/http-poll/
PoshXmpp\Connect-Chat $Chat $ChatNick

$nnc = $global:LastNewsCheck = [DateTime]::Now.AddHours(-10) # start
$feedReader = new-object Xml.XmlDocument

"PRESS ANY KEY TO STOP"
while(!$Host.UI.RawUI.KeyAvailable) {
   "Checking feeds..."
   foreach($feed in $AtomFeeds) {
      $feedReader.Load($feed)
      for($i = $feedReader.feed.entry.count - 1; $i -ge 0; $i--) {
         $e = $feedReader.feed.entry[$i]
         if([datetime]$e.updated -gt $global:LastNewsCheck) {
            PoshXmpp\Send-Message $Chat $("{0} {1} (Posted at {2:hh:mm} by {3})" `
               -f $e.title."#text",
                  $e.link.href,
                  [datetime]$e.updated,
                  $e.author.name)
            [Threading.Thread]::Sleep( 1000 )
         }
      }
      if( [datetime]$feedReader.feed.entry[0].updated -gt $nnc ) {
         $nnc = [datetime]$feedReader.feed.entry[0].updated
      }
   }
   $global:LastNewsCheck = $nnc # the most recent item in any feed
   $counter = 0
   "PRESS ANY KEY TO STOP" # we're going to wait 10 * 60 seconds
   while(!$Host.UI.RawUI.KeyAvailable -and ($counter++ -lt 600)) {
      [Threading.Thread]::Sleep( 1000 )
   }
}

$global:PoshXmppClient.Close()

Now, most of that script was spent parsing xml from the atom feed, so lets try another example. This script will join mirror a groupt chat to instant message. By default it joins the PowerShell IRC channel, and then instant messages you so that you can participate in the IRC chat via your instant messenger. You can register your Jabber account with the AIM, ICQ, MSN, or other transport on it’s Jabber server and then use this to mirror IRC to your MSN account or to AIM on your phone or whatever ;-) . I’ve been using im.flosoft.biz which allows you to not only register new accounts on the web, but also register them with the transports through a web form.


param (
    $JabberId = $( Read-Host "Bot's Jabber ID" )
   ,$Password = $( Read-Host "Bot's Password" -asSecure)
   ,$MirrorTo = $( Read-Host "Your Jabber ID" )                  # You can use Jabber Transport Id's too
   ,$Chat = "PowerShell%irc.FreeNode.net@irc.im.flosoft.biz"     # An IRC channel to join!
   ,$ChatNick = $("PowerBot$((new-object Random).Next(0,9999))") # Your nickname in IRC
)

$global:PoshXmppClient =
PoshXmpp\New-Client $JabberId $Password # http://im.flosoft.biz:5280/http-poll/
PoshXmpp\Connect-Chat $Chat $ChatNick
PoshXmpp\Send-Message $MirrorTo "Starting Jabber Mirror to $('{0}@{1}' -f $Chat.Split(@('%','@'),3))"

"PRESS ANY KEY TO STOP"
while(!$Host.UI.RawUI.KeyAvailable) {
   PoshXmpp\Receive-Message -All | foreach-object {
      if( $_.From.Bare -ne $MirrorTo ) {
         PoshXmpp\Send-Message $MirrorTo ("{0}<{1}> {2}" -f `
            ($_.From.User -split "%")[0],
             $_.From.Resource,
             $_.Body)
      } else {
         PoshXmpp\Send-Message $Chat $_.Body
      }
   }  
   [Threading.Thread]::Sleep( 100 )
}

PoshXmpp\Disconnect-Chat $Chat $ChatNick
$global:PoshXmppClient.Close();

There’s another example on the PowerShell Xmpp for Jabber project page, showing how you can use this to read a chat room out loud (sometimes I run that when I’m just monitoring a chat room while I do other work). You could also use the Xmpp library for notifications — set up your Windows 2008 server to instant message you when IIS fails, or when you receive an email, whatever. I’ve even been playing with Invoke-Expression … you can actually write a script to do powershell remoting over Jabber (yikes).

4 Responses to “PowerShell XMPP (Jabber) Snapin”