A while back I came across a way to display Growl notifications for iChat, and after a few tweaks for Lion I’ve been mostly happy with it.
But being unable to click on the notifications to activate iChat is annoying, so I spent a half hour looking for ways to:
- Extend my current AppleScript to support clickable notifications (alas, Growl itself has no way to do it in pure AppleScript, I have no intention to fiddle with AppleScript Studio, and their old issue tracker is gone, so no luck in even finding out if it’s being done).
- Set up a helper app to do the notifications for me and then activate iChat. That is reasonably simple to do using PyObjC and the Growl framework, but somewhat fiddly (I don’t want to have to set up a script and a helper app).
- Go all out and try to inject code into iChat to do the appropriate Chax-like method swizzling (they have source code available for the older versions, but SIMBL is dead and I needed some other way to inject code into the app).
That went pretty much nowhere until I stumbled across Apple’s IMServicePlugin protocol reference and realized that iChat in Lion supports plugins for adding other instant message transports.
A while later, I had a bogus IM transport “running” that consisted of a PyObjC bundle created with the following setup.py
:
from distutils.core import setup
import py2app
plist = {
'NSPrincipalClass':'TestPlugin',
'CFBundleInfoDictionaryVersion':'6.0',
'CFBundlePackageType':'APPL',
'CFBundleName':'TestPlugin',
'CFBundleIdentifier':'com.taoofmac.ichat.test',
'CFBundleSignature':'????',
'CFBundleGetInfoString':'TestPlugin 0.1',
'CFBundleVersion':'0.1',
'CFBundleShortVersionString':'0.1',
'IMUsesEnableSSLAccountSetting': False,
'IMUsesPasswordAccountSetting': False,
'IMServiceCapabilities': [
'IMServiceCapabilityPresenceSupport',
'IMServiceCapabilityInstantMessagingSupport',
'IMServiceCapabilityChatRoomSupport',
]
}
setup(
plugin = ['TestPlugin.py'],
options=dict(py2app=dict(extension='.imserviceplugin', plist=plist))
…and these lines of code:
from AppKit import *
from Foundation import *
import objc
objc.loadBundle("IMServicePlugIn", globals(), bundle_path=objc.pathForFramework(u'/Library/Frameworks/IMServicePlugIn.framework'))
class TestPlugin(NSObject):
_dispatcher = None
def initWithServiceApplication_(self, app):
self._dispatcher = app
NSLog("TestPlugin got %s" % app)
self._dispatcher.plugInDidLogIn()
NSLog("Logged in")
def updateAccountSettings_(self, nsdict):
NSLog("TestPlugin settings %s" % nsdict)
I’ve got a number of interesting ideas for this (not the least of which would be to implement direct, old-school MSN protocol support using msnlib)1, but so far I’m trying to unravel the internals with class-dump
to see if I can use this mechanism to somehow gain access to alerts (given that there seems to be no other plugin protocol whatsoever).
Alas, as always, I have precious little time to do more than figure out attack vectors, but maybe this will kindle someone else’s curiosity - if so, do drop me a line.
For those of you who’d be plenty happy to have working (even if non-clickable) Growl notifications regardless, my modified script is here. Just go to the Alerts tab and bind it to the relevant events.