Create Mac Mail msg with Rich Text content that also has cc: address filled in

When I try to use myRtfString (an object with class ConcreteAttributedString) as the body of an email message, it is not accepted:

tell application “Mail”
set theMessage to make new outgoing message with properties {visible:true, content:myRtfString}
– the attempt above to use the NSConcreteAttributedString fails with an error:
(Mail got an error: Can’t get «class ocid» id «data optr00000000206CA30000600000».)

--  but a plain text string works as expected:	
set theMessage to make new outgoing message with properties {visible:true, content:"a plain text string"}

A google search reveals that lots of people have run into this roadblock over the years. I have tried getting the rich text from the pasteboard without success. Has anyone found a solution?

In X-Code, I can load a file and extract myRtfString as an NSAttributedString and then I can use “sharing” to create a rich text email message:

NSSharingService* myMailShareService = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];

NSArray* shareItems = @[myRtfString];
[myMailShareService performWithItems:shareItems];

This produces a “message” with the expected rich text content displayed in a new window in Mac Mail. The sharing service also supports setting the to: addresses, the subject, and it supports adding enclosures. BUT the documentation does not mention setting the cc: or bcc: fields, not does it mention how to set a specific sender account. It seems like there should be some way to expose these other message attributes to the NSSharingService interface in X-Code - any ideas how to do this?

After creating a rich text message with myMailShareService, I can get a reference to the new message using AppleScript by searching for its title in the Drafts mailbox. But the found object has class “message”, unlike the the object produced by Applescript above, which has class “outgoing message”. All the attributes of “message”, are read only - unlike an “outgoing message” where they are read/write. So how would I convert a “message” to an “outgoing message” so it can be edited (e.g. fill in the cc: field)?

Thanks in advance for any ideas or solutions.

You can’t pass a pointer («data optr…») to an application like that. But even if you convert it to the RTF as text format, it sadly won’t do what you want.

As for the limitations of NSSharingService, I suspect they are quite deliberate. My impression is that Apple sees Mail automation as a potential vector for malware, so the functionality is deliberately limited. If you want decent email automation, you really need to look at third-party email clients.

Thanks for the quick comment. I wish I could just convert the “message” created by NSSharingService to an “outgoing message” like the ones easily produced by AppleScript. But I don’t know if this is possible. In the mean time, I have resorted to NSSharingService to create the rich text content followed by GUI scripting to fix up the mail fields. There are some differences between OS 10.13 vs 10.14 that I don’t understand when calling procedures in various ways (Accessibility enabled), but the code below seems usable in both. (not sure why the code below is sometimes styled, sometimes in a scroll view ??).

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit" -- needed for used rtf methods and NSSharingService

------------------------- setup for testing ------------------------- 
set aFromAddress to "Joe Smith – joe@gmail.comm" -- with multiple mail accounts, specify the one to use as the sender (exact spelling using m-dash)
set aToAddress to "w@somewhereOrOther.comm"
set uniqueWindowName to "a unique msg subject/window-name"
set testValues to {mailTo:"whyChangeThis?", mailCc:"x@mailCc.Comm", mailBcc:"y@mailBcc.Comm", mailReplyTo:"z@mailReplyTo.Comm", mailSubject:"mailSubject", mailSender:aFromAddress}


(*
-- or construct a unique name for a new mail message window 
property NSDate : a reference to current application's NSDate
set theData to NSDate's timeIntervalSinceReferenceDate
set uniqueWindowName to theData as string
*)

-- Mail must be active with GUI scripting enabled
tell application "System Events"
	if not (exists of application process "Mail") then
		display alert "Mail must be running with accessibility enabled in Secruity settings (to allow GUI scripting)"
		return
	end if
end tell

--              create a message for testing  ----
-- BUT the appleScript translation below only works for OS 10.13.
-- In OS 10.14, create a Rich Text message using the NSSharingService code in objective-c 

-- classes, constants, and enums used for getting the Rich Text
-----   (https://forum.latenightsw.com/t/read-and-write-rtf-files/1200)
property NSData : a reference to current application's NSData
property NSAttributedString : a reference to current application's NSAttributedString
property NSDictionary : a reference to current application's NSDictionary
property NSString : a reference to current application's NSString
property NSRTFTextDocumentType : a reference to current application's NSRTFTextDocumentType

set posixPath to POSIX path of (choose file with prompt "Choose an RTF file" of type {"rtf"})
-- set posixPath to POSIX path of ((path to desktop folder as text) & "test.rtf")
-- read file as RTF data
set theData to NSData's dataWithContentsOfFile:posixPath
-- create attributed string from the data
set {theStyledString, docAttributes} to NSAttributedString's alloc()'s initWithRTF:theData documentAttributes:(reference)
if theStyledString is missing value then error "Could not read RTF file"

-- object-c NSSharingService code to create a Rich Text email - translated to applescript below (but the translation only works in OS 10.13)
(*
	NSSharingService* myMailShareService = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
	myMailShareService.subject = uniqueWindowName; 
	mailShare.recipients = @["denisd@sassafras.com"];
	NSArray* shareItems = @[theStyledString];
	[myMailShareService performWithItems:shareItems];
*)

property NSSharingService : a reference to current application's NSSharingService
set myMailShareService to NSSharingService's sharingServiceNamed:"com.apple.share.Mail.compose"
set subject of myMailShareService to uniqueWindowName
set recipients of myMailShareService to {aToAddress}
set shareItems to {theStyledString}
tell myMailShareService to performWithItems:shareItems
delay 1 -- now mail has a new Rich Text mail msg whose window title is uniqueWindowName


------------------------- end testing setup ------------------------- 

setMailFieldsInMsgWindow(testValues, uniqueWindowName)


on setMailFieldsInMsgWindow(mailFields, windowName)
	
	try
		mailTo of mailFields -- maybe testing the dictionary for the field name before attempting to pass its value makes thread or timing issues more predictable ?? when runing in script debugger??
		my setValueForFieldInWindow(mailTo of mailFields, "To:", windowName) -- mailTo: field can be set by NSSharing, so typically doen't need to be replaced here (so not included in the mailFields dictionary)
	end try
	try
		mailCc of mailFields
		my setValueForFieldInWindow(mailCc of mailFields, "Cc:", windowName)
	end try
	try
		mailBcc of mailFields
		my setValueForFieldInWindow(mailBcc of mailFields, "Bcc:", windowName)
	end try
	try
		mailReplyTo of mailFields
		my setValueForFieldInWindow(mailReplyTo of mailFields, "Reply To:", windowName)
	end try
	try
		mailSubject of mailFields
		my setValueForFieldInWindow(mailSubject of mailFields, "Subject:", windowName)
	end try
	try
		mailSender of mailFields
		my setSenderChoice(mailSender of mailFields, windowName) -- an alert will be posted if there are sender choices displayed but the designated From: address is not available in the pop down menu
	end try
end setMailFieldsInMsgWindow


on setValueForFieldInWindow(theValue, theFieldName, windowName)
	local theFields, theField
	tell application "System Events" to tell process "Mail"
		if system version of (system info) < "10.14" then
			set theFields to get every text field of splitter group 1 of window windowName
		else
			set theFields to get every text field of window windowName
		end if
		
		try
			repeat with a from 1 to count of theFields
				if name of item a of theFields is theFieldName then
					set theField to item a of theFields
					exit repeat
				end if
			end repeat
			set value of theField to theValue
		end try
	end tell
end setValueForFieldInWindow

on setSenderChoice(mailSender, windowName)
	local theSenderPopUp, theSenderMenu, theAccounts, a, theAccount
	tell application "System Events" to tell process "Mail"
		try
			if system version of (system info) < "10.14" then
				set theSenderPopUp to pop up button 1 of splitter group 1 of window windowName
			else
				set theSenderPopUp to pop up button 1 of window windowName
			end if
			
			-- set focused of theSenderPopUp to true
			tell theSenderPopUp
				click
				set theSenderMenu to menu 1 of theSenderPopUp
				set theAccounts to every menu item of theSenderMenu
				
				set a to count of theAccounts
				repeat until a = 0 or title of (item a of theAccounts) = mailSender
					set a to a - 1
				end repeat
				
				if a > 0 then
					set theAccount to menu item a of theSenderMenu
					tell theAccount
						click
					end tell
				else
					display alert "Mail sender account \"" & mailSender & "\" not available."
				end if
			end tell
		end try
	end tell
end setSenderChoice

You should include three backticks (```) on their own line before and after code. I’ve fixed it for you in the post above.

Here’s a script that will create an outgoing message in Mail styled with HTML.
Note: you can style the text (color/font/size), and control margin for elements but designating background colors and widths seem to be overridden by the default css file used by the Mail composer, OR… perhaps the passed HTML is being translated to RTF where “widths” and divs aren’t relevant?

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>

<p style=\"font-family:Helvetica;font-size:16px;color:red;text-align:left;\">
	Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas faucibus mollis interdum. Maecenas sed diam eget risus varius blandit sit amet non magna.
</p>
 
<p style=\"font-family:Helvetica;font-size:16px;color:red;text-align:left;\">
	Maecenas sed diam eget risus varius blandit sit amet non magna. Nullam quis risus eget urna mollis ornare vel eu leo. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Etiam porta sem malesuada magna mollis euismod. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis.
</p>

<ol style=\"font-family:Helvetica;font-size:18px;color:orange;text-align:left;\">
	<li style=\"font-family:Helvetica;font-size:18px;color:orange;text-align:left;\">First item</li>
	<li style=\"font-family:Helvetica;font-size:18px;color:orange;text-align:left;\">Second item</li>
	<li style=\"font-family:Helvetica;font-size:18px;color:orange;text-align:left;\">Third item</li>
</ol>"


set messageSubject to "HOWDY"
set recipientAddresses to {"johnny-appleseed@apple.com"}

-- 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:{"someone@somewhere.com"} then
	set aSharingService's subject to messageSubject
	set aSharingService's recipients to recipientAddresses
	tell aSharingService to performSelectorOnMainThread:"performWithItems:" withObject:{anAttributedString} waitUntilDone:false
end if
3 Likes

Cool :+1:

And good to see you here, Sal.

Hey Sal,

Many thanks.

Very pleased to see you on the forum!  :sunglasses:

Now…  What other tricks can you pull with this?

Attachments?

CC?

BCC?

I note that changing “bob@nowhereville.com” to one of my email addresses doesn’t affect the outcome – is there a way to get it to select a specific one of my email addresses?

Running on macOS 10.12.6 Sierra.


Take Care,
Chris

1 Like

Another question.

Is it possible to run this via osascript?

It errors for me on macOS 10.12.6 Sierra.

And – I can’t seem to get this kind of script to run via Keyboard Maestro either.

(KM uses osascript to run all AppleScripts…)

-Chris

Its also possible to do everything Sal script does, with 1 line of code in do shell script.
textutil -convert webarchive textToSend.rtf -output textToSend.webarchive

Now we use Quicklook to share it to mail.

1 Like

That’s easily fixed. Change this line:

tell aSharingService to performWithItems:{anAttributedString}

To:

	tell aSharingService to performSelectorOnMainThread:"performWithItems:" withObject:{anAttributedString} waitUntilDone:false

Well, no — that does a small part of what the script does.

That’s an interesting approach.

It would appear not.

Shane I’m not sure Apple will support webarchive in feature, they remove it from WebView from WebKit… but I believe its still possible to use pasteboard.

1 Like

Nice, Sal…

I could also share other discover of mine in web inspector in Safari.
In the element tab, there is icon (print) it force the frame to force print media style.
It could make a big difference, then a website is saved to PDF.

OneNote from Microsoft use HTML input to create a document/note so its possible to create a document page and share it to Apple Mail. The sad thing it doesn’t support AppleScript. It could be a quick way to make a email to be more powerful and what Apple mail provide. And with the Microsoft cloud feature its simple to create templates in OneNote to be
used anywhere.

OneNote Input and Output HTML
ex.

You could select some text anywhere in Safari or share the page to OneNote. In OneNote You could shape the layout and share it with Apple Mail.

Its other approach to do things.

Merry Christmas to all.

It’s the same for me. I would also be interested how I can specify a email address to send it from.

The address should look like this: “your name <you@yourdomain.com>” (don’t miss the <>characters)

The best way to get it is to make manually a new mail and then run this snippet in SD:

tell application "Mail" to return sender of outgoing message 1

Then copy the script’s result and paste it in this second script to replace resultOfPreviousScipt:

tell application "Mail"
	activate
	set newMail to make new outgoing message
	tell newMail
		set visible to true
		set sender to "resultOfPreviousScipt"
	end tell
end tell
1 Like

Answer the question I came with - how to send the email rather than leave it open. To the end of the script in post #4 above (sorry, my forum privs don’t let me link to it) add this code, paying attention to the need to Accessibility prefs for the app/process running the script:

-- IMPORTANT, for the next part to work add the 'Mail' app (and the app running the script, e.g. 'Script Editor'
--    to the Accessibility prefs under the Security & Privacy part of System Preferences
activate application "Mail"
tell window messageSubject of application "Mail"
	tell application "System Events"
		tell process "Mail"
			-- insert GUI Scripting statements here:
			click button 1 of toolbar 1 of window messageSubject
		end tell
	end tell
end tell

(GUI scripting figured using UI Browser app)

Hope that’s of use to others.

Hey @ShaneStanley, @otto-automator (Sal), et al…

In reference to Sal’s script above:

  • Is it possible to alter the sender in the script?
    • On my system I only get my default sender.
  • Is it possible to set a signature in the script?

TIA.

-Chris