Reading Specific Messages Out Loud At Home

I’m trying to devise a way to utilize my Mac Mini HTPC and AirFoil to speak specific messages out loud in the house while I’m away, using a simple SMS.

This is the part I’ve got working so far:

  1. I send an SMS to my HTPC email address. I include a code word in the message content: right now, the code word is simply ‘say’.

  2. My Mail rules scan incoming message content for the word “say” and if it finds that word, it first moves the unread message to a special folder, and then executes an AppleScript.

  3. The executed script looks like this:


set speakString to ""

tell application "Mail"
	set speakString to (content of every message of mailbox "Speak" whose read status is false)
end tell


say speakString

Two problems:

  1. Because I had to include a code word to get Mail to even take action, the Mac ends up speaking the code word, as well. So, if I send an SMS that says, “say I’m on my way home” the Mac Mini says “say I’m on my way home”. Obviously, I’m looking to use text delimiters or something similar to eliminate the first word of the string, but I haven’t been able to figure out how.

  2. The “code word” say is too prevalent in other emails to really make it the code word, so an obvious solution is making the “code word” something more like 's@y" or “$ay” or “$peak”. So that’s solved.

Thanks for your help.

P.S. Of course, I’ll need to edit the script to make the message read status as “true” at the end, so that all those unread messages don’t pile up.

About the “Say”:

One approach:consider this script

set theText to "one two three one"
set AppleScript's text item delimiters to {" "}
say (rest of words of theText) as text

Two approach: put the code word “say” in the subject of the mail rather than the text.

I considered your second approach briefly, but had to abandon it. While simpler, it prevents me from merely sending an SMS, because a SMS cannot have a subject. Sending an email from my phone also adds the complication of each email having a signature.

I added your first approach to my script. It looks like this:

set speakString to ""

tell application "Mail"
	set speakString to (content of every message of mailbox "Speak" whose read status is false)
end tell

set AppleScript's text item delimiters to {" "}
say (rest of words of speakString) as text

And I get this error:

 error "Can't make rest of every word of {\"say I'm on my way home right now
\"} into type text." number -1700 from rest of every word of {"say I'm on my way home right now
"} to text

Basically, I’m just looking to remove the first word of a string, even if that word is just a jumbled mess of letters. But I need the script the find the first space and remove everything before that first space.

When it returns the remaining text as a string, it should do it as a sentence, rather as individual words, so the Mac OS X speech synthesizer will speak it correctly.

Hi,

consider that the result of every . whose . is a list so you need a repeat loop

tell application "Mail"
	set contentOfFilteredMessages to (content of every message of mailbox "Speak" whose read status is false)
end tell

set TID to text item delimiters
set text item delimiters to {" "}
repeat with aMessage in contentOfFilteredMessages
	say (rest of words of aMessage) as text
end repeat
set text item delimiters to TID

A better alternative to ‘(rest of words of theText) as text’ would be ‘(text from text item 2 to -1 of theText)’. It’s better text handling and doesn’t need any adjustments for kkachurak beginning his messages with “s@y” or “$ay”, where “@” and “$” are ‘words’ in their own rights.

set theText to "S@y one two three one"
set AppleScript's text item delimiters to {" "}
say (text from text item 2 to -1 of theText)

Edit: Or, of course:

set theText to "S@y one two three one"
set AppleScript's text item delimiters to {"S@y", "$ay", "$peak"}
say (text from text item 2 to -1 of theText)

Great :smiley: the code technically works and I’m eliminating the first word which is the command that Mail detects as a “rule”.

This is what I have:

on run
	
	--declares the variable
	set speakString to ""
	
	--gets the phrase to speak from the most recent unread message in the speak mailbox
	tell application "Mail"
		set speakString to (content of every message of mailbox "Speak") as rich text
	end tell
	
	
	--edits the first "word" out of the phrase and says it
	set AppleScript's text item delimiters to {" "}
	say (text from text item 2 to -1 of speakString)
	
	delay 3
	
	tell application "Mail"
		delete every message of mailbox "Speak"
	end tell
	
	
end run

But now there’s a more interesting headache. When Mail evaluates rules, and it senses the content trigger, it moves the message to the “Speak” mailbox and executes the script so perfectly simultaneously, that the script doesn’t even see a message in that Mailbox.

In fact, if I write in a delay of 10 seconds, I can watch as I send a message from my phone, and approx 10.5 seconds later, the message appears in the “Speak” mailbox and the actions of the script never take place.

Well, they do take place, they just take place before Mail regards the message as being moved.

Can anyone think of a workaround for this?

From the code in post #3, is the variable SpeakString a list or a text value?

It should be a text value.

In my test script:

tell application "Mail"
	set sayMe to content of (every message of mailbox "Random" whose subject contains "test")
end tell

sayMe is a list.

Try

set speakString to ""

tell application "Mail"
   set speakString to (content of every message of mailbox "Speak" whose read status is false)
end tell

set AppleScript's text item delimiters to {" "}
say (rest of words of (item 1 of speakString) as text

Stefan gave a repeat for dealing with that in post #5 and I a couple of better ‘say’ lines in post #6.

Blowing the cobwebs from my copy of Mail 4.5 this morning, I find the most reliable way to do what’s described is to set up your Mail rule to:

  1. Run the script below, which just speaks the message(s).
  2. Move the message(s) to your “Speak” folder.
  3. Mark them as read or whatever else you want to do with them.

I’ve only been partially successful in getting the script to perform actions 2 and 3 in this context.

-- Run handler for testing.
on run
	tell application "Mail"
		set theMessages to every message of mailbox "Speak"
		my speak(theMessages)
	end tell
end run

-- Handler for when this script is run by a Mail rule.
using terms from application "Mail"
	on perform mail action with messages theMessages for rule thisRule
		my speak(theMessages)
	end perform mail action with messages
end using terms from

-- Speak the passed messages.
on speak(theMessages)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to space
	
	tell application "Mail"
		repeat with thisMessage in theMessages
			set theText to thisMessage's content
			say (text from text item 2 to -1 of theText) -- Assuming that the first space in the message comes after the trigger word.
		end repeat
	end tell
	
	set AppleScript's text item delimiters to astid
end speak