Friday, August 28, 2015

Jenkins and Skype: Back Together Again

So, all us developers use Skype. But there's one member of the team who, until today, wasn't on Skype: Jenkins.

Getting Jenkins onto Skype is possible, despite Microsoft killing API access in Skype. In this post, I show you a six step process to configure Jenkins to message you over Skype.



There is a Skype plugin, but it doesn't work now that Microsoft has discontinued the Skype API. I wasted a day trying. Undaunted, determined to make this work, I went back to old school days: scripting the GUI.

The solution uses a message queue on a Windows slave running Skype. A Jenkins job writes into the queue, then a scheduled task calls a script to read from the message queue and "drive" Skype, simulating a person typing into the client. This technique works on a slave that's dedicated to this task: a shared desktop will not suffice.

Configuration is a six-step process. You'll need a Jenkins master server and the ability to install a plugin on it.

1. SETTING UP SKYPE ON WINDOWS

We need a computer that has Skype reliably running on it.

  1. Get a Windows machine and install Skype. You can use the latest version of the Skype desktop client, because this method doesn't use the API.

    Optional: I recommend you create an account named "Jenkins". Henceforth, I assume the user doing all of this is Jenkins.

  2. Log in to Skype with a new account. The new Skype account will be the one Jenkins uses. I don't recommend re-using your existing accounts, because then you won't be able to message yourself.
  3. Arrange for Skype to start at Windows boot. (Tools > Options > General > Start Skype when I start Windows)
  4. Configure your Windows account to automatically log in on boot.

Alright, at this point you have a Windows box with Skype running on it. When you boot the box, the Jenkins user logs in automatically and Skype starts running also.

2. SETTING UP THE MESSAGE QUEUE READER

We need to be able to read messages from a queue and write those messages into Skype.

  1. On the desktop of your account, create a directory called "messages". That's where the Jenkins agent will dump the messages to send.
  2. Create a batch file called "dispatch.bat". It will do this:
    @echo off
    for %%f in (messages/*) do (
      for /f "delims=" %%x in (messages/%%f) do (
        "C:\Program Files\AutoHotkey\AutoHotkey.exe" write-skype.ahk "%%x"
      )
      ping 192.192.192.192 -n 1 -w 5000 > nul
      move "messages\%%f" %TEMP%
    )
    
  3. Create an Autohotkey script called write-skype.ahk. It will do this:
    #NoEnv
    #Warn
    SetWorkingDir %A_ScriptDir%
    
    ControlClick, TChatRichEdit1, ahk_class tSkMainForm,,,,,,
    ControlClick, TChatRichEdit2, ahk_class tSkMainForm,,,,,,
    ControlClick, TChatRichEdit3, ahk_class tSkMainForm,,,,,,
    Loop, %0%
    {
        param := %A_Index%
        SendRaw %param%
        SendInput {Enter}
    }
    
  4. Install AutoHotkey. Donate money to them.

At this point, you should be able to create a text file in the messages directory, then manually run the dispatch.bat file. You should then see your message get written into Skype in whatever chat you have open.

ImportantThis script does not have any way to choose the contact or group to message. Thus, you need to leave Skype running with the desired conversation open.

3. AUTOMATING QUEUE READ DRAINING

We need the queue reader to run periodically.

  1. Open up Task Scheduler
  2. Create a task with the following properties:
    • General > Name: Dispatch Skype Messages
    • General > Security Options: Run only when user is logged on
    • Triggers > Daily > On a schedule, starting today at midnight. Repeat task every 1 minute for a duration of indefinitely.
    • Actions > Run the command C:\Users\Jenkins\Desktop\dispatch.bat
    • Actions > Start in directory C:\Users\Jenkins\Desktop

    Alternatively, you import this definition:

    <?xml version="1.0" encoding="UTF-16"?>
    <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
      <RegistrationInfo>
        <Date>2015-08-28T14:55:18.4933635</Date>
        <Author>MACHINE\Jenkins</Author>
      </RegistrationInfo>
      <Triggers>
        <CalendarTrigger>
          <Repetition>
            <Interval>PT1M</Interval>
            <StopAtDurationEnd>false</StopAtDurationEnd>
          </Repetition>
          <StartBoundary>2015-08-28T00:00:00</StartBoundary>
          <Enabled>true</Enabled>
          <ScheduleByDay>
            <DaysInterval>1</DaysInterval>
          </ScheduleByDay>
        </CalendarTrigger>
      </Triggers>
      <Principals>
        <Principal id="Author">
          <UserId>MACHINE\Jenkins</UserId>
          <LogonType>InteractiveToken</LogonType>
          <RunLevel>LeastPrivilege</RunLevel>
        </Principal>
      </Principals>
      <Settings>
        <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
        <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
        <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
        <AllowHardTerminate>false</AllowHardTerminate>
        <StartWhenAvailable>true</StartWhenAvailable>
        <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
        <IdleSettings>
          <StopOnIdleEnd>true</StopOnIdleEnd>
          <RestartOnIdle>false</RestartOnIdle>
        </IdleSettings>
        <AllowStartOnDemand>true</AllowStartOnDemand>
        <Enabled>true</Enabled>
        <Hidden>false</Hidden>
        <RunOnlyIfIdle>false</RunOnlyIfIdle>
        <WakeToRun>true</WakeToRun>
        <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
        <Priority>7</Priority>
      </Settings>
      <Actions Context="Author">
        <Exec>
          <Command>C:\Users\Jenkins\Desktop\dispatch.bat</Command>
          <WorkingDirectory>C:\Users\Jenkins\Desktop\</WorkingDirectory>
        </Exec>
      </Actions>
    </Task>

Alright, at this point you have a process that will read text files out of the Desktop/messages directory, and write those into Skype. Now we just need to populate that directory via Jenkins.

4. INSTALL THE JENKINS SLAVE

We need Jenkins master to be able to speak to the new Windows machine.

  1. On the Jenkins master, create a node
  2. Label the node "skype". (Any label will suffice, but it must be unique because your message writing job is going to this slave only.)
  3. Follow the instructions to create a Jenkins slave. I used JNLP connection type, which basically means a particular Java command is run on the Windows slave at boot.

Alright, now you should be able to see your Jenkins slave node connected and running.

5. CREATE A MESSAGE SENDING JOB

Now, we need a job that will write a message to the Jenkins slave.

  1. Create a parameterized Jenkins job. I'll call this job "send skype message".
  2. Set this job to take one parameter: the message to send. I'll call it "MESSAGE".
  3. Rig the job to only run on "skype" slave. (Or use the label you chose earlier.)
  4. The job should start a Window batch command:
    (echo %MESSAGE%) > C:\Users\Jenkins\Desktop\messages\%BUILD_NUMBER%

Alright, this job takes the contents entered into the message and puts it into the queue directory on the slave. One last step to get all this wired together.

6. SEND A MESSAGE WHEN A JOB COMPLETES

Now that we can send messages to the Windows Skype machine, we need to configure post-build actions on our jobs to send Skype messages.
  1. From Manage > Jenkins, install the Parameterized Trigger Plugin
  2. Configure the job you want to send Skype messages.
  3. Add a Post-Build Action of "Trigger parameterized build on other projects"
  4. Choose the "send skype message" job under Projects to build
  5. Decide in what states to send the message. If you want to send different messages based on job success state, you can add multiple post-build actions.
  6. Click Add Parameters. Choose "Predefined Parameters".
  7. In the text field, enter MESSAGE=Sir, build #${BUILD_NUMBER} failed. ${BUILD_URL}

    Obviously, you can replace that string with any text you want. The text can be static, or it can come from any of the job's build parameters or environment variables. If you need access to even more environment variables, consider the EnvInject plugin.

SUMMARY

This approach has several moving parts, any one of which might wig out. So, don't rely on this alone to notify you of problems in your system. But it is nice to have Jenkins able to participate in Skype conversations.

Addendum for Headless / Remote Desktop / Virtual Servers

If your Windows slave doesn't have a physical console that you can leave on, unlocked, with Skype running, then you need to tweak your setup. (The following notes apply if you connect with Remote Desktop (via RDP). If you use VNC, these may not apply to you.)

First, follow these instructions to allow the script to run when your RDP session is minimized.

Second, don't just disconnect your RDP session, because that will also disconnect the console and AutoHotkey won't be able to run. Instead, follow these instructions to logout of the RDP in such a way to leave the session running.

1 comment:

Share your thoughts!