Getting the Active Chat in Adium

Getting the active chat in Adium from AppleScript appears at first look to be a little dicey: Adium needs to actually be the active application before the code actually works! For instance, if you open up the Script Editor, and type in the following:

tell application “Adium”     get contact of the active chat of the first interface controller end tell

You will get an empty list: {} To get a result, all you need to include is:

    activate

just before you access something belonging to the active chat. If you are creating an AdiumScript, and are testing it in the Script Editor, chances are you have used the idiom:

on run     my substitute() end run

To get this to work if you are using the active chat, it just needs to be changed to:

on run     tell application “Adium” to activate     my substitute() end run

Happy AdiumScripting!

Python Scripts inside AdiumScripts package

I’ve got some stuff to do that will be easier with python, so I’m wondering if it’s possible to package a python script up, and use it from within an AppleScript, without having to have it in the path. It may not be, since AppleScript’s

do shell script “pwd”

returns "/", indicating it is running at the root directory. Well, back to pipes and awk/sed. (Actually, I think I’d just used awk & sed inside the python script).

Phase One of iTunes Chat Client

I’ve started phase one of the iTunes controller bot: I’m downloading the jabber server for MacOS X. ` $ sudo port install jabberd ` Phase Two will soon be underway: I’ve also downloaded the source of a Python bot… more to come soon! Actually, getting the jabber server to work wasn’t as easy as it should have been: I’m using a private jabber account on a server only I know about, but I’ll post more stuff as I get it working. Python looks to be an easy way of getting the bot to work: it would be nice to be able to do it all in AppleScript though. I can’t believe I just said that!

Adium Plugin Scripts

Adium has great support for AppleScripts (or possibly other scripting languages), in that you define a string, and when this string is typed in as a message, the script is run, and the result is sent as the message instead. Thus, it’s possible to write scripts that allow you to type in: %_itunes And a nicely formatted string, with the current iTunes song is sent to the person you are chatting to. This string can even have hyperlinks embedded in it, so if you were serving your iTunes library up, the person could click on it and download/stream it. The same for %_safari/%_camino, this can be set up to send a hyperlink with the current page, including the title. What is not clear from the internet is how to create these. It’s really quite simple. The scripts must live inside a package: under MacOS X packages are just folders with particular extensions. For instance, Mail.app is a folder, and it appears to the system/user as an application, or a particular type of package. Double-clicking on it will not opent he folder, but (in this case) run the application. AdiumScripts packages (with the extension .AdiumScripts) will install into your Library when you double-click them. So, the format must be as follows:

PluginName.AdiumScripts/
    Contents/
        Info.plist
        Resources/
            Script1Name.scpt
            Script2Name.scpt

The ScriptxName.scpt files must be valid AppleScripts, of the format:

on substitute(arg) –do stuff here return value end substitute

Where value is the value you want printed. You should format it with starting and closing html tags, and any html markup you want. If you want the user to be able to send an argument (%command{argument}), then include it as shown. Inside the Info.plist file, you need to have some important information.

 1    <?xml version="1.0" encoding="UTF-8"?>
 2    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3    <plist version="1.0">
 4    <dict>
 5        <key>CFBundleDevelopmentRegion</key>
 6        <string>English</string>
 7        <key>CFBundleGetInfoString</key>
 8        <string>Matt's Adium Scripts</string>
 9        <key>CFBundleIdentifier</key>
10        <string>net.schinckel.AppName.scripts</string>
11        <key>CFBundleInfoDictionaryVersion</key>
12        <string>1.0</string>
13        <key>CFBundleName</key>
14        <string>bundleName</string>
15        <key>CFBundlePackageType</key>
16        <string>AdIM</string>
17        <key>Scripts</key>
18        <array>
19            <dict>
20                <key>File</key>
21                <string>Script1Name</string>
22                <key>Keyword</key>
23                <string>/script1</string>
24                <key>Title</key>
25                <string>Menu Item 1</string>
26            </dict>
27            <dict>
28                <key>File</key>
29                <string>Script2Name</string>
30                <key>Keyword</key>
31                <string>%_script2</string>
32                <key>Title</key>
33                <string>Menu Item 2</string>
34            </dict> </array>
35        <key>Set</key>
36        <string>Script Submenu</string>
37    </dict>
38    </plist>

There are some bits of data you’ll need to change: Matt's Adium Scripts should be a descriptive title for your Scripts. com.schinckel.AppName.scripts should be a unique identifier for your scripts. bundleName could be the name of your bundle. Realistically, these can be anything, although I suspect Adium will only allow one version of a script with a particular identifier, or bundle name. The next bits are the impart ones: Script1Name needs to be the name of a script, minus the .scpt extension. /script1 is the keyword that will trigger this script to be called. And Menu Item 1 is what will be written in the Scripts Sub Menu. It’s possible to have multiple calls to the same script, each one just requires a duplicate dictionary entry, with a different Keyword string. For instance, I use /camino and %_camino to mean the same function, without requiring seperate scripts to be called. Finally, the Script Submenu value needs to be replaced by which Script Sub Menu you want the item to be placed under. For instance, there is already one called System Statistics, and iTunes - Now Playing. Using a value that doesn’t appear already causes a new Sub Menu to be created. I’m not sure if there is a ‘rule’ against using /command, as most of the ones I’ve come across all use %_command. I find this too slow to type, and have adapted them accordingly. You will be able to do this also, now that you know how…

Chatting to Robots?

I had a thought just before. (Unusual, I know). I wrote a program called iTunesRater so I can rate my music as I listen to it in iTunes, and then went on to write a series of Adium Scripts to control iTunes ratings, and get data from iTunes.

What if instead of having to create scripts for iTunes that did everything you want, you just create a bot that runs, and you send messages to it, and it controls iTunes. So, I could have the program running, and instead of having to type: /itunes

_♫ The Chemical Brothers (Feat. The Flaming Lips) • The Golden Path _

/rate{44} /itunes

♫ The Chemical Brothers (Feat. The Flaming Lips) • The Golden Path ★★

I could just start up a chat with iTunes, and type all sorts of commands:

me: song

it: ♫ The Chemical Brothers (Feat. The Flaming Lips) • The Golden Path

me: rate 44

it: ♫ The Chemical Brothers (Feat. The Flaming Lips) • The Golden Path ★★

me: next track

[iTunes next track plays]

All it would seem to require is a program that can connect to a server of some sort (I think maybe Jabber, then I can run my own Jabber server!), and sends AppleScript messages to iTunes. Or any other application, for that matter.

Even better Current iTunes Track

It’s nice to show other users what you are listening to, and provide a handy link for them to see more about the artist and track. I noticed that some IM systems don’t like HTML, and I figured out a way to make the link only be sent to a user of an AIM compatible or Jabber server.

Basically, you test the result of serviceClass of contact of the active chat of the first interface controller, and if it is “AIM-compatible” or “Jabber”, then you use HTML, if not, just plain text. You can download it from Adium Xtras: Now Playing in iTunes.

Full Source:

 1 Better itunes adium link script
 2 Includes rating (if present), and links.
 3property message_prefix : «data utxt266B0020» as Unicode text
 4property not_listening_message : "Quiet"
 5property star : «data utxt2605» as Unicode text
 6property google : "http://www.google.com/search?q="
 7property server : ""
 8
 9property html_classes : {"AIM-compatible", "Jabber"}
10
11on substitute()
12    set server to "http://" & my getIP() & ":8080"  Use betterGetIP if on broadband!
13    
14    tell application "System Events"
15        try
16            get process "iTunes"
17        on error
18            return message_prefix & not_listening_message
19        end try
20    end tell
21    
22    tell application "iTunes"
23        if player state is not playing then
24            return message_prefix & not_listening_message
25        end if
26        set theArtist to artist of current track
27        if theArtist is "" then set theArtist to "Unknown Artist"
28        set theTrack to name of current track
29        set theRating to " " & my myRating(rating of current track)
30        set theLocation to location of current track
31        set pth to my encode_URL_string(characters 9 thru end of (POSIX path of theLocation) as string)
32    end tell
33    
34    tell application "Adium" to set accountType to serviceClass of contact of the active chat of the first interface controller
35    if first item of accountType is in html_classes then
36        return html(message_prefix & my myLink(google, theArtist) & " • " & my myLink(server & pth, theTrack) & theRating)
37    else
38        return message_prefix & theArtist & " • " & theTrack & theRating
39    end if
40end substitute
41
42on myRating(theRating)
43    set theResult to ""
44    set theTimes to (theRating - 9) / 20 as integer
45    repeat theTimes times
46        set theResult to theResult & star
47    end repeat
48    return theResult
49end myRating
50
51on myLink(URI, theText)
52    if URI is google then
53        set URI to URI & first word of theText
54        repeat with wd in (rest of every word of theText)
55            set URI to URI & "+" & wd
56        end repeat
57    else if server is "" then
58        return theText
59    end if
60    return "<a href=\"" & URI & "\">" & theText & "</a>"
61end myLink
62
63on html(str)
64    return ("<HTML>" as Unicode text) & str & "</HTML>"
65end html
66
67on getIP()
68    return do shell script "ifconfig ppp0 | grep inet | awk ‘{print $2}’"
69end getIP
70
71on betterGetIP()
72    return do shell script "curl -s http://www.showmyip.com/simple/ | grep GMT | awk ‘{print $1}’"
73end betterGetIP
74
75property allowed_URL_chars : (characters of "/$-_.+!*’(),1234567890abcdefghijklmnopqrstuvwxyz")
76property hex_list : (characters of "0123456789ABCDEF")
77
78on encode_URL_string(this_item)
79    set character_list to (characters of this_item)
80    repeat with i from 1 to number of items in character_list
81        set this_char to item i of character_list
82        if this_char is not in allowed_URL_chars then set item i of character_list to my encode_URL_char(this_char)
83    end repeat
84    return character_list as string
85end encode_URL_string
86
87on encode_URL_char(this_char)
88    set ASCII_num to (ASCII number this_char)
89    return ("%" & (item ((ASCII_num div 16) + 1) of hex_list) & (item ((ASCII_num mod 16) + 1) of hex_list)) as string
90end encode_URL_char
91
92on run
93    tell application "Adium" to activate
94    my substitute()
95end run

Hosted Gmail and Gtalk

There exists a small bug in Adium, which apparently has been fixed in an upcoming version, which means that hosted Gmail accounts cannot sign in as GTalk accounts. To fix it, I needed to create a new Jabber account, with the following settings: Account. Jabber ID: matt@schinckel.net Password: •••••••••••••••• Options. Connect Server: talk.google.com Resource: Adium Port: 5223 ☑ Force old-style SSL Then it connected fine, first time.

AdiumX Console Trash

I’m very sick of the INFO cmdproc.c:98:show_debug_cmd() rubbish that the latest Adium release is spewing into my Console Log twice a minute.

I’m finding I’d rather leave IM off, than see it traced across my desktop.

(I use MkConsole to keep an eye on things).

Update: it looks like 1.3.2b1 fixes this. Yay!