Insert hyperlink into selected text in MS Outlook email (with different base URL depending on first two letters of selected text)

I am hoping to create a single BTT button to insert a hyperlink into selected text within an MS Outlook email. The base URL used as part of the inserted hyperlink will be different depending on the first two letters of the user's selected text.

Has anyone done this or can point me in the right direction? I did spend some time trying to hunt down examples using AppleScript, but, I'm quite new to it and am struggling to find anything similar.

Examples below of how selected text will influence the base URL of the hyperlink on press of BTT button:

  1. selected text = "AA111" inserts hyperlink for "search engine 1" into the original display text "AA111"
  2. selected text = "BB222" inserts hyperlink for "search engine 2" into the original display text "BB222"
  3. selected text = "CC333" inserts hyperlink for "search engine 3" into the original display text "CC333"

By the way, this is similar to my earlier question today which @Dirk kindly assisted with:
Append selected text to one of the specified URLs, dependent on the first two letters of user’s selected text

This time around, I'm looking to insert the URL instead of opening the URL.

Surely you can manipulate an Outlook email directly via AppleScript - but I don't have it installed. But what always works is to change the selected text of the current application via the clipboard.

Here is a script that converts the selected text to a hyperlink and then replaces it with CMD + V.

Add the Action "Save selected text in "selected_text" variable" and than run this script:

tell application "BetterTouchTool"
	set theText to get_string_variable "selected_text"
end tell

set theUrl to ""

if theText begins with "AA" then
	set theUrl to "https://www.google.com/search?q=" & theText
else if theText begins with "BB" then
	set theUrl to "https://www.bing.com/search?q=" & theText
else if theText begins with "CC" then
	set theUrl to "https://duckduckgo.com/?q=" & theText
end if

if theText ≠ "" and theUrl ≠ "" then
	set the clipboard to "<a href=\"" & theUrl & "\" target=\"_blank\">" & theText & "</a>"
	set theHEX to do shell script "pbpaste | hexdump -ve '1/1 \"%.2x\"'"
	if theHEX ≠ "" then
		run script "set the clipboard to «data HTML" & theHEX & "»"
		tell application "System Events" to keystroke "v" using command down
	end if
end if

Tested with Apple Mail and Notes.

1 Like

Once again, this is such a great help, thanks @Dirk. This approach is even better than I was hoping for as it works across applications (tested in Outlook, Word, PowerPoint).

Would you have any tips on how to approach this with text in an Excel cell and/or a Slack message? Those apps both accept paste of rich text with hyperlink, however, currently they don't seem to accept the script actions.

The problem with Excel is not RTF. Excel does not accept markdown or HTML as input for links in cells. In addition, some functions are blocked if you have selected some text.

Here is a script which sends the links in markdown or HTML syntax as text and also as RTF.
Maybe this helps with Slack or other applications.

tell application "BetterTouchTool"
	set theText to get_string_variable "selected_text"
end tell

set theUrl to ""

if theText begins with "AA" then
	set theUrl to "https://www.google.com/search?q=" & theText
else if theText begins with "BB" then
	set theUrl to "https://www.bing.com/search?q=" & theText
else if theText begins with "CC" then
	set theUrl to "https://duckduckgo.com/?q=" & theText
end if

if theText ≠ "" and theUrl ≠ "" then
	-- copy as Markdown-Link  
	Pasteboard's copyMarkdownLinktoClipboard(theText, theUrl)
	-- or copy as HTML-Link
	--Pasteboard's copyHTMLtoClipboard("<a href=\"" & theUrl & "\" target=\"_blank\">" & theText & "</a>")
	
	-- paste to current application
	tell application "System Events" to keystroke "v" using command down
end if

script Pasteboard
	use framework "Foundation"
	use framework "AppKit"
	
	property NSUTF8StringEncoding : a reference to 4
	property NSString : a reference to current application's NSString
	property NSRTFTextDocumentType : a reference to current application's NSRTFTextDocumentType
	property NSPasteboardTypeRTF : a reference to current application's NSPasteboardTypeRTF
	property NSPasteboardTypeString : a reference to current application's NSPasteboardTypeString
	property NSDictionary : a reference to current application's NSDictionary
	property NSAttributedString : a reference to current application's NSAttributedString
	property NSLinkAttributeName : a reference to current application's NSLinkAttributeName
	property NSSingleUnderlineStyle : a reference to 1
	property NSColor : a reference to current application's NSColor
	property NSUnderlineStyleAttributeName : a reference to current application's NSUnderlineStyleAttributeName
	property NSForegroundColorAttributeName : a reference to current application's NSForegroundColorAttributeName
	property NSMutableAttributedString : a reference to current application's NSMutableAttributedString
	property |NSURL| : a reference to current application's |NSURL|
	
	on copyMarkdownLinktoClipboard(linkTextStr, urlStr)
		set mdLinkStr to "[" & linkTextStr & "](" & urlStr & ")"
		set linkURL to |NSURL|'s URLWithString:urlStr
		-- define attributes to apply
		set theAtts to NSDictionary's dictionaryWithObjects:{linkURL, NSColor's blueColor(), NSSingleUnderlineStyle} forKeys:{NSLinkAttributeName, NSForegroundColorAttributeName, NSUnderlineStyleAttributeName}
		-- make attributed string
		set attString to NSMutableAttributedString's alloc()'s initWithString:linkTextStr attributes:theAtts
		-- need it in RTF data form for clipboard
		set rtfData to attString's RTFFromRange:{0, attString's |length|()} documentAttributes:{DocumentType:NSRTFTextDocumentType}
		set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
		pb's clearContents()
		-- set both types for the first object on the clipboard
		pb's setData:rtfData forType:NSPasteboardTypeRTF
		pb's setString:mdLinkStr forType:NSPasteboardTypeString
	end copyMarkdownLinktoClipboard
	
	on copyHTMLtoClipboard(someHTML)
		-- convert to data
		set htmlString to NSString's stringWithString:someHTML
		set htmlData to htmlString's dataUsingEncoding:NSUTF8StringEncoding
		-- make attributed string
		set attString to NSAttributedString's alloc()'s initWithHTML:htmlData documentAttributes:(missing value)
		-- need it in RTF data form for clipboard
		set rtfData to attString's RTFFromRange:{0, attString's |length|()} documentAttributes:{DocumentType:NSRTFTextDocumentType}
		set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
		pb's clearContents()
		-- set both types for the first object on the clipboard
		pb's setData:rtfData forType:NSPasteboardTypeRTF
		pb's setString:someHTML forType:NSPasteboardTypeString
	end copyHTMLtoClipboard
	
end script
1 Like

@Dirk, you are amazing! Thanks so much for your help. It will take me some time to digest and try and more deeply understand the script, but it works in both Excel and Slack, as well as Outlook now.

I would love to be able to add two elements to this:

  1. Have method to add the link in BB code type format for a web form
  2. Add an additional "else if" line to account for scenario where the selected text begins with "Word AA111" would translate to a separate URL and remove the "Word " from "theText" (e.g. the resulting URL for this scenario should be "https://www.example.com/search?q=AA111")

I'm starting to feel I'm asking a bit too much from community post and wonder if I could hire services from someone in the BTT community, like yourself, to help with some additions to the functions and possibly some more scripts for use as BTT buttons! I hope it's not against the community rules to suggest this!

In any case, thanks so much for your help, I already have the BTT buttons set-up with the two scripts and they are already so useful!

There are two functions in the last script:

Pasteboard's copyMarkdownLinktoClipboard(theText, theUrl)

and:

Pasteboard's copyHTMLtoClipboard(theHTMLText)

I don't know the "BB code type format" and can't help you with that, but with the last one you can push any HTML into the clipboard.

Regarding splitting and composing text, there are many AppleScript examples. Here you would have to try to find out the correct display text and the URL yourself. Then you can either pass these to the first function (as theText and theUrl) or the second one as theHTMLText.

Finally you have to send the clipboard to the current application:

tell application "System Events" to keystroke "v" using command down

and that's it.

  1. The additional hyperlink format I would like to add is as attached image. Essentially this is using code tags to create the URL for web forms.

  1. With regard to the splitting of text, essentially I would like to add the option to find the below scenarios:
    A. "Word AA111" identifies with "wordexample.com/search?q=AA111", so that the URL association with "Word AA" is a different web domain, however, everything from start of the text string, up to and including the space are removed from the resulting URL.
    B. "Check BB222" identifies with "checkexample.com(forward slash)search?q=BB222", so that the URL association with "Check BB" is again a different web domain with everything from the start of the start of the text string, up to and including the space are removed from the resulting URL.

I'm really out of my depth here as I'm not quite sure how/where to add the above scenarios to the existing script and/or if it would be more wise to create separate scripts/buttons for these.

Here is the modified script, which you can use as a template for future requirements.

New:

  • The search queries are URL-encoded and can contain spaces
  • If the keyword contains spaces, the first word is ignored in the search query

Note:

tell application "BetterTouchTool"
	set theText to get_string_variable "selected_text"
end tell

set theUrl to ""

if theText begins with "AA" then
	set theUrl to "https://www.google.com/search?q=" & Pasteboard's urlEncode(theText)
else if theText begins with "BB" then
	set theUrl to "https://www.bing.com/search?q=" & Pasteboard's urlEncode(theText)
else if theText begins with "CC" then
	set theUrl to "https://duckduckgo.com/?q=" & Pasteboard's urlEncode(theText)
else if theText begins with "Word AA" then
	set theUrl to "https://wordexample.com/list/browse?q=" & Pasteboard's urlEncode(Pasteboard's getSubstringFromCharacter(theText, " "))
else if theText begins with "Check BB" then
	set theUrl to "https://checkexample.com/search?q=" & Pasteboard's urlEncode(Pasteboard's getSubstringFromCharacter(theText, " "))
end if

if theText ≠ "" and theUrl ≠ "" then
	-- copy as Markdown-Link  
	Pasteboard's copyMarkdownLinktoClipboard(theText, theUrl)
	-- or copy as HTML-Link
	--Pasteboard's copyHTMLtoClipboard("<a href=\"" & theUrl & "\" target=\"_blank\">" & theText & "</a>")
	
	-- paste to current application
	tell application "System Events" to keystroke "v" using command down
end if

script Pasteboard
	use framework "Foundation"
	use framework "AppKit"
	
	property NSUTF8StringEncoding : a reference to 4
	property NSString : a reference to current application's NSString
	property NSRTFTextDocumentType : a reference to current application's NSRTFTextDocumentType
	property NSPasteboardTypeRTF : a reference to current application's NSPasteboardTypeRTF
	property NSPasteboardTypeString : a reference to current application's NSPasteboardTypeString
	property NSDictionary : a reference to current application's NSDictionary
	property NSAttributedString : a reference to current application's NSAttributedString
	property NSLinkAttributeName : a reference to current application's NSLinkAttributeName
	property NSSingleUnderlineStyle : a reference to 1
	property NSColor : a reference to current application's NSColor
	property NSUnderlineStyleAttributeName : a reference to current application's NSUnderlineStyleAttributeName
	property NSForegroundColorAttributeName : a reference to current application's NSForegroundColorAttributeName
	property NSMutableAttributedString : a reference to current application's NSMutableAttributedString
	property |NSURL| : a reference to current application's |NSURL|
	
	on copyMarkdownLinktoClipboard(linkTextStr, urlStr)
		set mdLinkStr to "[" & linkTextStr & "](" & urlStr & ")"
		set linkURL to |NSURL|'s URLWithString:urlStr
		-- define attributes to apply
		set theAtts to NSDictionary's dictionaryWithObjects:{linkURL, NSColor's blueColor(), NSSingleUnderlineStyle} forKeys:{NSLinkAttributeName, NSForegroundColorAttributeName, NSUnderlineStyleAttributeName}
		-- make attributed string
		set attString to NSMutableAttributedString's alloc()'s initWithString:linkTextStr attributes:theAtts
		-- need it in RTF data form for clipboard
		set rtfData to attString's RTFFromRange:{0, attString's |length|()} documentAttributes:{DocumentType:NSRTFTextDocumentType}
		set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
		pb's clearContents()
		-- set both types for the first object on the clipboard
		pb's setData:rtfData forType:NSPasteboardTypeRTF
		pb's setString:mdLinkStr forType:NSPasteboardTypeString
	end copyMarkdownLinktoClipboard
	
	on copyHTMLtoClipboard(someHTML)
		-- convert to data
		set htmlString to NSString's stringWithString:someHTML
		set htmlData to htmlString's dataUsingEncoding:NSUTF8StringEncoding
		-- make attributed string
		set attString to NSAttributedString's alloc()'s initWithHTML:htmlData documentAttributes:(missing value)
		-- need it in RTF data form for clipboard
		set rtfData to attString's RTFFromRange:{0, attString's |length|()} documentAttributes:{DocumentType:NSRTFTextDocumentType}
		set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
		pb's clearContents()
		-- set both types for the first object on the clipboard
		pb's setData:rtfData forType:NSPasteboardTypeRTF
		pb's setString:someHTML forType:NSPasteboardTypeString
	end copyHTMLtoClipboard
	
	-- Helper
	on urlEncode(input)
		set rawUrl to NSString's stringWithString:input
		set theEncodedURL to rawUrl's stringByAddingPercentEscapesUsingEncoding:(NSUTF8StringEncoding)
		return theEncodedURL as Unicode text
	end urlEncode
	
	on getSubstringFromCharacter(sourceStr, char)
		set sString to NSString's stringWithString:sourceStr
		set rangeOf to sString's rangeOfString:char
		return (sString's substringFromIndex:((rangeOf's location) + 1)) as text
	end getSubstringFromCharacter
	
end script
1 Like

Thanks again @Dirk, you are seriously awesome. Are you part of the BTT team and/or can you share what it is you do outside of the BTT community?

I was able to change the web form hyperlink format into the desired structure (per the image I attached in my previous reply) by changing this line:

	set mdLinkStr to "[" & linkTextStr & "](" & urlStr & ")"

to this:

	set mdLinkStr to "[code]" & "<a href=\"" & urlStr & "\">" & linkTextStr & "</a>[/code]"

I can't understand why you change the copyMarkdownLinktoClipboard
function for this instead of using the function provided for this purpose:

Pasteboard's copyHTMLtoClipboard(theHTMLText)
 -- or copy as HTML-Link
 -- Pasteboard's copyHTMLtoClipboard("<a href=\"" & theUrl & "\" target=\"_blank\">" & theText & "</a>")

You’re probably already aware, but in case you’re not, stringByAddingPercentEscapesUsingEncoding: has been deprecated for some time, so may not continue beyond the current version of MacOS.

This should be as text instead of as Unicode text.

Thanks for the infos!

Hi @Dirk , apologies for the delayed response.

I found that changing "Pasteboard's copyHTMLtoClipboard" still presented the output in webforms as:

[AA111](https://www.google.com/search?q=AA111)

Instead if I changed copyMarkdownLinktoClipboard mdLinkStr as mentioned above, I was able to configure the below desired format for the web forms I am working with, and hyperlinks still continue to work in outlook, slack & excel
[code]<a href="https://www.google.com/search?q=AA111">AA111</a>[/code]

I don't fully understand why.

With regard to updates from @CJK

  1. stringByAddingPercentEscapesUsingEncoding I tried a simple replace of this with stringByAddingPercentEncodingWithAllowedCharacters as apple developer site suggests this is now to be used, however, I couldn't get this to work.

  2. I changed the return theEncodedURL as Unicode text to return theEncodedURL as text and all appears to be functioning.

It’s because you asked @Dirk in the earlier part of this thread for a markdown link. Markdown links have this form:

[%description%](%url%)

which will display the literal text given by %description% as a hyperlink pointing to the address given by %url%, when rendered by a Markdown-enabled editor, such as the one used in this forum.

Microsoft Outlook isn’t Markdown-aware. It only knows HTML and RTF.

However, this:

[code]%text%[/code]

is called BBCode, and is used by forums stuck in the late 1990s. This specific example tells the rendering software to display the literal text given by %text%, preserving whitespace, using a monospaced font. This is used most often for those wishing to display source code snippets, hence the BB tag code.

A BBCode-formatted URL is described like this, as far as I recall:

[url=%href%]%description%[/url]

that will display the literal text of %description% when rendered, and hyperlink it to the supplied ‘%href%` URL.

You have an amalgamation of HTML syntax inside BBCode syntax, which I’ll let you decide what the purpose of that is, but I’m pretty sure one of those isn’t doing anything useful.

Then you probably didn’t do it correctly. If I recall (I don’t have a computer, so I’m currently having to recall rather than test), this method requires that a valid NSCharacterSet class object be passed as the parameter. You can either declare a new instance of an NSCharacterSet class object, should you wish to have control over customising what the set contains, or, more easily and more wisely in this situation, use the predefined character set object that the Apple Developer website contains a list of named keys that refer to commonly used sets for purpose. It includes a load pertaining to encoding URL strings, which is a complicated situation we’re stuck with just because of how URLs evolved over time, and it’s not actually possible to encode an entire URL using a single homogeneous mapping function, so each component of a URL is usually encoded separately, and pieced together. You can safely encode everything from the start of the URL to the end of a named file including its extension with one character set, but query strings (everything from a question mark up to the first occurrence of a hash symbol if there is one) must be encoded with a different character set, because they, for instance, don’t need to encode a slash, whereas a URL path interprets a slash that separates domains from sub-domains and file names, etc. Likewise, paths can contain ampersands without encoding them, but query strings interpret ampersands as a delimiter for each parameter in the query string. There’s a ton of other differences, but you get the idea. The character sets will be named in obvious ways, like NSURLComponentQueryStringEncodedAllowedCharacterSet or something massively long and vaguely similar-ish.

It’s usually already accessible to any AppleScript that imports the Foundation or AppKit frameworks, by simply referencing current application’s NSStupidlyLongIdentifierForNamedConstant, which you can assign directly to a variable if you don’t want to type it more than once, or just insert that reference as and when you need it, which is fine to do because it’s a constant.