if (word(2 $loadinfo()) != [pf]) { load -pf $word(1 $loadinfo()); return; }; package qcmd; # All these aliases take commands as arguments and have the property that # they will not exand those commands prior to execution, making them safe. # # This may require some explanation: # # The ircii language has the concept of "expansion", whereby variable # references are replaced with the variable values and escaped characters # are unescaped. This is one of the side effects of an eval. In the # following two commands, if entered from the command line, the first will # whois the nick "\\`anderer", and the second will whois "\`anderer", # because the eval will unescape it. # # /whois \\`anderer # /eval whois \\`anderer # # This is from the command line. If these commands are executed from an # alias, the first command will whois "\`anderer", and the second will # whois "`anderer". This is because commands within an alias are always # expanded prior to execution. There is no way to stop this, but you can # control it by passing a variable to the whois command instead of the raw # text. Since expansion only occurs once, the variables expansion will # "shelter" the text from being expanded. # # If you wish to add more levels of expansion, you use more evals. If you # wish to have _no_ expansion, you keep all your text in variables, and # avoid using eval. # # It is easy to see that there may be occasions where you may want to whois # someone from within a hook without having to worry about expansion. This # is easy enough. /whois does not expand its arguments, however, hooks can # generate floods, so you put the whois in a timer say. The problem with # this is that /timer _does_ expand its arguments, so you end up with an # extra level of expansion to deal with. # # These aliases solve these problems. Commands are given as arguments and # are normally not expanded, just as if they had been given to a built in # command. If you wish to have them expanded when they are run, precede # them with /eval, or use a /timer or a /queue instead. # # Synonym for /timer, but which doesn't expand args. # # This is less than perfect and will break if numbers are used for commands. # alias timer.ue (args) { while (word(0 $args) =~ [-*]) { push :pre $shift(args); if (isnumber(b10 $word(1 $args))) { push :pre $shift(args); }; }; timer $pre $shift(args) \$urldecode\($urlencode($args)\); }; # # Usage: # scmd [server [command]] # # Execute command on server _without_ expanding command. # alias.recurse 2 scmd (args) fe ($split(, $shift(args))) serv { xeval -s $serv -w $serverwin($serv) {$args}; }; # # Usage: # 1cmd [time,reset [command]] # # command will not be executed if the same command has been executed in # the last time seconds. # # The time of the last execution of the command will be set to the current time # if it was executed, or if the last execution was less than "reset" seconds # ago. This is useful for riding events which occur in waves. # alias.recurse 2 1cmd (sec,cmd) { @ :foo = encode($tolower($cmd)); @ :time = 1 + time(); @ :eserv = 0 > servernum() ? [] : servernum(); @ :reset = split(, $sec); @ :sec = shift(reset); @ :reset = shift(reset); if (!sec) { if (cmd) { $cmd; } else { foreach onecmd[$eserv] cmd { echo $eserv ${time()-onecmd[$serv][$cmd][r]} ${time()-onecmd[$serv][$cmd][t]} $decode($cmd) } } } elsif (time - onecmd[$eserv][$foo][t] <= reset) { @ onecmd[$eserv][$foo][r] = time + sec; @ onecmd[$eserv][$foo][t] = time; @ onecmd[$eserv] = min($oonecmd[$eserv] $onecmd[$eserv][$foo][r] $onecmd[$eserv][$foo][t]); } elsif (time - onecmd[$eserv][$foo][t] >= sec) { @ onecmd[$eserv][$foo][r] = time + sec; @ onecmd[$eserv][$foo][t] = time; @ onecmd[$eserv] = min($oonecmd[$eserv] $onecmd[$eserv][$foo][r] $onecmd[$eserv][$foo][t]); $cmd; }; @ :time = time(); if (time >= onecmd[$eserv]) { foreach onecmd[$eserv] bar { if (onecmd[$eserv][$bar][r] < time) { @ onecmd[$eserv][$bar][t] = []; @ onecmd[$eserv][$bar][r] = []; }; }; @ onecmd[$eserv] = time(); }; }; # # Usage: # qcmd [queue [command]] # fqcmd [queue [command]] # # Queue command, and schedule a timer for later execution. Prevents flooding. # fqcmd adds the command to the beginning of the queue instead of the end. # # q1cmd [time [queue [command]]] # fq1cmd [time [queue [command]]] # # The same as "1cmd {time} qcmd {queue} {command}", only, the command also # won't be scheduled if the same command is already scheduled. # # Also, with [f]q1cmd, you can specify a coma separated list of queues for # the second argument. All these queues will be searched for duplicates but # only the first will be the queue given to qcmd. # stack push alias alias.tt; alias alias.tt (cmd,op,args) { @ sar(gr/\${cmd}/${cmd}/args); @ sar(gr/\${op}/${op}/args); alias.recurse $args; }; fe (q push fq unshift) cmd op { alias.tt $cmd $op 3 ${cmd}1cmd (qo,qc,args) { @ :oxd = xdebug(dword); xdebug dword; @ :sn = servernum(); @ :sn = sn < 0 ? [_] : sn; @ :argz = args; #@ sar(gr/\\/\\\\/argz); #@ sar(gr/\"/\\\"/argz); @ :qc = split(, $qc); fe ($qc) qqcc { if (0 <= findw("$argz" $qcmd[$sn][$qqcc])) { xdebug $oxd; return; }; }; xdebug -dword; 1cmd $qo ${cmd}cmd $shift(qc) $args; xdebug $oxd; }; alias.tt $cmd $op 2 ${cmd}cmd { @ :oxd = xdebug(dword); @ :sn = servernum(); @ :sn = sn < 0 ? [_] : sn; xdebug dword; if (1 < #) { @ :foo = [$1-]; #@ :foo = ${op}(qcmd.${sn}.$0 "$msar(gr/\\/\\\\/\"/\\\"/foo)"); @ :foo = ${op}(qcmd.${sn}.$0 $foo); } else { if (1 == #) { @ :foo = [qcmd.${sn}.$0]; @ :foo = aliasctl(assign match $foo); @ :foo = shift(foo); @ :foo = after(2 . $foo); } elsif (islagged()) { @ :bar = [ ]; # Do nothing if we're lagged. # This is meant to be a link to a # fictitious lag measurement script. } elsif (0 == #) { foreach qcmd[$sn] bar { @ :foo = bar; break; }; }; @ :bar = shift(qcmd[$sn][$foo]); }; xdebug $oxd; if (functioncall()) { #^timer -ref qcmd.$sn -update 20 qcmd; ^timer -ref qcmd.$sn 20 qcmd; #return $msar(gr/\\\\/\\/\\\"/\"/bar); return $msar(gr/\\\"/\"/bar); #return $bar; } elsif (@bar) { #$msar(gr/\\\\/\\/\\\"/\"/bar); $msar(gr/\\\"/\"/bar); #$bar; ^timer -ref qcmd.$sn -update 5 qcmd; } elsif (@aliasctl(assign match qcmd.${sn}.)) { ^timer -ref qcmd.$sn 5 qcmd; }; }; }; stack pop alias alias.tt; # Command loop using whatever timing mechanism you require. This essentially # reschedules the command once it has been run. It's all recursively tab # completable (even the final command) and requires that the word "loop" appear # at least once in the args. Any time spent running the command will be added # to the time between iterations and can potentially cause multiple loops to be # reordered.. # # loop timer -u -ref x 3 loop echo looping every 3 seconds. # loop q1cmd 1 9 loop echo looping using q1cmd. # loop if.rand 10 q1cmd 1 9 loop echo conditional loop using q1cmd. # # WARNING: Avoid using this with any other looping mechanism such as /timer's # -rep feature. Also, it may be tempting to use the word "loop" as a timer ref # or qcmd queue. Don't. # # The source is not trivial. loop commands are essentially quines. The word # "loop" doesn't have to be the same internally as the alias name but this does # make tab completion much easier. # alias.recurse 1 loop { $afterw(loop $*); $beforew(loop $*) loop $*; }; alias.recurse 1 lob { $beforew(loop $*) loop $*; }; # Empty out the queue's (or specified queue) with /wait. # # This can end up in an endless loop if commands are requeue'd during the # process and can be mitigated by queueing "0" as a command. # # qcmd.421 is a less evolved form of qcmd.wait but with the advantage that it # will operate on multiple servers concurrently. # alias qcmd.421 foreach qcmd sv {fe ($*) que {scmd $sv q1cmd 0 $que 0};scmd $sv 1cmd 20 quote qc}; alias qcmd.wait fe ($*) que {qcmd $que 0;while (:cmd = encode($qcmd($que))) {wait for $decode($cmd)};}; alias qcmd.uniq foreach qcmd.\$servernum() que {if (rmatch($que $*)) {repeat $#qcmd[$servernum()][$que] {q1cmd 0 $que $qcmd($que)}}}; # on ^421 "% QC" if (:cmd = qcmd()) {$cmd;1cmd 0 quote qc}; on ^421 "% QC *" if (:cmd = qcmd()) {$cmd;1cmd 0 quote qc}; # These are meant to be used FROM qcmd to skip the current queue. Will also # run the given command. qcmd.before runs the que before the command. # alias qcmd.before (arg,...) { @ arg = [QCMD.$servernum().$toupper($arg)]; fe ($aliasctl(assign match qcmd.$servernum().)) queue { if (sort($arg $queue) == uniq($arg $queue)) { qcmd $after(2 . $queue); break; }; }; $*; }; # alias qcmd.after (arg,...) { $*; @ arg = [QCMD.$servernum().$toupper($arg)]; fe ($aliasctl(assign match qcmd.$servernum().)) queue { if (sort($arg $queue) == uniq($arg $queue)) { qcmd $after(2 . $queue); break; }; }; }; # Various mass commands. # alias.recurse 1 allchans (args) repeat \$#windowctl(get $winnum() channels) {parsekey switch_channels;$args}; alias.recurse 1 allgroups (args) fe ($uniq($tolower($serverctl(allgroups)))) group {@ :sv=serverctl(gmatch $group);scmd $word(0 $shuffles($common($myservers(,) / $sv)) $sv) $args}; alias.recurse 2 allgroup (serv,args) fe ($split(, $serv)) serv {fe ($shufflef($serverctl(gmatch $serv))) serv {xeval -server $serv -window $serverwin($serv) {$args}}}; alias.recurse 1 allservs () fe ($shufflef($uniq($serverctl(gmatch *)))) serv {xeval -server $serv -window $serverwin($serv) {$*}}; alias.recurse 1 allservs () fe ($shufflef($myservers(,))) serv {xeval -server $serv -window $serverwin($serv) {$*}}; alias.recurse 1 allwins () fe ($revw($winrefs())) win {xeval -window $win -server $winserv($win) {$*}}; alias.recurse 1 allvwins () fe ($revw($winrefs())) win {window swap $win;$*}; # Various mass command modifiers. # fe (if unless) cmd { alias.recurse 1 ${cmd}.connected (args) ${cmd} (isconnected($servernum())) {$args}; alias.recurse 1 ${cmd}.chop (args) ${cmd} (ischanop($servernick() $C)) {$args}; alias.recurse 1 ${cmd}.hop (args) ${cmd} (ishalfop($servernick() $C)) {$args}; alias.recurse 2 ${cmd}.chan (mask,args) ${cmd} (match($mask $C)) {$args}; alias.recurse 2 ${cmd}.chans (count,args) ${cmd} (count<=#mychannels()) {$args}; alias.recurse 2 ${cmd}.chanusers (mask,args) ${cmd} (match($mask $chanusers($C))) {$args}; alias.recurse 2 ${cmd}.chops (count,args) ${cmd} (count<=#chops()) {$args}; alias.recurse 2 ${cmd}.nochops (count,args) ${cmd} (count<=#nochops()) {$args}; alias.recurse 2 ${cmd}.cmdmask (mask,args) ${cmd} (args =~ mask) {$args}; alias.recurse 2 ${cmd}.empty (maxu,args) ${cmd} (maxu >= #remws($mynicks($serverctl(gmatch $servergroup())) / $chanusersa())) {$args}; alias.recurse 2 ${cmd}.idle (count,args) ${cmd} (E>=count) {$args}; alias.recurse 2 ${cmd}.lagged (time,args) ${cmd} (islagged($time)) {$args}; alias.recurse 2 ${cmd}.net (mask,args) ${cmd} (servergroup() =~ mask) {$args}; alias.recurse 2 ${cmd}.nick (mask,args) ${cmd} (N =~ mask) {$args}; alias.recurse 2 ${cmd}.nuh (mask,args) ${cmd} (N#[!]#X =~ mask) {$args}; alias.recurse 2 ${cmd}.onchan (mask,args) ${cmd} (match($mask $mychannels())) {$args}; alias.recurse 2 ${cmd}.rand (num,args) ${cmd} (rand($num)) {$args}; alias.recurse 2 ${cmd}.serv (mask,args) ${cmd} (servernum() == mask || servername() =~ mask) {$args}; alias.recurse 2 ${cmd}.servs (count,args) ${cmd} (count<=#common($myservers(,) / $serverctl(gmatch $servergroup()))) {$args}; alias.recurse 2 ${cmd}.uh (mask,args) ${cmd} (X =~ mask) {$args}; alias.recurse 2 ${cmd}.umode (mode,args) ${cmd} (pass($mode $serverctl(get $servernum() umode))) {$args}; alias.recurse 2 ${cmd}.windows (num,args) ${cmd} (num<=#serverwins($servernum())) {$args}; alias.recurse 3 ${cmd}.005 (ix,mask,args) ${cmd} (match($mask $serverctl(get $servernum() 005 $ix))) {$args}; alias.recurse 3 ${cmd}.array (array,mask,args) ${cmd} (getmaskitems($array $mask)) {$args}; alias.recurse 3 ${cmd}.divi (var,num,args) ${cmd} (($var)++/num) {$args}; alias.recurse 3 ${cmd}.modi (var,num,args) ${cmd} (($var)++%num) {$args}; alias.recurse 3 ${cmd}.tag (ix,mask,args) ${cmd} (match($mask $aliasctl(assign get $ix))) {$args}; alias.recurse 2 ${cmd}.ver (mask,args) ${cmd} (match($mask $serverctl(get $servernum() version))) {$args}; };