Problem Passing Along Values to Script Application On Idle

Hello, I’m a very new Applescripter but I’m finding it a lot of fun. Such a cool tool and such cool people who are into it. :slight_smile:

So, I’ve run into a problem with one of my scripts that I’m sure has a very simple solution, but I’ve been scratching my head over it for hours and it’s finally time to ask for some help.

Here’s the deal: I have written a script that will let me send out a Tweet to Twitter using Launchbar (http://www.obdev.at/products/launchbar/index.html) and a “do shell script”. It works just like I want it to. (I even got it to work with Growl which makes this baby coder very proud!) But, of course, Twitter goes down all the time, so I got the bright idea of using a separate Stay Open Script Application to try to resend the Tweet every 5 minutes (for up to 2 hours) in the event that the first script encounters an error. Reading around on Google, it seemed like the smart way to do that would be to use On Idle in the Script Application – that way it wouldn’t use up much CPU power during the loops when it was called for and could be easily quit at any time.

But try as I might – and I tried and tried and tried – I couldn’t figure out how to pass the variables from the first script over to the Script Application so they could be used within an On Idle/End Idle routine. No matter what I did, when the Script Application ran, I kept getting the error “Variable Not Defined”. :frowning:

The only way I was able to make it work was by making the Script Application a subroutine of the initial script and passing along the variables in parentheses, like this:

tell application "Keep Trying Twitter" to TryAgain(Tweet, s)

Those variables passed along fine and I was then able to use “do shell script sleep” within TryAgain to create the necessary delay between loops while still keeping CPU consumption down. The problem is that when the Stay Open App is “sleeping”, it won’t quit from the Dock (I have to Force Quit it) and when the Stay Open App first runs, it gives me a spinning beach ball for 2 minutes in Launchbar for some reason. I just think that On Idle is the better way to go than the subroutine I used, but I just can’t make it work.

Can anyone help me? It feels like there is some simple inheritance thing that I’m missing…

It’s tempting to post my actual scripts, but I think it’ll be easier if I just post up some sample bits of code that show what I’m trying to do – I don’t want to scare anyone off by asking you to help rewrite my actual scripts (although I’m happy to post them if people want to see them!)

So keeping it very simple, if I use this script to get the text from LaunchBar

using terms from application "LaunchBar"
    on handle_string(s)
        run application "Banana"
    end handle_string
end using terms from

How can I get this Stay Open Script Application – “Banana” – to be able to use what’s in that s variable?


on idle
    display dialog s
    return 10
end idle

Any help would be greatly appreciated! Thank you very much!

Alex

Hi alexwoolfson,

First of all: an «on idle» handler can be called directly and does also accept parameters:


idle {"Martin", "Alex"}

on idle {myname, yourname}
	display dialog myname
	display dialog yourname
	return 5
end idle

But this won’t help you, because whenever the script calls itself, it will throw an exception:

«script» doesn’t match the parameters {…} for idle

Therefor I suggest you a different approach/idea. Have one script to generate text/script files in a folder containing your message parameters and a second script (on idle) to constantly read those message files, trying to send them via Twitter. After success or 10 unsuccessful tries, the message file is removed and the script goes on with the next message file.

Of course this is more work, but a cleaner solution in the long run.

Just an idea :smiley:

Not sure I am fully getting what your script does, but it seems to me you might want to just make one script with an on idle loop that does everything you want it to. Having two scripts control each other or communicate with each other might be pretty difficult.

Matt-boy:

Thank you for your response. :slight_smile: And you’re right – I think that would be the easiest and most straightforward way to go. The problem is the interaction with LaunchBar. The way my script is supposed to work is that you bring up the script name in LaunchBar (so Cmd-Space, then type a few letters of the name) and then you hit the space bar and then Launchbar provides you with a field for you to type in text that would be sent on the the script. The problem is, this behavior only seems to work in Launchbar with straight on scripts. If I turn that script into a Stay Open App – in order to give me the option to easily Quit it if I want to – then Launchbar doesn’t give me that field to fill in when I hit the space bar (or anything else). It treats script applications differently than scripts it seems. Thus, it seems like I need to start off with a plain script and then call a separate Stay Open App if I want to be able to use Launchbar to send on text and then easily Quit the process later. Hence, my desire to send a variable over to a separate app running On Idle…

Martin Michel,

Well, I think you’re right. Though I’d love to be able to make my original evil plan work, I think your way is ultimately the smarter way to go. And it offers the advantage of being able to cue multiple tweets – even from different scripts (for example, I also have created a script to update my Remember the Milk todo list through twitter) – without needing different instances of my TryAgain app.

So… you wouldn’t happen to have a link to an example script that would

  1. Create a new folder if one wasn’t already created
  2. Send some text to a new file in a folder

would you? :slight_smile:

And then a script that would look for those files, grab its text to put into a variable so it could do some work on it, and then delete the file once the work is done?

The script wouldn’t have to be dead-on what I need – I just need some kind of reference for what commands are involved and I could probably figure out the rest. If there’s no easy way to point me to a script or two, then even if you could just refer me to a good tutorial or something about reading and writing to text files from AppleScript (and deleting files/creating folders) that would be very helpful.

Hmmm… y’know, it sounds like I need to read about using AppleScript with TextEdit and Finder. Maybe I’ll start doing Google searches on that right now… :wink:

Thanks again to both of your for your responses and help! It’s given me some good ideas… and made me feel very welcome!

Alex

OK – so I think I’m part of the way there. I’ve created a script that will place the text into a file on the desktop using TextEdit.


to date_format(old_date) -- Old_date is text, not a date.
	set {year:y, month:m, day:d} to date old_date
	tell (y * 10000 + m * 100 + d) as string to text 5 thru 6 & "-" & text 7 thru 8 & "-" & text 1 thru 4
end date_format

using terms from application "LaunchBar"
	on handle_string(s)
		set now to (current date) as text		
		set now to (((date_format(now) as text) & " (" & time string of (current date) as text) & ")")
		display dialog now
		set DocName to "Example - " & now
		tell application "TextEdit"
			set E to make new document at end of documents
			tell E
				set its text to s
				save in file ((path to desktop as Unicode text) & DocName)
			end tell
		end tell
	end handle_string
end using terms from

I had some questions, though. Why do I need to use “set E” and “tell E” to make this work? What’s the E for? (I tried doing it just using ‘tell application “TextEdit”’ with similar commands and had no luck until I found this thread: http://tinyurl.com/6ocbf6) which clued me in to adding this extra language.

Also what does it mean to make a document at “end of documents”?

Finally, is this the most elegant way to put my text into a simple text file? Plain text is fine and in fact better for my purposes, so I don’t need any rich text features that TextEdit offers. Would some kind of shell script doing this create less overhead? Is there an elegant way to do this without launching TextEdit at all?

Oh, and the script is a little broken… :confused: I wanted to put the timestamp at the end of the filename so I could use it in the main script I want to create, but it seems like Applescript is interpreting the colons in the timestamp as some kind of file info. Without adding the timestamp, it creates the file and saves it to my desktop. With it added to the name, though, it throws up an error. Anyway to get that timestamp to be added to the end of the filename?

Thank you again for all your help!

Alex

Hi Alex,

E is the reference to the new document. To change the properties of a document you have to refer to it.
As documents is a list, you have to specify the insertion location (e.g. end of or beginning of)

The syntax is either:

set E to make new document at end of documents 
tell E
      set its text to s
end tell

or

set E to make new document at end of documents 
set  text of E to s

A timestamp can be created very easily with a shell call, but it is not allowed to use colons in a file name,
because colons are (HFS-)path delimiters. I changed the colons to periods.
AppleScript has commands to create a plain text file, TextEdit is not needed


on handle_string(s)
	set TimeStamp to do shell script "date -n +%m-%d-%Y' '\\(%H.%M.%S\\)"
	set DocName to ((path to desktop as Unicode text) & "Example - " & TimeStamp)
	try
		set ff to open for access file DocName with write permission
		write s to ff
		close access ff
		return true
	on error
		try
			close access file DocName
		end try
		return false
	end try
end handle_string

Note: I don’t know, what the using terms block is for, there is no term of LaunchBar in the script

StefanK,

Thank you, that’s very, very helpful. The open for access, write (and ultimately) read commands are exactly what I needed. And after your explanation and reading this section from the AppleScript language guide (http://tinyurl.com/58da42):

I think I really understand why you needed to use the “ff” in the script you created for me. :slight_smile:

(I’ll admit, though, that the “Documents” list concept still eludes me. You say it’s part of a list? What list? The list of files in that directory? It needs to be added to the “end” of that so nothing gets overwritten?)

And thank you also for your Timestamp solution. I will admit, I was a little hesitant to use a do shell script to get the time instead of AppleScript as I read somewhere that it incurred CPU overhead or extra time or something, but that’s really a non-issue, right? This certainly seems simpler than what I had come up with…

My next step will be to learn how to create a folder to put my Tweets into for the Script Application to read.

This is what I have right now:

using terms from application "LaunchBar"
	on handle_string(s)
		set TimeStamp to do shell script "date -n +%m-%d-%Y' '\\(%H.%M.%S\\)"
		tell application Finder
			set TweetFolder to make new folder at (path to desktop as Unicode text) with properties {name:"Tweets Pending"}
		end tell
		set DocName to ((path to TweetFolder as Unicode text) & "Example - " & TimeStamp)
		try
			set ff to open for access file DocName with write permission
			write s to ff
			close access ff
			return true
		on error
			try
				close access file DocName
			end try
			return false
		end try
	end handle_string
end using terms from

But it seems to do exactly nothing – it doesn’t create a folder or the file… And ultimately, I think I’ll need some more code, because once I create the folder, it won’t need to be created again (unless it gets deleted later…) – I’ll need some kind of “if” statement that checks for it already being there, right?

Thank you again for all your help – I feel like I’m learning a lot with this script.

Alex

P.S. The the using terms block is there so this script will interface with LaunchBar – the ultimate objective. It’s just comforting for me to continue to activate it with LaunchBar and use that to define “s” – helps me keep my end goal in mind… :wink:

documents and windows are elements of the application TextEdit.
Here is a picture which displays the structure of TextEdit’s properties

using terms does actually nothing at all at runtime.
It resolves terminolgy of the targeted application at compile time.
As there are no explicit terms of LaunchBar in the script, the block is not needed.
and as far as I know LaunchBar isn’t scriptable anyway.

Ah! Now it makes sense to me! Thank you.

Oh, OK. I just copied that from another script that interfaced with LaunchBar that did the same thing (you call the script via LB, hit the space bar, and it gives you a field to enter text which I suppose calls the “handle_string” handler). But I removed it and it still gets the string, so I guess it’s not needed!


   tell application Finder
           set TweetFolder to make new folder at (path to desktop as Unicode text) with properties {name:"Tweets Pending"}
       end tell
       set DocName to ((path to TweetFolder as Unicode text) & "Example - " & TimeStamp)
       try
           set ff to open for access file DocName with write permission
           write s to ff
           close access ff
           return true
       on error
           try
               close access file DocName
           end try
           return false
       end try

Any idea what I’m doing wrong here in this section – or, more likely, the several things I’m doing wrong here? Why doesn’t this make the new folder when it runs? Why doesn’t the (path to TweetFolder as Unicode text) work to create a path for the Doc? Also, will I need to use an “if” statement in order to not create this folder if it already exists or is there another command I should be using that would take that into account all on its own?

Thanks again for your help!

Alex

#1: it’s tell application “Finder” not tell application Finder
#2: the “root” folder of the Finder is always the desktop, so a specification is not needed

try this:


set TweetFolder to ((path to desktop as Unicode text) & "Tweets Pending:")
tell application "Finder"
	if not (exists folder TweetFolder) then make new folder with properties {name:"Tweets Pending"}
end tell
set DocName to TweetFolder & "Example - " & TimeStamp
.


#1:  it's tell application "Finder" not tell application Finder

Smacks forehead very, very hard

All right – I’m officially an idiot. :rolleyes: Thank you.

If I do the command with an “as alias”, will that only create the folder if it doesn’t already exist? Or will it work like that right now? If not, I think I’d try to use something like this:


        if not (exists folder "Tweets Pending" of (path to desktop as Unicode text)) then

            make new folder at (path to desktop as Unicode text) with properties {name:"Tweets Pending"}

        end if

Also, to put the new files into this created folder, I’ve tried


		set DocName to ((path to desktop as Unicode text) & "Tweets Pending/Example - " & TimeStamp)


and

		set DocName to ((path to desktop as Unicode text) & "/Tweets Pending/Example - " & TimeStamp)

and

		set DocName to ((path to TweetFolder as Unicode text) & "Example - " & TimeStamp)

(but a display dialog TweetFolder doesn’t bring up anything, so I suppose I shouldn’t expect TweetFolder to be used this way…)

What am I doing wrong here?

Thank you again for your help and your quick response,

Alex

P.S. What BBCode are you using to turn your text blue? That’s kinda cool.

if you use my code, it will create the folder only if it doesn’t exist

set TweetFolder to ((path to desktop as Unicode text) & "Tweets Pending:")

is just a path string (e.g MacHD:Users:alex:Desktop:Tweets Pending:)

to move files into this folder with the Finder use the keyword folder

tell application "Finder" to move file "someFile" to folder TweetFolder

if a file or folder doesn’t exist at run time while coercing a string path to an alias (as alias), an error occurs

blue is [ color=blue]blue[ /color] (without the space characters after the both “[”)

Thank you. I really very much appreciate your patience with me. Your initial script suggestion works great. My understanding of working with files and folders in AppleScript is very limited, so I feel like I’m asking dumb questions… :frowning:

So here’s another dumb question I encountered, when I tried to tailor the script you gave me.

In the larger script you created for me, this works:


		set TweetFolder to ((path to desktop as Unicode text) & "Tweets Pending:")

This doesn’t:

		set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")

This doesn’t either:

		set TweetFolder to ((path to documents as Unicode text) & "Tweets Pending:")

But I don’t know why not. From what I read in other scripts, it seems that “documents” is one of those folders AppleScript is supposed to recognize, along with “desktop”, so why doesn’t this create the folder in my Documents folder? What am I doing wrong?

of course, in all cases except the desktop folder you have to specify the folder explicitly

set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
tell application "Finder"
	if not (exists folder TweetFolder) then make new folder at (path to documents folder) with properties {name:"Tweets Pending"}
end tell
set DocName to TweetFolder & "Example - " & TimeStamp

the term path to documents isn’t valid
look at the dictionary of Standard Additions > path to for the valid enumerations

Oh, OK. Great. That works now. :slight_smile: And now that I see it, it seems obvious to me…

And thank you for telling me where to find this. I had seen this list on the Web and couldn’t find it again.

OK, I am now into the home stretch. There’s only one more thing I’ll need to do to make this work – have my Script Application look into the folder, check to see if there are any files in it and then work with each file in order of creation date (deleting when finished) until there are no more files to work on.

Finding out if there are files in the folder seems straightforward:

tell application "Finder"
	set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
	set everyfile to every file in folder TweetFolder
	set n to count of items in everyfile
end tell
if n is greater than 0 then
	display dialog "There are files!"
else
	display dialog "There are NO files!"
end if

But I’m not sure how to then select a file in that folder with the earliest creation date, read from it and then (eventually) delete it and move on to the next newest one.

I imagine I would start with something like

	sort everyfile by creation date

But then how would I choose the first file in that list to work with? And is this the best way to do that?

Thank you again so much for all of your help. It really feels above and beyond the call of duty. :slight_smile:

Alex

something like this


tell application "Finder"
	set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
	set everyfile to sort (get every file in folder TweetFolder) by creation date
	repeat with oneFile in everyfile
		-- do something
	end repeat
end tell

OK, cool. Here is a better idea of what I’m trying to do based on the script you created (the repetition will be created by an “on Idle” handler later)

tell application "Finder"
	set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
	set everyfile to sort (get every file in folder TweetFolder) by creation date
	set n to count of items in everyfile
	
	if not (n = 0) then
		try
			set hh to open for access file oneFile in everyfile
			set Tweet to read hh
			display dialog Tweet
			close access hh
			return true
		on error
			try
				close access file oneFile
			end try
			return false
		end try
	else
		display dialog "There are NO files here!"
	end if
	
end tell

So far doesn’t work – but am I close?

(Also I realized I’d need to create a subfolder in the Tweets Pending for some other text my Script Application will need to grab, but this didn’t work:


		set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
		set UpdateFolder to ((path to documents folder as Unicode text) & "Tweets Pending:Tweets Formatted For Twitter:")
		tell application "Finder"
			if not (exists folder TweetFolder) then make new folder at (path to documents folder) with properties {name:"Tweets Pending"}
			if not (exists folder UpdateFolder) then make new folder at (path to documents folder & "Tweets Pending:") with properties {name:"Tweets Formatted For Twitter"}
		end tell

nor did this

		set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
		set UpdateFolder to ((path to documents folder as Unicode text) & "Tweets Pending:Tweets Formatted For Twitter:")
		tell application "Finder"
			if not (exists folder TweetFolder) then make new folder at (path to documents folder) with properties {name:"Tweets Pending"}
			if not (exists folder UpdateFolder) then make new folder at (path to documents folder) with properties {name:"Tweets Pending:Tweets Formatted For Twitter"}
		end tell

What am I missing here to create a folder within the folder?

Thank you for your continued help! :slight_smile:

Alex


set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
set UpdateFolder to TweetFolder & "Tweets Formatted For Twitter:"
tell application "Finder"
	if not (exists folder TweetFolder) then make new folder at (path to documents folder) with properties {name:"Tweets Pending"}
	if not (exists folder UpdateFolder) then make new folder at folder TweetFolder with properties {name:"Tweets Formatted For Twitter"}
end tell

or shorter


do shell script "/bin/mkdir -p " & quoted form of (POSIX path of (path to documents folder) & "Tweets Pending/Tweets Formatted For Twitter")

this creates the folders silently regardless of their existence

Awesome – that worked great. I guess the key is to keep it simple and use the variables already created. I’ll remember that.

And how can I now get the text from the file with the first creation date? Why is this not working?



global DocName

tell application "Finder"
	set TweetFolder to ((path to documents folder as Unicode text) & "Tweets Pending:")
	set everyfile to sort (get every file in folder TweetFolder) by creation date
	set n to count of items in everyfile
	
	set DocName to name of (info for oneFile in everyfile)
	display dialog DocName as text
	
end tell

if not (n = 0) then
	try
		set hh to open for access file DocName with write permission
		set Tweet to read hh
		display dialog Tweet
		close access hh
		return true
	on error
		try
			close access file DocName
		end try
		return false
	end try
else
	display dialog "There are NO files here!"
end if


I moved the end tell up before the AppleScript commands and I tried using a Global Variable, I’m trying even just to print the name of that most recent file in a dialog box, but no dice. Can can I read the oldest file in the folder and then later delete it?

Thanks again for all your help! Slowly but surely this is coming together!

Alex

Hmmm… I think I might be figuring out how to do this… no need to reply to that last post… let me try to figure this out… :slight_smile: