Is there a reason my AppleScript is running so slow? - bash

I have a script to run from a command line prompt. The command,
followed by some text, will set a reminder in the Reminder.app with a
due date of next friday. The script is taking about 100 seconds to run
which seem very high.
on nextWeekday(wd)
set today to current date
set twd to weekday of today
if twd is wd then
set d to 7
else
set d to (7 + (wd - twd)) mod 7
end if
return today + (d * days)
end nextWeekday
on run argv
set nextFriday to nextWeekday(Friday)
tell application "Reminders"
set newLable to argv as string
set myList to list "Do."
tell myList
set newReminder to make new reminder
set name of newReminder to newLable
#set remind me date of newReminder to theDay
set allday due date of newReminder to nextFriday
end tell
end tell
end run
The code in the .bash_profile is as follows:
function reme() {
cd
cd /Users/jamieauburn/Documents/projects/reminder
osax reme.scpt $1
}
Have I got something wrong in the script?

You have a lot of unnecessary things here. Your script can be written much more compactly, and it becomes about 4 times faster.
on run argv
tell (current date) to set nextFriday to it + (6 - (its weekday)) * days
tell application "Reminders" to tell list "Do."
make new reminder with properties {name:(argv as string), allday due date:nextFriday}
end tell
end run

Related

Why is this AppleScript idle timer not triggering correctly?

I am trying to create a timer that increments at a certain time (in this case, whenever the time is xx:03:24) and sends a notification when it reaches a user-inputted value. Once it does so, it resets the increment and repeats until manually quit.
However, this code is not reliably triggering when it is supposed to. Does anyone have a guess as to why?
on quit
continue quit
end quit
tell application "Notifications Scripting"
set event handlers script path to (path to me)
end tell
global actions, newActions
using terms from application "Notifications Scripting"
on notification activated
tell application "Safari"
activate
set winlist to every window
repeat with win in winlist
set numtabs to 0
try
set tablist to every tab of win
set numtabs to length of tablist
end try
if numtabs is greater than 0 then
repeat with t in tablist
if "fallenlondon.storynexus.com" is in (URL of t as string) then
tell application "System Events"
tell window id (id of win as integer) of application "Safari" to set current tab to tab (index of t as integer)
end tell
end if
end repeat
end if
end repeat
end tell
end notification activated
end using terms from
display dialog "How many actions do you want to build up before being notified?" default answer "1"
set actions to (text returned of result) as number
set newActions to 0
on idle
if ((seconds of (current date)) = 24) and ((minutes of (current date)) mod 10 = 3) then
set newActions to (newActions + 1)
set delayTime to 540
else
set delayTime to 0.5
end if
if newActions ≥ actions then
if newActions = 1 then
tell application "Notifications Scripting" to display notification "Fallen London" message "You have " & newActions & " new action!" sound name "Glass"
else
tell application "Notifications Scripting" to display notification "Fallen London" message "You have " & newActions & " new actions!" sound name "Glass"
end if
set newActions to 0
end if
return delayTime
end idle
I would not expect your idle implementation to work reliably. It is not a precision timing mechanism; therefore the following assumption is unsound:
if ((seconds of (current date)) = 24) and ((minutes of (current date)) mod 10 = 3) then
You're attempting to hit a 1-second window in a 10-minute loop here, and if you miss that window it'll be another 10 minutes till you get to try again.
A reliable way to do it is to store a date representing the time upon which to next trigger, then periodically check if the current date is now after that date and trigger if it does:
to nextTriggerDate() -- returns next xx:x3:23 date
set d to current date
set {d's minutes, d's seconds} to {((d's minutes) div 10 * 10) + 3, 23}
if d < (current date) then set d to d + 10 * minutes
d
end nextTriggerDate
property _triggerDate : nextTriggerDate()
on idle
if (current date) > _triggerDate then
set _triggerDate to nextTriggerDate()
-- DO STUFF HERE...
end if
return 1
end idle
Alternatively, you could use NSTimer via the AppleScript-ObjC bridge, which is designed specifically for this type of task, or set up a launchd script to run an AppleScript on the specified times if you don't want to be tied to a stay-open applet.
I think you have a few things working against you with your code.
First thing to note is that only the code within the idle handler will be called on each idle event. In other words, if you're expecting the main body of your script to run with each idle process, it will not.
try this as an example...
on run
display dialog "hello!" giving up after 3
end run
on idle
display dialog "Idle test!" giving up after 3
return 5
on idle
When you save the code above as a stay open script, you'll get one dialog that says "hello" then you'll get the "Idle test!" message several times, until you quit the script. Remember to save it as "Stay Open"
To fix this, if you expect all the code outside of the idle handler to run, create a custom function from it that you call from your idle handler.
on yourCustomFunction()
-- All the code you want to run prior when the idle function is called
end yourCustomFunction
on idle
yourCustomFunction()
-- any other additional code you want to run here
return 5
end idle
Another thing I'd like to point out. You don't need to tell application "Notifications Scripting", you can just display notification as shown below.
on idle
if ((seconds of (current date)) = 24) and ((minutes of (current date)) mod 10 = 3) then
set newActions to (newActions + 1)
set delayTime to 540
else
set delayTime to 0.5
end if
if newActions ≥ actions then
if newActions = 1 then
display notification "You have " & newActions & " new action!" with title "Fallen London" sound name "Glass"
else
display notification "You have " & newActions & " new actions!" with title "Fallen London" sound name "Glass"
end if
set newActions to 0
end if
return delayTime
end idle
Lastly, I would recommend adding an on run handler rather than just writing your code in the script without it.

Spotify giving wrong value of current track (AppleScript)

I am currently trying to make an application that gets the duration of the current song playing in Spotify. To do this I am using AppleScript.
Here is my code:
tell application "Spotify"
return the duration of the current track
end tell
The length of the song is 2.52 minutes, the code is telling me 172026 seconds.
By looking at the return value, it looks like it is returning the milliseconds and not the seconds of the track length. This I can fix easily by doing milliseconds*1000. The problem is 172026ms is not 2.52mins, instead it is 2.8671mins.
How am I meant to get the true value of the song duration?
Here is the Spotify AppleScript documentation:
track n : A Spotify track.
duration (integer, r/o) : The length of the track in seconds.
Just found that I needed to do a little bit of maths.
Here is the code:
tell application "Spotify"
set tM to round (((duration of current track) / 1000) / 60) rounding down
set tS to round (((duration of current track) / 1000) mod 60) rounding down
set myTime to ((tM as text) & "," & tS as text)
return myTime
end tell
Thanks to dronir for his code at https://github.com/dronir/SpotifyControl
You can also use the following code to read song & artist name (if you need it):
"inline": "if application \"Spotify\" is running then\rtell application \"Spotify\"\rreturn \" \" & (get artist of current track) & \" – \" & (get name of current track)\rend tell\rend if\rreturn \"\"\r"
(thanks to Toxblh from https://github.com/Toxblh/MTMR/blob/master/Resources/aadi_vs_anand.json)

Get date added of Finder items in Applescript

I can get files by date modified using this bit of code:
get (files of entire contents of folder "Macintosh HD:General Music:05 Reggae:" whose modification date is less than ((current date)) - modDate * days)
but I can't seem to get their date added (nor is it listed in Applescript dictionary for Finder that I can see). This is weird, 'cause I can do a smart folder that uses this property.
Any idea on how to get files who were added within 15 days? Otherwise I'm doing loads of weird stuff with GUI at the moment and I'd like to automate it further.
Thanks
Tardy
You can search Spotlight's metadata with the mdfind command, use the kMDItemDateAdded key:
set _15daysAgo to -15 * days -- number of seconds
set tFolder to quoted form of POSIX path of "Macintosh HD:General Music:05 Reggae:"
-- find files, not folders
do shell script "mdfind -onlyin " & tFolder & " 'kMDItemDateAdded>$time.now(" & _15daysAgo & ") && ! kMDItemContentType == public.folder'"
set tFiles to paragraphs of the result
repeat with i in tFiles
tell i to set contents to i as POSIX file as alias
end repeat
tFiles -- list of files who were added within 15 days
Or, use the methods of the NSFileManager Class to get the NSURLAddedToDirectoryDateKey of the files (require Yosemite or El Capitan),
Here's the AppleScript:
set _15daysAgo to -15 * days -- number of seconds
set f to POSIX path of "Macintosh HD:General Music:05 Reggae:"
do shell script "/usr/bin/python -c 'import sys; from Foundation import NSFileManager, NSURL, NSDate, NSDirectoryEnumerationSkipsHiddenFiles
def procFolder(tDir):
p = dfM.contentsOfDirectoryAtURL_includingPropertiesForKeys_options_error_(tDir, myKeys, NSDirectoryEnumerationSkipsHiddenFiles, None)[0]
for f in p:
myDict, error=f.resourceValuesForKeys_error_(myKeys, None)
if error is None:
if (myDict.get(\"NSURLIsDirectoryKey\")): procFolder(f)
elif (myDict.get(\"NSURLAddedToDirectoryDateKey\").compare_(d) == 1):
print f.path().encode(\"utf8\")
fold=NSURL.fileURLWithPath_isDirectory_(sys.argv[1].decode(\"utf8\"), True)
dfM=NSFileManager.defaultManager()
d=NSDate.dateWithTimeIntervalSinceNow_(" & _15daysAgo & ")
myKeys=[\"NSURLIsDirectoryKey\", \"NSURLAddedToDirectoryDateKey\"]
procFolder(fold)' " & f
set tFiles to paragraphs of the result
repeat with i in tFiles
tell i to set contents to i as POSIX file as alias
end repeat
tFiles -- list of files who were added within 15 days

Mavericks AppleScript count every desktop always returns 1

I've got a script taken from GitHub that is supposed to set the wallpaper of every desktop to a certain image depending on the time of day. (I have modified it from the original code to include more time ranges, issue shows in both versions)
The script attempts to count the number of desktops in order to change more than just the current desktop. It does this by first telling System Events the following
set theDesktops to a reference to every desktop
And then, in order to loop through every desktop, it does the following:
if ((count theDesktops) > 1) then
repeat with x from 2 to (count theDesktops)
--some code removed, see full code below
end repeat
end if
The issues is that count theDesktops always returns a 1, no matter how many desktops there are, as seen in the following screenshot.
http://ss.kobitate.com/2013-12-28_0922_2.png
What can be done to fix this? Here is the full code
(*
Script by Philip Hutchison, April 2013
http://pipwerks.com
MIT license http://pipwerks.mit-license.org/
This script assumes:
1. You have a folder named "Wallpapers" in your Pictures folder
2. You have a subfolder named "Time of Day" in Wallpapers
3. You have six subfolders inside "Time of Day", with names that match the variables below.
* If you decide to use different folder names, you must change the variables to match the new folder names
4. You have images inside each folder
For example:
/Users/YOUR_USER_NAME/Pictures/Wallpapers/Time of Day/Afternoon Early/image.jpg
GeekTool can execute this script for you at specified intervals. Use this line in the command field:
osascript ~/Pictures/Wallpapers/Time\ of\ Day/wallpaper.scpt
*)
-- BEGIN USER CONFIGURATION
-- supply folder names
set morningEarly to "Morning Early"
set morningLate to "Morning Late"
set afternoonEarly to "Afternoon Early"
set afternoonLate to "Afternoon Late"
set eveningEarly to "Evening Early"
set eveningLate to "Evening Late"
set nightEarly to "Night Early"
set nightLate to "Night Late"
-- for multiple monitor support.
-- set to true to display the same image on all desktops, false to show unique images on each desktop
set useSamePictureAcrossDisplays to true
-- END USER CONFIGURATION
-- get current hour
set h to hours of (current date)
-- set default periodOfDay
set periodOfDay to nightLate
-- change value of periodOfDay based on current time
if (h > 6 and h < 8) then
set periodOfDay to morningEarly
else if (h ≥ 8 and h < 10) then
set periodOfDay to morningLate
else if (h ≥ 10 and h < 12) then
set periodOfDay to afternoonEarly
else if (h ≥ 12 and h < 16) then
set periodOfDay to afternoonLate
else if (h ≥ 16 and h < 18) then
set periodOfDay to eveningEarly
else if (h ≥ 18 and h < 20) then
set periodOfDay to eveningLate
else if (h ≥ 20 and h < 22) then
set periodOfDay to nightEarly
else if (h ≥ 22) then
set periodOfDay to nightLate
end if
-- helper function ("handler") for getting random image
on getImage(folderName)
tell application "Finder"
return some file of folder ("Pictures:Wallpapers:Time of Day:" & folderName) of home as text
end tell
end getImage
tell application "Finder"
-- wrapped in a try block for error suppression
try
-- determine which picture to use for main display
set mainDisplayPicture to my getImage(periodOfDay)
-- set the picture for additional monitors, if applicable
tell application "System Events"
-- get a reference to all desktops
set theDesktops to a reference to every desktop
-- handle additional desktops
if ((count theDesktops) > 1) then
-- loop through all desktops (beginning with the second desktop)
repeat with x from 2 to (count theDesktops)
-- determine which image to use
if (useSamePictureAcrossDisplays is false) then
set secondaryDisplayPicture to my getImage(periodOfDay)
else
set secondaryDisplayPicture to my mainDisplayPicture
end if
-- apply image to desktop
set picture of item x of the theDesktops to secondaryDisplayPicture
end repeat
end if
end tell
-- set the primary monitor's picture
-- due to a Finder quirk, this has to be done AFTER setting the other displays
set desktop picture to mainDisplayPicture
end try
end tell
Edit: Fixed an unrelated mistake I found in the code
This seems to be fixed now. I remember testing this when #KobiTate posted it.
I am now on 10.9.1 but not sure when it was fixed
tell application "System Events"
set theDesktops to count of (a reference to every desktop)
set theDesktops2 to count every desktop
end tell
log "count of (a reference to every desktop) = " & theDesktops
log "count every desktop = " & theDesktops2
Returns:
(count of (a reference to every desktop) = 2)
(count every desktop = 2)

Getting today's events from iCal with Applescript

I iterate over all the events in iCal to get only today's events, but I don't think this is efficient. Is there a function/method to get only today's events?
tell application "iCal"
tell calendar "Lotus Notes"
set today to (current date)
set this_year to year of today
set this_month to month of today
set this_day to day of today
repeat with c in every event
set d to start date of c
set the_year to year of d
set the_month to month of d
set the_day to day of d
if (the_year = this_year) and (the_month = this_month) and (the_day = this_day) then
show c
end if
end repeat
end tell
end tell
Try this:
set {year:y, month:m, day:d} to current date
set str to (m as string) & " " & (d as string) & " " & (y as string)
set today to date str
set tomorrow to today + 60 * 60 * 24
tell application "iCal"
tell calendar "Lotus Notes"
set curr to every event whose start date is greater than or equal to today ¬
and start date is less than or equal to tomorrow
end tell
end tell

Resources