applescriptmacos-mail-app

AppleScript Mail Message Reference Cannot Be Used


If I use AppleScript to get Mac Mail's selection like this:

tell application "Mail" to get selection

It returns a list of message references like this one: message id 1 of mailbox "INBOX" of account id "1234" of application "Mail".

However, if I copy/paste these references back into another script, I get a syntax error.

enter image description here

I can use the reference in this more convoluted way:

tell application "Mail" to get first message of mailbox "INBOX" of account id "1234" of application "Mail" whose id is 1

But conceptually, why the first way does not work? It seems to subvert the purpose of having a reference and complicate client code.

Accepted answer rationale: I accepted @CJK's answer (even though @Stephen Kaplan's answer was also correct) based on the consensus here to prefer the first correct answer unless another gives significantly more explanation. Also, @matt's answer, while not specifically addressing my main question, cut to the heart of the matter - avoid using AppleScript so you don't get into hassles like this! I will instead be trying appscript, which I had stopped using long ago because it was no longer supported in Ruby, but apparently does again and still works in Python.

p.s. my use case is that I prefer to use lots of small scripts which are run via osascript and glued together in a language I'm more comfortable with, like Python or Ruby. I started exporting Mail's selection with a monolithic AppleScript, but it failed if the message's subject - which was used as part of the filename - used illegal characters. It seemed too complicated for me to address in AS, so I figured I'd get the reference, and then use it to cobble together the subject, date received and message source myself outside of AS, but I hit this snag. I then went down a rabbit hole trying to do the same thing in JXA, but eventually hit another problem: message source returned in osascript's output has newlines printed as \n. That was however easier to overcome because writing the source to a file in JXA instead of using osascript's output was fairly straightforward.

p.p.s. is there no way to search AppleScript-Users's archive? The only interface I found seemed to require poking around blindly by year and month.


Solution

  • The problem stems from how AppleScript resolves the terminology that is defined for a particular application. Almost all applications that are scriptable with AppleScript have a scripting definition (.sdef) file included in its application bundle, which contains the custom commands, property names, class elements, and constants defined for it. This so-called application scripting dictionary has two faces: the one presented to us offers a means of looking up these terms by accessing the terminology dictionary from the Script Editor library:

    Mail's Scripting Dictionary

    It's second face is the one used by the compiler, for which the .sdef file serves as a syntax mapping or translation tool, taking a human-readable piece of terminology, such as the class element in Mail's dictionary named message that denotes an individual email message, and converting it to a code comprised of four characters, namely 'mssg'. Here's how it's defined in the scripting definition file, where I've marked the relevant line with ❶ →:

    ❶ →   <class name="message" code="mssg" description="An email message">
                    <cocoa class="MCMessage"/>
            
                    <element type="bcc recipient"/>
                    <element type="cc recipient"/>
                    <element type="recipient"/>
                    <element type="to recipient"/>
                    <element type="header">
                            <cocoa key="appleScriptHeaders"/>
                    </element>
            
            ➋ →   <property name="id" code="ID  " type="integer" access="r" 
                              description="The unique identifier of the message.">
                            <cocoa key="libraryID"/>
                    </property>
            
                    <property name="content" code="ctnt" type="rich text" access="r" 
                              description="Contents of an email message"/>
            
                    <property name="date received" code="rdrc" type="date" access="r" 
                              description="The date a message was received"/>
            
                    <property name="date sent" code="drcv" type="date" access="r" 
                              description="The date a message was sent"/>
            
                    <property name="flagged status" code="isfl" type="boolean" 
                              description="Indicates whether the message is flagged or not">
                            <cocoa key="isFlagged"/>
                    </property>
            
            ❸ →   <property name="message id" code="meid" type="text" access="r" 
                              description="The unique message ID string">
                            <cocoa key="messageIDHeader"/>
                    </property>
            
                    <property name="source" code="raso" type="text" access="r" 
                              description="Raw source of the message">
                                            .
                                            .
                                            .
    

    To recapitulate, line defines the class that we know by its name attribute, "message", and which the compiler converts to the a type code given by its code attribute, 'mssg' (this actually gets converted further using the values of each character given by its code point in the Unicode table, then evaluating these as coefficients in the hexadecimal expansion for the resulting 32-bit number).

    There are two other lines in the definition of the message class that I've marked: line defines the id property for the message class object, which is the property you are trying (and quite rightly) to leverage in order to reference a particular instance of a message object. Ordinarily, this is exactly the right thing to do, and in all other applications, the id property of an object is a reliable property that is guaranteed to remain unchanged for the object's lifetime, and, as far as I know (in that, I've not come across an example to the contrary), when id values are integers, they get assigned to a group of objects in the same class in order of increasing magnitude that matches the order of the objects' creation, i.e. the id of the message object associated with an email you've just received should be greater than the id of all other messages objects that are associated to emails within the same inbox.

    BUT...

    The line marked defines another property of a message class object, which we know by its name attribute, "message id", that the compiler converts to the type code 'meid'.

    Here's where the problem arises: message class references get returned to you in the following form:

    message id 1 of mailbox "INBOX" of account id "1234" of application "Mail"
    

    When we try and make use of this reference, what we want is for the compiler to interpret message id 1 of... as the object with type class message with the id property equal to 1. However, the compiler parses the consecutive terms "message" followed by "id" and matches this particular combination of words with the terminology from Mail's scripting dictionary, which contains a property named "message id". So, rather than converting the natural language expression into an event descriptor for the object with type code 'mssg' and a reference form with type code 'ID ', it converts it into a single type code 'meid`` that would form part of a totally different event descriptor for a mailbox` class object, at which point an error is thrown.