| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- # qstat.tcl
- #
- # This script stores game servers in a server list file and queries their
- # status with the utility "qstat" with the server commands below.
- #
- # Usage:
- # !addserver server add server to server list
- # !delserver server remove server from server list
- # !serverlist show servers in server list
- # !refresh query status of servers in server list
- #
- # Enable for a channel with: .chanset #channel +qstat
- # Disable for a channel with: .chanset #channel -qstat
- #
- # See https://github.com/hwipl/eggdrop-scripts for the latest version and
- # additional information including the license (MIT).
- # tested versions, might run on earlier versions
- package require Tcl 8.6
- package require eggdrop 1.8.4
- namespace eval ::qstat {
- # channel flag for enabling/disabling
- setudef flag qstat
- # command names
- variable addcommand "!addserver"
- variable delcommand "!delserver"
- variable showcommand "!serverlist"
- variable refreshcommand "!refresh"
- # path to your qstat binary
- variable qstat "/usr/local/bin/qstat"
- # qstat options for querying all servers
- variable optionsall "-nh -u -default q2s"
- # qstat options for querying single server
- variable optionssingle "-nh -P -sort F -u -q2s"
- # file to store servers in and its backup file
- variable file "servers.lst"
- variable filebak "servers.lst.bak"
- }
- # read server list from server file
- proc ::qstat::fileGet {} {
- variable file
- # check is server list entries exist
- if {![file exists $file] || [file size $file] == 0} {
- return ""
- }
- # read servers from server file
- set servers ""
- if {[catch {open $file r} input]} {
- return ""
- }
- while {[gets $input line] >= 0} {
- lappend servers $line
- }
- close $input
- return $servers
- }
- # this procedure shows the saved servers:
- proc ::qstat::showServers {nick host hand chan arg} {
- # check channel flag if enabled in this channel
- if {![channel get $chan qstat]} {
- return 0
- }
- # nick must be op
- if {![isop $nick $chan]} {
- return 0
- }
- # read servers from server file
- set servers [fileGet]
- if {$servers == ""} {
- puthelp "PRIVMSG $nick :No servers in server list."
- return 0
- }
- # send each server as a separate message
- puthelp "PRIVMSG $nick :*** Server List ***:"
- set i 1
- foreach s $servers {
- puthelp "PRIVMSG $nick :($i) $s"
- incr i
- }
- return 1
- }
- # this procedure deletes saved servers:
- proc ::qstat::delServer {nick host hand chan arg} {
- variable file
- variable filebak
- # check channel flag if enabled in this channel
- if {![channel get $chan qstat]} {
- return 0
- }
- # nick must be op
- if {![isop $nick $chan]} {
- return 0
- }
- # read servers from server file
- set servers [fileGet]
- # check if argument contains a valid server number
- if {$arg == "" || $arg > [llength $servers] || $arg == 0} {
- puthelp "NOTICE $nick :Invalid server number."
- return 0
- }
- # backup server file
- file copy -force $file $filebak
- # write servers to server file, omitting deleted server
- if {[catch {open $file w} output]} {
- puthelp "NOTICE $nick :Error opening file: $output"
- putlog "match.tcl: ERROR! Error opening file: $output"
- return 0
- }
- set i 1
- foreach s $servers {
- if {$i != $arg} {
- puts $output $s
- }
- incr i
- }
- close $output
- puthelp "NOTICE $nick :Attempted to delete server number $arg."
- putlog "match.tcl: $nick@$chan attempted to delete server number $arg."
- return 1
- }
- # this procedure adds matches to the list:
- proc ::qstat::addServer {nick host hand chan arg} {
- variable file
- # check channel flag if enabled in this channel
- if {![channel get $chan qstat]} {
- return 0
- }
- # nick must be op
- if {![isop $nick $chan]} {
- return 0
- }
- # check if arg contains valid ip and port
- if { $arg == "" } {
- puthelp "NOTICE $nick :Can't add empty entry"
- return 0
- }
- # NOTE: this only checks ipv4 addresses
- set match [regexp {[\d]+.[\d]+.[\d]+.[\d]+:[\d]+} $arg matchl]
- if { $match != 1 } {
- return 0
- }
- # append server to server file
- if {[catch {open $file a} output]} {
- puthelp "NOTICE $nick :Error opening file: $output"
- putlog "match.tcl: ERROR! Error opening file: $output"
- return 0
- }
- puts $output "$arg"
- close $output
- puthelp "NOTICE $nick :Attempted to add server."
- putlog "match.tcl: $nick@$chan attempted to add a server to the list"
- return 1
- }
- # format a server info line
- proc ::qstat::formatServerLine {line servers} {
- # check if line is a server line and parse it
- set pattern {(?x)
- # server address + whitespace
- ([\d]+.[\d]+.[\d]+.[\d]+:[\d]+)[\s]+
- # players cur/max + whitespace
- ([\d]+/[\d]+)[\s]+
- # spectators cur/max + whitespace
- ([\d]+/[\d]+)[\s]+
- # map + whitespace
- ([\w]+)[\s]+
- # ping, retries + whitespace
- ([\d]+)[\s]*/[\s]*[\d]+[\s]+
- # server name
- (.+)
- }
- if {[regexp $pattern $line matchln address players spectators \
- map ping name] != 1} {
- return ""
- }
- # format the output
- set number [expr {[lsearch $servers $address] +1}]
- set fmt "%s \0030,1\00307%-21s \00315%-45s \0034%-7s \00315%-10s"
- return [format $fmt ($number) $address $name ($players) ($map)]
- }
- # format a player info line
- proc ::qstat::formatPlayerLine {line} {
- # check if line is a player line and parse it
- set pattern {(?x)
- # frags
- [\s]*(-*[\d]+)[\s]*frags
- # ping
- [\s]*([\d]+)ms
- # player name
- [\s]*(.+)
- }
- if {[regexp $pattern $line matchln playerfrags playerping \
- playername] != 1} {
- return ""
- }
- # format the output
- set p "\0030,1\00307$playername \00315(${playerfrags} frags, "
- set p "$p\00304${playerping}ms)"
- return $p
- }
- # query all servers in server list with qstat
- proc ::qstat::refreshAll {nick chan servers} {
- variable file
- variable qstat
- variable optionsall
- # run qstat and parse output
- if {[catch {open "|$qstat $optionsall -f $file" r} input]} {
- puthelp "PRIVMSG $nick :Error refreshing servers: $input"
- return 0
- }
- while {[gets $input line] >= 0} {
- # show each server line in the channel
- set result [formatServerLine $line $servers]
- if {$result != ""} {
- puthelp "PRIVMSG $chan :$result"
- }
- }
- close $input
- }
- # query a single server with qstat
- proc ::qstat::refreshSingle {nick chan servers server} {
- variable qstat
- variable optionssingle
- # run qstat and parse output
- set playerlist ""
- if {[catch {open "|$qstat $optionssingle $server" r} input]} {
- puthelp "NOTICE $nick :Error refreshing server: $input"
- return 0
- }
- while {[gets $input line] >= 0} {
- # show each server line in the channel
- set result [formatServerLine $line $servers]
- if {$result != ""} {
- puthelp "PRIVMSG $chan :$result"
- }
- # look for players and add them to player list
- set result [formatPlayerLine $line]
- if {$result != ""} {
- lappend playerlist $result
- }
- }
- close $input
- # show the player list in the channel
- if {$playerlist != ""} {
- set players [join $playerlist ", "]
- puthelp "PRIVMSG $chan :$players"
- }
- }
- # query servers with qstat
- proc ::qstat::refreshServers {nick host hand chan arg} {
- # check channel flag if enabled in this channel
- if {![channel get $chan qstat]} {
- return 0
- }
- # read servers from server file
- set servers [fileGet]
- if {$servers == ""} {
- puthelp "PRIVMSG $nick :No servers in server list."
- return 0
- }
- if {$arg == ""} {
- # no extra parameters, refresh all servers
- refreshAll $nick $chan $servers
- } else {
- # only refresh server specified in arg
- set server [lindex $servers [expr {$arg -1}]]
- if {$server == ""} {
- puthelp "PRIVMSG $nick :Server not found."
- return 0
- }
- refreshSingle $nick $chan $servers $server
- }
- }
- namespace eval ::qstat {
- bind pub - $showcommand ::qstat::showServers
- bind pub - $addcommand ::qstat::addServer
- bind pub - $delcommand ::qstat::delServer
- bind pub - $refreshcommand ::qstat::refreshServers
- putlog "Loaded qstat.tcl"
- }
|