Sometimes the combination of Mail and AppleScript is frustrating. It's easy to make an emails with AppleScript for Outlook. In Mail this has been broken since El Capitan or thereabouts. I tried hard to find a workaround. But I was able to create only simple html emails.

Today's blog post is going to be a bit more technical. For the last version of my script I'm also going to switch to Xojo. The code is similar to AppleScript but I can do things more easily there.

Fail: Simply make a html email and send it

When goggling for a solution I found one that sounded very simple: make some html, make an invisible email and send it. At the other end a html email was supposed to arrive.

The script is similar to the one for Outlook. Again I gather my information. I make a new "outgoing message". The visibility is set to false! The "html content" of the message is set and then the message is sent. I commented the sending out. You will end up with an invisible email!

set textBody to "<HTML><BODY bgcolor=\"#99CCFF\"><H1>Hi There</H1><p>This is very minimal <u>\"hello world\"</u>HTML document.</p> </BODY></HTML>"
set textSubject to "HTML Test"
set toAddress to "[email protected]"
set toName to "xxx"

mailMessageCreate(toNametoAddresstextSubjecttextBody)

on mailMessageCreate(toNametoAddresstextSubjecttextBody)

   
tell application "Mail"
       
activate
       
set refMessage to make new outgoing message with properties {subject:textSubjectvisible:false-- visible MUST be false to work
       
tell refMessage
           
set html content to textBody -- must set the HTML CONTENT rather than the CONTENT
           
make new to recipient at end of to recipients with properties {name:toNameaddress:toAddress}
           
--send
       
end tell
   
end tell
end mailMessageCreate

At some point this must have worked. However, for Ventura I got an email with a blank message body when I sent the email to myself.

Somewhat working: Using NSSharingService

After a lot of goggling I found a solution with NSSharingService by the grand master of AppleScript Sal Soghoian. It's possible to use all the functions of macOS. I had a brief look at this when trying to get the length of a string for exporting to eml. NSSharingService is a bit like AppleScript without AppleScript for social media:

An object that facilitates the sharing of content with social media services, or with apps like Mail or Safari.

The big benefit of using NSSharingService in non-AppleScript code is that it doesn't need the lovely approval for AppleScript. The AppleScript looks much more complicated than the one for Outlook or the one above. However, it's doing exactly the same thing.

First everything needed is initialised. Again some properties for the message are set including the html for the message body.

Now comes the magic: NSSharingService uses something called NSAttributedString which is like rtf - text with style information like color, size etc.. The NSAttributedString can be made out of html! The html of the email is set directly with this styled text.

Here is the script:

use framework "Foundation"
use framework "AppKit"
use scripting additions

-- classes, constants, and enums used
property NSUTF8StringEncoding : a reference to 4
property NSSharingServiceNameComposeEmail : a reference to current application's NSSharingServiceNameComposeEmail
property NSAttributedString : a reference to current application's NSAttributedString
property NSString : a reference to current application's NSString
property NSSharingService : a reference to current application's NSSharingService

-- MAIL MESSAGE VALUES
set headlineText to "<p style=\"font-family:-apple-system,san-serif;font-size:64px;text-align:left;margin-bottom:0;\">&#63743;</p>"
set bodyText to "<h1 style=\"font-family:-apple-system;\">This is a headline level 1</h1><h2 style=\"font-family:-apple-system;\">This is a headline level 2</h2><p style=\"font-family:Helvetica;font-size:16px;color:red;text-align:left;\"> Nulla vitae elit libero, a pharetra augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec sed odio dui.</p>"
set messageSubject to "HOWDY"
set recipientAddresses to {"[email protected]"}

-- THE HTML FOR THE MESSAGE
set thisHTML to "<!DOCTYPE html><html lang=\"en\"> <head><meta charset=\"utf-8\" /><title>MAIL TEMPLATE</title></head>
<body style=\"margin:0;width:100%;Height:100vh;\">" &
headlineText & return & bodyText & return & "</body></html>"

set theSource to NSString's stringWithString:thisHTML
set theData to theSource's dataUsingEncoding:NSUTF8StringEncoding
set anAttributedString to NSAttributedString's alloc()'s initWithHTML:theData documentAttributes:{}

-- USE THE MAIL SHARING SERVICE TO CREATE A NEW MAIL MESSAGE
set aSharingService to NSSharingService's sharingServiceNamed:(NSSharingServiceNameComposeEmail)
if aSharingService's canPerformWithItems:{"[email protected]"} then
   
set aSharingService's subject to messageSubject
   
set aSharingService's recipients to recipientAddresses
   
tell aSharingService to performSelectorOnMainThread:"performWithItems:" withObject:{anAttributedStringwaitUntilDone:false
end if

And here is the result:

Email created with AppleScript and NSSharingService

This doesn't look bad! However, there is only one tiny problem. NSAttributedString is made for writing text. And text usually doesn't have blockquotes.

If you want to create simple emails with AppleScript in Mail then the above solution is fine.

Trying to improve: Using NSSharingService in Xojo

For developing Mail Archiver I use Xojo which also can do NSSharingService. So I had the idea if I could do indents similar to the Outlook script.

I made some simple html so that I could test different levels of quoting:

test before quote

quote 1

quote 2

quote 1

no quote

I assume the following levels of quoting for the paragraphs:
0
1
2
1
0

The idea is to take each paragraph like "test before quote" and the multiple it with the array I have for the quote levels and some basic indent value like 10 pixels. quote 1: 1 * 10 and quote 2: 2 * 10 and so on. Each paragraph gets it's own NSAttributedString. I combine the paragraphs at the end and then I make an email.

'simulate html with blockquotes
dim theText as String = "test before quote,quote 1,quote 2,quote 1,no quote"
dim Blocks(-1) as String = theText.Split(",")
dim LevelText as String = "0,1,2,1,0"
dim theLevels(-1) as String = LevelText.Split(",")

Dim documentAttributes As Dictionary
Dim documentOptions As New Dictionary
'define the text encoding
Const NSUTF8StringEncoding = 4
documentOptions.Value(NSAttributedStringMBS.NSCharacterEncodingDocumentOption) = NSUTF8StringEncoding
documentOptions.Value(NSAttributedStringMBS.NSTimeoutDocumentOption) = 30
Dim theFont as NSFontMBS = NSFontMBS.fontWithName("Helvetica", 14.0)
documentOptions.Value(NSAttributedStringMBS.NSFontAttributeName) = theFont

'loop over the html parts and give them different levels of indentation
dim FinalString as new NSMutableAttributedStringMBS
for currentBlockquote as Integer = 0 to Blocks.LastIndex
Dim MutableString As New NSMutableAttributedStringMBS
call MutableString.initWithHTML(Blocks(currentBlockquote), documentOptions, documentAttributes)
if val(theLevels(currentBlockquote)) > 0 then
Dim theRange As NSRangeMBS = New NSRangeMBS(0, MutableString.Length - 1)
Dim MutableStyle As New NSMutableParagraphStyleMBS
MutableStyle.setHeadIndent(10 * val(theLevels(currentBlockquote)))
MutableStyle.setFirstLineHeadIndent(10 * val(theLevels(currentBlockquote)))
MutableString.addAttribute(MutableString.NSParagraphStyleAttributeName, MutableStyle, theRange)
end if
FinalString.appendAttributedString(MutableString)
next

'make email
Dim items As New NSSharingServiceItemsMBS
items.AddAttributedString(FinalString)
Dim service As New NSSharingServiceMBS("com.apple.share.Mail.compose")
service.subject = "theSubject"
service.setRecipients array("[email protected]")
service.performWithItems items


The result was really disappointing:

Email made with different NSAttributedStrings

There is an additional newline. The quote level of the last line is not correct. I played around with the code. I couldn't get a larger indent and I couldn't style the text properly.

Verdict

Apple has become really bad at fixing bugs. Creating emails with html in Mail with AppleScript has been broken for a long time. What is easy to do in Outlook is a pain in the behind in Mail.

The solution with NSSharingService in AppleScript is super clever. It's suitable for creating simple styled emails.

Originally, I wanted to use the script for Mail Archiver. But in Mail Archiver the functionality for creating emails with AppleScript means replying to emails and replies always are in blockquotes.