#by wiebe @ QuakeNet


#queue script as used in some support channels
#this script can work on multiple channels at the same time
#the queue is based on user's join time
#every 5 minutes, if a new user joined,
#  a message is send to the channel saying "queue mode is on, have your question ready"
#done users are saved so they wont be put back into the queue
#*** this script does not kick(ban) idle users ***

#channel commands for users with either l o m n flags (global or for the channel)
#to enable the queue		-queue on
#to disable the queue		-queue off
#to auto queue			-queue auto off|N|+N|-N
#to show help			-queue help

#to voice the next user		-next
#to voice N next user(s)	-next <number>
#to voice specific user(s)	-next nick1 nick2 nick3

#to remove user(s)		-done nick1 nick2 nick3

#-next -done can be mixed	-done nick1 -next 2
#					-next -done nick1 nick2

#private commands for same users
#/msg bot queue channel ..
#/msg bot next channel ..
#/msg bot done channel ..


#######################################################
#code
#######################################################

setudef flag queue
setudef flag queue-msg
setudef int queue-auto



#help queue pub
bind pubm lomn|lomn "% ${botnet-nick} help queue" c_queue:help:pub

proc c_queue:help:pub { nick uhost handle chan text } {
  lappend output "queue commands: queue <on|off|auto|help>"
  lappend output "-next \[N|<nick1> <nick2> .. <nick6>\] (default N = 1)"
  lappend output "-done <nick1> \[<nick2> <nick3> .. <nick6>\]"
  lappend output "-done and -next commands can be mixed"
  lappend output "/msg $::botnick queue|done|next <channel> \[<params>\]"
  lappend output "example: -done <nick1> <nick2> -next 3"
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  putloglev c $chan "[lindex [split $text] 1]: $nick $uhost $handle $chan [join [lrange [split $text] 2 end]]"
}



#help queue msg
bind msgm lomn|lomn "help queue" c_queue:help:msg

proc c_queue:help:msg { nick uhost handle text } {
  lappend output "usage: queue <channel> <on|off|auto|help>"
  lappend output "usage: next <channel> \[N|<nick1> <nick2> .. <nick6>\] (default N = 1)"
  lappend output "usage: done <channel> <nick1> \[<nick2> <nick3> .. <nick6>\]"
  lappend output "done and next commands can be mixed"
  lappend output "example: done <nick1> <nick2> -next 3"
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  putcmdlog "($nick!$uhost) !$handle! $text"
}



#next pub
bind pubm lomn|lomn "% -next" queue:next
bind pubm lomn|lomn "% -next *" queue:next

proc queue:next { nick uhost handle chan text } {
  global queue
  set output ""
  if { ![validchan $chan] } { return 0 }
  set param [join [lrange [split $text] 1 end]]
  if { ![botisop $chan] && ![botishalfop $chan] } { return 0 }
  set p [lsearch -exact [split [string tolower $param]] -done]
  if { ![string equal $p -1] } {
    queue:done $nick $uhost $handle $chan [join [lrange [split $param] $p end]]
    set param [join [lrange [split $param] 0 [expr $p - 1]]]
  }
  if { [channel get $chan queue] && ![string match *m* [lindex [split [getchanmode $chan]] 0]] } {
    pushmode $chan +m
  }
  set first [lindex [split $param] 0]
  if { ![channel get $chan queue] } {
    return 0
    lappend output "The user queue is off."
  } elseif { [string equal $param ""] } {
    set users [queue:users $chan]
    set user [lindex [split $users] 0]
    if { ![string equal $user ""] } {
      if { ![info exists queue($chan,voiced)] } { set queue($chan,voiced) "" }
      if { [string equal [lsearch -exact $queue($chan,voiced) $user] -1] } {
        lappend queue($chan,voiced) $user
      }
      pushmode $chan +v $user
      puthelp "PRIVMSG $chan :$user, Please state your question."
    } else {
      lappend output "The user queue is empty."
    }
  } elseif { [string is digit $first] && $first >= 1 } {
    set param $first
    if { $param > "6" } { set param 6 }
    set param [expr $param - 1]
    set users [queue:users $chan]
    if { ![string equal $users ""] } {
      set users [lrange $users 0 $param]
      foreach user $users {
        if { ![info exists queue($chan,voiced)] } { set queue($chan,voiced) "" }
        if { [string equal [lsearch -exact $queue($chan,voiced) $user] -1] } {
          lappend queue($chan,voiced) $user
        }
        pushmode $chan +v $user
      }
      puthelp "PRIVMSG $chan :[join $users], Please state your question."
    } else {
      lappend output "The user queue is empty."
    }
  } else {
    set users ""
    foreach user [split $param] {
      if { ![onchan $user $chan] } { continue }
      if { [onchansplit $user $chan] } { continue }
      if { [isvoice $user $chan] } { continue }
      if { [ishalfop $user $chan] } { continue }
      if { [isop $user $chan] } { continue }
      if { [matchattr [nick2hand $user] bq|q $chan] } { continue }
      if { ![info exists queue($chan,voiced)] } { set queue($chan,voiced) "" }
      if { [string equal [lsearch -exact $queue($chan,voiced) $user] -1] } {
        lappend queue($chan,voiced) $user
      }
      pushmode $chan +v $user
      lappend users $user
    }
    if { ![string equal $users ""] } {
      puthelp "PRIVMSG $chan :[join $users], Please state your question"
    }
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}




#users
proc queue:users { chan } {
  global queue
  if { ![validchan $chan] } { return 0 }
  set reg ""
  if { ![info exists queue($chan,queue)] } {
    set queue($chan,queue) ""
    foreach user [chanlist $chan] {
      if { [onchansplit $user $chan] } { continue }
      if { [isvoice $user $chan] } { continue }
      if { [ishalfop $user $chan] } { continue }
      if { [isop $user $chan] } { continue }
      if { [matchattr [nick2hand $user] bqfvlomn|qfvlomn $chan] } { continue }
      set user [expr [clock seconds] - [getchanjoin $user $chan] + 1000000000].$user
      lappend reg $user
    }
    set reg [lsort -decreasing $reg]
    set users ""
    foreach user $reg {
      set user [lindex [split $user .] 1]
      lappend queue($chan,queue) $user
    }
  }
  return $queue($chan,queue)
}




#done pub
bind pubm lomn|lomn "% -done" queue:done
bind pubm lomn|lomn "% -done *" queue:done

proc queue:done { nick uhost handle chan text } {
  global queue
  set output ""
  if { ![validchan $chan] } { return 0 }
  if { ![channel get $chan queue] } { return 0 }
  set param [join [lrange [split $text] 1 end]]
  set p [lsearch -exact [split [string tolower $param]] -next]
  if { $p >= "0" } {
    queue:next $nick $uhost $handle $chan "[join [lrange [split $param] $p end]]"
    set param [join [lrange [split $param] 0 [expr $p - 1]]]
  }
  set param [join [lrange [split $text] 1 6]]
  if { ![string match *m* [lindex [split [getchanmode $chan]] 0]] } {
    pushmode $chan +m
  }
  foreach user [split $param] {
    if { ![onchan $user $chan] } { continue }
    if { [onchansplit $user $chan] } { continue }
    if { ![isvoice $user $chan] } { continue }
    if { [isop $user $chan] } { continue }
    if { [ishalfop $user $chan] } { continue }
    pushmode $chan -v $user
    if { ![info exists queue($chan,voiced)] } { continue }
    set p [lsearch -exact $queue($chan,voiced) $user]
    if { [string equal $p -1] } { continue }
    set queue($chan,voiced) [lreplace $queue($chan,voiced) $p $p]
  }
  queue:auto $chan
  if { [string equal $param ""] } {
    lappend output "-done <nick1> \[<nick2> <nick3> .. <nick6>\]"
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}




#queue pub
bind pubm lomn|lomn "% ${botnet-nick} queue" c_queue:pub
bind pubm lomn|lomn "% ${botnet-nick} queue *" c_queue:pub

proc c_queue:pub { nick uhost handle chan text } {
  set output ""
  global queue
  if { ![validchan $chan] } { return 0 }
  set param [lindex [split $text] 2]

#on
  if { [string equal -nocase $param on] } {
    if { ![channel get $chan queue] } {
      channel set $chan +queue
      if { ![string match *m* [lindex [split [getchanmode $chan]] 0]] } {
        pushmode $chan +m
      }
      if { ![isop $nick $chan] && ![ishalfop $nick $chan] && ![isvoice $nick $chan] } {
        pushmode $chan +v $nick
      }
      lappend output "The user queue has been turned on."
      if { ![string equal [queue:users $chan] ""] } {
        puthelp "PRIVMSG $chan :Channel is in queue mode. You will be voiced when a staff member is available. Please have your question ready."
      }
    } else {
      lappend output "The user queue is already on."
    }

#off
  } elseif { [string equal -nocase $param off] } {
    if { [channel get $chan queue] } {
      channel set $chan -queue
      channel set $chan queue-auto 0
      if { [string match *m* [lindex [split [getchanmode $chan]] 0]] } { pushmode $chan -m }
      if { [info exists queue($chan,voiced)] } {
        foreach user $queue($chan,voiced) {
          if { [onchansplit $user $chan] } { continue }
          if { ![isvoice $user $chan] } { continue }
          if { [isop $user $chan] } { continue }
          if { [ishalfop $user $chan] } { continue }
          if { [matchattr [nick2hand $user] fvlomnb|fvlomn $chan] } { continue }
          pushmode $chan -v $user
        }
        unset queue($chan,voiced)
      }
      if { [info exists queue($chan,queue)] } { unset queue($chan,queue) }
      if { [info exists queue($chan,join)] } { unset queue($chan,join) }
      puthelp "PRIVMSG $chan :The queue has been turned off."
    } else {
      lappend output "The user queue is already off."
    }

#help
  } elseif { [string equal -nocase $param help] } {
    lappend output "queue commands: queue <on|off|auto|help>"
    lappend output "-next \[N|<nick1> <nick2> .. <nick6>\] (default N = 1)"
    lappend output "-done <nick1> \[<nick2> <nick3> .. <nick6>\]"
    lappend output "-done and -next commands can be mixed"
    lappend output "/msg $::botnick queue|done|next <channel> \[<params>\]"
    lappend output "example: -done <nick1> <nick2> -next 3"

#auto
  } elseif { [string equal -nocase $param auto] } {
    set value [lindex [split $text] 2]
    if { ![channel get $chan queue] } {
      lappend output "The user queue is disabled."
    } elseif { [string equal -nocase $value off] } {
      if { [string equal [channel get $chan queue-auto] 0] } {
        lappend output "Auto user queue is turned off."
      } else {
        channel set $chan queue-auto 0
        lappend output "Auto user queue has been turned off."
      }
    } elseif { [string is digit $value] && ![string equal $value ""] } {
      channel set $chan queue-auto $value
      if { [string equal $value 0] } {
        lappend output "Auto user queue has been turned off."
      } else {
        lappend output "Set auto user queue to $value"
        queue:auto $chan
      }
    } elseif { [string match {[+-]} [string range $value 0 0]] } {
      set n [string range $value 1 end]
      if { ![string is digit $n] || [string equal $n 0] } {
        lappend output "usage: queue auto off|N|+N|-N"
      } elseif { [string is digit $n] } {
        set n [expr [channel get $chan queue-auto]$value]
        if { $n < "0" } { set n 0 }
        channel set $chan queue-auto $n
        if { [string equal $n 0] } {
          lappend output "Auto user queue has been turned off."
        } else {
          lappend output "Changed auto queue with $value ($n)"
          queue:auto $chan
        }
      }
    } else {
      lappend output "usage: queue auto \[+|-\]N|off"
    }

#else
  } else {
    lappend output "usage: queue on|off|auto"
  }
  if { [channel get $chan queue] && ![string match *m* [lindex [split [getchanmode $chan]] 0]] } {
    pushmode $chan +m
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}




#join
bind join -|- * queue:join
bind rejn -|- * queue:join

proc queue:join { nick uhost handle chan } {
  global queue
  if { ![validchan $chan] } { return 0 }
  if { ![channel get $chan queue] } { return 0 }
  if { [isbotnick $nick] } {
    utimer 10 [list queue:chan $chan]
    if { [info exists queue($chan,queue)] } { unset queue($chan,queue) }
    return 0
  }
  if { [matchattr $handle fvlomnb|fvlomn $chan] } { return 0 }
  set queue($chan,join) 1
  if { ([botisop $chan] || [botishalfop $chan]) && ![string match *m* [lindex [split [getchanmode $chan]] 0]] } {
    pushmode $chan +m
  }
  if { ![info exists queue($chan,queue)] } { set queue($chan,queue) "" }
  set s [lsearch -exact [string tolower $queue($chan,queue)] [string tolower $nick]]
  if { [string equal $s -1] } {
    lappend queue($chan,queue) $nick
    queue:auto $chan
  }
}



#time
bind time -|- "?5 * * * *" queue:time
bind time -|- "?0 * * * *" queue:time

proc queue:time { mi ho da mo ye } {
  if { [string equal $::botname $::botnick] } { return 0 }
  foreach chan [channels] {
    queue:chan $chan
  }
}



#chan
proc queue:chan { chan } {
  if { ![validchan $chan] } { return 0 }
  global queue
  if { [channel get $chan queue] && ([botisop $chan] || [botishalfop $chan]) } {
    if { [channel get $chan queue-msg] && [info exists queue($chan,join)] && $queue($chan,join) && ![string equal [queue:users $chan] ""] } {
      puthelp "PRIVMSG $chan :Channel is currently in queue mode. You will be voiced when a staff member is available. Please have your question ready."
    }
    set queue($chan,join) 0
    if { ![string match *m* [lindex [split [getchanmode $chan]] 0]] } {
      pushmode $chan +m
    }
    queue:auto $chan
  }
}



#mode
bind mode -|- "% +o" queue:mode
bind mode -|- "% +h" queue:mode
bind mode -|- "% ?v" queue:mode
proc queue:mode { nick uhost handle chan mode victim } {
  if { [string match {+[vho]} $mode] } {
    queue:removeq $chan $victim
  } elseif { [string equal -v $mode] } {
    queue:removev $chan $victim
  }
}



#part
bind part -|- * queue:part
proc queue:part { nick uhost handle chan {msg ""}} {
  queue:removev $chan $nick
  queue:removeq $chan $nick
}



#splt
bind splt -|- * queue:splt
proc queue:splt { nick uhost handle chan } {
  queue:removev $chan $nick
  queue:removeq $chan $nick
}



#kick
bind kick -|- * queue:kick
proc queue:kick { nick uhost handle chan target reason } {
  queue:removev $chan $target
  queue:removeq $chan $target
}



#sign
bind sign -|- * queue:sign
proc queue:sign { nick uhost handle chan reason } {
  if { [string equal -nocase $reason registered] } { return 0 }
  if { [string equal -nocase $reason "host change"] } { return 0 }
  queue:removev $chan $nick
  queue:removeq $chan $nick
}


#removeq
proc queue:removeq { chan nick } {
  global queue
  if { ![validchan $chan] } { return 0 }
  if { ![channel get $chan queue] } { return 0 }
  if { ![info exists queue($chan,queue)] } { return 0 }
  set p [lsearch -exact $queue($chan,queue) $nick]
  if { [string equal $p -1] } { return 0 }
  set queue($chan,queue) [lreplace $queue($chan,queue) $p $p]
}



#removev
proc queue:removev { chan nick } {
  global queue
  if { ![info exists queue($chan,voiced)] } { return 0 }
  set p [lsearch -exact $queue($chan,voiced) $nick]
  if { [string equal $p -1] } { return 0 }
  set queue($chan,voiced) [lreplace $queue($chan,voiced) $p $p]
}



#nick
bind nick -|- * queue:nick

proc queue:nick { nick uhost handle chan newnick } {
  global queue
  if { ![validchan $chan] } { return 0 }
  if { ![channel get $chan queue] } { return 0 }
  if { [info exists queue($chan,queue)] } {
    set p [lsearch -exact $queue($chan,queue) $nick]
    if { ![string equal $p -1] } {
      set queue($chan,queue) [lreplace $queue($chan,queue) $p $p $newnick]
    }
  }
  if { [info exists queue($chan,voiced)] } {
    set p [lsearch -exact $queue($chan,voiced) $nick]
    if { ![string equal $p -1] } {
      set queue($chan,voiced) [lreplace $queue($chan,voiced) $p $p $newnick]
    }
  }
}



#queue msg
bind msg lomn|lomn queue queue:queue:msg

proc queue:queue:msg { nick uhost handle text } {
  set chan [lindex [split $text] 0]
  set output ""
  if { [string equal $chan ""] } {
    lappend output "usage: queue <channel>"
  } elseif { ![validchan $chan] || ![matchattr $handle lomn|lomn $chan] } {
    lappend output "$chan\017 is an unknown channel or you have no access on it."
  } else {
    set text "bot queue [join [lrange [split $text] 1 end]]"
    c_queue:pub $nick $uhost $handle $chan $text
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}



#done msg
bind msg lomn|lomn done queue:done:msg

proc queue:done:msg { nick uhost handle text } {
  set chan [lindex [split $text] 0]
  set output ""
  if { ![validchan $chan] || ![matchattr $handle lomn|lomn $chan] } {
    lappend output "$chan\017 is an unknown channel or you have no access on it."
  } else {
    set text "-done [join [lrange [split $text] 1 end]]"
    queue:done $nick $uhost $handle $chan $text
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}



#next msg
bind msg lomn|lomn next queue:next:msg

proc queue:next:msg { nick uhost handle text } {
  set chan [lindex [split $text] 0]
  set output ""
  if { ![validchan $chan] || ![matchattr $handle lomn|lomn $chan] } {
    lappend output "$chan\017 is an unknown channel or you have no access on it."
  } else {
    set text "-next [join [lrange [split $text] 1 end]]"
    queue:next $nick $uhost $handle $chan $text
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
}



#auto
proc queue:auto { chan } {
  global queue
  set auto [channel get $chan queue-auto]
  if { ![info exists queue($chan,voiced)] } { set queue($chan,voiced) "" }
  set voiced [llength $queue($chan,voiced)]
  if { ![string equal $auto 0] && $voiced < $auto } {
    set auto [expr $auto - $voiced]
    set users [queue:users $chan]
    if { ![string equal $users ""] } {
      set users [lrange $users 0 [expr $auto -1]]
      foreach user $users {
        if { ![info exists queue($chan,voiced)] } { set queue($chan,voiced) "" }
        if { [string equal [lsearch -exact $queue($chan,voiced) $user] -1] } {
          lappend queue($chan,voiced) $user
        }
        pushmode $chan +v $user
      }
      puthelp "PRIVMSG $chan :[join $users], Please state your question."
      return 1
    }      
  }
  return 0
}

