Script: Add online version of newsletter to reading list

This mail rule script adds the online version of a newsletter to DEVONthink’s or Safari’s reading list.

Setup

1 - Save the script library

As it seems it’s not possible to use AppleScriptObjC inline in a Mail.app rule we need to create a script library.

  • Paste this script in Script Editor.app
  • Save it
    • with name Add newsletter to reading list
    • in folder /Users/USERNAME/Library/Script Libraries
-- Script library - Add newsletter to reading list

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

on regexFind(theText, thePattern)
	try
		set theString to current application's NSString's stringWithString:theText
		set {theExpr, theError} to current application's NSRegularExpression's regularExpressionWithPattern:(thePattern) options:0 |error|:(reference)
		set theMatches to theExpr's matchesInString:theString options:0 range:{0, theString's |length|()}
		set theResults to {}
		repeat with thisMatch in theMatches
			set thisMatchRange to (thisMatch's rangeAtIndex:0)
			set thisMatchString to (theString's substringWithRange:thisMatchRange) as text
			set end of theResults to thisMatchString
		end repeat
		return theResults
	on error error_message number error_number
		activate
		display alert "Error: Handler \"regexFind\"" message error_message as warning
		error number -128
	end try
end regexFind

on tid(theInput, theDelimiter)
	set d to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	if class of theInput = text then
		set theOutput to text items of theInput
	else if class of theInput = list then
		set theOutput to theInput as text
	end if
	set AppleScript's text item delimiters to d
	return theOutput
end tid

on decodeBase64(theText, returnPlainAppleScript)
	try
		set theString to (current application's NSString's stringWithString:theText)
		set theData to (current application's NSData's alloc()'s initWithBase64EncodedString:theString options:0)
		set theString_decoded to (current application's NSString's alloc()'s initWithData:theData encoding:(current application's NSUTF8StringEncoding))
		return theString_decoded as string
	on error error_message number error_number
		activate
		if the error_number is not -128 then display alert "Error: Handler \"decodeBase64String\"" message error_message as warning
		error number -128
	end try
end decodeBase64

2 - Save the mail rule script

  • Paste this script in Script Editor.app
  • Save it in /Users/USERNAME/Library/Application Scripts/com.apple.mail
-- Mail rule script - Add online version of newsletter to reading list

property theNewsletterName : "Altpapier" -- adjust this
property theAssertion_Lookbehind : "Lin=\\nk zum Artikel\" href=3D\"" -- adjust this
property theAssertion_Lookahead : "\" target=3D\"_blank\" rel=3D\"noopener\">ZUM ALTPAPIER" -- adjust this

property theAppName : "DEVONthink" -- "DEVONthink" or "Safari"

using terms from application "Mail"
	on perform mail action with messages theMessages for rule theRule
		
		tell application "Mail"
			try
				repeat with theMessage in theMessages
					set theMessage_Source to source of theMessage
					
					------------------------------------------------------- If necessary: decode base64 --------------------------------------------------------
					
					if theMessage_Source contains "Content-Transfer-Encoding: base64" then
						set theMessage_Source_encoded_text to item 1 of script "Add newsletter to reading list"'s regexFind(theMessage_Source, "(?s:(?<=Content-Transfer-Encoding: base64)[^-]*?(?=------=))")
						set theMessage_Source_encoded_text_withoutLinefeeds to script "Add newsletter to reading list"'s tid(script "Add newsletter to reading list"'s tid(theMessage_Source_encoded_text, linefeed), "")
						set theMessage_Source to script "Add newsletter to reading list"'s decodeBase64(theMessage_Source_encoded_text_withoutLinefeeds, true)
					end if
					
					----------------------------------------------------------------- Extract URL ------------------------------------------------------------------
					
					set thePattern to "(?<=" & theAssertion_Lookbehind & ")(?s:https://.*?)(?=" & theAssertion_Lookahead & ")"
					
					set theResults to script "Add newsletter to reading list"'s regexFind(theMessage_Source, thePattern)
					
					if theResults ≠ {} then
						set theResult to item 1 of theResults
						set theResult_Parts to script "Add newsletter to reading list"'s tid(theResult, "=" & linefeed)
						set theURL to script "Add newsletter to reading list"'s tid(theResult_Parts, "")
						if theURL contains "=3D" then
							set theURL to script "Add newsletter to reading list"'s tid(script "Add newsletter to reading list"'s tid(theURL, "=3D"), "=")
						end if
					else
						error "No URL found"
					end if
					
					------------------------------------------------------------- Add to reading list --------------------------------------------------------------
					
					if theAppName = "DEVONthink" then
						tell application id "DNtp" to add reading list URL theURL
						
					else if theAppName = "Safari" then
						tell application "Safari" to add reading list item theURL
					end if
					
					------------------------------------------------------------------ Move mail -------------------------------------------------------------------
					
					set theMailbox to mailbox theNewsletterName of mailbox "Newsletter" of account "iCloud" -- adjust this
					move theMessage to theMailbox
				end repeat
				
				if the error_number is not -128 then
					activate
					display alert "Mail" message error_message as warning
				end if
			end try
		end tell
		
	end perform mail action with messages
end using terms from

3 - Copy the mail’s source

  • In Mail.app select a newsletter
  • Use this helper script to copy the source
  • Paste the copied source in a text editor
-- Helper script - Copy selected mail's source

tell application "Mail"
	try
		set theSelection to selection
		if theSelection = {} then error "Please select a mail"
		if (count of theSelection) ≠ 1 then error "Please select one mail"
		set theMessage to item 1 of theSelection
		
		set theMessage_Source to source of theMessage
		
		if theMessage_Source contains "Content-Transfer-Encoding: base64" then
			set theMessage_Source_encoded_text to item 1 of script "Add newsletter to reading list"'s regexFind(theMessage_Source, "(?s:(?<=Content-Transfer-Encoding: base64)[^-]*?(?=------=))")
			set theMessage_Source_encoded_text_withoutLinefeeds to script "Add newsletter to reading list"'s tid(script "Add newsletter to reading list"'s tid(theMessage_Source_encoded_text, linefeed), "")
			set theMessage_Source to script "Add newsletter to reading list"'s decodeBase64(theMessage_Source_encoded_text_withoutLinefeeds, true)
		end if
		
		set the clipboard to theMessage_Source
		
	on error error_message number error_number
		if the error_number is not -128 then
			activate
			display alert "Mail" message error_message as warning
		end if
		return
	end try
end tell

4 - Find the URL in the mail’s source

Unfortunately it’s a bit tricky to find the URL of the online version in a mail’s source. The source of most mails got a maximum line length, if the URL (or the displayed text) is longer than this maximum length then there’s a continuation marker used (=\n , equal sign and a linefeed). Because of that one can’t simply search for the link’s URL (or the displayed text).

If you can’t find the URL (or the displayed text) in the source try to search for a part of the URL (or the displayed text).

5 - Find regex assertions

Once you’ve found the URL of the online version in the source you’ll need to

  • identify what comes before the URL. Use this string in property theAssertion_Lookbehind
  • identify what comes after the URL. Use this string in property theAssertion_Lookahead

6 - Adjust the mailbox path

The script moves the newsletters to a subfolder of my “Newsletter” mailbox. You need to adjust this line to your Mail.app’s setup

set theMailbox to mailbox theNewsletterName of mailbox "Newsletter" of account "iCloud"

7 - Create a mail rule

  • Select a newsletter in Mail.app
  • Menu Preferences > Rules
  • Create a new rule
  • Choose the mail rule script you’ve created in step 2
1 Like

Great script @pete31! I’m doing something similar but with a very different approach. I’m redirecting all my newsletters to Feedbin. As Feedbin creates a URL for everything sent to it, it’s easy to bookmark that URL to read-later (I’m using Fiery Feeds with a quick share action)

Also worth mentioning despite the cringey URL:

Updated the script. It now replaces =3D in a URL with =

Background:

The HTML is encoded with what is called quoted-printable . Basically an = at the end of a line indicates a line wrap, so

he=
llo

should be read as “hello” and not as “he llo”

because the = has this special meaning some other character (sequence) is needed to put = characters in the text literally, and that is =3D

https://www.sitepoint.com/community/t/what-does-3d-mean-in-this-html-email/7033