I'm new to Applescript and I've built this from code I've found online, and can't really get it to work.
What I want to do is the following
A rule in Apple Mail will trigger the script to find 2 text strings inside the body of the mail.
I want to extract two things from the e-mail
Due date (Återlämningsdatum)
Title of the book (Titel)
Then I want to create a todo in Things with the title of the book as name of the todo and the due date as due date.
The problem I run into now is that I don't get any data from the mail, just a empty todo is created.
Any ideas?
E-mail below
2017-03-22 18:43:55
MALMÖ STADSBIBLIOTEK
Stadsbiblioteket
Låntagarnummer: **********
Utlån
-------------------------
Titel: Ägg : recept & teknik / Tove Nilsson ; [fotografi: Charlie Drevstam]
Exemplarnummer: 3054550018
Återlämningsdatum: 2017-04-19
-------------------------
Antal utlånade material: 1
Code below
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
set subjectText to "Återlämningsdatum: "
set contentSearch to "Titel: "
using terms from application "Mail"
on perform mail action with messages theMessages for rule theRule
tell application "Mail"
set theContent to content
set theDate to my getFirstWordAfterSearchText(subjectText, theContent)
set theTitle to my getFirstWordAfterSearchText(contentSearch, theContent)
end tell
end perform mail action with messages
end using terms from
tell application "Things3"
set newToDo to make new to do
set name of newToDo to theTitle
set due date of newToDo to theDate
end tell
(*============== SUBROUTINES =================*)
on getFirstWordAfterSearchText(searchString, theText)
try
set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, searchString}
set textItems to text items of theText
set AppleScript's text item delimiters to tids
return (first word of (item 2 of textItems))
on error theError
return ""
end try
end getFirstWordAfterSearchText
In my testing, your script returns the following values:
theDate: "2017"
theTitle: "Ägg"
So, I'm assuming you should perhaps get a blank to-do named "Ägg" ? If not, you could try:
tell application "Things3" to make new to do with properties ¬
{name: theTitle, due date: theDate}
(although I neither use nor own Things 3, so cannot test this for you).
Anyway, to address the original problem, the issue is in only asking your handler getFirstWordAfterSearchText to return the first word. I don't speak Swedish, but it looks to me that the title contains more than one word. The date most definitely does, as each word is separated by a hyphen (which is considered a non-word character in AppleScript).
My suggestion would be to split the email content into paragraphs, isolate the lines of text that start with either "Titel" or "Återlämningsdatum", then return those lines, excluding any unwanted words. Here's a handler that does this:
to getFirstParagraphThatStarts on searchString ¬
from theText ¬
apart from excludedStrings : null
local searchString, theText, excludedStrings
repeat with P in theText's paragraphs
if P starts with the searchString then exit repeat
end repeat
set the text item delimiters to {null} & the excludedStrings
text items of P as text
end getFirstParagraphThatStarts
I called the handler much like you did yours:
set theDate to getFirstParagraphThatStarts on subjectText from theContent
set theTitle to getFirstParagraphThatStarts on contentSearch from theContent
(I omitted the apart from parameter to begin with, to test the return result before any exclusions were imposed). The return result was this:
theDate: "Återlämningsdatum: 2017-04-19"
theTitle: "Titel: Ägg : recept & teknik / Tove Nilsson ; [fotografi: Charlie Drevstam]"
Then I added in the apart from parameter:
set theDate ¬
to getFirstParagraphThatStarts on subjectText ¬
from theContent ¬
apart from subjectText
set theTitle ¬
to getFirstParagraphThatStarts on contentSearch ¬
from theContent ¬
apart from contentSearch
and the return results were as follows:
theDate: "2017-04-19"
theTitle: "Ägg : recept & teknik / Tove Nilsson ; [fotografi: Charlie Drevstam]"
which, I think, is more along the lines of what you are wanting.
ADDENDUM 1: Dates
Another thought I had regarding Things 3 is that the due date property might require an actual AppleScript date object, rather than just a string that looks like it might be a date. That is, "2017-04-19" might not be a valid due date. Again, I neither use nor own Things 3, so this is just speculation.
AppleScript date formats are intrinsically tied to your system settings. As I have my system date/time preferences set to use international ISO-8601 date representations, i.e. yyyy-mm-dd, I can create the AppleScript date object straight from theDate variable like so:
date theDate
If this is the case with you, and it turns out that you do require a date object, then you can simply set due date of newToDo to date theDate, or (if using my suggested code from above):
tell application "Things3" to make new to do with properties ¬
{name: theTitle, due date: date theDate}
If, however, your system settings are set differently, you'll need to construct the AppleScript date object yourself. Here's a handler that will do this:
to makeASdate out of {year:y, month:m, day:d}
local y, m, d
tell (the current date) to set ¬
[ASdate, year, its month, day, time] to ¬
[it, y, m, d, 0]
ASdate
end makeASdate
You would then use your theDate variable with this handler like so:
makeASdate out of {year:word 1, month:word 2, day:word 3} of theDate
--> date "Wednesday, 19 April 2017 at 0:00:00"
ADDENDUM 2: Scripting Mail (added 2018-08-12)
After further testing and debugging, you've found that there are other issues with your script that stop it from functioning. This was an oversight on my part, as I ought to have highlighted these errors initially, but became focussed on your handler returning incomplete strings that I neglected to come back to the rest of the script and address its other problems.
The problem section, as you've discerned, is this one here:
using terms from application "Mail"
on perform mail action with messages theMessages for rule theRule
tell application "Mail"
set theContent to content
set theDate to my getFirstWordAfterSearchText(subjectText, theContent)
set theTitle to my getFirstWordAfterSearchText(contentSearch, theContent)
end tell
end perform mail action with messages
end using terms from
You tell application "Mail" to set theContent to content, however, you haven't specified what the content belongs to (or, rather, implicitly, you've specified that the content belongs to application "Mail", which it doesn't).
Presumably, you wish to refer to the content of theMessages that are sent through to this script by your mail rules ?
The other major thing to note is that on perform mail action with messages is an event handler, i.e. it responds to an event taking place in Mail, which causes the handler to be invoked. Defining other handlers elsewhere in the script is perfectly fine; but having stray lines of code that don't belong to any explicit handler will therefore belong to an implicit (hidden) on run handler. This, in a way, is an event handler as well: it responds to a script being run, which is problematic if the script is also being asked to respond to a Mail event simultaneously.
Therefore, the tell app "Things3" block needs to be moved, and I imagine the variable declarations at the beginning need to be housed as well.
Bearing all this in mind, I reworked your script to quite a large extent, whilst also trying to keep it resembling your original so it was somewhat recognisable:
using terms from application "Mail"
on perform mail action with messages theMessages for rule theRule
set dateToken to "Återlämningsdatum: "
set titleToken to "Titel: "
repeat with M in theMessages
set theContent to getMessageContent for M
set datum to restOfLineMatchedAtStart by dateToken from theContent
set titel to restOfLineMatchedAtStart by titleToken from theContent
if false is not in [datum, titel] then
set D to makeASdate out of (datum's words as list)
createToDoWithDueDate on D given name:titel
end if
end repeat
end perform mail action with messages
end using terms from
to makeASdate out of {Y, M, D}
local Y, M, D
tell (the current date) to set ¬
[ASdate, year, its month, day, time] to ¬
[it, Y, M, D, 0]
ASdate
end makeASdate
to createToDoWithDueDate on D as date given name:N as text
tell application "Things3" to make new to do ¬
with properties {name:N, due date:D}
end createToDoWithDueDate
to getMessageContent for msg
local msg
tell application "Mail" to return msg's content
end getMessageContent
on restOfLineMatchedAtStart by searchString from theText
local searchString, theText
ignoring white space
repeat with P in theText's paragraphs
if P starts with the searchString then
set i to (length of searchString) + (offset of searchString in P)
try
return text i thru -1 of P
on error -- matched entire line
return ""
end try
end if
end repeat
end ignoring
false -- no match
end restOfLineMatchedAtStart
Now, as I don't use Mail and cannot test these mail rules out myself, there could be one or two minor tweaks that you'll discover need to be made before it's running properly. On the other hand, it might run correctly as it is, which would amaze me. But, having spent a lot of time thinking about each line of code, I'm hopeful it's close to what you need.
The major change you can see is that every piece of code now belongs inside a handler. The custom handlers each get called from inside the main event handler, on perform mail action with messages. I also changed some of the variable names, mainly as part of my mental debugging that was going on (I changed them several times, and have left them to what their last meaningful label was in my head).
Let me know how it goes. Report back any errors.
NOTE: Don't forget, however, that this script is designed to be called by the Mail application in response to a mail rule being actioned. Running it from within Script Editor will not do anything.
This works:
tell application "Mail"
set x to every message in inbox whose subject contains "deal"
end tell
Now I want to do the same using the current selection, but this does not work:
tell application "Mail"
set x to every message in selection whose subject contains "deal"
end tell
getting error
"Mail got an error: Can’t make every message of selection whose subject contains \"deal\" into type specifier." number -1700 to specifier
What am I missing?
Unfortunately you cannot use the whose clause on the selection property.
Workaround is a repeat loop
tell application "Mail"
set theMessages to selection
set filteredMessages to {}
repeat with aMessage in theMessages
if subject of aMessage contains "deal" then set end of filteredMessages to contents of aMessage
end repeat
end tell
Okay i want to check to see if the "user" has entered a number above 10, but i do not know where to put it or how to do it.
set volume 10
say "Hello and Welcome to Tyler's Spam Bot"
set amount to text returned of (display dialog "How many times?" default answer "More than 10")
repeat amount times
tell application "Finder" to make new Finder window
if amount comes before 10 then
return true
else
return false
end if
end repeat
Check if the value can be converted to an integer using a try/on error block. If it can, continue, and ask if the number is greater than 10; if so, perform your action.
set received_integer to "False"
repeat while received_integer is not "True"
set amount to text returned of (display dialog "How many times?" default answer "More than 10")
try
set amt_as_int to amount as integer
if amt_as_int ≥ 10 then
set received_integer to "True"
repeat amt_as_int times
tell application "Finder" to make new Finder window
end repeat
end if
end try
end repeat
I use a google account, say myaccount#google.com for work and I use aliases for the various projects I'm working on. Thus, for project1, I use myaccount+projet1#google.com, for project2 myaccount+projet2#google.com and so on. I do not need to add additional account in Mail since GMail ignores what's after the "+" sign.
Now, in the configuration of my mail account, I put all the addresses (including the ones with aliases) in the "Email address" field, separated by commas. So I can send a message from any of my emails myaccount+project*#google.com.
My question is about the "reply-to" header that I have to manually choose in order to receive the mail in the right folder. I looked in the settings and could not find a solution. Do you know any way to do it automatically (set the reply-to header the same as the from)? Apple script perhaps?
I know that I can use the "default write" command but that only allows to specify one specific "reply-to" header to all the emails, which is not what I'm looking for.
Thank you in advance.
Here is my WIP, I'm posting it now incase I never finish it. Hopefully I'll update this with an example that reads the required fields, rather than being hardcoded.
(3rd and probably final update)
Apologies if this doesn't do exactly what you need, but it did exactly what I needed, and I believe you requirements are simpler than mine.
-- Reply to current message with additional header changes.
-- (To be triggered from Keyboard Maestro, or ControllerMate on Alt-R)
-- function to read/focus/change data in message field
-- usage: set fieldValue to message_field("reply_to", false)
-- message_field("reply to", "moron")
on message_field(fieldname, newValue)
local tElements, tElement, tField, tValue, tClass
tell application "System Events"
tell process "Mail"
set frontmost to true
set tElements to scroll areas of window 1
repeat with tElement in tElements
set tName to get name of tElement as string
try
set tField to text field 1 of tElement
ignoring white space and punctuation
if (name of tField as string = fieldname) then
try
set tValue to get value of tField as string
on error
set tValue to ""
end try
set focused of tField to true
if (newValue ≠ false) then
set value of tField to newValue -- as string
end if
exit repeat
end if
end ignoring
end try
end repeat
end tell
end tell
return tValue
end message_field
-- split function (julifos # macscripter.net)
to split(someText, delimiter)
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""} --> restore delimiters to default value
return someText
end split
-- Get information from current/selected message
tell application "Mail"
set theSignatureName to "Signature #1"
set theMessages to the selected messages of the front message viewer
set theMessage to first item of theMessages
-- Get the email address (ours, hopefully) the message was sent to
set theMessageWasTo to address of first to recipient of theMessage as string
-- Unfortunately, it seesm that Mail doesn't honour the existing Reply-To header
-- when a reply is triggered from a script, instead of from the Reply menu.
-- So here is a bit of a dance to get it.
set theMessageHeaders to headers of theMessage
try
set theMessageReplyToWas to first item of {reply to} of theMessage -- (thx to Brian Christmas)
tell me to set theMessageReplyToWas to item 1 of split(theMessageReplyToWas, ",")
on error
set theMessageReplyToWas to sender of theMessage
end try
-- you can also use: set temp to {deleted status, all headers, was replied to, flag index, date received, message id, background color, subject, read status, flagged status, message size, date sent, junk mail status, source, sender, was forwarded, was redirected, content} of foo
-- set Theheaders to all headers of theMessage -- If you want to get all the headers in text form and process manually
-- set mySource to source of theMessage -- If you want the message source
set theOutgoingMessage to reply theMessage with opening window
-- I want to set "Reply-To" to be the address the message was sent to
tell me to message_field("reply to", theMessageWasTo)
tell theOutgoingMessage
-- I don't like this, as it adds an extra recipient
make new to recipient with properties {address:theMessageWasTo}
-- so I'll do it my way
tell me to message_field("to", theMessageWasTo)
end tell
-- It's easier if you just want to change the sender or signature or something
-- set message signature of theOutgoingMessage to signature theSignatureName
-- set sender of theOutgoingMessage to "Federico Viticci "
end tell
-- Now all that remains is to set the focus back to the body of the message.
I am trying to write a script that does the following job: it goes through all of the emails in the mailbox, finds the ones that have the word "French" in their subject line and then copies all the subject lines of those emails in a text file. Here is what I came up with
tell application "TextEdit"
make new document
end tell
tell application "Mail"
tell the mailbox "Inbox" of account "tigeresque#gmail.com"
set numm to count of messages
repeat with kk from 1 to numm
set wordsub to subject of the message kk
tell application "TextEdit"
if "French" is in wordsub then
set paragraph kk of front document to wordsub & return
end if
end tell
end repeat
end tell
end tell
Unfortunately, I keep receiving the error
"TextEdit got an error: The index of the event is too large to be valid."
and I have already spent a couple of hours trying to fix it without much success. Could you please take a look at my code and see what is wrong with it?
Your main problem is that the number of paragraphs in TextEdit and the number of email messages have nothing to do with each other, so if you're counting on the number of messages then TextEdit will not understand it. For example you may have 50 messages but TextEdit does not have 50 paragraphs so it errors. As such we just use a separate counter for TextEdit.
I made other changes too. I often see errors happen by having one "tell application" block of code inside another... so I separated them. Also notice that the only code inside of any "tell application" block is only what is necessary for that application to handle. This too avoids errors. These are good habits to have when programming.
Therefore give this a try...
set searchWord to "French"
set emailAddress to "tigeresque#gmail.com"
tell application "Mail"
set theSubjects to subject of messages of mailbox "INBOX" of account emailAddress
end tell
set paraCounter to 1
repeat with i from 1 to count of theSubjects
set thisSubject to item i of theSubjects
if thisSubject contains searchWord then
tell application "TextEdit"
set paragraph paraCounter of front document to thisSubject & return
end tell
set paraCounter to paraCounter + 1
end if
end repeat