I am working on a script that sends a message x amount of times, to y person with the message z. However, my main issue comes up in prompting the user to enter x, y and z. This is my base code.
on run
tell application "Messages"
set targetService to 1st service whose service type = iMessage
set targetBuddy to buddy "18008888888" of targetService
repeat 3 times
send " beef bowl " to targetBuddy
end repeat
end tell
end run
As you can see, it will send the message " beef bowl " to 18008888888 3 times. However, when I add in prompts, it breaks the code.
set numberTo to display dialog "What is the number?" default answer ""
...
set targetBuddy to buddy numberTo of targetService
What is wrong with my code?
Thanks!
repeat
try
set theResult to text returned of (display dialog "blabla" default answer "" buttons {"OK"} default button "OK")
set numberTo to (theResult as integer)
exit repeat
on error
-- You may show a warning here to enter a number
end try
end repeat
Use a repeat loop to show the dialog again on wrong entry.
Use the try block to check the coercion (as integer).
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.
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
Traditionally, each person gets only one vote. I'd like to do the same thing with a voting poll.
I have a bunch of accounts on my Mac OS X at work. We are voting to elect someone as our new department head (no I won't say who) to see if he qualifies for the job. I decided to write a little script that does the job for us. However, I can't seem to do one thing: make sure a user can only vote one time. My script is below (it obviously won't compile):
if the current user has never voted then --pseudo-code
set the rating_results to (choose from list {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} with prompt "Please rate (anonymous) based on your observations. BE HONEST!")
email_results(rating_results)
else
display dialog "You already rated (anonymous)!"
end if
I know you will ask "What have I tried so far?" so I will tell you. I tried get the current user, to no avail. I've tried do shell script "echo $USER" as well, which worked, but I still have know idea how to verify whether or not the user voted.
I guess the question title should be "How do I verify whether or not a user voted?".
Thanks so much,
Bill
If I were you, I would make a database in the Database Events application called "Voters". When the script is run, check for a record whose name is the current user's name. If the record exists, the user has voted. Likewise, if the record doesn't exist, the user hasn't voted. Translated into code this paragraph reads:
set user_has_voted to false --inital value
tell application "Database Events"
try
get database "Voters"
open database POSIX path of (path to documents folder) & "/Databases/Voters.dbev"
on error
make new database with properties {name:"Voters"}
save
end try
end tell
set the current_user to the long user name of (system info)
tell application "Database Events"
tell database "Voters"
try
get record current_user
--No error, the user has voted
set user_has_voted to true
on error
make new record with properties {name:current_user}
set user_has_voted to false
end try
end tell
close database "Voters" saving yes
end tell
if user_has_voted then
...
else
vote()
end if
This is more secure than using a property because it can determine if the same user has voted within any time frame.
UPDATE: Per #regulus6633's comment, I have added a subroutine (that you can put at the bottom of the previous script) and another script to help you with the job. The subroutine stores the votes in a database, and the script following the subroutine retrieves the info from the database.
on vote()
set the rating_results to (choose from list {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} with prompt "Please rate (somebody) based on your observations. BE HONEST!")
if the rating_results is false then error number -128
tell application "Database Events"
try
get database "Votes"
open database POSIX path of (path to documents folder) & "/Databases/Votes.dbev"
on error
make new database with properties {name:"Votes"}
save
end try
try
get record "Vote Frequency" of database "Votes"
on error
tell database "Votes" to make new record with properties {name:"Vote Frequency"}
end try
tell database "Votes"
tell record "Vote Frequency"
if exists field rating_results then
set the value of field rating_results to the value of field rating_results & the rating_results
else
tell (make new field with properties {name:rating_results, value:{}}) to set the end of the value of it to the rating_results
end if
end tell
end tell
close database "Votes" saving yes
end tell
end vote
Here is how you would retrieve the voting information from the Votes database.
tell application "Database Events"
try
get database "Votes"
open database POSIX path of (path to documents folder) & "/Databases/Votes"
on error
my nobody_voted_yet() --Database Events doesn't allow any user interaction, so we have to use a subroutine to display information to the user
end try
set the votes to {}
set the vote_total to 0
tell database "Votes"
tell record "Vote Frequency"
repeat with i from 1 to the count of fields
repeat with this_vote in the value of field i
set the end of votes to this_vote
set the vote_total to the vote_total + this_vote
end repeat
end repeat
end tell
end tell
close database "Votes" saving yes --It is always a good idea to save the database, even if you're just retrieving values. This reduces the risk of the values changing themselves.
my average_votes(votes, vote_total)
end tell
on nobody_voted_yet()
display dialog "Nobody voted yet! You can't retrieve information that doesn't exist!" buttons{"Cancel"} default button 1
end nobody_voted_yet
on average_votes(vote_length, vote_total)
set the voting_result to (the vote_total / (length of the vote_length))
display dialog "The average rating for (somebody) is: " & (round the voting_result as string)
end average_votes
I am a pretty good applescripter, and would like some help with this. I am making a recycle bin application, similar to that of the Windows XP Recycle Bin. It is of course a droplet. When I drop items onto the application, the application starts a subroutine designed to check whether the size limit of the Recycle Bin (Trash) was exceeded. However, when I try to get the info for the items in the Trash, the error message that comes up is: "Finder got an error. File item 1 wasn't found." I really need help :(
The subroutine is below:
on check()
tell application "Finder"
set the total_size to 0
set the trash_items to get every item in trash
if the trash_items is not {} then
repeat with i from 1 to the count of items in trash
set this_info to get info for item i of trash --ERROR ON THIS LINE
set the total_size to the total_size + (size of this_info)
end repeat
try
set the second_value to the free_space / (RBPFMS / 100)
if the total_size is greater than the second_value then
display alert "Size Limit Exceeded" message "The Recycle Bin cannot receive any more items because it can only use " & RBPFMS as string & " of your hard drive." buttons {"OK"} default button 1
return false
else
return true
end if
on error
set RBP to ((path to startup disk) as string) & "Recycle Bin Properties"
display dialog "Error: You have modified the properties file for the Recycle Bin. Do not modify the properties file. It is there to store information that is used to determine the properties for the Recycle Bin." with title "Property File Modified" with icon 0 buttons {"OK"} default button 1
set the name of RBP to "DELETE ME"
error number -128
end try
end if
end tell
end check
The error is caused by the expression info for item i of trash. The subexpression item i of trash returns a (Finder) item object. The info for command however requires an alias or file reference to the file (see AppleScript language guide).
There are two ways to fix the expression. Explicitly cast the item to an alias, i.e.:
repeat with i from 1 to the (count of items) in trash
set this_info to get info for (item i of trash as alias)
set the total_size to the total_size + (size of this_info)
end repeat
Or instead of using the info for command, simply use the Finder item's size property:
repeat with i from 1 to the (count of items) in trash
set the total_size to the total_size + (size of item i)
end repeat
Be sure to have both RBPFMS and free_space declared as global variables in the check function:
on check()
global RBPFMS
global free_space
...
end
Another bug: put parentheses around RBPFMS as string in the display alert statement:
display alert "Size Limit Exceeded" message "The Recycle Bin cannot receive any more items because it can only use " & (RBPFMS as string) & " of your hard drive." buttons {"OK"} default button 1
when I try to call the subroutine, it errors "Can't continue check."
The call needs a "my" in front of "check()".
Telling the Finder to call its "check" subroutine won't work. The Finder will tell you that it doesn't know how to "check". The words it will use are "Finder can't continue check." But you can tell the Finder to call your locally-defined "check" subroutine. You simply prefix my to "check" when you call the subroutine. You need to do this every time you call your own subroutine from inside a tell block.