pythonpyobjcscripting-bridge

Sending a message in Messages with PyObjC and ScriptingBridge in Python


this is probably a simple question but I am a bit confused as I haven't found many examples online.

I've been able to send messages through Messages in Mac OS using Javascript (Using this tutorial) but I can't figure out how to do it using Python and PyObjC.

Using Javascript I'd do something like this:

var messages = Application('Messages');
var buddy = messages.services["E:%REPLACE_WITH_YOUR_IMESSAGE_EMAIL%"].buddies["%REPLACE_WITH_BUDDYS_EMAIL%"];
messages.send("JavaScript sent this message!", {to: buddy});

I can't figure out how to set the buddy variable to the relevant object with Python. The following works fine to access Messages

from Foundation import *
from ScriptingBridge import *
Messages = SBApplication.applicationWithBundleIdentifier_("com.apple.iChat")

Then in Python I'm able do something like this.

In [182]: s = Messages.services()
In [183]: [x.name() for x in s]
Out[183]: ['E:foo@icloud.com', 'Bonjour', 'SMS']

But I'm not sure how to make the leap from this to actually getting it to send a message using Messages.send_to_ after I create the Messages object.

Your help would be greatly appreciated, thank you very much!


Solution

  • You can do it like this:

    from ScriptingBridge import SBApplication
    
    Messages = SBApplication.applicationWithBundleIdentifier_("com.apple.iChat")
    
    # get the first budddy who's name is Chris Cummings
    buddy_to_message = [b for b in Messages.buddies() if b.fullName() == "Chris Cummings"][0]
    
    # send text to buddy
    Messages.send_to_("sending this from python test", buddy_to_message)
    

    Something I've found really useful when trying to use the largely undocumented ScriptingBridge module from pyobjc is to search for methods that are available on the class that I'm trying to get access to in the repl

    >>>[method for method in dir(Messages) if "bud" in method.lower()]
    ["buddies", "buddies"]  # found the buddies method
    >>>[method for method in dir(Meessages.buddies()[0]) if "name" in method.lower()]
    [ ... 'accessibilityParameterizedAttributeNames', 'className',
    'elementWithCode_named_', 'entityName', 'firstName', 'fullName',
    'fullName', 'lastName', 'name', 'name', 'scriptAccountLegacyName',
    'valueWithName_inPropertyWithKey_']
    
    # ... this one had a bunch of other junk but hopefully this illustrates the idea
    

    An additional note on dir: Of course dir() can take arguments as well and you can get a list of methods that are defined on the object that match a string with dir('name') but the ObjectiveC classnames are almost never capitalized as I'd expect, so I think it's useful to search for them all lowercase.