AppleScript - Geographic Location - applescript

Is it possible to write a script that identifies my location? I am trying to make an if statement that lets my laptop find my location at a certain time and tell me things I need to do at that location

This may request for you to authorise Script Editor to have access to your location service (don't forget to turn Location Services on), so you may need to run the script a second time once access has been granted.
use framework "CoreLocation"
use framework "Foundation"
use scripting additions
property this : a reference to the current application
property nil : a reference to missing value
property _1 : a reference to reference
property CLLocationManager : a reference to CLLocationManager of this
property kCLLocationAccuracyThreeKilometers : a reference to 3000.0
--------------------------------------------------------------------------------
property running : false
property result : missing value -- Lat./long. or error description
property number : 0 -- Error code
property seconds : 10 -- Maximum time to allow script to run
--------------------------------------------------------------------------------
# IMPLEMENTATION:
my performSelectorOnMainThread:"getLocation" withObject:nil waitUntilDone:true
return my result
--------------------------------------------------------------------------------
# HANDLERS & SCRIPT OBJECTS:
to getLocation()
set locationManager to CLLocationManager's new()
locationManager's setDelegate:me
locationManager's setDesiredAccuracy:kCLLocationAccuracyThreeKilometers
set my running to true
set started to current date
locationManager's startUpdatingLocation()
repeat while my running
delay 0.5
if (current date) - started > my seconds then exit repeat
end repeat
end getLocation
on locationManager:locationManager didUpdateLocations:locations
local locationManager, locations
locationManager's stopUpdatingLocation()
set my running to false
set my result to (locations's valueForKey:"coordinate") as record
end locationManager:didUpdateLocations:
on locationManager:locationManager didFailWithError:err
local locationManager, err
tell err's code()
set my number to it
set my result to item (it + 1) in my enum's kCLError
if it ≠ 0 then set my running to false
end tell
end locationManager:didFailWithError:
script enum
property kCLError : {¬
"Location Unknown", ¬
"Denied", ¬
"Network", ¬
"Heading Failure", ¬
"Region Monitoring Denied", ¬
"Region Monitoring Failure", ¬
"Region Monitoring Setup Delayed", ¬
"Region Monitoring Response Delayed", ¬
"Geocode Found No Result", ¬
"Geocode Found Partial Result", ¬
"Geocode Canceled", ¬
"Deferred Failed", ¬
"Deferred Not Updating Location", ¬
"Deferred Accuracy Too Low", ¬
"Deferred Distance Filtered", ¬
"Deferred Canceled", ¬
"Ranging Unavailable", ¬
"Ranging Failure"}
property CLAuthorizationStatus : {¬
"Not Determined", ¬
"Restricted", ¬
"Denied", ¬
"Authorized (Always)", ¬
"Authorized When In Use"}
end script
---------------------------------------------------------------------------❮END❯
If the return message is "Location Unknown", this may indicate you need to reset your network settings.

Related

How do I pause and resume an AppleScript without a dialog box?

I am working to outline a workflow in AppleScript. The script takes the next task I need to do from Omnifocus and requires me to determine if I can do it in 2 minutes or less. If I can, it starts a timer and I want it to wait until I actually do the task. Right now I have a dialog box pop up and I can mark the task complete when I am done with it. Unfortunately, some of the tasks I need to do are in Omnifocus and I can't do anything in Omnifocus with the dialog box open.
I would like to avoid using the dialog box so I can work in Omnifocus while the script is running. I'd like to be able to tell the script I'm done so it can stop the timer, tell me how long it took to do the task and then go on to mark the task complete in Omnifocus. I initially thought the best way to do this would be by entering a key combination. After a bit of research, I don't think I can do this in AppleScript. I am open to any idea of how to allow me to work in the middle of my script and then tell the program I am done with the task.
Here's my code:
on run {}
with timeout of (30 * 60) seconds
tell application "OmniFocus"
activate
end tell
tell application "OmniFocus"
tell default document to tell front document window
set perspective name to "Daily Wrap-Up"
tell content to set TreeList to (value of first leaf)
repeat with ListItem in TreeList
set ProjectName to name of containing project of ListItem as text
set TaskName to " - " & name of ListItem
set NoteName to " - " & note of ListItem
display dialog "The task is:" & return & ProjectName & TaskName & NoteName & return & "Can you do this in 2 minutes or less?" buttons {"Yes", "No"} default button "Yes"
set Button_Returned to button returned of result
if Button_Returned = "Yes" then
say "Get to work!"
set T1 to minutes of (current date)
set T1s to seconds of (current date)
display dialog "Click when done." buttons {"Complete", "Cancel"} default button "Complete"
set Button_Returned to button returned of result
if Button_Returned = "Complete" then
set T2 to minutes of (current date)
set T2s to seconds of (current date)
set TT_ to ((T2 * 60) + T2s) - ((T1 * 60) + T1s)
say "that took you" & TT_ & "seconds to complete"
display dialog ProjectName & TaskName & NoteName buttons {"Complete", "Defer", "Cancel"} default button "Complete"
set Button_Returned to button returned of result
if Button_Returned = "Complete" then
mark complete ListItem
tell application "OmniFocus"
compact default document
end tell
else if Button_Returned = "Defer" then
display dialog "Defer for how long (in minutes)?" default answer "60"
set TimeAdd to text returned of result
set defer date of ListItem to ((current date) + (TimeAdd * minutes))
tell application "OmniFocus"
compact default document
end tell
else if Button_Returned = "Cancel" then
tell application "OmniFocus"
compact default document
end tell
else if Button_Returned = "No" then
tell application "OmniFocus"
compact default document
end tell
end if
else if Button_Returned = "No" then
display dialog "Breakdown task."
set perspective name to "Projects"
end if
end if
end repeat
end tell
end tell
end timeout
end run
Thanks in advance for any help.
I don't have OmniFocus on my machine, so I can't properly compile this much less test it, but in vanilla AppleScript you can do something like the following:
global start_time, end_time, TreeList, current_task_index, TaskName, NoteName
on run
tell application "OmniFocus"
tell default document to tell front document window
set perspective name to "Daily Wrap-Up"
tell content to set TreeList to (value of first leaf)
end
end
set current_task_index to 1
beginTask()
end
on reopen
-- inserted try block to aid debugging
try
set end_time to (current date)
set elapsed_time to end_time -start_time
say "that took you " & elapsed_time & " seconds to complete"
display dialog ProjectName & TaskName & NoteName buttons {"Complete", "Defer", "Cancel"} default button "Complete"
set Button_Returned to button returned of result
if Button_Returned = "Complete" then
mark complete ListItem
tell application "OmniFocus"
compact default document
end tell
else if Button_Returned = "Defer" then
display dialog "Defer for how long (in minutes)?" default answer "60"
set TimeAdd to text returned of result
set defer date of ListItem to ((current date) + (TimeAdd * minutes))
tell application "OmniFocus"
compact default document
end tell
else if Button_Returned = "Cancel" then
tell application "OmniFocus"
compact default document
end tell
else if Button_Returned = "No" then
tell application "OmniFocus"
compact default document
end tell
end if
else if Button_Returned = "No" then
display dialog "Breakdown task."
set perspective name to "Projects"
end if
set current_task_index to current_task_index + 1
if current_task_index <= count of TreeList then
beginTask()
else
quit
end
on error errstr number errnum
display alert "Error " & errnum & ": " & errstr
end try
end
on idle
(*
you can use this handler if you want the app to give you a countdown, or
announce a time limit, or anything that needs doing while you're working on the task
*)
end
on beginTask()
tell application "OmniFocus"
tell default document to tell front document window
set perspective name to "Daily Wrap-Up"
set ListItem to item current_task_index of TreeList
set ProjectName to name of containing project of ListItem as text
set TaskName to " - " & name of ListItem
set NoteName to " - " & note of ListItem
display dialog "The task is:" & return & ProjectName & TaskName & NoteName & return & "Can you do this in 2 minutes or less?" buttons {"Yes", "No"} default button "Yes"
set Button_Returned to button returned of result
if Button_Returned = "Yes" then
say "Get to work!"
set start_time to (current date)
end if
end tell
end tell
end
Copy this into the script editor, debug it, and save it as an application with the 'Stay open after run handler' checkbox checked. Operation is as follows:
When OmniFocus is ready, run this script application. It will prompt you to begin the first task.
When you have finished the first task, double-click the script application icon again to invoke the reopen handler. The script will present you with the elapsed time for the first task, give you the choices you outlined, and then prompt you to begin the second task.
After the last task is complete, the script will automatically quit.
The global variable at the beginning allow necessary data to be passed between handlers as the script progresses.
If you want something more complex — e.g., a floating window or menu bar item that gives you more fine-grained control — then you'll need to start using ASOC to build that up. But that's fine-tuning; this should give you a general structure.

"tell application 'Mail'" won't accept string from IBOutlet in AppleScript-ObjC app

I'm working on an AppleScript-ObjC application that generates an email message in Mail. My UI has three IBOutlets (two text fields and a pop-up menu) where users can enter text that will be filled into the email. I can save the values from those outlets into variables, but when I try to use those variables inside my tell application "Mail" statement, I get this error:
AppleEvents: received mach msg which wasn't complex type as expected
in getMemoryReference.
And here's what prints in the log:
(
"<NSAppleEventDescriptor: 'ctxt'>",
": ",
"Status: MyProjectName, Part No.: 12345"
)
It seems like there's a difference between ctxt (which I think is an NSString) and an AppleScript string, but I can't figure out how to convert to an AppleScript string. Please let me know how if you do.
Here's the code for the whole function:
-- IBOutlets
property theWindow : missing value
property statusMenu : missing value
property partNumberField : missing value
property projectNameField : missing value
on generateButtonClicked:sender
set projectName to projectNameField's stringValue() as text
set partNumber to partNumberField's stringValue() as text
set status to statusMenu's objectValueOfSelectedItem as text
set theSubject to (status & ": " & projectName & ", Part No.: " & partNumber) as string
log (class of theSubject) & ": " & theSubject
tell application "Mail"
try
set newMessage to make new outgoing message with properties {subject: theSubject, theContent: "", visible: true}
on error e
log e
end try
activate
end tell
end generateButtonClicked:
set newMessage to make new outgoing message ¬
with properties {subject: theSubject, theContent: "", visible: true}
theContent isn't a property of outgoing message. Change it to content.

Signing Applescript Application So It Doesn't Ask For Password

I have an applescript application I wrote modifies my network settings to connect me to my vpn provider. Problem: It asks me for my password every time I want to run it.
Hopeful Solution: It runs without asking for a password.
(************************ Run *************************)
on run #{VPNconfiguration}
set rand_vpn to random_vpn()
set VPNconfiguration to pick_vpn(rand_vpn) # Returns a list with one item
set VPNconfiguration to item 1 of VPNconfiguration # Selects that item from the list
connect_vpn(VPNconfiguration)
end run
(******************** Random Vpn **********************)
on random_vpn()
set rand_vpn to some item of {"US-Cali", "US-West", "US-East", "US-Midwest", ¬
"US-Texas", "US-Flordia", "US-Seattle", "US-Siliconvallay", "US-NewYorkCity"}
return rand_vpn
end random_vpn
(********************* Pick Vpn ***********************)
on pick_vpn(rand_vpn)
set VPNconfiguration to choose from list {¬
"US-Cali", "US-West", "US-East", "US-Midwest", "US-Texas", "US-Flordia", ¬
"US-Seattle", "US-Siliconvallay", "US-NewYorkCity", "Mexico", "Brazil", ¬
"Canada", "Canada-Toronto", "UK-London", "UK-Southampton", "German", ¬
"France", "Switzerland", "Netherlands", "Sweden", "Romania", "Hong-Cong", ¬
"Isreal", "Russia", "Turkey", "Singapore", "Australia", "Australia-Melbourne", "Japan"} ¬
with title ¬
"VPN Connection Client" with prompt ¬
"Choose One Location" OK button name ¬
"This VPN" cancel button name ¬
"Quit" default items {rand_vpn}
if result = false then quit
return VPNconfiguration
end pick_vpn
(******************** Connect Vpn *********************)
on connect_vpn(VPNconfiguration)
tell application "System Events"
tell network preferences
tell current location
set aPPPoEService to a reference to configuration VPNconfiguration of service "AllVPNs"
connect aPPPoEService
end tell
end tell
end tell
end connect_vpn
(*********************** Quit *************************)
on quit
continue quit
end quit

How can I check for existence of Itunes track in Applescript without throwing error

the idea of this script (simplified to show the issue) is checks if a track exists,
tell application "iTunes"
set matchtrack to get first track in playlist 1 whose persistent ID is "F1A68AD90AA66648"
if matchtrack is not {} then
print name of matchtrack
else
print "no track found"
end if
end tell
unfortunately if the track does not exist it gives error
"iTunes got an error: Can’t get track 1 of playlist 1 whose persistent ID = \"F1A68AD90AA66648\"." number -1728 from track 1 of playlist 1 whose persistent ID = "F1A68AD90AA66648"
'
instead of just printing 'no track found'
get first track give a track object or an error
tracks give a list of one track or an empty list
tell application "iTunes"
set matchtrack to tracks in playlist 1 whose persistent ID is "F1A68AD90AA66648"
if matchtrack is not {} then
return name of item 1 of matchtrack
else
return "no track found"
end if
end tell
Simplest way would be to use a try block, like this:
set persistentID to "F1A68AD90AA66648"
tell application "iTunes"
try
set matchtrack to get first track in playlist 1 whose persistent ID is persistentID
if matchtrack is not {} then
log (get name of matchtrack)
else
log "no track found"
end if
on error the error_message number the error_number
log "Error (probably no track found)"
-- display dialog "Error: " & the error_number & ". " & the error_message buttons {"Cancel"} default button 1
end try
end tell
BTW: use log instead of print and (get name of matchtrack) instead of (name of matchtrack)
This would work but goes thru all the tracks in a named playlist:
set flagFound to false
set mTrack to ""
# SETUP THOSE FIRST
# -------------------------------------------------------
property Library_Name : "Library" # "Mediathek" in german
property Playlist_Name : "Bob Marley"
set persistentID to "F1A68AD90AA66648"
# -------------------------------------------------------
tell application "iTunes"
tell source Library_Name
tell playlist Playlist_Name
repeat with i from the (count of tracks) to 1 by -1
if (the persistent ID of track i is persistentID) then
set mTrack to track i
set flagFound to true
exit repeat
end if
end repeat
end tell
end tell
if flagFound then
tell mTrack
log "Found…"
log "Name: " & (get name)
log "persistent ID: " & (get persistent ID)
end tell
else
log "no track with persistent ID " & persistentID & " found"
end if
end tell

Get reference to an Outook message duplicate

This code will duplicate a Microsoft Outlook message to the drafts folder:
tell application "Microsoft Outlook"
...
-- find the template give an ID
set theTemplate to message id theID
-- duplicate the message
duplicate theTemplate to drafts
...
end tell
I need a reference to the duplicate for additional processing.
Unfortunately, this doesn't work:
...
-- this will create a duplicate
set theDuplicate to (duplicate theTemplate to drafts)
-- produces an error that reads "The variable theDuplicate is not defined."
display dialog (subject of theDuplicate) & " [" & (id of theDuplicate) & "]"
How do I get a reference to the message that was just duplicated? Its ID would be a satisfactory alternative.
There must be a better way but...
--For Testing
set theID to 39110
tell application "Microsoft Outlook"
set oldIds to my getDraftIds()
-- find the template give an ID
set theTemplate to message id theID
--duplicate the message
duplicate theTemplate to drafts
set newIds to my getDraftIds()
set duplicatedMessage to message id (my findNewID(oldIds, newIds))
end tell
on getDraftIds()
set messageIDs to {}
tell application "Microsoft Outlook"
set draftFolders to (every folder whose name = "Drafts")
repeat with dFolder in draftFolders
set messageIDs to messageIDs & id of dFolder's messages
end repeat
end tell
end getDraftIds
on findNewID(oldList, newList)
repeat with mID in newList
if mID is not in oldList then return mID
end repeat
end findNewID
I guess the duplicate method is pretty limited in this regard. I tried the more generic copy method too - the message is copied, but again no ID is returned.
Here's another possible way to do this with no repeat loops:
Create a new, temporary category
Mark the original message with the new category
Duplicate - the duplicate message will also be marked with the category
search for messages marked with the temporary category - you should only get two - the original and the duplicate
Delete the temporary category
Here is the code (rather unfinished):
tell application "Microsoft Outlook"
try
set tempCategory to category "Temporary Category"
on error number -1728
set tempCategory to (make new category with properties {name:"Temporary Category"})
end try
set messageList to selected objects
set origMsg to item 1 of messageList
display dialog "Original Message ID: " & id of origMsg
set msgCat to category of origMsg
set end of msgCat to tempCategory
set category of origMsg to msgCat
duplicate origMsg to drafts
delay 1 -- sigh, it seems to take a bit of time before the category markings are reflected in the spotlight DB
--set msgList to messages whose category contains tempCategory
set currentIdentityFolder to quoted form of POSIX path of (current identity folder as string)
set tempCatMsgs to words of (do shell script "mdfind -onlyin " & currentIdentityFolder & " 'com_microsoft_outlook_categories == " & id of tempCategory & "' | xargs -I % mdls -name com_microsoft_outlook_recordID '%' | cut -d'=' -f2 | sort -u | paste -s -")
if item 1 of tempCatMsgs is (id of origMsg) as text then
set dupMsgId to item 2 of tempCatMsgs
else
set dupMsgId to item 1 of tempCatMsgs
end if
delete tempCategory
display dialog "Original Message ID: " & id of origMsg & return & "Duplicate Message ID: " & dupMsgId
end tell
I thought it would be easier to find messages with a given category using the OL dictionary, but instead had to resort a spotlight search. I'm sure there is a better way to do this.
Also adding a category to a message was harder than I though - again I'm sure this can be done more efficiently.
-- create a temporary folder to hold duplicates
on createFolder(theName)
tell application "Microsoft Outlook"
set theFolder to make new mail folder with properties {name:theName}
end tell
end createFolder
-- find folder by name
on findFolder(theName)
tell application "Microsoft Outlook"
set theFolder to make new mail folder with properties {name:theName}
end tell
end findFolder
-- duplicate the message and get a reference
on duplicateIt(theID)
set theDestination to findFolder("foo bar")
tell application "Microsoft Outlook"
--find the template
set theTemplate to message id theID
-- duplicate it to the temporary location
(duplicate theTemplate to theDestination)
-- get the first item in the folder
set theDuplicate to item 1 of theDestination
-- do something with it
...
end tell
end duplicateIt

Resources