How do I do this?

I’m really new to applescript and have been struggling to find the best way to extract text from an email and create an iCal event. The email’s are consistent and look similar to below. I would like an applescript to pull out the location (in this case Suite 4A / 46-56 Holt Street SURRY HILLS, NSW 2010) , StartDate (in this case Wednesday 06/08/2008 9:00am) and EndDate (which will be 1 hour after start date) and Description. Any help would be greatly appreciated. Thanks.

email contents:

Hi Joe Bloggs

We are pleased to let you know we have booked a job for you.

Job ID 26446
When: Wednesday 06/08/2008 9:00am
Name: Mr Roger Jones
Address: Suite 4A / 46-56 Holt Street SURRY HILLS, NSW 2010
Phone: 02 9310 3000
Work:
Mobile: 0415 000 470
Access Notes:
Job Description: NEC laptop. WinXP. Unable to log into windows.
Notes:

This should get you started:

tell application "Mail"
	set ms to message 1 in mailbox "Drafts" of account "your account name" -- This gets the message. Yours may be different.
	set bdy to content of ms
	set pars to every paragraph of bdy as list
end tell
repeat with eachpar in pars
	if eachpar contains "Address:" then
		set Loc to eachpar
	else if eachpar contains "When:" then
		set startdate to text 7 thru -1 of eachpar
	end if
end repeat

Hi johnagr,

If you select the corresponding eMail message in Apple Mail, this code will get you started:


on run
	tell application "Mail"
		set msg to item 1 of (selection as list)
		set msgbody to content of msg
	end tell
	
	set joblocation to missing value
	set jobdescription to missing value
	set jobstart to missing value
	
	set bodylines to paragraphs of msgbody
	repeat with bodyline in bodylines
		if bodyline begins with "Address:" then
			set joblocation to my extractinfo(bodyline)
		else if bodyline begins with "When:" then
			set jobstart to my extractinfo(bodyline)
		else if bodyline begins with "Job Description:" then
			set jobdescription to my extractinfo(bodyline)
		end if
	end repeat
end run

on extractinfo(bodyline)
	set offsetspace to offset of " " in bodyline
	set theinfo to ((characters (offsetspace + 1) through -1 of bodyline) as Unicode text)
	return theinfo
end extractinfo

I guess the only problem will be the conversion of the date string to a valid AppleScript date object.

fantastic work guys, thanks so much - especially you Martin, I was able to get it to work a couple of additions to your code. For your reference, here is the working version…


on run
	tell application "Mail"
		set msg to item 1 of (selection as list)
		set msgbody to content of msg
	end tell
	
	set joblocation to missing value
	set jobdescription to missing value
	set jobstart to missing value
	
	set bodylines to paragraphs of msgbody
	repeat with bodyline in bodylines
		if bodyline begins with "Address:" then
			set joblocation to my extractinfo(bodyline)
		else if bodyline begins with "When:" then
			set jobstart to my extractinfo(bodyline)
		else if bodyline begins with "Job Description:" then
			set jobdescription to my extractinfo(bodyline)
		end if
		
	end repeat
	set the jobstart to date (jobstart)
	my CalendarLoad(joblocation, jobdescription, jobstart)
end run

on extractinfo(bodyline)
	set offsetspace to offset of " " in bodyline
	set theinfo to ((characters (offsetspace + 1) through -1 of bodyline) as Unicode text)
	return theinfo
end extractinfo
on CalendarLoad(joblocation, jobdescription, jobstart)
	tell application "iCal"
		tell calendar "Work"
			
			make new event at end with properties {description:jobdescription, summary:"text", location:joblocation, start date:jobstart, end date:jobstart + 60 * minutes}
		end tell
	end tell
	return
end CalendarLoad

Hi,

just for fun,
a version based on Martin’s script with a date conversion routine, which works completely independent from international date format settings.
I’ve added the Job ID as summary


property theCalendar : "myCalendar"

tell application "Mail"
	set msg to item 1 of (get selection)
	set msgbody to content of msg
end tell

set {joblocation, jobdescription, jobstart, jobID} to {"", "", "", ""}

set bodylines to paragraphs of msgbody
repeat with bodyline in bodylines
	if bodyline begins with "Job ID" then
		set jobID to my extractinfo(bodyline)
	else if bodyline begins with "Address:" then
		set joblocation to my extractinfo(bodyline)
	else if bodyline begins with "When:" then
		set jobstart to my extractinfo(bodyline)
	else if bodyline begins with "Job Description:" then
		set jobdescription to my extractinfo(bodyline)
	end if
end repeat
set jobstart to convertDate(jobstart)

tell application "iCal"
	tell calendar theCalendar
		make new event at end of events with properties {start date:jobstart, end date:jobstart + hours, location:joblocation, description:jobdescription, summary:jobID}
	end tell
end tell

on extractinfo(bodyline)
	set offsetspace to offset of " " in bodyline
	set theinfo to ((characters (offsetspace + 1) through -1 of bodyline) as Unicode text)
	return theinfo
end extractinfo

on convertDate(dateString)
	set cd to current date
	set {TID, text item delimiters} to {text item delimiters, space}
	set {wk, _date, _time} to text items of dateString
	set text item delimiters to "/"
	set {dy, mn, yr} to text items of _date
	set text item delimiters to ":"
	tell _time to set {t, am_pm} to {text 1 thru -3, text -2 thru -1}
	set sc to 0
	if (count text items of t) is 3 then
		set {hr, mi, sc} to text items of t
	else
		set {hr, mi} to text items of t
	end if
	set text item delimiters to TID
	tell cd to set {year, its month, day, its hours, its minutes, its seconds} to {yr, mn, dy, hr, mi, sc}
	if am_pm is "pm" then
		if cd's hours is not 12 then set cd to cd + 12 * hours
	else
		if cd's hours is 12 then set cd's hours to 0
	end if
	return cd
end convertDate

you guys are geniuses. Hey, just one really dumb noobie question… The code works on the selected message; what I want to do is have a rule in mail trigger the script; I have the rule set up to start the script, but I’m assuming the code needs to be modified slightly for this scenario?? Again, thanks in advance

I have not tested this, but it might just work :wink:


using terms from application "Mail"
	on perform mail action with messages theMessages for rule theRule
		repeat with theMessage in theMessages
			tell application "Mail"
				set msgbody to content of theMessage
			end tell
			
			set {joblocation, jobdescription, jobstart, jobID} to {"", "", "", ""}
			
			set bodylines to paragraphs of msgbody
			repeat with bodyline in bodylines
				if bodyline begins with "Job ID" then
					set jobID to my extractinfo(bodyline)
				else if bodyline begins with "Address:" then
					set joblocation to my extractinfo(bodyline)
				else if bodyline begins with "When:" then
					set jobstart to my extractinfo(bodyline)
				else if bodyline begins with "Job Description:" then
					set jobdescription to my extractinfo(bodyline)
				end if
			end repeat
			set jobstart to my convertDate(jobstart)
			
			tell application "iCal"
				tell calendar theCalendar
					make new event at end of events with properties {start date:jobstart, end date:jobstart + hours, location:joblocation, description:jobdescription, summary:jobID}
				end tell
			end tell
		end repeat
	end perform mail action with messages
end using terms from

on extractinfo(bodyline)
	set offsetspace to offset of " " in bodyline
	set theinfo to ((characters (offsetspace + 1) through -1 of bodyline) as Unicode text)
	return theinfo
end extractinfo

on convertDate(dateString)
	set cd to current date
	set {TID, text item delimiters} to {text item delimiters, space}
	set {wk, _date, _time} to text items of dateString
	set text item delimiters to "/"
	set {dy, mn, yr} to text items of _date
	set text item delimiters to ":"
	tell _time to set {t, am_pm} to {text 1 thru -3, text -2 thru -1}
	set sc to 0
	if (count text items of t) is 3 then
		set {hr, mi, sc} to text items of t
	else
		set {hr, mi} to text items of t
	end if
	set text item delimiters to TID
	tell cd to set {year, its month, day, its hours, its minutes, its seconds} to {yr, mn, dy, hr, mi, sc}
	if am_pm is "pm" then
		if cd's hours is not 12 then set cd to cd + 12 * hours
	else
		if cd's hours is 12 then set cd's hours to 0
	end if
	return cd
end convertDate

Hi Martin,

Unfortunately that script does not seem to work, so I have reverted to the one that is working. However, I have one problem; the script does a perfect job of reading out each line for Description, Time of appointment etc… but, what I really need is more than 1 line for the description. In fact, it would probably be easier just to have all text past the word Description: but I just cant work out how to do it. Any ideas on this one?

For example, here is a description of a different job (notice how the description spans 2 lines)

Job Description: Dell Laptop. XP. Cable wired.
Getting msgs saying that she has virus. Log on page used to be her work but now it is google. running slowly.
Notes:

To consider more than one description line try this


.
set bodylines to paragraphs of msgbody
repeat with i from 1 to (count bodylines)
	set bodyline to item i of bodylines
	if bodyline begins with "Job ID" then
		set jobID to my extractinfo(bodyline)
	else if bodyline begins with "Address:" then
		set joblocation to my extractinfo(bodyline)
	else if bodyline begins with "When:" then
		set jobstart to my extractinfo(bodyline)
	else if bodyline begins with "Job Description:" then
		set jobdescription to my extractinfo(bodyline)
		repeat while item (i + 1) of bodylines does not start with "Notes"
			set jobdescription to jobdescription & return & item (i + 1) of bodylines
			set i to i + 1
		end repeat
	end if
end repeat
set jobstart to my convertDate(jobstart)
.

I’m wondering that Martin’s script doesn’t work. the syntax is correct

Hi.

Please forgive me if I sound overly critical here. :slight_smile: The conversion routine may be independent of the International settings on the computer running the script, but it assumes (has to assume) that the short date in the message is in day/month/year order. I may be wrong, but it seems to me from johnagr’s description that the messages come from some system designed to send e-mails to his computer, in which case it seems reasonable to assume that the short-date order will match anyway.

The routine offered is definitely not independent of OS version. The hours, minutes, and seconds settings don’t work on systems earlier than Tiger and setting the month by numeric string won’t work before Panther.

Any date-construction method that begins with the current date runs the risk of returning the wrong results with certain combinations of current date and target date. Say the script is run at the end of this month and the date in the message is a few days later, say 5th September:

set cd to date "Sunday 31 August 2008 00:00:00" -- Result of 'current date'.

set {dy, mn, yr} to {"05", "09", "2008"} -- Result of parsing the short date.

tell cd to set {year, its month, day} to {yr, mn, dy}
cd
--> date "Sunday 5 October 2008 00:00:00"

cd’s month is set to September while its day is still 31, causing it to overflow into October. The only reliable work-rounds are to set cd’s day to 1 first or to start off with a known safe date. I quite like this, which works on any system:

set cd to date "Monday 1 December 1000 00:00:00" -- Or any day in December, any year.

set {dy, mn, yr} to {"05", "09", "2008"}

tell cd to set {year, day, day} to {yr - 1, mn * 32, dy}
cd
--> date "Friday 5 September 2008 00:00:00"

The ‘tell cd’ line here changes cd’s date to December in the year before the target, then sets the day high enough to overflow into the early part of the required month in the target year, then sets the day again to the required day. :slight_smile:

I have a similar scenerio whereby I need to extract addresses from selected emails if they meet certain criteria (such as if the text “Backup” is found in the body of the email).

My format is as such:

Address

John Doe
510 country lane
Spring, TX 78620
US


I need to copy each address with name and paste into a Word document for label creation. If anyone can add to this, I would appreciate the help. It’s been 4 years since I wrote my first intricate AS!

Thanks,
Marlon

I apologize but this script doesn’t compile on a French system.
I had to edit it this way :

"1/1/1 00:00:00"
set cd to date result -- Or any day in December, any year.
tell cd to set {its month, its year} to {12, 1000}
log cd (*date lundi 1 décembre 1000 00:00:00*)
set {dy, mn, yr} to {"05", "09", "2008"}

tell cd to set {year, day, day} to {yr - 1, mn * 32, dy}
cd
--> date "samedi 5 janvier 2008 00:00:00"

As you see, the intermediate value of cd is the same than yours but the final result is different.

Yvan KOENIG (VALLAURIS, France) mardi 15 avril 2014 21:05:31

I’m really puzzled because on my French system, I may set mn fro 1 thru 12, I get a wrong date.
Trying to understand I splitted the final instruction into 3 steps but it’s no more clear.

"1/1/1 00:00:00"
set cd to date result -- Or any day in December, any year.
tell cd to set {its month,  year} to {12, 1000}
log cd (*date lundi 1 décembre 1000 00:00:00*)
set {dy, mn, yr} to {"05", 9, "2008"}
--tell cd to set {year, day, day} to {yr - 1, mn * 32, dy}
tell cd to set year to yr - 1
log cd (*date samedi 1 décembre 2007 00:00:00*)
tell cd to set day to (mn * 32)
log cd (*date mardi 1 janvier 2008 00:00:00*)
tell cd to set day to dy
cd
--> date "samedi 5 janvier 2008 00:00:00"

mn = 1 → “samedi 5 janvier 2008 00:00:00”
mn = 2 → “mardi 5 février 2008 00:00:00”
mn = 3 → “mercredi 5 mars 2008 00:00:00”
mn = 4 → “jeudi 5 juillet 2007 00:00:00”
mn = 5 → “dimanche 5 août 2007 00:00:00”
mn = 6 → “mercredi 5 septembre 2007 00:00:00”
mn = 7 → “vendredi 5 octobre 2007 00:00:00”
mn = 8 → “lundi 5 novembre 2007 00:00:00”
mn = 9 → “samedi 5 janvier 2008 00:00:00”
mn = 10 → “mardi 5 février 2008 00:00:00”
mn = 11 → “mercredi 5 mars 2008 00:00:00”
mn = 12 → “jeudi 5 juillet 2007 00:00:00”

To get what you get I must use :

"1/1/1 00:00:00"
set cd to date result -- Or any day in December, any year.
tell cd to set {its month,  year} to {12, 1000}
log cd (*date lundi 1 décembre 1000 00:00:00*)
set {dy, mn, yr} to {"05", 9, "2008"}
--tell cd to set {year, day, day} to {yr , mn * 32, dy}
tell cd to set year to yr - 1
log cd (*date samedi 1 décembre 2007 00:00:00*)
set cd to cd + (mn * 32 * days)
log cd (*date dimanche 14 septembre 2008 00:00:00*)
tell cd to set day to dy
cd
--> "vendredi 5 septembre 2008 00:00:00"
"1/1/1 00:00:00"
set cd to date result -- Or any day in December, any year.
tell cd to set {its month, year} to {12, 1000}
log cd (*date lundi 1 décembre 1000 00:00:00*)
set {dy, mn, yr} to {"05", 9, "2008"} 
tell cd to set {year, its month, day} to {yr, mn, dy} # it's the its which makes the difference

cd
--> "vendredi 5 septembre 2008 00:00:00"

Hi Yvan.

The “overflow” method I used for setting the month (ie. setting the month to December and then setting the day to 32 times the month number) doesn’t work well in recent versions of Applescript. It’s also not necessary, as it’s possible to set the month directly by number. There’s also a bug in early dates now which causes a mismatch between the time string (or the time in the date as string) and the value of the date’s time property.

"1/1/1 00:00:00"
set cd to date result --> date "Monday 1 January 0001 00:00:00"
-- But:
cd's time --> 75 on my system, not 0.

The degree of error depends on whereabouts in the world you are. It’s the property value which is correct, not the string, so you’d now also have to set the time to 0 if you set a date this way. It’s better nowadays to build a date in something like one of the following two ways:

set cd to (current date)
tell cd to set {its day, its time, its year, its month, its day} to {1, 0, "2008", "09", "05"}
cd --> date "Friday 5 September 2008 00:00:00"

-- Or:
set cd to «data isot303030312D30312D3031» as date
tell cd to set {its year, its month, its day} to {"2008", "09", "05"}
cd --> date "Friday 5 September 2008 00:00:00"

Hello Nigel

Only properties displayed in blue require the prefix its. For those displayed in purple its just wasted typing.

Why are you defining some values as strings ?
I get the same result when I define them as integer.

As I am lazyI would use :

"9/9/9"
set cd to date result
tell cd to set {year, its month, day} to {2008, 9, 5}
cd

Playing the fool, I determining the number of characters.

version 1


set cd to (current date)
tell cd to set {day, time, year, its month, day} to {1, 0, "2008", "09", "05"}
cd

According to the statistics service from Word Services there are 106 characters (including the spaces)
According to Pages 4.3 stats tool there are 104 characters (including the spaces)
If I remove the double quotes and the leading zeroes, Pages report 96 characters and the service report 98.

version 2


set cd to «data isot303030312D30312D3031» as date
tell cd to set {year, its month, day} to {"2008", "09", "05"}
cd

According to the statistics service from Word Services there are 106 characters (including the spaces)
According to Pages 4.3 stats tool there are 104 characters (including the spaces)
If I remove the double quotes and the leading zeroes, Pages report 96 characters and the service report 98.

With my preferred one they are only 76 characters (police count) or 78 characters (demonstrators count).

For sure, you’re right, I spent 1052 characters to prove that my scheme spare 28 of them (76 versus 104).

Yvan KOENIG (VALLAURIS, France) mercredi 16 avril 2014 16:23:56

Hi Yvan.

All properties are green under my formatting preferences, which I’ve been using since the Apple defaults were all black.

Nowadays, I often use ‘its’ anyway (where appropriate) for clarity, or for consistency when some of the properties require it anyway.

Just following on from my 2008 script (post #10 above), which in turn was a comment on Stefan’s and Martin’s scripts (#5 and #7), which contain handlers for deriving date objects from short-date texts.

Thanks

I must apologize; It was odd to assume that everybody use the default colorisation scheme. After all, I don’t use it. I force some objects to be underlined which is a neat way to see words which are in fact a single language component.

About its, I’m just too lazy to type it everywhere and use it only when it’s required. I have a post-it about that in case my memory fails.

Yvan KOENIG (VALLAURIS, France) mercredi 16 avril 2014 21:53:20