How to Open URLs Containing Emoji Characters from an AppleScript

My script correctly creates mailto: URLs in the form:

mailto:Wile Coyote<beepbeeb@acme.com>?&subject=Happy 57th Birthday 🎈&body=Wile:\r\n\r\nI hope that you have a special celebration planned for today.\r\n\r\n- roadRunner

However, when I use open location to launch it in the (Chrome) web interface to Gmail, the emoji in the subject is converted to ?

I presume this conversion is due to open location not encoding UTF-8 characters correctly. (If I simply copy/paste the above string into the Chrome address bar, the resulting Gmail message subject contains the emoji.)

How should I open this URL within an AppleScript? Is there a way for open location to encode it correctly? If not, do I need to use a shell script to open it?

Would someone please share a code snippet or URL to some examples?

Thank you.

1 Like

It was a little difficult.

-- Created 2016-04-19 by Takaaki Naganoya
-- Modified 2022-06-05 by Takaaki Naganoya
-- 2022 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSURLQueryItem : a reference to current application's NSURLQueryItem
property NSURLComponents : a reference to current application's NSURLComponents
property NSMutableDictionary : a reference to current application's NSMutableDictionary

set aBaseURL to "mailto:beepbeeb@acme.com"
set aRec to {subject:"Happy 57th Birthday 🎈", body:"Wile:

I hope that you have a special celebration planned for today.

- roadRunner"}

set urlRes to retNSURLwithParams(aBaseURL, aRec) of me

current application's NSWorkspace's sharedWorkspace()'s openURL:urlRes



on retNSURLwithParams(aBaseURL as string, aRec as record)
	set aDic to NSMutableDictionary's dictionaryWithDictionary:aRec
	
	set aKeyList to (aDic's allKeys()) as list
	set aValList to (aDic's allValues()) as list
	set aLen to length of aKeyList
	set bLen to length of aValList
	if aLen is not equal to bLen then return false
	
	set qList to {}
	repeat with i from 1 to aLen
		set aName to (contents of item i of aKeyList) as string
		set aVal to (contents of item i of aValList) as string
		set the end of qList to (NSURLQueryItem's queryItemWithName:aName value:aVal)
	end repeat
	
	set aComp to NSURLComponents's alloc()'s initWithString:aBaseURL
	aComp's setQueryItems:qList
	
	return aComp's |URL|()
end retNSURLwithParams

The URL standard states that only US ASCII characters can appear in a URL. Converting your Unicode Emoji characters to UTF-8 is not sufficient. You need to escape the UTF-8 character codes as hex bytes (%##).

Pure AppleScript is not the best tool for this sort of thing. You could do this with ASObjC or you could shell out to perl or python to do the conversion.

@Piyomaru 's code is a good place to start with ASObjC.

Which of these three approaches do you recommend?

A fourth option is, of course, is to use:

osascript -l JavaScript

in place of:

osascript -l AppleScript


In JS:

encodeURIComponent("Happy 57th Birthday 🎈")

evaluates directly to:

"Happy%2057th%20Birthday%20%F0%9F%8E%88"

so if you wanted to use the shell, an alternative to perl or python would be:

osascript -l JavaScript -e 'encodeURIComponent("Happy 57th Birthday 🎈")'

(and it might be easier to do the the whole thing in osascript -l JavaScript, or in Script Editor with the language selector at top left set to JavaScript in lieu of AppleScript)

I wrote a perfectly cromulent set of AppleScript standard libraries some years back. The Web library wraps the ObjC URL APIs in friendly AppleScript-style commands. The commands to use when assembling URLs:

  • encode URL characters
  • join URL query string
  • join URL

Examples are included in the library’s dictionary.

WRT calling out to Perl/Python/JavaScript/etc, to paraphrase Zawinski:

Some people, when confronted with a problem, think “I know, I’ll use ${TECHNOLOGY}.” Now they have two problems.

Or, alternately:

“Yo Dawg i herd you like character escapes so we put a character escapes in yo character escapes…”

Shelling out to Python et al is really a non-starter nowadays as Apple no longer bundles the P-languages as standard in macOS. (Even when they were present, going through do shell script and multiple layers of character escaping was non-trivial to get right.)

As for JXA, though still included in macOS (Dog knows why), that corpse walks in a small sad world of Fail all of its own, and trying to use it for non-trivial work is just a miserable exercise in futility. (Honestly, if you want to use JavaScript for anything, run—don’t walk—to Node.js.)

Calling into ObjC’s NSURL APIs is the one good and sensible solution here. There’s no contest. If OP doesn’t want to deal with ASOC directly (it is a chore), there is as I say already a Web library that wraps all the ObjC ugliness behind familiar friendly AppleScript syntax.

It’s a real pity most of the AS community never really “got” libraries. While AppleScript, like JavaScript, will never be a good language in itself, having a popular populated repository of third-party libraries a-la npm or PyPI goes a long way to minimizing the pain of boring generic crapwork, so you can focus on doing what the AppleScript language is genuinely good at (automating Mac apps) instead of wading through the stuff that it sucks at (everything else).

Good to hear from you, still as angry at everything, not least the unforgiveably invaluable JXA, as ever :slight_smile:

Indeed. My RegexAndStuffLib contains commands for this stuff too:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use script "RegexAndStuffLib" version "1.0.7"

set theSubject to "Happy 57th Birthday 🎈"
set theBody to "Wile:

I hope that you have a special celebration planned for today.

- roadRunner"
set theSubject to percent encode theSubject
set theBody to percent encode theBody

The command also has parameters to customise the encoding according to component being encoded.

Available next door:

1 Like

FYI, I have Hhas libraries installed on all my Macs and use them frequently. Almost as much as I use Shane’s libraries.

Thank you for your example. I certainly never would have figured this out on my own as I find Objective-C absolutely inscrutable.

In your example, you appear to be leaving the email address unencoded. Apparently, however, UTF-8 encoding is required for email addresses containing Internationalized Domain Names (IDNs).

Does your example code take IDNs into account?

How about this?

-- Created 2017-02-23 by Takaaki Naganoya
-- Modified 2022-06-06 by Takaaki Naganoya
-- 2022 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set shareItems to {"Wile:

I hope that you have a special celebration planned for today.

- roadRunner", "Footer?"}
set aRecipients to {"Wile Coyote<beepbeeb@acme.com>"}
set aSubject to "Happy 57th Birthday 🎈"

set aParam to {myShares:shareItems, myRecipients:aRecipients, mySubject:aSubject}

my performSelectorOnMainThread:"composeAnEmail:" withObject:aParam waitUntilDone:true
--my composeAnEmail:aParam--For debugging with Script Editor by using command-control-r

on composeAnEmail:aParamObj
	set aRecipients to (myRecipients of aParamObj) as list
	set aSubject to (mySubject of aParamObj) as string
	set aShareItems to (myShares of aParamObj) as list
	
	set aService to current application's NSSharingService's sharingServiceNamed:(current application's NSSharingServiceNameComposeEmail)
	aService's setDelegate:me
	aService's setRecipients:(aRecipients)
	aService's setSubject:(aSubject)
	
	aService's performWithItems:(aShareItems)
end composeAnEmail:

Thank you for the specificity and completeness of your instructions.

I downloaded the library you recommended and took a look at it. I’ll try wiring it all together tomorrow.

In the meantime, in reading the dictionary I noticed this URL scheme several times: suiteref:///Miscellaneous%20Text%20Suite; what is this?

Reading what dictionary? Not that of my lib.

I used Piyomaru’s script to create a new text message instead of a new email. The script succeeded relatively well with only two errors.

1, subject value did not show
2. Messages erred with either dialog
a, mail system at host bbtpnj33wzwvetm-c-nk-x-00-sms-01.vtext.com expanded the recipient incorrectly
b. Message Send Failure

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set shareItems to {"Wile:
I hope that you have a special celebration planned for today.
🤸🏼‍♀️

-  roadRunner"}

set aRecipients to {"Wile Coyote<beepbeeb@acme.com>"}
set aSubject to "Happy 57th Birthday 🎈"

set aParam to {myShares:shareItems, myRecipients:aRecipients, mySubject:aSubject}

my performSelectorOnMainThread:"composeAMessage:" withObject:aParam waitUntilDone:true


on composeAMessage:aParamObj
	set aRecipients to (myRecipients of aParamObj) as list
	set aSubject to (mySubject of aParamObj) as string
	set aShareItems to (myShares of aParamObj) as list
set aService to current application's NSSharingService's sharingServiceNamed:(current application's NSSharingServiceNameComposeMessage)
	aService's setDelegate:me
	aService's setRecipients:(aRecipients)
	aService's setSubject:(aSubject)

	aService's performWithItems:(aShareItems)
end composeAMessage:

I would be interested in any solutions to correct those two Message Sharing Service errors

Yes, a file with the word “dictionary” in its name contained within the .zip file for your RegexAndStuffLib:

  1. Download RegexAndStuffLib v1.0.7
  2. Unzip RegexAndStuffLib_stuff.zip
  3. Open and read RegexAndStuffLib Dictionary.rtf

That’s a link that’s live in Script Debugger’s Dictionary view, but becomes meaningless when saved as a PDF. The PDF is a convenience – you’re better off viewing the dictionary in Script Debugger.

Works perfectly and I understand it.

Thank you.

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.