####################################################
# by wiebe @ QuakeNet
#
# queue users on channels
# 
# can use: chase.tcl idle.tcl irc.tcl ircconsole.tcl whichchan.tcl
#
# provides proc queue:dnmo for dnmo.tcl
#    (queue:dnmo <nick> <chan>, returns 1 on success, else 0)
#
# provides info for: script.tcl userflag.tcl
####################################################

# array syntax:
# queuedb(<chan>,q) nick1 .. nickN - queued users
# queuedb(<chan>,v) nick1 .. nickN - voiced users
# queuedb(<chan>,t) nick1 .. nickN - highlight users

# todo:
# queue status messages partyline?
# next / done mask?
# next / done in msg?

setudef flag queue
setudef int queue-auto
setudef int queue-idle

####################################################
# queue:help:pubm
####################################################
bind pubm lomn|lomn "% ${botnet-nick} help queue" queue:help:pubm
proc queue:help:pubm { n u h c t } {
  if {[matchattr $h bkZ]} { return 0 }
  lappend o "queue: usage queue \[<chan>\] on|off|next|done|auto|status|list|help"
  if {[string equal [info procs cnotice] ""]} { foreach l $o { puthelp "NOTICE $n :$l" }
  } else { foreach l $o { cnotice $n $l puthelp "queue: " } }
  putloglev c $c "help: $n $u $h $c queue"
  return 1
}


####################################################
# queue:help:msg
####################################################
bind msgm lomn|lomn "help queue" queue:help:msgm
proc queue:help:msgm { n u h t } {
  if {[matchattr $h bkZ]} { return 0 }
  lappend o "queue: usage queue \[<chan>\] on|off|next|done|auto|status|list|help"
  if {[string equal [info procs cnotice] ""]} { foreach l $o { puthelp "NOTICE $n :$l" }
  } else { foreach l $o { cnotice $n $l puthelp "queue: " } }
  putcmdlog "($n!$u) !$h! help queue"
  return 1
}


####################################################
# queue:pubm
####################################################
bind pubm lomn|lomn "% ${botnet-nick} queue" queue:pubm
bind pubm lomn|lomn "% ${botnet-nick} queue *" queue:pubm
proc queue:pubm { n u h c t } {
  if {[matchattr $h bkZ]} { return 0 }
  if {![validchan $c]} { return 0 }
  set t [lrange [split $t] 2 end]; set d [string tolower [lindex $t 0]]
  if {[string equal [lsearch -exact "on off next done auto status list help" $d] "-1"]} {
    set d [string tolower [lindex $t 1]]
    if {[string equal $d ""]} { set d [string tolower [lindex $t 0]] }
  }
  if {[string equal $d "on"]} { set o [queue:on $h $t $c]
  } elseif {[string equal $d "off"]} { set o [queue:off $h $t $c]
  } elseif {[string equal $d "next"]} { set o [queue:next $h $t $c]
  } elseif {[string equal $d "done"]} { set o [queue:done $h $t $c]
  } elseif {[string equal $d "auto"]} { set o [queue:auto $h $t $c]
  } elseif {[string equal $d "status"]} { set o [queue:status $h $t $c]
  } elseif {[string equal $d "list"]} { set o [queue:list $h $t $c]
  } elseif {[string equal $d "help"]} { set o [queue:help $t]
  } else {
    if {![string equal $d ""]} { lappend o "queue: unknown subcommand $d" }
    lappend o "queue: usage queue \[<chan>\] on|off|next|done|auto|status|list|help"
    lappend o "queue: the current (console) channel is used, unless one is specified."
  }
  if {[string equal [info procs cnotice] ""]} { foreach l $o { puthelp "NOTICE $n :$l" }
  } else { foreach l $o { cnotice $n $l puthelp "queue: " } }
  putloglev c $c "queue: $n $u $h $c [join $t]"
  return 1
}


####################################################
# queue:next:pubm
####################################################
bind pubm lomn|lomn "% -next" queue:next:pubm
bind pubm lomn|lomn "% -next *" queue:next:pubm
proc queue:next:pubm { n u h c t } {
  if {[matchattr $h bkZ]} { return 0 }
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  set t [join [lrange [split $t] 1 end]]
  queue:pubm $n $u $h $c "${::botnet-nick} queue next $t"
  return 1
}


####################################################
# queue:done:pubm
####################################################
bind pubm lomn|lomn "% -done" queue:done:pubm
bind pubm lomn|lomn "% -done *" queue:done:pubm
proc queue:done:pubm { n u h c t } {
  if {[matchattr $h bkZ]} { return 0 }
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  set t [join [lrange [split $t] 1 end]]
  queue:pubm $n $u $h $c "${::botnet-nick} queue done $t"
  return 1
}


####################################################
# queue:msg
####################################################
bind msg lomn|lomn queue queue:msg
proc queue:msg { n u h t } {
  if {[matchattr $h bkZ]} { return 0 }
  set t [split $t]; set d [string tolower [lindex $t 0]]
  if {[string equal [lsearch -exact "on off next done auto status list help" $d] "-1"]} {
    set d [string tolower [lindex $t 1]]
    if {[string equal $d ""]} { set d [string tolower [lindex $t 0]] }
  }
  if {[string equal $d "on"]} { set o [queue:on $h $t]
  } elseif {[string equal $d "off"]} { set o [queue:off $h $t]
  } elseif {[string equal $d "next"]} { set o [queue:next $h $t]
  } elseif {[string equal $d "done"]} { set o [queue:done $h $t]
  } elseif {[string equal $d "auto"]} { set o [queue:auto $h $t]
  } elseif {[string equal $d "status"]} { set o [queue:status $h $t]
  } elseif {[string equal $d "list"]} { set o [queue:list $h $t]
  } elseif {[string equal $d "help"]} { set o [queue:help $t]
  } else {
    if {![string equal $d ""]} { lappend o "queue: unknown subcommand $d" }
    lappend o "queue: usage queue \[<chan>\] on|off|next|done|auto|status|list|help"
    lappend o "queue: the current (console) channel is used, unless one is specified."
  }
  if {[string equal [info procs cnotice] ""]} { foreach l $o { puthelp "NOTICE $n :$l" }
  } else { foreach l $o { cnotice $n $l puthelp "queue: " } }
  return 1
}


####################################################
# queue:dcc
####################################################
bind dcc -|- queue queue:dcc
proc queue:dcc { h i t } {
  if {![valididx $i]} { return 0 }
  set c [lindex [split [console $i]] 0]
  if {![validchan $c]} { set c "" }
  set t [split $t]; set d [string tolower [lindex $t 0]]
  if {[string equal [lsearch -exact "on off next done auto status list help" $d] "-1"]} {
    set d [string tolower [lindex $t 1]]
    if {[string equal $d ""]} { set d [string tolower [lindex $t 0]] }
  }
  if {[string equal $d "on"]} { set o [queue:on $h $t $c]
  } elseif {[string equal $d "off"]} { set o [queue:off $h $t $c]
  } elseif {[string equal $d "next"]} { set o [queue:next $h $t $c]
  } elseif {[string equal $d "done"]} { set o [queue:done $h $t $c ]
  } elseif {[string equal $d "auto"]} { set o [queue:auto $h $t $c]
  } elseif {[string equal $d "status"]} { set o [queue:status $h $t $c]
  } elseif {[string equal $d "list"]} { set o [queue:list $h $t $c]
  } elseif {[string equal $d "help"]} { set o [queue:help $t]
  } else {
    if {![string equal $d ""]} { lappend o "queue: unknown subcommand $d" }
    lappend o "queue: usage queue \[<chan>\] on|off|next|done|auto|status|list|help"
    lappend o "queue: the current (console) channel is used, unless one is specified."
  }
  foreach l $o { putidx $i $l }
  return 1
}


####################################################
# queue:next:dcc
####################################################
bind dcc -|- next queue:next:dcc
proc queue:next:dcc { h i t } {
  if {![valididx $i]} { return 0 }
  set c [lindex [split [console $i]] 0]; set o ""
  if {![validchan $c]} {
    lappend o "next: invalid console channel"
  } elseif {![channel get $c queue]} {
    lappend o "next: queue is not active on $c"
  } else { queue:dcc $h $i "next $t" }
  foreach l $o { putidx $i $l }
  return 1
}


####################################################
# queue:done:dcc
####################################################
bind dcc -|- done queue:done:dcc
proc queue:done:dcc { h i t } {
  if {![valididx $i]} { return 0 }
  set c [lindex [split [console $i]] 0]; set o ""
  if {![validchan $c]} {
    lappend o "done: invalid console channel"
  } elseif {![channel get $c queue]} {
    lappend o "done: queue is not active on $c"
  } else { queue:dcc $h $i "done $t" }
  foreach l $o { putidx $i $l }
  return 1
}


####################################################
# queue:on
####################################################
# <hand> "[<chan>] on [<N>]" [<chan>]
proc queue:on { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]
  if {![string equal -nocase $d "on"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {[channel get $c queue]} {
    lappend o "queue: queue already active on $c"
  } elseif {![botisop $c] && ![botishalfop $c]} {
    lappend o "queue: I am not opped on $c"
  } else {
    channel set $c +queue; set x [lindex $t 0]; set m [lindex [split [getchanmode $c]] 0]
    set d [string tolower $c]; global queuedb; set queuedb($d,q) ""; set queuedb($d,v) ""
    if {![string match *m* $m]} { pushmode $c +m }
    foreach n [chanlist $c] {
      if {![onchan $n $c] || [onchansplit $n $c] || [isbotnick $n]} { continue }
      if {[isop $n $c] || [ishalfop $n $c] || [isvoice $n $c]} { continue }
      if {[matchattr [nick2hand $n $c] kqQfvlomnb|kqQfvlomn $c]} { continue }
      if {[matchban $n![getchanhost $n $c] $c]} { continue }
      set n [string tolower $n]; lappend queuedb($d,q) $n
    }
    if {[string is digit $x] && $x > 1} {
      if {$x > 12} { set x 12 }
      channel set $c queue-auto $x; queue:autonext $c
    } else { set x 0; channel set $c queue-auto $x }
    if {![string equal [info procs ircconsole] ""]} {
      ircconsole $c q QUEUE "$h turns on user queue ([llength $queuedb($c,q)] users in queue, auto queue $x users)"
    }
    lappend o "queue: placed [llength $queuedb($c,q)] users in queue, auto queue $x users on $c"
  }
  return $o
}


####################################################
# queue:off
####################################################
# <hand> "[<chan>] off" [<chan>]
proc queue:off { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]
  if {![string equal -nocase $d "off"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {![channel get $c queue]} {
    lappend o "queue: queue not active on $c"
  } else {
    queue:offqueue $c
    if {![string equal [info procs ircconsole] ""]} {
      ircconsole $c q QUEUE "$h turns off user queue"
    }
    lappend o "queue: queue disabled on $c"
  }
  return $o
}


####################################################
# queue:next
####################################################
# <hand> "[<chan>] next N|nick [<nick> .. <nick>]" [<chan>]
proc queue:next { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]; set o ""
  if {![string equal -nocase $d "next"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {![channel get $c queue]} {
    lappend o "queue: queue not active on $c"
  } elseif {![botisop $c] && ![botishalfop $c]} {
    lappend o "queue: I am not opped on $c"
  } else {
    set d [string tolower $c]; set x [lindex $t 0]; set z 0; global queuedb
    queue:clean $c
    if {[string equal [llength $t] 0]} { set x 1 }
    if {[string is digit $x]} {
      if {$x < 1} { set x 1 }; if {$x > 12} { set x 12 }
      set y 0
      foreach n $queuedb($d,q) {
        queue:delqueue $n $c; queue:addvoice $n $c; queue:addtext $n $c
        incr y; incr z; pushmode $c +v $n
        if {$y >= $x} { break }
      }
    } else {
      foreach n $t {
        if {![onchan $n $c] && ![string equal [info procs chase] ""]} { set n [chase $n] }
        if {[string equal $n ""]} { continue }
        if {![onchan $n $c] || [onchansplit $n $c]} { continue }
        if {[isop $n $c] || [ishalfop $n $c] || [isvoice $n $c]} { continue }
        set g [nick2hand $n $c]
        if {[matchattr $g kq|kq $c]} { continue }
        if {![matchattr $g Qfvlomn|Qfvlomn $c]} {
          queue:delqueue $n $c; queue:addvoice $n $c; queue:addtext $n $c
        }
        incr z; pushmode $c +v $n
      }
    }
    queue:autonext $c
    if {[string equal $z 0]} { lappend o "queue: found no users to voice on $c" }
  }
  return $o
}


####################################################
# queue:done
####################################################
# <hand> "[<chan>] done <nick> [<nick> .. <nick>]" [<chan>]
proc queue:done { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]; set o ""
  if {![string equal -nocase $d "done"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {![channel get $c queue]} {
    lappend o "queue: queue not active on $c"
  } elseif {![botisop $c] && ![botishalfop $c]} {
    lappend o "queue: I am not opped on $c"
  } elseif {[llength $t] < 1} {
    lappend o "queue: usage queue \[<chan>\] next <nick> \[<nick> .. <nick>\]"
  } else {
  # mask
    #if {[string match {*[?*]*} [lindex $t 0]]} {  
    #}
    set z 0
    foreach n $t {
      if {![onchan $n $c] && ![string equal [info procs chase] ""]} { set n [chase $n] }
      if {[string equal $n ""]} { continue }
      if {![onchan $n $c] || [onchansplit $n $c]} { continue }
      if {[isop $n $c] || [ishalfop $n $c] || ![isvoice $n $c]} { continue }
      if {[matchattr [nick2hand $n $c] vlomn|vlomn $c]} { continue }
      queue:delqueue $n $c; queue:deltext $n $c
      queue:delvoice $n $c; queue:autonext $c
      incr z; pushmode $c -v $n
    }
    if {[string equal $z 0]} { lappend o "queue: found no users to devoice on $c"
    } else { queue:autonext $c }
  }
  return $o
}


####################################################
# queue:auto
####################################################
# <hand> "[<chan>] auto off|<N>" [<chan>]
proc queue:auto { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]
  if {![string equal -nocase $d "auto"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  set n [string tolower [lindex $t 0]]; if {[string equal -nocase $n "off"]} { set n 0 }
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {![channel get $c queue]} {
    lappend o "queue: queue not active on $c"
  } elseif {[string equal $n ""]} {
    lappend o "queue: usage queue \[<chan>\] auto \[+-\]<N>"
  } elseif {![regexp {^[+-]?\d+$} $n]} {
    lappend o "queue: use N to set auto queue to value N, use +N or -N to change the current auto queue value, use 0 or off for N to disable auto queue"
  } else {
    if {[regexp {^[+-]\d+} $n]} {
      set n [expr [channel get $c queue-auto] $n]
      if {$n < 0} { set n 0 }
    } else { if {$n > 12} { set n 12 } }
    channel set $c queue-auto $n; queue:autonext $c
    if {$n > 1} { lappend o "queue: auto queue set to $n on $c"
    } else { lappend o "queue: auto queue disabled on $c" }    
  }
  return $o
}


####################################################
# queue:status
####################################################
# <hand> "[<chan>] status" [<chan>]
proc queue:status { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]
  if {![string equal -nocase $d "status"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {![channel get $c queue]} {
    lappend o "queue: queue not active on $c"
  } else {
    queue:clean $c
    set d [string tolower $c]; set q 0; set v 0; set t 0; global queuedb 
    if {[info exists queuedb($d,q)]} { set q [llength $queuedb($d,q)] }
    if {[info exists queuedb($d,v)]} { set v [llength $queuedb($d,v)] }
    set a [channel get $c queue-auto]
    lappend o "queue: queue active, auto queue $a users, $q users in queue, $v users being helped on $c"
  }
  return $o
}


####################################################
# queue:list
####################################################
# <hand> "[<chan>] list" [<chan>]
proc queue:list { h t {c ""} } {
  set d [lindex $t 0]; set t [lrange $t 1 end]
  if {![string equal -nocase $d "list"]} { set c $d; set t [lrange $t 1 end] }
  if {![validchan $c] && ![string equal [info procs whichchan] ""]} { set c [whichchan $h $c] }
  
  if {[string equal $c ""]} {
    set d ""; foreach c [channels] { if {[channel get $c queue]} { lappend d $c } }
    if {[string equal [llength $d] 1]} { set c [lindex $d 0] } else { set c "" }
  }
  if {![validchan $c] || ![matchattr $h lomn|lomn $c]} {
    lappend o "queue: no access or unknown channel $c"
  } elseif {![channel get $c queue]} {
    lappend o "queue: queue not active on $c"
  } else {
    set d [string tolower $c]; global queuedb
    if {![info exists queuedb($d,q)] || [llength $queuedb($d,q)] < 1} {
      lappend o "queue: no users in queue on $c"
    } else {
      queue:clean $c
      set x -1; set t [clock seconds]; set m "[expr ($t - [getchanjoin $::botnick $c]) / 60]m"
      lappend o "queue: [llength $queuedb($d,q)] users in queue on $c (listing first 25 users)"
      foreach n $queuedb($d,q) {
        incr x; set p [lsearch -exact [string tolower [chanlist $c]] $n]
        if {$p < 0} { continue }
        set n [lindex [chanlist $c] $p]; set j [getchanjoin $n $c]
        if {$j < 1} { set j $m } else { set j "[expr ($t - $j) / 60]m" }
        lappend z "[expr $x +1]. ${n}($j)"
      }
    }
    lappend o "queue: [join [lrange $z 0 24] "   "]"
  }
  return $o
}


####################################################
# queue:help
####################################################
proc queue:help { t } {
  set d [string tolower [lindex [split $t] 1]]
  if {[string equal $d "on"]} {
    lappend o "queue: usage queue \[<chan>\] on \[<N>\]"
    lappend o "queue: enables the queue, and sets the auto queue value to N (default is 0)"
  } elseif {[string equal $d "off"]} {
    lappend o "queue: usage queue \[<chan>\] off"
    lappend o "queue: disables the queue and devoice all users that should not have voice"
  } elseif {[string equal $d "next"]} {
    lappend o "queue: usage queue \[<chan>\] next \[<N>|<nick> .. <nick>\]"
    lappend o "queue: voices the next N users in the queue (default is 1), or the given nicks"
    lappend o "queue: -next can be used when the queue is active"
  } elseif {[string equal $d "done"]} {
    lappend o "queue: usage queue \[<chan>\] done \[<nick> .. <nick>\]"
    lappend o "queue: devoices the given nicks (and voices new users from the queue depending on the auto setting)"
    lappend o "queue: -done can be used when the queue is active"
  } elseif {[string equal $d "auto"]} {
    lappend o "queue: usage queue \[<chan>\] auto off|<\[+|-\]N>"
    lappend o "queue: sets the queue auto value, off, to N, or modifies the current value with -N or +N"
  } elseif {[string equal $d "status"]} {
    lappend o "queue: usage queue \[<chan>\] status"
    lappend o "queue: shows current settings and number of users in the queue"
  } elseif {[string equal $d "list"]} {
    lappend o "queue: usage queue \[<chan>\] list"
    lappend o "queue: lists users in the queue (max 25)"
  } else {
    lappend o "queue: usage help on|off|next|done|auto|status"
    lappend o "queue: shows help for the given subcommand"  
  }
  return $o
}


####################################################
# queue:join
####################################################
bind join -|- * queue:join; bind rejn -|- * queue:join
proc queue:join { n u h c } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  if {[isbotnick $n]} {
    channel set $c -queue; channel set $c queue-auto 0
    set c [string tolower $c]; global queuedb
    if {[info exists queuedb($c,q)]} { unset queuedb($c,q) }
    if {[info exists queuedb($c,v)]} { unset queuedb($c,v) }
    if {[info exists queuedb($c,t)]} { unset queuedb($c,t) }
    return 0
  }
  if {[matchattr $h kqQfvlomnb|kqQfvlomn $c]} { return 0 }
  queue:addqueue $n $c
}


####################################################
# queue:part
####################################################
bind raw -|- "PART" queue:part
proc queue:part { s r t } {
  if {![string equal $r "PART"]} { return 0 }
  set s [split $s !@]; set t [split $t]; set n [lindex $s 0]; set c [lindex $t 0]
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {[isbotnick $n]} {
    if {[info exists queuedb($c,q)]} { unset queuedb($c,q) }
    if {[info exists queuedb($c,v)]} { unset queuedb($c,v) }
    if {[info exists queuedb($c,t)]} { unset queuedb($c,t) }
    return 0
  }
  if {[validchan $c]} {
    if {![channel get $c queue]} { return 0 }
    queue:delqueue $n $c; queue:delvoice $n $c; queue:deltext $n $c
  } 
  return 0
}


####################################################
# queue:quit
####################################################
bind sign -|- * queue:quit; bind splt -|- * queue:quit
proc queue:quit { n u h c {t ""} } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  set t [string tolower $t]
  if {[string equal $t "registered"] || [string equal $t "host change"]} { return 0 }
  queue:delqueue $n $c; queue:delvoice $n $c
  queue:deltext $n $c; queue:autonext $c
}


####################################################
# queue:kick
####################################################
bind kick -|- * queue:kick
proc queue:kick { n u h c v t } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  if {[isbotnick $n]} { return 0 }
  queue:delqueue $t $c; queue:delvoice $t $c
  queue:deltext $t $c; queue:autonext $c
}


####################################################
# queue:nick
####################################################
bind nick -|- * queue:nick
proc queue:nick { n u h c m } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  if {![onchan $m $c] || [onchansplit $m $c]} { return 0 }
  if {[isop $m $c] || [ishalfop $m $c]} { return 0 }
  if {[string equal -nocase $n $m]} { return 0 }
  set c [string tolower $c]; set n [string tolower $n]
  set m [string tolower $m]; global queuedb
  if {[info exists queuedb($c,q)]} {
    set p [lsearch -exact $queuedb($c,q) $n]
    if {$p > -1} {
      if {[matchattr $h kqQfvlomnb|kqQfvlomn $c]} { set queuedb($c,q) [lreplace $queuedb($c,q) $p $p]
      } else { set queuedb($c,q) [lreplace $queuedb($c,q) $p $p $m] }
    }
  }
  if {[info exists queuedb($c,v)]} {
    set p [lsearch -exact $queuedb($c,v) $n]
    if {$p > -1} {
      if {[matchattr $h kqQfvlomnb|kqQfvlomn $c]} { set queuedb($c,v) [lreplace $queuedb($c,v) $p $p]
      } else { set queuedb($c,v) [lreplace $queuedb($c,v) $p $p $m] }
    }
  }
  if {[info exists queuedb($c,t)]} {
    set p [lsearch -exact $queuedb($c,t) $n]
    if {$p > -1} {
      if {[matchattr $h kqQfvlomnb|kqQfvlomn $c]} { set queuedb($c,t) [lreplace $queuedb($c,t) $p $p]
      } else { set queuedb($c,t) [lreplace $queuedb($c,t) $p $p $m] }
    }
  }
}


####################################################
# queue:mode
####################################################
bind mode -|- "% ?v" queue:mode; bind mode -|- "% +h" queue:mode; bind mode -|- "% +o" queue:mode
proc queue:mode { n u h c m t } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  if {[string equal $m +v]} {
    queue:delqueue $t $c
  } elseif {[string equal $m -v]} {
    queue:delvoice $t $c; queue:deltext $t $c; queue:autonext $c
  } elseif {[string match {+[ho]} $m]} {
    queue:delqueue $t $c; queue:delvoice $t $c
    queue:deltext $t $c; queue:autonext $c
  }
}


####################################################
# queue:offqueue
####################################################
proc queue:offqueue { c } {
  channel set $c -queue; channel set $c queue-auto 0
  set m [lindex [split [getchanmode $c]] 0]
  if {[string match *m* $m] && ([botisop $c] || [botishalfop $c])} { pushmode $c -m }
  set d [string tolower $c]; global queuedb
  if {[info exists queuedb($d,q)]} { unset queuedb($d,q) }
  if {[info exists queuedb($d,t)]} { unset queuedb($d,t) }
  if {[info exists queuedb($d,v)]} {
    if {[botisop $c] || [botishalfop $c]} {
      foreach n $queuedb($d,v) {
        if {![onchan $n $c] || [onchansplit $n $c]} { continue }
        if {[isop $n $c] || [ishalfop $n $c] || ![isvoice $n $c]} { continue }
        if {[matchattr [nick2hand $n $c] vlomn|vlomn $c]} { continue }
        pushmode $c -v $n
      }
    }
    unset queuedb($d,v)
  }
}


####################################################
# queue:addqueue
####################################################
proc queue:addqueue { n c } {
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {[info exists queuedb($c,q)] && ![string equal [lsearch $queuedb($c,q) $n] -1]} { return 0 }
  lappend queuedb($c,q) $n
  return 1
}


####################################################
# queue:delqueue
####################################################
proc queue:delqueue { n c } {
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {![info exists queuedb($c,q)]} { return 0 }
  set p [lsearch -exact $queuedb($c,q) $n]
  if {[string equal $p -1]} { return 0 }
  set queuedb($c,q) [lreplace $queuedb($c,q) $p $p]
  return 1
}


####################################################
# queue:addtext
####################################################
proc queue:addtext { n c } {
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {[info exists queuedb($c,t)] && ![string equal [lsearch $queuedb($c,t) $n] -1]} { return 0 }
  lappend queuedb($c,t) $n
  return 1
}


####################################################
# queue:deltext
####################################################
proc queue:deltext { n c } {
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {![info exists queuedb($c,t)]} { return 0 }
  set p [lsearch -exact $queuedb($c,t) $n]
  if {[string equal $p -1]} { return 0 }
  set queuedb($c,t) [lreplace $queuedb($c,t) $p $p]
  return 1
}


####################################################
# queue:addvoice
####################################################
proc queue:addvoice { n c } {
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {[info exists queuedb($c,v)] && ![string equal [lsearch $queuedb($c,v) $n] -1]} { return 0 }
  lappend queuedb($c,v) $n
  return 1
}


####################################################
# queue:delvoice
####################################################
proc queue:delvoice { n c } {
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {![info exists queuedb($c,v)]} { return 0 }
  set p [lsearch -exact $queuedb($c,v) $n]
  if {[string equal $p -1]} { return 0 }
  set queuedb($c,v) [lreplace $queuedb($c,v) $p $p]
  return 1
}


####################################################
# queue:autonext
####################################################
proc queue:autonext { c } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  if {![botisop $c] && ![botishalfop $c]} { return 0 }
  if {![string match *m* [lindex [split [getchanmode $c]] 0]]} { pushmode $c +m }
  global queuedb; set c [string tolower $c]; set x [channel get $c queue-auto]; set t [clock seconds]
  if {$x < 0} { return 0 }
  if {![info exists queuedb($c,v)]} { set queuedb($c,v) "" }
  if {[llength $queuedb($c,v)] >= $x} { return 0 }
  foreach n $queuedb($c,q) {
    if {[llength $queuedb($c,v)] >= $x} { break } 
    if {![onchan $n $c] || [onchansplit $n $c]} { queue:delqueue $n $c; continue }
    if {[isop $n $c] || [ishalfop $n $c] || [isvoice $n $c]} { queue:delqueue $n $c; continue }
    if {[matchattr [nick2hand $n $c] kqQfvlomnb|kqQfvlomn $c]} { queue:delqueue $n $c; continue }
    if {[expr $t - [getchanjoin $n $c]] < 30} { break }
    queue:delqueue $n $c; queue:addvoice $n $c; queue:addtext $n $c; pushmode $c +v $n
  }
  return 0
}


####################################################
# queue:dnmo
####################################################
proc queue:dnmo { n c } {
  if {![validchan $c]} { return 0 }
  if {![channel get $c queue]} { return 0 }
  if {![onchan $n $c] || [onchansplit $n $c]} { return 0 }
  set n [string tolower $n]; set c [string tolower $c]; global queuedb
  if {![info exists queuedb($c,q)]} { return 0 }
  set p [lsearch -exact $queuedb($c,q) $n]
  if {[string equal $p -1]} { return 0 }
  set queuedb($c,q) [lreplace $queuedb($c,q) $p $p]; lappend queuedb($c,q) $n
  return 1
}


####################################################
# queue:evnt
####################################################
bind evnt - init-server queue:evnt
proc queue:evnt { t } { global queuedb; if {[info exists queuedb]} { unset queuedb } }


####################################################
# queue:time
####################################################
bind time -|- "* * * * *" queue:time
proc queue:time { n h d m y } {
  set t 1; set x 0
  if {[string equal [info procs ircconsole] ""] || [expr round(fmod([clock seconds] / 60,10))] > 0} { set t 0 }
  foreach c [channels] {
    if {![channel get $c queue]} { continue }
    set x 1
    queue:clean $c
    if {[queue:idlestaff $c]} { continue }
    queue:idle $c
    queue:autonext $c
    queue:text $c
    if {!$t} { continue }
    set d [string tolower $c]; set q 0; set v 0; set t 0; global queuedb 
    if {[info exists queuedb($d,q)]} { set q [llength $queuedb($d,q)] }
    if {[info exists queuedb($d,v)]} { set v [llength $queuedb($d,v)] }
    set a [channel get $c queue-auto]
    if {$q < 1 && $v < 1} { continue }
    ircconsole $c q QUEUE "status: $q users in queue, auto queue $a users, $v users being helped"
  }
  if {$x} { utimer 30 [list queue:timer] }
}


####################################################
# queue:timer
####################################################
proc queue:timer { } {
  foreach c [channels] {
    if {![channel get $c queue]} { continue }
    queue:clean $c
    queue:autonext $c
    queue:text $c
  }
}


####################################################
# queue:clean
####################################################
proc queue:clean { c } {
  set c [string tolower $c]; global queuedb
  if {[info exists queuedb($c,q)]} {
    set x -1; set y ""
# queue
    foreach n $queuedb($c,q) {
      incr x; set h [nick2hand $n $c]
      if {![isop $n $c] && ![ishalfop $n $c] && ![isvoice $n $c] && [onchan $n $c] && ![onchansplit $n $c] && ![matchattr $h kqQfvlomnb|kqQfvlomn $c]} { continue }
      lappend y $x
    }
    set y [lsort -dictionary -decreasing $y]
    foreach n $y { set queuedb($c,q) [lreplace $queuedb($c,q) $n $n] }
  }
# voice
  if {[info exists queuedb($c,v)]} {
    set x -1; set y ""
    foreach n $queuedb($c,v) {
      incr x; set h [nick2hand $n $c]
      if {![isop $n $c] && ![ishalfop $n $c] && [isvoice $n $c] && [onchan $n $c] && ![onchansplit $n $c] && ![matchattr $h kqQfvlomnb|kqQfvlomn $c]} { continue }
      lappend y $x
    }
    set y [lsort -dictionary -decreasing $y]
    foreach n $y { set queuedb($c,v) [lreplace $queuedb($c,v) $n $n] }
  }
  return 0
}


####################################################
# queue:text
####################################################
proc queue:text { c } {
  if {![botisop $c] && ![botishalfop $c]} { return 0 }
  set c [string tolower $c]; set x ""; global queuedb
  if {![info exists queuedb($c,t)]} { return 0 }
  foreach n $queuedb($c,t) {
    if {![onchan $n $c] || [onchansplit $n $c]} { continue }
    if {[isop $n $c] || [ishalfop $n $c]} { continue }
    if {[matchattr [nick2hand $n $c] kqQfvlomnb|kqQfvlomn $c]} { continue }
    if {![string equal [info procs idle:get] ""]} {
      set i [idle:get $n $c]; if {![string equal $i -1] && $i < 30} { continue }
    } else { set i [getchanidle $n $c]; if {[string equal $i 0]} { continue } }
    set p [lsearch -exact [string tolower [chanlist $c]] $n]
    if {[string equal $p -1]} { continue }
    set n [lindex [chanlist $c] $p]; lappend x $n
  }
  unset queuedb($c,t); set x [join $x]; if {[string equal $x ""]} { return 0 }
  lappend q "$x: Please state your questions on this channel"
  if {[string equal [info procs privmsg] ""]} { foreach l $q { puthelp "PRIVMSG $c :$l" }
  } else { foreach l $q { privmsg $c $l puthelp "" } }
  return 0
}


####################################################
# queue:idle
####################################################
proc queue:idle { c } {
  global queuedb
  set x [channel get $c queue-idle]; set c [string tolower $c]
  if {$x < 1} { return 0 }
  if {$x < 10} { set x 10; channel set $c queue-idle $x }
  if {![info exists queuedb($c,v)]} { return 0 }
  if {[llength $queuedb($c,v)] < 1} { return 0 }
  foreach n $queuedb($c,v) {
    if {![string equal [info procs idle:get] ""] && [set i [idle:get $n $c]] >= 0} {
      if {[expr $i / 60] < $x} { continue }
    } elseif {[getchanidle $n $c] < $x} { continue }
    queue:delvoice $n $c; queue:deltext $n $c; queue:autonext $c; pushmode $c -v $n
  }
  return 0
}


####################################################
# queue:idlestaff
####################################################
proc queue:idlestaff { c } {
  # turn queue off after 20 minutes of no activity
  set x 20
  foreach n [chanlist $c] {
    set h [nick2hand $n $c]
    if {![matchattr $h lomn|lomn $c]} { continue }
    if {[matchattr $h b]} { continue }
    if {![string equal [info procs idle:get] ""] && [set i [idle:get $n $c]] >= 0} {
      if {[expr $i / 60] < $x} { return 0 }
    } elseif {[getchanidle $n $c] < $x} { return 0 }
  }
  queue:offqueue $c
  if {![string equal [info procs ircconsole] ""]} {
    ircconsole $c q QUEUE "staff is idle, turned user queue off"
  }
  return 1
}


set userflagdb(queue) {
  "queue.tcl: Q=exclude user from queue"
}


set scriptdb(queue) {
  "provides user queue, +queue (set with 'queue on/off', do not set by other means), queue-auto (set with 'queue auto', do not set by other means, queue-idle (devoice idle users in queue after X minutes)"
}

