| View previous topic :: View next topic |
| Author |
Message |
Fill Halfop
Joined: 18 Jan 2009 Posts: 80
|
Posted: Tue Mar 31, 2009 8:58 am Post subject: utimer not working??? |
|
|
Hi folks,
I'm trying to create a script that sends a warning message some seconds after a user joins the channel, but it is not working. The script is like this:
| Code: |
bind join - "#cyber-world *" caps:join
bind nick - "#cyber-world *" caps:nick
proc caps:join { nick uhost hand chan } {
if {[regexp {^[A-Z]+$} $nick]} {
set letras_nick [split $nick {}]
utimer 15 [puthelp "PRIVMSG $chan :message"]
}
}
|
However it seems to "ignore" the timer and sends the message in the moment the user joins the chan. What's wrong with it? |
|
| Back to top |
|
 |
arfer Master

Joined: 26 Nov 2004 Posts: 436 Location: Manchester, UK
|
Posted: Tue Mar 31, 2009 11:58 am Post subject: |
|
|
Using square brackets suggests that what they contain is a command and its arguments, if there are any. Command substitution will occur and the result fed back into the utimer statement. In your case the output command executes immediately and returns nothing. Hence the code will be interpreted as simply
utimer 15
Use the command [list] to return the appropriate arguments to the utimer command and have the output occur when the utimer triggers
| Code: |
bind join - "#cyber-world *" caps:join
bind nick - "#cyber-world *" caps:nick
proc caps:join { nick uhost hand chan } {
if {[regexp {^[A-Z]+$} $nick]} {
set letras_nick [split $nick {}]
utimer 15 [list puthelp "PRIVMSG $chan :message"]
}
}
|
_________________ I must have had nothing to do |
|
| Back to top |
|
 |
Fill Halfop
Joined: 18 Jan 2009 Posts: 80
|
Posted: Tue Mar 31, 2009 2:57 pm Post subject: |
|
|
ahmmm I see. But we must use square brackets with utimers isn't it?
I understood now, but how would I make to execute another proc after the 15 secs.?
Thanks for the help and great explanation |
|
| Back to top |
|
 |
arfer Master

Joined: 26 Nov 2004 Posts: 436 Location: Manchester, UK
|
Posted: Tue Mar 31, 2009 3:27 pm Post subject: |
|
|
It rather depends on whether the proc you want to execute requires arguments to be passed to it, because the syntax for the command utimer is :-
utimer seconds command
That is why, taking the example above, you could not have simply written as follows :-
utimer 15 puthelp "PRIVMSG $chan :message"
That would result in the Tcl error :-
wrong # args: should be "utimer seconds command"
The square brackets group together the command name and its arguments into a single command. However, as we have seen, that will bring about command substitution during a preliminary pass through the interpreter. We simply used the list command to prevent immediate output (during command substitution). A Tcl list of arguments is passed to the utimer command instead of the return value of an output command (in actuality nothing).
Therefore, to answer your question. If the proc you want to execute after a delay does not take any arguments then you don't need square brackets :-
utimer 15 procName
If the proc does take arguments, then you do need square brackets to group them. In which case substitution will occur and to prevent immediate execution you would generally make use of the list command :-
utimer 15 [list procName arg1 arg2]
I think that's just about the best explanation I can manage from my understanding of Tcl. There are more knowledgeable folk here that could perhaps give you a technically better answer.
I will tell you this with some degree of certainty. Everybody learning Tcl has fallen foul of this or similar. _________________ I must have had nothing to do |
|
| Back to top |
|
 |
nml375 Revered One
Joined: 04 Aug 2006 Posts: 2857
|
Posted: Tue Mar 31, 2009 4:06 pm Post subject: |
|
|
To get technical:
The reason we recommend using the list command when using timer/utimer is to avoid "double substitution and evaulation". This problem arises because of the way variable and command substitutions are done in tcl.
Contrary to many other programming languages, when you prefix a variable name with $ in tcl, this causes the tcl interpreter to read the variable and replace the $variable with it's content. Same goes for command substitutions ( [command] ). In essence, the command will never see the variable, let alone be aware of it's existence.
The problem with timer/utimer, is that the command line argument gets passed to the tcl interpreter as the timer expires. As such, the interpreter will first check for any variable and command substitutions before executing the command. Hence, we got a second substitution.
The effect of this is perhaps best illustrated with an example:
| Code: | #Set tmpnick to contain #evil[die]nick
set tmpnick "evil\[die\]nick"
#msg $tmpnick Hello1!
puthelp "PRIVMSG $tmpnick :Hello1!"
#Now lets create a timer that msg's $tmpnick Hello2! after 5 secondsutimer 5 "puthelp \"PRIVMSG $tmpnick :Hello2!\"" |
Here one might think that we've been careful and escaped everything properly. For a simple case, such as the Hello1! msg, that would be true. The tmpnick variable is substituted and the command will send an msg to evil[die]nick.
However, in the second case, we don't execute the puthelp command instantly, but store it for later execution:
| Code: | #timer command after all substitutions are done:
utimer 5 {puthelp "PRIVMSG evil[die]nick :Hello2!"} |
When the timer expires, the second argument is sent to eval:
| Code: | eval {puthelp "PRIVMSG evil[die]nick :Hello2!"}
puthelp "PRIVMSG evil[die]nick :Hello2!"
#the interpreter sees [die], and does a command substitution, calling die.
#Your eggdrop dies... |
So, why don't we just escape the $ in the first case?
Well, that would work in this simple example, but consider this one, where namespaces becomes a factor:
| Code: | proc myproc {nickname} {
utimer 5 "puthelp \"PRIVMSG \$nickname :Hello3!\""
}
myproc $tmpnick |
Here, we call myproc with the content of tmpnick (same as prev. example), so within myproc, nickname will contain that malicious nickname. We escaped the $ so it will not be evaluated immediately, thus we do not have an issue with double evaluation.
| Code: | #timer command after all substitutions are done:
utimer 5 {puthelp "PRIVMSG $nickname :Hello3!"}
#and when the timer expires..
eval {puthelp "PRIVMSG $nickname :Hello3!"}
puthelp "PRIVMSG $nickname :Hello3!"
puthelp {PRIVMSG eggdrop :Hello3!}
#This time, we got "lucky", as nickname happens to be defined, and contain your eggdrop's current nickname.
# Had it been anything else, we'd possibly disclose sensitive configuration settings,
# or end up trying to read a non-existant variable. |
The idea of using tcl-lists, is that it's a data structure that is designed to preserve contents from being "mangled" by substitutions.
| Code: | proc mysafeproc {nickname} {
utimer 5 [list puthelp "PRIVMSG $nickname :Hello4!"]
}
mysafeproc $tmpnick |
First thing, we have a command substitution (list). Within this, the variable will also be substituted:
| Code: | list puthelp "PRIVMSG $nickname :Hello4!
#this returns: puthelp {PRIVMSG evil[die]nick :Hello4!}
utimer 5 {puthelp {PRIVMSG evil[die]nick :Hello4!}}
#as the timer expires:
eval {puthelp {PRIVMSG evil[die]nick :Hello4!}}
puthelp {PRIVMSG evil[die]nick :Hello4!}
#Since the "PRIVMSG ..." argument is enclosed with {} rather than "", the tcl interpreter will skip any further substitutions within this argument.
#Puthelp will thus send the message to evil[die]nick |
_________________ NML_375, idling at #eggdrop@IrcNET |
|
| Back to top |
|
 |
Fill Halfop
Joined: 18 Jan 2009 Posts: 80
|
Posted: Wed Apr 01, 2009 5:49 am Post subject: |
|
|
NIce, understood everything!!! Your explanation was great, thanks for the great help nml375 and arfer. We're always learning
Once again, thanks a lot. I appreciate this website.
See ya  |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|