Mailmate Redirect Bundle Generator

With a relatively recent update around views in the beta of Readwise’s Reader service beta, I am now getting deeper into its use and building it into my various workflows in an aim to help me better manage and process the various feeds of information I have coming in. The Reader service includes an option to accept input via e-mail for newsletters, etc. While I plan to set up a number of e-mail redirect rules in my personal Gmail account to push some newsletters into the service, I also have a backlog of newsletters I want to pick from and pass them to Reader.

I didn’t want to blanket forward sets at this point and drown my Reader feeds, so I decided I wanted to redirect on a per e-mail basis. Fortunately, I use Mailmate as my Mac e-mail client of choice, and I ended up scripting something I think provides an easy way of adding this sort of functionality for yourself.

Mailmate Bundles

Mailmate has a plugin framework known as Mailmate bundles. The documentation for the bundles is listed as being incomplete, but with some experimentation, I was able to create a bundle that adds a redirect selected e-mails to a predefined address and then archive the e-mail.

The bundle consists of three files:

  1. A PLIST file to define ta set of data about the bundle to Mailmate.
  2. A Mailmate command file that defines a command to be made available in the bundle.
  3. A script file that generates a set of instructions for Mailmate to carry out.

The bundle itself is defined to be a package file, and is placed in ~/Library/Application Support/MailMate/Bundles/, where Mailmate will automatically pick it up and include it in the commands menu.

The details for these files are covered below to explain how they are put together. However, below I have also created a shortcut to automate the generation of the bundle, so you won’t need to hand craft these files yourself.

The PLIST File

The PLIST file (info.plist) contains details such as the name of the bundle, a unique identifier string, a description of the bundle and contact details for the bundle creator. Note that the e-mail address is encoded with the ROT13 cipher.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>name</key>
		<string>Readwise</string>
		<key>uuid</key>
		<string>F7BC11CD-01D7-4702-9735-74B5838D0191</string>
		<key>description</key>
		<string>Redirect to Readwise</string>
		<key>contactName</key>
		<string>Stephen Millard</string>
		<key>contactEmailRot13</key>
		<string>fbeel@abrznvynqqerff.pbz</string>
	</dict>
</plist>

The Mailmate Command File

Each Mailmate command file defines a single command available within the bundle. The file detailed below (Redirect to Readwise.mmBundle) corresponds to the single command included in this bundle.

The command, named “Redirect to Readwise”, is assigned a unique ID and is set to operate on each selected e-mail individually rather than merging the e-mails first. It is set to have a keyboard shortcut of CTRL + OPTION + CMD + r, and to process the result of the command it runs (the actions property is set to actions rather than discard).

{
	executionMode = "singleMessage";
	name          = "Redirect to Readwise";
	uuid          = "9A199BA6-D200-4D25-BC78-58573F270187";
	output        = "actions";
	command       = '#!/bin/bash\n"${MM_BUNDLE_SUPPORT}/bin/Redirect to Readwise"';
	keyEquivalent = "^⌥⌘r";
}

The command being run is a Bash shell command. The command runs a script file that is part of the bundle.

#!/bin/bash
"${MM_BUNDLE_SUPPORT}/bin/Redirect to Readwise"

The Output Shell Script

The shell script (Redirect to Readwise) was given no file extension, and so is just called by name. It is relatively basic in that all it does is output a set of instructions to be carried out by Mailmate, and then play a sound (as an audible cue it has run). Often bundle scripts involve more than this, but in this particular case, we only need to instruct Mailmate what to do, so nothing more involved is required.

Two Mailmate actions are defined in the output of the script. The first action is to redirect the e-mail to a specified e-mail address (my fictional private Readwise e-mail address of abcdefgh@library.readwise.io below), and the second then instructs Mailmate to move the e-mail to the archive folder.

#!/bin/zsh
echo ' {'
echo '	actions = ('
echo '		{'
echo '			type = "redirectMessage";'
echo '			recipient = "abcdefgh@library.readwise.io";'
echo '		},'
echo '		{'
echo '			type = "moveMessage";'
echo '			mailbox = "archive";'
echo '		}'
echo '	);'
echo ' }'
afplay "/System/Library/Sounds/tink.aiff"

The resulting output from the script that is passed back to Mailmate will then look like this.

{
	actions = (
		{
			type = "redirectMessage";
			recipient = "abcdefgh@library.readwise.io";
		},
		{
			type = "moveMessage";
			mailbox = "archive";
		}
	);
}

Automating Generating the Bundle

As noted earlier, I also created an automation to make the creation of the bundle quick and easy. After all, the files are all well structured, and based off only a handful of parameters. But I also decided to take it a step further. A whole bundle for one redirect command seemed greedy, so I ultimately decided that I would create a bundle for redirect commands in which you could specify the redirect to Readwise as just one redirect.

I only have need for Readwise right now, but it wasn’t much effort to future-proof this and make it a little more flexible. To that end, the shell script below has a set of parameters that can be set up however you like, and are defined in the commented section at the top of the script. Feel free the change the values of the BUNDLE_* variables to suit your own naming and language preferences.

Following this are a set of redirect command-related variables. Note they include an array index ([1]), and you can define as many instances of these commands as you like. A second one is included in the script to illustrate this, but it is commented out.

Assuming that you also want to use this bundle to provide a Readwise redirect, you should change the value of the recipient e-mail address (RECIPIENT_EMAIL[1])to be your Readwise Reader forward e-mail address (found in your Import Documents > Add to Library section). You should also add the originating address of any sending service to your list of associated e-mail addresses as redirected e-mails will appear to be sent from that address and not forwarded from your own address.

If you want to set up a redirect for another service, just take similar steps to ensure your redirect functions as you require, by setting the address to redirect to, and ensuring the receiving service is configured to allow redirected e-mails for the originating e-mail address.

It should also be noted that you can set the keyboard shortcut to whatever you wish. I have included the key modifiers in the comment to the right of the variable that specifies the keyboard shortcut, and leaving it blank simply means it will only be accessible from the menu and not via a keyboard shortcut.

You may also recall that a couple of unique identifiers are also required (for the bundle and the command). You do not need to specify these. They are generated at random each time the script is run.

To use the automated bundle creator script, copy the script content block below into your favourite text editor.

#!/bin/zsh

# User Defined settings
# You should only need to change the settings in this section
# === SETTINGS SECTION START ===
BUNDLE_NAME="Redirections"
BUNDLE_DESCRIPTION="Redirect selected e-mails to other services"
BUNDLE_CONTACT_NAME="Stephen Millard"
BUNDLE_CONTACT_EMAIL="sorry@noemailaddress.com"

# Command 1
COMMAND_NAME[1]="Redirect to Readwise"
RECIPIENT_EMAIL[1]="abcdefgh@library.readwise.io"
KEYBOARD_SHORTCUT[1]="^⌥⌘r" # Modifier Key Codes: ⌃ = Control | ⌥ = Option | ⇧ = Shift | ⌘ = Command

# Command 2
#COMMAND_NAME[2]="Redirect to Another Service"
#RECIPIENT_EMAIL[2]="something@anotherservice.com"
#KEYBOARD_SHORTCUT[2]="" # Modifier Key Codes: ⌃ = Control | ⌥ = Option | ⇧ = Shift | ⌘ = Command
# === SETTINGS SECTION END ===


# === FUNCTIONS SECTION START ===
buildBundleFolderStructure()
{
	# Define the bundle root folder
	BUNDLE_FOLDER="$HOME/Library/Application Support/MailMate/Bundles/$BUNDLE_NAME.mmBundle"
	
	# Remove the bundle folder if it already exists
	rm -R "$BUNDLE_FOLDER"
	
	# Progress Info
	echo "BUNDLE: $BUNDLE_NAME"
	echo "[Starting Build]"
	
	echo " > building folders ..."
	
	# Create the commands folder (and root on the way)
	BUNDLE_COMMANDS_FOLDER="$BUNDLE_FOLDER/Commands"
	mkdir -p "$BUNDLE_COMMANDS_FOLDER"
	
	# Create the support/bin folder
	BUNDLE_BIN_FOLDER="$BUNDLE_FOLDER/Support/bin"
	mkdir -p "$BUNDLE_BIN_FOLDER"
}

buildPLISTFile()
{
	echo " > building root PLIST ..."
	
	# Specify the ROT13 version of the bundle contact e-mail address
	BUNDLE_CONTACT_EMAIL_ROT13=$(echo $BUNDLE_CONTACT_EMAIL | tr 'A-Za-z' 'N-ZA-Mn-za-m')
	
	# Generate the bundle UUID
	BUNDLE_UUID=$(uuidgen | tr -d "\n")
	
	# Define path to PLIST file
	PLIST_PATH="$BUNDLE_FOLDER/info.plist"
	
	# Create root PLIST
	# -- START --
	echo '<?xml version="1.0" encoding="UTF-8"?>' > "$PLIST_PATH"
	echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' >> "$PLIST_PATH"
	echo '<plist version="1.0">' >> "$PLIST_PATH"
	echo "	<dict>" >> "$PLIST_PATH"
	# -- Bundle Name --
	echo "		<key>name</key>" >> "$PLIST_PATH"
	echo "		<string>$BUNDLE_NAME</string>" >> "$PLIST_PATH"
	# -- Bundle ID --
	echo "		<key>uuid</key>" >> "$PLIST_PATH"
	echo "		<string>$BUNDLE_UUID</string>" >> "$PLIST_PATH"
	# -- Description --
	echo "		<key>description</key>" >> "$PLIST_PATH"
	echo "		<string>$BUNDLE_DESCRIPTION</string>" >> "$PLIST_PATH"
	# -- Contact Name --
	echo "		<key>contactName</key>" >> "$PLIST_PATH"
	echo "		<string>$BUNDLE_CONTACT_NAME</string>" >> "$PLIST_PATH"
	# -- Contact E-mail in ROT13 --
	echo "		<key>contactEmailRot13</key>" >> "$PLIST_PATH"
	echo "		<string>$BUNDLE_CONTACT_EMAIL_ROT13</string>" >> "$PLIST_PATH"
	# -- END --
	echo "	</dict>" >> "$PLIST_PATH"
	echo "</plist>" >> "$PLIST_PATH"
}


buildCommandDefinitionFile()
{
	echo " > building command file for \"$COMMAND_NAME[$COMMAND_INDEX]\"..."
	
	# Generate the command UUID
	COMMAND_UUID=$(uuidgen | tr -d "\n")
	
	# Set the command file path
	COMMAND_FILE_PATH="$BUNDLE_COMMANDS_FOLDER/$COMMAND_NAME[$COMMAND_INDEX].mmCommand"
	
	# Create redirect command file
	echo "{" > "$COMMAND_FILE_PATH"
	echo "	executionMode = \"singleMessage\";" >> "$COMMAND_FILE_PATH"
	echo "	name          = \"$COMMAND_NAME[$COMMAND_INDEX]\";" >> "$COMMAND_FILE_PATH"
	echo "	uuid          = \"$COMMAND_UUID\";" >> "$COMMAND_FILE_PATH"
	echo "	output        = \"actions\";" >> "$COMMAND_FILE_PATH"
	echo "	command       = '#!/bin/bash\\\n\"\${MM_BUNDLE_SUPPORT}/bin/$COMMAND_NAME[$COMMAND_INDEX]\"';" >> "$COMMAND_FILE_PATH"
	echo "	keyEquivalent = \"$KEYBOARD_SHORTCUT[$COMMAND_INDEX]\";" >> "$COMMAND_FILE_PATH"
	echo "}" >> "$COMMAND_FILE_PATH"
}

buildCommandScriptFile()
{
	echo " > building command script file for \"$COMMAND_NAME[$COMMAND_INDEX]\"..."
	
	# Set the file path for the redirect
	COMMAND_BIN_PATH="$BUNDLE_BIN_FOLDER/$COMMAND_NAME[$COMMAND_INDEX]"
	
	# Create redirect script file
	echo "#!/bin/zsh" > "$COMMAND_BIN_PATH"
	echo "echo ' {'" >> "$COMMAND_BIN_PATH"
	echo "echo '	actions = ('" >> "$COMMAND_BIN_PATH"
	echo "echo '		{'" >> "$COMMAND_BIN_PATH"
	echo "echo '			type = \"redirectMessage\";'" >> "$COMMAND_BIN_PATH"
	echo "echo '			recipient = \"$RECIPIENT_EMAIL[$COMMAND_INDEX]\";'" >> "$COMMAND_BIN_PATH"
	echo "echo '		},'" >> "$COMMAND_BIN_PATH"
	echo "echo '		{'" >> "$COMMAND_BIN_PATH"
	echo "echo '			type = \"moveMessage\";'" >> "$COMMAND_BIN_PATH"
	echo "echo '			mailbox = \"archive\";'" >> "$COMMAND_BIN_PATH"
	echo "echo '		}'" >> "$COMMAND_BIN_PATH"
	echo "echo '	);'" >> "$COMMAND_BIN_PATH"
	echo "echo ' }'" >> "$COMMAND_BIN_PATH"
	echo 'afplay "/System/Library/Sounds/tink.aiff"' >> "$COMMAND_BIN_PATH"
	# Make it executable
	chmod +x "$COMMAND_BIN_PATH"
}
# === FUNCTIONS SECTION END ===

# === MAIN START ===
# Set up folders
buildBundleFolderStructure

# Set up root PLIST file
buildPLISTFile 

# Loop over all commands and build the command file and the command script file
for ind in {1..${#COMMAND_NAME}}
do
	COMMAND_INDEX=$ind
	buildCommandDefinitionFile
	buildCommandScriptFile
done

echo "[Build Complete]"
# === MAIN END ===

Update the variables with your own details as described above, and save the file with a name of your choice. As an example you could save it to my Desktop and call it GenerateRedirectionsBundle.sh. Then, to run the script and generate the bundle, open the Terminal app and issue the following two commands.

First a command to ensure the new file can be executed as a script.

chmod +x "~/Desktop/GenerateRedirectionsBundle.sh"

Second, a command to execute the file.

"~/Desktop/GenerateRedirectionsBundle.sh"

The resulting output in the terminal should look like this, and have generated a new bundle which Mailmate should pick up immediately and you can start using.

BUNDLE: Redirections
[Starting Build]
 > building folders ...
 > building root PLIST ...
 > building command file for "Redirect to Readwise"...
 > building command script file for "Redirect to Readwise"...
[Build Complete]

Conclusion

While the Mailmate bundle documentation could certainly be improved, the architecture was pretty clear once I had read it end to end and looked at an existing bundle to clarify some of the less obvious points. With a little effort I was able to not only create a bundle to do exactly what I wanted, but also to create a script to allow me to easily build out the bundle in the future and provide an easy way to share a bundle with others.

I can’t share a static bundle file directly as Readwise private e-mail addresses are unique, and I don’t want to be generating them dynamically on a server as my web server does not support that and I don’t want to be capturing people’s private e-mail addresses in a web form on my site to drive any generation. I would match rather just provide people with a tool they can use to generate their own bundle file locally. Hopefully, running a script file is easy enough, though if enough people have trouble, I may be tempted to build a Shortcuts equivalent utilising the same script under the hood.

If you find this useful, let me know.

Author: Stephen Millard
Tags: | mailmate | readwise reader | scripting |

Buy me a coffeeBuy me a coffee



Related posts that you may also like to read