Adium

Selective Sign-on Example Script

This document will provide an example of scripting Adium from the outside. This simply means that it will show how to develop a script that uses Adium's AppleScript support that can be run from Script Editor.app, as a stand alone application or by some other means (like Quicksilver). This is different than running a script from within Adium.

The example that will be presented is what I call the "Selective Sign-on Example."

Problem Statement

This is what the script is trying to solve.

I have 7 different accounts - three are used for work (Sametime, AIM and Yahoo!) and four are used for personal (GTalk, Yahoo!, AIM and .Mac). One of the work accounts will not connect unless I'm on a corporate network. Adium currently allows users to configure accounts to sign-on when Adium opens, but there is no way to selectively sign-on to specific accounts. I need a script that will figure out which accounts are offline and then ask me to pick which ones I would like to be online. Once they are selected the script should take the accounts online. It would also be nice if I could run the script even if Adium is currently launched so I could quickly select which accounts I would like to take online.

Design Goals

Here is a list of design goals for the script:

  • Get all of the Adium accounts that are currently offline
  • Prompt the user for which accounts they would like to have go online
  • The prompt should display the accounts in a way that most users will easily understand which accounts they refer to and since a user can have multiple accounts for a single service there needs to be a way to distinguish them
  • Have all the accounts that the user selected go online
  • Display a growl notification for which accounts are being taken online with a picture that represents the service of that account
  • If no accounts are offline no action should be taken and a growl notification should be printed stating that so the user knows why the script appeared to do nothing
  • If the user selects no accounts no action should be taken
  • If the user cancels out of the account selection, no action should be taken
  • Because of changes in Adium's AppleScript support in version 1.2, previous versions of Adium will not be able to run this script, error out nicely.

Building the Script

Those are some pretty hefty design goals. I'm not going to go through each one in the order they are presented in this list. Rather I'm going to go through the final script piece by piece and explain what each part does. The end result is at the bottom of this document and is highly commented (probably to a fault because it can be hard to read).

Step 1 - Register with Growl

Since the script will require that we send growl notifications to the user, we first need to register with growl. More information on how to use Growl with AppleScript can be found in the Growl documentation. Here's how we can register all of our notifications with Growl:

-- Register with growl
tell application "GrowlHelperApp"
  set myAllNotesList to {"Connecting", "No Offline Accounts", "Adium too old"}
  register as application "AdiumSelectiveSignon" all notifications myAllNotesList default notifications myAllNotesList icon of application "Adium"
end tell

The myAllNotesList variable is used to store a list of notifications types. These names will be seen in the growl preferences for this application so the user can define how they want that notification to look and act. The design goals require notification for three circumstances

  • if the installed version of adium is less than version 1.2
  • if there are no offline accounts
  • when the script instructs an account to go online

So that is why we have three different strings for the myAllNotesList list.

The register line provides growl with an application name that these notifications should come from and a default icon to show in the notifications if no icon is provided. Once the line starting with register as application is run, growl knows about this script and will accept requests for it to display notifications. Even though the register only needs to be run once, doing it every time we run the script is not that big of a deal.

Step 2 - Check Adium version

The Adium application provides version information as part of it's application class. So to get the version we have to tell Adium to provide it. This is done with a tell block. Because we are going to be sending Adium many AppleScript commands we can leave the tell block open until the end of the script. Here's the code to check the Adium version and display a notification and quit if the version is less than 1.2:

tell application "Adium"
  -- Check Adium version.  This script requires Adium 1.2 or later.
  set myVersion to version as string
  if myVersion is less than "1.2" then
    tell application "GrowlHelperApp" to notify with name "Adium too old" title "Adium too old" description "This script requires Adium version 1.2 or later." application name "AdiumSelectiveSignon" icon of application "Adium"
    error number -128
  end if

If the version is less than 1.2 we display a growl notification with the name of "Adium too old" and then use an error command to get out of the script. error number -128 is the AppleScript "standard" way of exiting a script silently. In fact when you click cancel on a dialog box that was displayed with the display dialog command, you are actually sending an error number -128 which means "User Cancelled."

Step 3 - Activate Adium

This step is really simple. The Adium application accepts the activate command to tell it open (if it's not already open) and come to the foreground:

  activate

In Leopard, placing this step after the version checking has an interesting side affect in that Adium won't open if the Adium version is less than the required version. This is because AppleScript in Leopard has been updated to allow version checking without launching the scriptable application.

Step 4 - Get all offline accounts

Here is where we really start interacting with Adium's AppleScript support. We need to get all offline accounts. Also remember from out design goals that if there are no offline accounts we need to print a notification and exit.

  -- Create a list of all offline accounts
  set AllAccounts to every account whose status type is offline
  -- If there are no offline accounts, bail
  if AllAccounts is {} then
    tell application "GrowlHelperApp" to notify with name "No Offline Accounts" title "No Offline Accounts" description "There are no offline Adium accounts." application name "AdiumSelectiveSignon" icon of application "Adium"
    error number -128
  end if

First we get all the accounts that have a status type of offline. Then we check that we actually got at least one account. If the AllAccounts list is empty we print a growl notification with a name of "No Offline Accounts" and use the error number -128 to exit the script.

Step 5 - Create a list of strings for the user to choose from

When we ask the user for which accounts they want to have go online we have a couple of challenges. There is really only one good way in AppleScript to request this type input from a user and that is by using the choose from list functionality from Standard Additions. The limitation of this functionality though is that it only accepts a list of text items and it will return the items (also as text) that were selected as a list. We currently have all of the offline accounts as Adium account objects, not text. So we need some way to create text for each account that the user will recognize and be able to select. Then once the selections are made we need some way to turn that text back into something that will represent the account objects again.

What I've opted to do is to create a text string based the name/title of the service the account is part of and the name/title of the account. The text strings will look something like this "AIM - accountname". That way when the user has made their selections I can simply pull apart the string to recreate the account in the form account "accountname" of service "AIM" and use it to tell the account to go online.

-- Create an empty list that will be used to display account descriptions for the user to choose from
set myTextList to {}
-- Loop through the offline accounts and build the text that will be displayed to the user
-- AppleScript doesn't provide an easy way for users to choose from a list of non-text items 
-- using strings.  So the simplest workaround I could find was to build a text string with the 
-- name of the account and the name of the service that can be extracted later. 
repeat with oneAccount in AllAccounts
  set end of myTextList to title of service of oneAccount & " - " & title of oneAccount
end repeat

First I create an empty list that I can add things to the end of. Then I loop through the list of accounts and for each account I grab the title of service of the account and the title of account and create a text string which is set to the end of the myTextList list.

Step 6 - Ask the user for input

Here is where we prompt the user to select the accounts they want to go online.

  -- Have the users choose which accounts they want to have go online
  set ChosenOnes to choose from list myTextList with prompt "Login to which account(s)?" OK button name "Ok" with multiple selections allowed
  -- If they clicked on cancel, bail silently.
  if ChosenOnes is false then error number -128

We set the selection to the variable ChosenOnes. The items the user can select from are passed into the choose from list as the variable myTextList. We use a prompt of "Login to which account(s)?" and allow multiple selections. The default behavior of the choose from list command is to not activate the OK button until the user has selected at least one item. If the user selected cancel, the ChosenOnes variable will be false and so we can exit out.

Step 7 - Tell the selected accounts to go online

This is the final step. We are going to take the chosen accounts the user has picked and extract account name and service name to recreate the accounts that were chosen so we can have them go online.

  -- Save the current text item delimiters to restore them when we are done
  set oldDelimiter to AppleScript's text item delimiters
  -- Set the text item delimiters to the text I inserted between the service name and account
  set AppleScript's text item delimiters to " - "
  -- Now that we have the selected accounts, loop through them to have them go online
  repeat with OneChoice in ChosenOnes
    -- Get the account by extracting the account name and service from the choice
    set theAccount to account (text item 2 of OneChoice) of service (text item 1 of OneChoice)
    -- Get the image of the service for this account so we can display it in a helpful 
    -- growl notification
    set theImage to image of service of theAccount
    -- Display the growl notification
    tell application "GrowlHelperApp" to notify with name "Connecting" title OneChoice description "Connecting" application name "AdiumSelectiveSignon" image theImage
    -- tell the account to go online
    tell theAccount to go online
  end repeat
  -- Reset the text item delimiters to what it was before
  set AppleScript's text item delimiters to oldDelimiter
end tell

First we save the old text item delimiters so we can restore them when we are done. Then we loop through all of the chosen items. For each chosen item we extract the service and account by using the specific text that we added between them when we built the list: " - ".

If you review the design requirements again you will see that one of the requirements was to provide a growl notification that shows which account is being taken online and the growl notification needs to have image of the service of the account. This is accomplished by getting the image of the service of the account and then displaying the growl notification with that image.

Then the account is told to go online.

Once we are done looping through the chosen items, we need to reset the text item delimiters back to what they were and then we have to close out the tell block for Adium which is what the final end tell is for.

Pulling it all together

Here is the full Selective Sign-on example script:

-- Register with growl
tell application "GrowlHelperApp"
  set myAllNotesList to {"Connecting", "No Offline Accounts", "Adium too old"}
  register as application "AdiumSelectiveSignon" all notifications myAllNotesList default notifications myAllNotesList icon of application "Adium"
end tell

tell application "Adium"
  -- Check Adium version.  This script requires Adium 1.2 or later.
  set myVersion to version as string
  if myVersion is less than "1.2" then
    tell application "GrowlHelperApp" to notify with name "Adium too old" title "Adium too old" description "This script requires Adium version 1.2 or later." application name "AdiumSelectiveSignon" icon of application "Adium"
    error number -128
  end if
  -- Activate Adium so that when we do the "choose from list" it's in the foreground.
  activate
  -- Create a list of all offline accounts
  set AllAccounts to every account whose status type is offline
  -- If there are no offline accounts, bail
  if AllAccounts is {} then
    tell application "GrowlHelperApp" to notify with name "No Offline Accounts" title "No Offline Accounts" description "There are no offline Adium accounts." application name "AdiumSelectiveSignon" icon of application "Adium"
    error number -128
  end if

  -- Create an empty list that will be used to display account descriptions for the user to choose from
  set myTextList to {}
  -- Loop through the offline accounts and build the text that will be displayed to the user
  -- AppleScript doesn't provide an easy way for users to choose from a list of non-text items 
  -- using strings.  So the simplest workaround I could find was to build a text string with the 
  -- name of the account and the name of the service that can be extracted later. 
  repeat with oneAccount in AllAccounts
    set end of myTextList to title of service of oneAccount & " - " & title of oneAccount
  end repeat
  -- Have the users choose which accounts they want to have go online
  set ChosenOnes to choose from list myTextList with prompt "Login to which account(s)?" OK button name "Ok" with multiple selections allowed
  -- If they clicked on cancel, bail silently.
  if ChosenOnes is false then error number -128
	
  -- Save the current text item delimiters to restore them when we are done
  set oldDelimiter to AppleScript's text item delimiters
  -- Set the text item delimiters to the text I inserted between the service name and account
  set AppleScript's text item delimiters to " - "
  -- Now that we have the selected accounts, loop through them to have them go online
  repeat with OneChoice in ChosenOnes
    -- Get the account by extracting the account name and service from the choice
    set theAccount to account (text item 2 of OneChoice) of service (text item 1 of OneChoice)
    -- Get the image of the service for this account so we can display it in a helpful 
    -- growl notification
    set theImage to image of service of theAccount
    -- Display the growl notification
    tell application "GrowlHelperApp" to notify with name "Connecting" title OneChoice description "Connecting" application name "AdiumSelectiveSignon" image theImage
    -- tell the account to go online
    tell theAccount to go online
  end repeat
  -- Reset the text item delimiters to what it was before
  set AppleScript's text item delimiters to oldDelimiter
end tell