Skip to content

Instantly share code, notes, and snippets.

@EdGruberman
Created December 10, 2020 16:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save EdGruberman/b92d15cee0e6845546a4f5850a82e7a8 to your computer and use it in GitHub Desktop.
Save EdGruberman/b92d15cee0e6845546a4f5850a82e7a8 to your computer and use it in GitHub Desktop.
mIRC TFC Server Management Script
on *:START:{ sentinel.load }
on *:EXIT:{ sentinel.unload }
on *:CONNECT:{ if ( $hget(sentinel.general,autoactivate) == 1 && $hget(sentinel.general.status,activated) != 1 ) sentinel.activate }
alias sentinel.load {
;--Load settings from ini file into hash tables
hmake sentinel.irc 5
hload -i sentinel.irc sentinel.ini irc
if ( $hget(sentinel.irc,admin) == $null ) hadd sentinel.irc admin #clan.private password
if ( $hget(sentinel.irc,tv) == $null ) hadd sentinel.irc tv #clan.tv
if ( $hget(sentinel.irc,monitor) == $null ) hadd sentinel.irc monitor #clan
hmake sentinel.general 5
hload -i sentinel.general sentinel.ini general
hadd sentinel.general version v0.5.0b - 2008/06/11 by EdGruberman
hadd sentinel.general url http://sentinel.rjump.com
if ( $hget(sentinel.general,port.type) == $null ) hadd sentinel.general port.type dynamic
if ( $hget(sentinel.general,port.log) == $null ) hadd sentinel.general port.log 49889
if ( $hget(sentinel.general,show.connects) == $null ) hadd sentinel.general show.connects 1
if ( $hget(sentinel.general,show.sayteam) == $null ) hadd sentinel.general show.sayteam 1
.fullname Sentinel Bot $hget(sentinel.general,version)
hmake sentinel.server 5
hload -i sentinel.server sentinel.ini server
if ( $hget(sentinel.server,address) == $null ) hadd sentinel.server address 127.0.0.1
if ( $hget(sentinel.server,rcon_password) == $null ) hadd sentinel.server rcon_password password
hmake sentinel.log 5
hload -i sentinel.log sentinel.ini log
hmake sentinel.teams 20
hload -i sentinel.teams sentinel.ini teams
if ( $hget(sentinel.teams,0).item == 0 ) {
hadd sentinel.teams Blue 12
hadd sentinel.teams Red 04
hadd sentinel.teams Green 03
hadd sentinel.teams Yellow 08
hadd sentinel.teams Spectator 14
hadd sentinel.teams Console 07
hadd sentinel.teams Unassigned 15
}
hmake sentinel.rules.default 5
hload -i sentinel.rules.default sentinel.ini rules.default
if ( $hget(sentinel.rules.default,players) == $null ) hadd sentinel.rules.default players $ctime Default 8
if ( $hget(sentinel.rules.default,max) == $null ) hadd sentinel.rules.default max $ctime Default 900
if ( $hget(sentinel.rules.default,min) == $null ) hadd sentinel.rules.default min $ctime Default 300
if ( $hget(sentinel.rules.default,type) == $null ) hadd sentinel.rules.default type $ctime Default ignore
if ( $hget(sentinel.rules.default,maps) == $null ) hadd sentinel.rules.default maps $ctime Default cp_badlands cp_dustbowl cp_granary cp_gravelpit cp_well ctf_2fort ctf_well pl_goldrush tc_hydro
hmake sentinel.invited 5
hload -i sentinel.invited sentinel.ini invited
;--Load the rules for any invited channels
var %i = 1
while ( %i <= $hget(sentinel.invited,0).item ) {
hmake [ sentinel.rules. $+ [ $hget(sentinel.invited,%i).item ] ] 5
hload -i [ sentinel.rules. $+ [ $hget(sentinel.invited,%i).item ] ] sentinel.ini [ rules. $+ [ $hget(sentinel.invited,%i).item ] ]
inc %i
}
}
alias sentinel.unload { sentinel.deactivate | hfree -w sentinel.* }
alias sentinel.activate {
if ( $hget(sentinel.general.status,activated) == 1 ) return
hadd -m sentinel.general.status activated 1
;--Join monitor and admin irc channels
join $hget(sentinel.irc,monitor)
if ( ($gettok($hget(sentinel.irc,monitor),2,32) != $null) ) .timersentinel.monitor.pw 1 2 mode #$gettok($hget(sentinel.irc,monitor),1,32) +k $gettok($hget(sentinel.irc,monitor),2,32)
join $hget(sentinel.irc,admin)
if ( ($gettok($hget(sentinel.irc,admin),2,32) != $null) ) .timersentinel.admin.pw 1 2 mode #$gettok($hget(sentinel.irc,admin),1,32) +k $gettok($hget(sentinel.irc,admin),2,32)
;--Join invited irc channels
var %i = 1
while ( %i <= $hget(sentinel.invited,0).item ) {
join $hget(sentinel.invited,%i).item $gettok($hget(sentinel.invited,%i).data,3,32)
inc %i
}
sentinel.server.open
}
alias sentinel.deactivate {
if ( $hget(sentinel.general.status,activated) != 1) return
hadd sentinel.general.status activated 0
sentinel.server.close
;--Depart invited channels
var %i = 1
while ( %i <= $hget(sentinel.invited,0).item ) {
part $gettok($hget(sentinel.invited,%i).item,1,32) 5Bot Deactivated
inc %i
}
;--Depart irc channels
part $gettok($hget(sentinel.irc,admin),1,32) 5Bot Deactivated
part $gettok($hget(sentinel.irc,monitor),1,32) 5Bot Deactivated
;--Clean-up yer mess!
hfree -w sentinel.server.*
hfree -w sentinel.rcon.*
timers off
}
alias sentinel.server.open {
;--Join the tv channel and then open the log socket.
hmake sentinel.server.status 5
join $hget(sentinel.irc,tv)
if ( ($gettok($hget(sentinel.irc,tv),2,32) != $null) ) .timersentinel.tv.pw 1 2 mode #$gettok($hget(sentinel.irc,tv),1,32) +k $gettok($hget(sentinel.irc,tv),2,32)
sentinel.rcon status
.timersentinel.rcon 1 3 sentinel.rcon sv_visiblemaxplayers
.timersentinel.log.open 1 3 sentinel.log.open
}
alias sentinel.server.close {
;--Close the log socket and leave the tv channel.
hfree sentinel.server.status
sentinel.log.close
if ( ($gettok($hget(sentinel.irc,tv),1,32) != $gettok($hget(sentinel.irc,admin),1,32)) $&
&& ($gettok($hget(sentinel.irc,tv),1,32) != $gettok($hget(sentinel.irc,monitor),1,32)) ) $&
part $gettok($hget(sentinel.irc,tv),1,32) 5Server Closed
else msg $gettok($hget(sentinel.irc,tv),1,32) 5Server Closed
}
alias sentinel.server.address {
var %address = $hget(sentinel.server,address)
if ( $pos(%address,:,0) == 0 ) var %address = %address $+ :27015
return $replace(%address,:,$chr(32))
}
alias sentinel.port {
var %port = $hget(sentinel.general,$1)
if ( $hget(sentinel.general,port.type) == dynamic ) return $null
else return %port
}
alias sentinel.log.open {
hadd sentinel.general.status log.status Open
sockudp -k sentinel.socket.log $sentinel.port(port.log) $sentinel.server.address
sentinel.rcon logaddress_add $ip $+ : $+ $sock(sentinel.socket.log).port
}
alias sentinel.log.close {
hadd sentinel.general.status log.status Closed
sentinel.rcon logaddress_del $ip $+ : $+ $sock(sentinel.socket.log).port
sockclose sentinel.socket.log
}
alias sentinel.log.check {
;--If log has been explicitly closed, don't re-open it!
if ( $hget(sentinel.general.status,log.status) == Closed ) return
;--If no log entry for over 5 mins, check status which will generate a log entry for the rcon.
if ( $sock(sentinel.socket.log).lr > $calc(5 * 60) ) sentinel.rcon status
;--If no log entry still in over 15 mins, attempt to reopen the log.
if ( $sock(sentinel.socket.log).lr > $calc(15 * 60) ) sentinel.log.open
}
alias sentinel.log.address {
var %address = $hget(sentinel.log,address)
if ( $pos(%address,:,0) == 0 ) var %address = %address $+ :28888
return $replace(%address,:,$chr(32))
}
alias sentinel.rcon {
var %cmd = $1-
if ( $1 == =rcon ) var %cmd = $2-
;--Save command in the rcon queue.
var %request_id = $sentinel.rcon.queue(%cmd)
;--If this is an =rcon request, associate the request_id with the response target.
if ( $1 == =rcon ) set -u10 %sentinel.rcon.response %sentinel.rcon.response %request_id
;--If the socket is already attempting to authenticate, just let it do it's thing.
if ( %sentinel.socket.rcon.status == authenticating ) return
if ( $sock(sentinel.socket.rcon).to == $null ) {
set -u15 %sentinel.socket.rcon.status authenticating
sockclose sentinel.socket.rcon
sockopen sentinel.socket.rcon $sentinel.server.address
}
else { sentinel.rcon.write %request_id }
return %request_id
}
alias sentinel.rcon.queue {
var %id = $hget(sentinel.rcon.queue,$hget(sentinel.rcon.queue,0).item).item + 1
hadd -m sentinel.rcon.queue %id $1-
return %id
}
alias sentinel.rcon.queue.check {
if ( $hget(sentinel.rcon.queue,$1) != $null ) sentinel.rcon.write $1
}
on *:SOCKOPEN:sentinel.socket.rcon:{
;--Set 15 second connection timeout.
.timersentinel.socket.rcon.timeout 1 15 sockclose sentinel.socket.rcon
;--Send the initial authentication request.
sentinel.rcon.write 0
unset %sentinel.rcon.request_id
}
alias sentinel.rcon.write {
;--Retrieve command in rcon queue.
var %request_id = $1
var %cmd = $hget(sentinel.rcon.queue,%request_id)
var %type = 2
;--If request_id is 0, this needs to be an authentication request.
if ( %request_id == 0 ) {
var %type = 3
var %cmd = $hget(sentinel.server,rcon_password)
}
var %packet_size = $calc(10 + $len(%cmd))
;--Compile packet data to be sent.
bset &packet 1 %packet_size
bset &packet 5 %request_id
bset &packet 9 %type
bset -t &packet 13 %cmd
bset &packet $calc(13 + $len(%cmd)) 0 0
;--Debug output.
if ( %sentinel.rcon.debug == 2 ) debug.binvar &packet RCON TCP OUT $chr($asc(|)) FROM: $sock(sentinel.socket.rcon).bindip $+ : $+ $sock(sentinel.socket.rcon).bindport $chr($asc(|)) TO: $sock(sentinel.socket.rcon).ip $+ : $+ $sock(sentinel.socket.rcon).port
if ( %sentinel.rcon.debug >= 1 ) {
echo -s $timestamp RCON TCP OUT $chr($asc(|)) FROM: $sock(sentinel.socket.rcon).bindip $+ : $+ $sock(sentinel.socket.rcon).bindport $chr($asc(|)) TO: $sock(sentinel.socket.rcon).ip $+ : $+ $sock(sentinel.socket.rcon).port
echo -si11 ---------- Packet Size: %packet_size -- Request ID: %request_id -- Request Type: %type
echo -si11 ---------- %cmd
}
;--Actual socket write.
sockwrite sentinel.socket.rcon &packet
[ .timersentinel.rcon.queue $+ [ %request_id ] ] 1 10 sentinel.rcon.queue.check %request_id
}
on *:SOCKREAD:sentinel.socket.rcon:{
;--Reset 15 second connection timeout.
.timersentinel.socket.rcon.timeout 1 15 sockclose sentinel.socket.rcon
sockread $sock(sentinel.socket.rcon).rq &data
if ( %sentinel.rcon.debug == 2 ) debug.binvar &data RCON TCP IN $chr($asc(|)) FROM: $sock(sentinel.socket.rcon).ip $+ : $+ $sock(sentinel.socket.rcon).port $chr($asc(|)) TO: $sock(sentinel.socket.rcon).bindip $+ : $+ $sock(sentinel.socket.rcon).bindport
;--Might be more than one packet of data. Seperate each packet by finding the double null at the end of the packet.
var %i = 1
while (%i <= $bvar(&data,0)) {
var %eop = $bfind(&data,$calc(%i + 12),0) + 1
if ( %eop == 1 ) var %eop = $bvar(&data,0)
bcopy -c &packet 1 &data %i $calc(%eop - %i + 1)
sentinel.rcon.parsePacket &packet
var %i = %eop + 1
}
}
alias sentinel.rcon.parsePacket {
var %packet_size = $calc($bvar($1,1) + $bvar($1, 2) * 256 + $bvar($1, 3) * (256 ^ 2) + $bvar($1, 4) * (256 ^ 3))
var %request_id = $calc($bvar($1,5) + $bvar($1, 6) * 256 + $bvar($1, 7) * (256 ^ 2) + $bvar($1, 8) * (256 ^ 3))
var %type = $calc($bvar($1,9) + $bvar($1,10) * 256 + $bvar($1,11) * (256 ^ 2) + $bvar($1,12) * (256 ^ 3))
;--Parse strings seperated by linefeeds into a hash table.
var %i = 13
var %j = 1
while (%i <= $calc($bvar($1,0) - 2)) {
var %len = $calc($bfind($1,%i,10) - %i)
if ( %len < 0 ) var %len = $calc($bvar($1,0) - %i - 2)
if ( %len >= 0 ) {
hadd -m sentinel.rcon.string1 %j $bvar($1,%i,%len).text
inc %j
}
var %i = $calc(%i + %len + 1)
}
;--Debug output
if ( %sentinel.rcon.debug >= 1 ) {
echo -s $timestamp RCON TCP IN $chr($asc(|)) FROM: $sock(sentinel.socket.rcon).ip $+ : $+ $sock(sentinel.socket.rcon).port $chr($asc(|)) TO: $sock(sentinel.socket.rcon).bindip $+ : $+ $sock(sentinel.socket.rcon).bindport
echo -si11 ---------- Packet Size: %packet_size -- Request ID: %request_id -- Response Type: %type
var %i = 1
while ( %i <= $hget(sentinel.rcon.string1,0).item ) {
echo -si11 ---------- String 1: $hget(sentinel.rcon.string1,%i)
inc %i
}
}
if ( %type == 2 ) {
;--SERVERDATA_AUTH_RESPONSE
;--If a request_id is 0xFFFFFFFF(4294967295) or not matching any request_id we've sent, then it's an error.
;--The exception is that when we first open or close and reopen the rcon socket, we send the authentication as request_id = 0.
if ( (%request_id != 4294967295) && (($hget(sentinel.rcon.queue,%request_id) != $null) || (%request_id == 0)) ) {
unset %sentinel.socket.rcon.status
;--Send every command that built up in the rcon queue while authenticating now.
;--As the queue may start emptying before we finish stepping through it, save out a list of request ids first.
var %i = 1
while ( %i <= $hget(sentinel.rcon.queue,0).item ) {
var %id_list = %id_list $hget(sentinel.rcon.queue,%i).item
inc %i
}
var %i = 1
while ( %i <= $numtok(%id_list,32) ) {
sentinel.rcon.write $gettok(%id_list,%i,32)
inc %i
}
}
else { echo 4 -s RCON Authentication Error: %request_id }
}
elseif ( %type == 0 ) {
;--SERVERDATA_RESPONSE_VALUE
if ( %request_id != 0 ) {
;--Not an authentication request response. So, remove request from the rcon queue and parse the data.
hdel sentinel.rcon.queue %request_id
if ( $hget(sentinel.rcon.string1,0).item > 0 ) sentinel.rcon.parse %request_id
}
}
else echo 4 -s Unrecognized RCON Response Type: %type
hfree -w sentinel.rcon.string1
}
alias sentinel.rcon.parse {
;--Here is where we do meaningful manipulation of the data in the packets we got.
var %lines = $hget(sentinel.rcon.string1,0).item
;--If this is the response to an =rcon command, then show the results back
if ( $1 == $gettok(%sentinel.rcon.response,2,32) ) {
var %i = 1
while ( %i <= %lines ) {
;--Frequently rcon responses end with two linefeeds resulting in an empty line following the response.
;--No reason to relay an empty line at the end in IRC.
if ( (%i == $hget(sentinel.rcon.string1,0).item) && ($hget(sentinel.rcon.string1,%i) == $null) ) { }
else msg $gettok(%sentinel.rcon.response,1,32) RCON: $hget(sentinel.rcon.string1,%i)
inc %i
}
unset %sentinel.rcon.response
}
;--"status" response; Example:
;hostname: EdGruberman's Test Server
;version : 1.0.3.1/14 3504 secure
;udp/ip : 192.168.1.103:27015
;map : cp_roswell at: 0 x, 0 y, 0 z
;players : 2 (24 max)
;
;# userid name uniqueid connected ping loss state adr
;# 1 "EdGruberman" STEAM_0:0:204432 54:06 69 0 active 192.168.1.11:27005
;# 2 "Player" BOT active
if ( $gettok($hget(sentinel.rcon.string1,1),1,32) == hostname: ) {
;hfree -w sentinel.server.status
hadd -m sentinel.server.status hostname $gettok($hget(sentinel.rcon.string1,1),2-,32)
hadd -m sentinel.server.status map $gettok($hget(sentinel.rcon.string1,4),3,32)
hadd -m sentinel.server.status player.max $mid($gettok($hget(sentinel.rcon.string1,5),4,32),2)
;--Verify TV channel topic
var %tv = $gettok($hget(sentinel.irc,tv),1,32)
if ( $chan(%tv).topic != MAP:  $+ $hget(sentinel.server.status,map) $+  on $hget(sentinel.server.status,hostname) at $hget(sentinel.server,address) ) $&
topic %tv MAP:  $+ $hget(sentinel.server.status,map) $+  on $hget(sentinel.server.status,hostname) at $hget(sentinel.server,address)
;--Clean out/reset old player information
hfree -w sentinel.rcon.status
hadd -m sentinel.server.status player.count 0
hadd -m sentinel.server.status bot.count 0
;--Loop through player list
var %i = 8
while ( %i <= %lines ) {
var %line = $hget(sentinel.rcon.string1,%i)
;--Example: # 1 "EdGruberman" STEAM:0:1:204432 25:09 19 0 active 192.168.1.11:27005
;--Example: #68 "Player" BOT active
var %p_name = $gettok(%line,2,$asc("))
var %p_stats = $gettok(%line,3,$asc("))
var %p_uniqueid = $gettok(%p_stats,1,32)
var %p_userid = $mid($gettok(%line,1,$asc(")),2)
if ( %p_uniqueid == BOT ) hinc -m sentinel.server.status bot.count
elseif ( $left(%p_uniqueid,4) == hltv ) hinc -m sentinel.server.status hltv.count
else {
;--.rcon.status = ID1 time2 ping3 loss4 adr5 state6 name7-
hinc -m sentinel.server.status player.count
hadd -m sentinel.rcon.status %p_userid %p_uniqueid $gettok(%p_stats,2-4,32) $gettok(%p_stats,6,32) $gettok(%p_stats,5,32) %p_name
}
inc %i
}
;--Since we've just processed the current status, might as well check to see if anyone wants to be updated
sentinel.notify
}
;--Server variable response; Example: "maxplayers" is "12"
elseif ( $left($hget(sentinel.rcon.string1,1),1) == " ) {
tokenize $asc(") $hget(sentinel.rcon.string1,1)
hadd -m sentinel.server.status $1 $3
}
;--server_game_time response; Example: Server game time: 499.919983
elseif ( $gettok($hget(sentinel.rcon.string1,1),1-3,32) == Server game time: ) {
tokenize 32 $hget(sentinel.rcon.string1,1)
hadd -m sentinel.server.status server_game_time $gettok($4,1,$asc(.))
}
;--logaddress_add response; Example: logaddress_add: 1.2.3.4:49889
elseif ( $gettok($hget(sentinel.rcon.string1,1),1,32) == logaddress_add: ) {
if ( $gettok($hget(sentinel.rcon.string1,1),2,32) == $ip $+ : $+ $sock(sentinel.socket.log).port ) $&
msg $gettok($hget(sentinel.irc,tv),1,32) 5Server Opened
}
else { }
}
on *:UDPREAD:sentinel.socket.log:{
;--Only accept data from our designated server.
if ( $sock(sentinel.socket.log).saddr != $gettok($sentinel.server.address,1,32) ) return
sockread $sock(sentinel.socket.log).rq &packet
if ($sockbr == 0) return
if (%sentinel.log.debug == 2) debug.binvar &packet LOG UDP IN $chr($asc(|)) FROM: $sock(sentinel.socket.log).saddr $+ : $+ $sock(sentinel.socket.log).sport $chr($asc(|)) TO: $sock(sentinel.socket.log).ip $+ : $+ $sock(sentinel.socket.log).port
sentinel.log.parsePacket &packet
}
alias sentinel.log.parsePacket {
var %line = $bvar($1,1,$bvar($1,0)).text
if ( %sentinel.log.debug >= 1 ) {
echo -s $timestamp LOG UDP IN $chr($asc(|)) FROM: $sock(sentinel.socket.log).saddr $+ : $+ $sock(sentinel.socket.log).sport $chr($asc(|)) TO: $sock(sentinel.socket.log).ip $+ : $+ $sock(sentinel.socket.log).port
echo -si11 ---------- %line
}
;--Strip the header and the last character which is always a linefeed.
if ( $left(%line,6) == ÿÿÿÿRL ) sentinel.log.parse $left($mid(%line,6),$calc($len($mid(%line,6)) - 1))
}
alias sentinel.log.parse {
var %tv = $gettok($hget(sentinel.irc,tv),1,32)
var %monitor = $gettok($hget(sentinel.irc,monitor),1,32)
var %admin = $gettok($hget(sentinel.irc,admin),1,32)
;--Reset 1min timer to check if still open
.timersentinel.log.check -o 0 60 sentinel.log.check
;--Record to log file if desired
if ( $hget(sentinel.log,file) != $null ) { write $shortfn($hget(sentinel.log,file)) $+ $replace($hget(sentinel.server,address),:,.) $+ .log $1- }
;--Example: L 07/09/2003 - 23:34:02: Kick: "EdGruberman<5><204432><>" was kicked by "Console" (message "spawn camping")
;--Example: L 07/09/2003 - 23:34:02: Kick: "EdGruberman<6><204432><>" was kicked by "Console"
if ( $5 == Kick: ) {
tokenize $asc(") $1-
msg %tv * $sentinel.format.player($2) was kicked. $iif($len($6) > 0,Reason: 07 $+ $6)
}
;--Example: L 07/09/2003 - 22:49:21: Started map "avanti" (CRC "-2138426516")
elseif ( $5-6 == Started map ) {
set -u120 %sentinel.status mapchange
sentinel.rcon status
;--We know all players start out as Unassigned at a map change.
var %i = 1
while ( %i <= $hget(sentinel.rcon.status,0).item ) {
hadd -m sentinel.players Unassigned $gettok($hget(sentinel.players,%i).data,2,32)
inc %i
}
tokenize $asc(") $1-
msg %tv ¯¯¯¯¯¯¯¯¯¯ Map changed to " $+ $2 $+ " ¯¯¯¯¯¯¯¯¯¯
topic %tv MAP:  $+ $2 $+  on $hget(sentinel.server.status,hostname) at $hget(sentinel.server,address)
}
;--Example: L 07/09/2003 - 23:21:40: Team "Blue" scored "0" with "1" player (kills "0") (kills_unaccounted "0")
elseif ( ($5 = Team) && ($7 == scored) ) {
tokenize $asc(") $1-
msg %tv ===== $sentinel.format.team($2) scored $4 $+ pts with $6 players on $hget(sentinel.server.status,map) $+ . =====
}
;--Example: L 06/15/2008 - 21:01:22: Team "Blue" triggered "pointcaptured" (cp "0") (cpname "Courtyard") (numcappers "4") (player1 "^N^BlueInGreen<53><STEAM_0:0:17012120><Blue>") (position1 "921 2765 -152") (player2 "BladeGamer[cnlm]<62><STEAM_0:1:17491723><Blue>") (position2 "886 2866 -152") (player3 "Swashbuckler<94><STEAM_0:1:5199409><Blue>") (position3 "892 2745 -152") (player4 "ScorpioZ<108><STEAM_0:1:7492850><Blue>") (position4 "866 2812 -152")
elseif ( ($5 = Team) && ($7 == triggered) ) {
tokenize $asc(") $1-
if ( $4 == pointcaptured ) msg %tv ===== $sentinel.format.team($2) captured $8 $+  with $10 cappers on $hget(sentinel.server.status,map) $+ . =====
}
;--Example: L 06/18/2008 - 10:59:42: server_message: "quit"
elseif ( $5-6 == server_message: "quit" ) {
msg %tv Server shutdown.
}
;--Player event... parse type of event
elseif ( $left($5,1) == " ) {
var %playerinfo = $mid($1-,27,$calc($pos($1-,",2) - 27))
var %team = $replace($mid($gettok(%playerinfo,-1,$asc(>)),2),$chr(32),_)
var %player = $left(%playerinfo,$calc($pos(%playerinfo,<,$calc($pos(%playerinfo,<,0) - 2)) - 1))
var %userid_tok = $gettok(%playerinfo,$calc($numtok(%playerinfo,$asc(<)) - 2),$asc(<))
var %userid = $left(%userid_tok,$calc($len(%userid_tok) - 1))
;--Ensure players hash is up to date on team status for player.
hadd -m sentinel.players %userid %team $gettok($hget(sentinel.players,%userid),2,32)
var %event_start = $calc($pos($1-,",2) + 2)
var %event_end = $calc($pos($1-,",3) - 1)
if ( %event_end == -1 ) { var %event_end = $calc($len($1-) + 1) }
var %event = $mid($1-,%event_start,$calc(%event_end - %event_start))
;--Example: L 06/14/2008 - 16:14:04: "CYBER<69><STEAM_0:1:1485814><Red>" say_team "sry"
;--Example: L 06/14/2008 - 00:28:01: "Console<0><Console><Console>" say "#sentinel.tv | EdGruberman : hi"
if ( %event == say || ( (%event == say_team) && ($hget(sentinel.general,show.sayteam) == 1) ) ) {
var %text_start = $pos($1-,",3) + 1
var %text_end = $len($1-)
var %text = $mid($1-,%text_start,$calc(%text_end - %text_start))
var %admin = $mid(%text,$pos(%text,admin,1),5)
if ( %event == say_team ) {
if ( %team != Spectator ) var %playerinfo = $chr(40) $+ TEAM $+ $chr(41) %playerinfo
else var %playerinfo = $chr(40) $+ Spectator $+ $chr(41) %playerinfo
}
if ( (%event == say) && (%team == Spectator) ) { var %playerinfo = *SPEC* %playerinfo }
if ( (%team == Unassigned) && (%event == say) ) var %playerinfo = *DEAD* %playerinfo
if ( (%team == Unassigned) && (%event == say_team) ) var %playerinfo = *DEAD* $+ %playerinfo
var %message = $sentinel.format.player(%playerinfo) : $replace(%text,admin, $+ %admin $+ )
msg %tv %message
if ( admin isin %text ) { msg %monitor 07 $+ %tv $chr($asc(|)) %message }
}
;--Example: L 07/09/2003 - 10:17:54: "EdGruberman<1><204432><>" connected, address "1.2.3.4:27005"
elseif ( %event == connected, address ) {
if ( $hget(sentinel.general,show.connects) == 1 ) {
tokenize $asc(") $1-
msg %tv * $sentinel.format.player(%playerinfo) $+ < $+ $mid($gettok(%playerinfo,-2,$asc(>)),2) $+ > is trying to connect $&
$iif($hget(sentinel.general,show.ip) == 1,from $4,)
}
}
;--Example: L 07/09/2003 - 23:07:42: "EdGruberman<2><204432><>" entered the game
elseif ( %event == entered the game ) {
if ( ($hget(sentinel.general,show.connects) == 1) && (%sentinel.status != mapchange) ) {
msg %tv * $sentinel.format.player(%playerinfo) $+ < $+ $mid($gettok(%playerinfo,-2,$asc(>)),2) $+ > has entered the game.
sentinel.rcon status
}
else {
.timersentinel.enteredthegame 1 15 sentinel.rcon status
.timersentinel.players 1 30 sentinel.players %tv
}
}
;--Example: L 06/15/2008 - 22:01:51: "EdGruberman<111><STEAM_0:0:204432><Blue>" disconnected (reason "Disconnect by user.")
elseif ( %event == disconnected $chr(40) $+ reason ) {
if ( $hget(sentinel.general,show.connects) == 1 ) {
tokenize $asc(") $1-
var %reason = $4
if ($right(%reason,1) = $chr(10)) var %reason = $mid(%reason,1,$calc($len(%reason) - 1))
msg %tv * $sentinel.format.player(%playerinfo) $+ < $+ $mid($gettok(%playerinfo,-2,$asc(>)),2) $+ > disconnected. $chr(40) $+ %reason $+ $chr(41)
}
sentinel.rcon status
}
;--Example: L 07/09/2003 - 23:21:54: "EdGruberman<3><204432><Blue>" changed name to "Luka"
elseif ( %event == changed name to ) {
tokenize $asc(") $1-
msg %tv * $sentinel.format.player(%playerinfo) changed name to " $+ $4 $+ ".
}
;--Example: L 07/09/2003 - 23:35:39: "EdGruberman<6><204432><SPECTATOR>" joined team "Blue"
elseif ( %event == joined team ) {
tokenize $asc(") $1-
hadd -m sentinel.players %userid $4 $gettok($hget(sentinel.players,%userid),2,32)
}
else { }
}
else { }
}
alias sentinel.format.weapon {
;--Returns an irc formatted, ascii art representation of a weapon
if ( $1 == axe ) { return 14.04/14` }
elseif ( $1 == caltrop ) { return 15¸ }
elseif ( $1 == medikit ) { return 15,00[04,00+15,00] }
elseif ( $1 == spanner ) { return 14©==C }
else { return $null }
}
alias sentinel.format.player {
;--Returns the player name passed to it colored according to it's team as per the ini
;--Color is left open, must terminate in calling function if so desired
;--Example: EdGruberman<4><204432><#Dustbowl_team1>
;--Example: Console<0><Console><Console>
var %team = $replace($mid($gettok($1,-1,$asc(>)),2),$chr(32),_)
var %color = $gettok($hget(sentinel.teams,%team),1,32)
var %player = $left($1-,$calc($pos($1-,<,$calc($pos($1-,<,0) - 2)) - 1))
if ( %color == $null && %team != $null ) { var %color = 01 $+ < $+ %team $+ > }
if ( %color != $null ) { var %color =  $+ %color }
return %color $+ %player
}
alias sentinel.format.team {
;--Returns the team name passed to it colored as per the ini
;--Color is left open, must terminate in calling function if so desired
var %color = $gettok($hget(sentinel.teams,$replace($1,$chr(32),_)),1,32)
var %team = $gettok($hget(sentinel.teams,$replace($1,$chr(32),_)),2,32)
if ( %team == $null ) { var %team = $1 }
if ( %color == $null ) { return " $+ %team $+ " }
else { return  $+ %color $+ %team }
}
on *:INVITE:#:{
join $chan
.timersentinel.invite.check 1 5 sentinel.invite.check $nick $chan
}
alias sentinel.invite.check {
;--If invited to the admin channel, just say there so the =activate command can be used correctly.
if ( $2 == $gettok($hget(sentinel.irc,admin),1,32) ) return
;--Force users to use /invite so the bot can record who exactly invited it
if ( $1 = ChanServ ) { msg $2 rawr! you need to use the following command to invite me: /invite $me $2 | part $2 | return }
;--If ChanServ is not a part of channel, don't stick around
if ( ChanServ !isop $2 ) { msg $2 i will only stay in a registered channel, sorry... :~( | part $2 | return }
;--Notify the monitor channel then join and introduce yerself!
msg $gettok($hget(sentinel.irc,monitor),1,32) rawr! i just got invited to $2 by $1 $+ ! i feel teh 4<3 ;)~
msg $2 rawr! i'm a Sentinel bot!
msg $1 i've just added $2 to my list of auto-joins because you invited me. all ops in $2 can use =rules to change my channel notification settings. use =rules help to get started!
;--Save invited information to ini
hadd sentinel.invited $2 $ctime $1
hsave -i sentinel.invited sentinel.ini invited
}
on *:KICK:#:{
;--For the GameSurge IRC network, after netsplits ChanServ will kick you with the reason of "Net Rider" for channel management purposes.
;--Simply rejoining the channel is acceptable. The trick is make sure we have the password or not.
if ( ( $knick == $me ) && ( $1- == Net Rider ) ) {
if ( $chan == $gettok($hget(sentinel.irc,monitor),1,32) ) { var %rejoin = $hget(sentinel.irc,monitor) }
elseif ( $chan == $gettok($hget(sentinel.irc,admin),1,32) ) { var %rejoin = $hget(sentinel.irc,admin) }
elseif ( $chan == $gettok($hget(sentinel.irc,tv),1,32) ) { var %rejoin = $hget(sentinel.irc,tv) }
else { var %rejoin = $chan }
join %rejoin
return
}
;--If this is the admin channel, just leave as expected to.
if ( $2 == $gettok($hget(sentinel.irc,admin),1,32) ) return
;--Else it looks like this really is a goodbye. :~(
if ( $knick == $me ) {
part $chan 5Sentinel Bot: Removed from service.
notice $nick thx for having me! i won't come back to $chan unless someone invites me again...
msg $gettok($hget(sentinel.irc,monitor),1,32)  $+ $nick just kicked me outta $chan ... :~( i fear they might not wub me anymore *sniff*
;--Remove this channel from the invited list
hdel sentinel.invited $chan
hsave -i sentinel.invited sentinel.ini invited
;--Remove all the rules for this channel
hfree [ sentinel.rules. $+ [ $chan ] ]
remini sentinel.ini [ rules. $+ [ $chan ] ]
}
}
on *:TEXT:=rules*:#:{
if ( $nick !isop $chan ) { [ .timersentinel.denied 1 1 notice $nick Denied: You lack operator status in $chan $+ . }
else {
if ( $2 == $null ) {
;--Get channel rules
var %players = $hget([ sentinel.rules. $+ [ $chan ] ],players)
var %maps = $hget([ sentinel.rules. $+ [ $chan ] ],maps)
var %type = $hget([ sentinel.rules. $+ [ $chan ] ],type)
var %min = $hget([ sentinel.rules. $+ [ $chan ] ],min)
var %max = $hget([ sentinel.rules. $+ [ $chan ] ],max)
;--If a rule is not defined for a channel, use the default
if ( %players == $null ) { var %players = $hget(sentinel.rules.default,players) }
if ( %maps == $null ) { var %maps = $hget(sentinel.rules.default,maps) }
if ( %type == $null ) { var %type = $hget(sentinel.rules.default,type) }
if ( %min == $null ) { var %min = $hget(sentinel.rules.default,min) }
if ( %max == $null ) { var %max = $hget(sentinel.rules.default,max) }
;--Give the user the info
notice $nick $chan $+ : Players = $gettok(%players,3,32)  $+ $chr(40) $+ Default: $gettok($hget(sentinel.rules.default,players),3,32) $+ $chr(41) $chr($asc(|)) Min number of players, bots are not counted.
notice $nick $chan $+ : Min = $gettok(%min,3,32)  $+ $chr(40) $+ Default: $gettok($hget(sentinel.rules.default,min),3,32) $+ $chr(41) $chr($asc(|)) Shortest time in seconds before bot can notify again.
notice $nick $chan $+ : Max = $gettok(%max,3,32)  $+ $chr(40) $+ Default: $gettok($hget(sentinel.rules.default,max),3,32) $+ $chr(41) $chr($asc(|)) Longest time in seconds bot will wait before notifying again.
notice $nick $chan $+ : Type = $gettok(%type,3,32)  $+ $chr(40) $+ Default: $gettok($hget(sentinel.rules.default,type),3,32) $+ $chr(41) $chr($asc(|)) If notify, Maps contains maps to notify if played. If ignore, Maps contains maps that no notifications will occur on.
notice $nick $chan $+ : Maps = $gettok(%maps,3-,32)
}
else {
;--Verify the parameter is one this function accepts
var %params = players min max forced type maps help
if ( $findtok(%params,$lower($2),0,32) == 0 ) { [ .timersentinel. $+ [ $nick ] ] 1 1 notice $nick Error: I don't understand the $2 parameter. See =rules help for more info. }
elseif ( $lower($2) == help ) {
[ .timersentinel. $+ [ $nick ] $+ 1 ] 1 1 notice $nick =rules ( View/change/reset channel notification rules for all servers with this command. )
[ .timersentinel. $+ [ $nick ] $+ 2 ] 1 1 notice $nick Syntax: =rules $chr($asc([)) $+ <rule_name> $+ $chr($asc([)) <rule_value> $+ $chr($asc(])) $+ $chr($asc(]))
[ .timersentinel. $+ [ $nick ] $+ 3 ] 1 1 notice $nick To view the channel's current rules, do not include any parameters. Example: =rules
[ .timersentinel. $+ [ $nick ] $+ 4 ] 1 1 notice $nick To edit a rule, include the <rule_name> and the <rule_value> parameters. Example: =rules min 120
[ .timersentinel. $+ [ $nick ] $+ 5 ] 1 1 notice $nick To reset a rule to default, only put the <rule_name> parameter. Example: =rules min
}
else {
;--Edit hash entry if param $3- isn't null, else del the hash entry
var %old = $gettok($hget([ sentinel.rules. $+ [ $chan ] ],$2),3-,32)
if ( %old == $null ) { var %old = $gettok($hget(sentinel.rules.default,$2),3-,32) }
if ( $3- != $null ) { hadd -m [ sentinel.rules. $+ [ $chan ] ] $2 $ctime $nick $3- }
else { hdel [ sentinel.rules. $+ [ $chan ] ] $2 }
hsave -i [ sentinel.rules. $+ [ $chan ] ] sentinel.ini [ rules. $+ [ $chan ] ]
if ( $3- != $null ) { [ .timersentinel. $+ [ $nick ] ] 1 1 notice $nick  $+ $2 rule changed for $chan $+  from " $+ %old $+ " to " $+ $3- $+ " }
else { [ .timersentinel. $+ [ $nick ] ] 1 1 notice $nick  $+ $2 rule reset to default for $chan $+  from " $+ %old $+ " to " $+ $gettok($hget(sentinel.rules.default,$2),3-,32) $+ " }
}
}
}
}
on *:TEXT:=help:*:{
var %adminop = $iif($nick isop $gettok($hget(sentinel.irc,admin),1,32),$true,$false)
var %monitorop = $iif($nick isop $gettok($hget(sentinel.irc,monitor),1,32) || $nick isop $gettok($hget(sentinel.irc,tv),1,32),$true,$false)
var %status = You $iif(%adminop,are,are not) an op in the admin channel ( $+ $gettok($hget(sentinel.irc,admin),1,32) $+ ) and you $iif(%monitorop,are,are not) an op in the monitor ( $+ $gettok($hget(sentinel.irc,monitor),1,32) $+ ) or tv ( $+ $gettok($hget(sentinel.irc,tv),1,32) $+ ) channel.
[ .timersentinel. $+ [ $nick ] $+ 1 ] 1 1 notice $nick %status $iif(%adminop || %monitorop,With your current status you can use the following commands:,)
if ( ( $nick isop $chan ) && ( $chan != $gettok($hget(sentinel.irc,tv),1,32) ) ) { [ .timersentinel. $+ [ $nick ] $+ 2 ] 1 1 notice $nick =rules help :: Adjusts channel notification criteria. :: Usable in any channel except $gettok($hget(sentinel.irc,tv),1,32) by Ops in calling channel }
if ( $nick isop $gettok($hget(sentinel.irc,monitor),1,32) || $nick isop $gettok($hget(sentinel.irc,tv),1,32)$nick isop $gettok($hget(sentinel.irc,admin),1,32) ) {
[ .timersentinel. $+ [ $nick ] $+ 3 ] 1 1 notice $nick =chat <message> :: Sends <message> into game public chat. :: Usable only in $gettok($hget(sentinel.irc,tv),1,32) by Ops in $gettok($hget(sentinel.irc,monitor),1,32) or $gettok($hget(sentinel.irc,tv),1,32)
[ .timersentinel. $+ [ $nick ] $+ 4 ] 1 1 notice $nick =status :: Displays current game status on server. :: Usable in any channel by Ops in $gettok($hget(sentinel.irc,monitor),1,32) or $gettok($hget(sentinel.irc,tv),1,32)
[ .timersentinel. $+ [ $nick ] $+ 5 ] 1 1 notice $nick =players[ <player_name>] :: Lists current player names or detailed player info if <player_name> matches a current player. :: Usable in any channel by Ops in $gettok($hget(sentinel.irc,monitor),1,32) or $gettok($hget(sentinel.irc,tv),1,32)
[ .timersentinel. $+ [ $nick ] $+ 6 ] 1 1 notice $nick =invited :: Lists channels bot has been invited into. :: Usable in any channel by Ops in $gettok($hget(sentinel.irc,monitor),1,32) or $gettok($hget(sentinel.irc,tv),1,32)
}
if ( $nick isop $gettok($hget(sentinel.irc,admin),1,32) ) {
[ .timersentinel. $+ [ $nick ] $+ 7 ] 1 1 notice $nick =close :: Closes server log/rcon interaction with bot but keeps bot available in irc to open. :: Usable in any channel by Ops in $gettok($hget(sentinel.irc,admin),1,32)
[ .timersentinel. $+ [ $nick ] $+ 8 ] 1 1 notice $nick =open :: Opens server log/rcon interaction with bot. :: Usable in any channel by Ops in $gettok($hget(sentinel.irc,admin),1,32)
if ( $hget(sentinel.general,allowrcon) == 1 ) { [ .timersentinel. $+ [ $nick ] $+ 9 ] 1 1 notice $nick =rcon <cmd> :: Issues RCON command <cmd> to server. :: Usable in any channel by Ops in $gettok($hget(sentinel.irc,admin),1,32) }
}
[ .timersentinel. $+ [ $nick ] $+ 9 ] 1 1 notice $nick Sentinel Bot $hget(sentinel.general,version) :: $hget(sentinel.general,url)
}
on *:TEXT:=rcon *:*:{
if ( $nick !isop $gettok($hget(sentinel.irc,admin),1,32) ) { .timersentinel.denied 1 1 notice $nick Denied: You lack operator status in $gettok($hget(sentinel.irc,admin),1,32) $+ . | return }
if ( $hget(sentinel.general,allowrcon) != 1 ) { .timersentinel.disabled 1 1 notice $nick Feature Disabled: RCON access not allowed. | return }
if ( %sentinel.rcon.response != $null ) { [ .timersentinel. $+ [ $nick ] ] 1 1 notice $nick RCON Temporarily Unavailable. Currently in use $iif($left(%sentinel.rcon.response,1) == $chr($asc(#)),in,by) %sentinel.rcon.response $+ . Please try again in a few seconds. }
set -u10 %sentinel.rcon.response $iif($chan != $null,$chan,$nick)
sentinel.rcon =rcon $2-
}
on *:TEXT:=chat *:#:{
if ( $chan != $gettok($hget(sentinel.irc,tv),1,32) ) return
if ( $nick !isop $gettok($hget(sentinel.irc,monitor),1,32) && $nick !isop $gettok($hget(sentinel.irc,tv),1,32) && $nick !isop $gettok($hget(sentinel.irc,admin),1,32) ) return
var %message = $strip($2-)
if ( %message == $null ) return
var %message = $chan $chr($asc(|)) $nick : %message
sentinel.rcon say %message
}
on *:TEXT:=close:*:{ if ( $nick isop $gettok($hget(sentinel.irc,admin),1,32) ) sentinel.server.close }
on *:TEXT:=open:*:{ if ( $nick isop $gettok($hget(sentinel.irc,admin),1,32) ) sentinel.server.open }
on *:TEXT:=activate:*:{ if ( $nick isop $gettok($hget(sentinel.irc,admin),1,32) ) sentinel.activate }
on *:TEXT:=deactivate:*:{ if ( $nick isop $gettok($hget(sentinel.irc,admin),1,32) ) sentinel.deactivate }
alias sentinel.servers { msg $1  $+ %i  $+ $chr(40) $+  $+ %closed $+  $+ $chr(41) $chr($asc(|)) $+ 1,15 $hget([ server $+ [ %i ] $+ .status ],hostName) $+ $chr(40) $+ $hget([ server $+ [ %i ] ],ip) $+ : $+ $hget([ server $+ [ %i ] ],port) $+ $chr(41)  $+ $chr($asc(|)) $hget([ server $+ [ %i ] ],tv)  $+ $chr($asc(|)) $+ 1,15 $hget([ server $+ [ %i ] $+ .status ],map) $+ $chr(40) $+ $hget([ server $+ [ %i ] $+ .status ],playerCount) $+ $chr($asc(/)) $+ $hget([ server $+ [ %i ] $+ .status ],playerMax) $+ $chr(41) }
on *:TEXT:=invited:*:{
echo -s chan: $chan nick: $nick
if ( $nick !isop $gettok($hget(sentinel.irc,monitor),1,32) && $nick !isop $gettok($hget(sentinel.irc,tv),1,32) && $nick !isop $gettok($hget(sentinel.irc,admin),1,32) ) { return }
[ .timersentinel.invited. $+ [ $chan ] ] 1 2 sentinel.invited $iif($chan != $null,$chan,$nick)
}
alias sentinel.invited {
if ($hget(sentinel.invited,0).item == 0) {
msg $1 Nobody has invited me. :~(
return
}
var %i = 1
while ( %i <= $hget(sentinel.invited,0).item ) {
var %invited = $hget(sentinel.invited,%i).item $hget(sentinel.invited,%i).data
msg $1 $gettok(%invited,1,32) $chr($asc(|)) $+ 1,15 $gettok(%invited,3,32)  $+ $chr($asc(|)) $asctime($gettok(%invited,2,32),m/d/yy @ h:mmtt) 
inc %i
}
}
on *:TEXT:=status:*:{
if ( $nick !isop $gettok($hget(sentinel.irc,monitor),1,32) && $nick !isop $gettok($hget(sentinel.irc,tv),1,32) && $nick !isop $gettok($hget(sentinel.irc,admin),1,32) ) { return }
if ( $timer(sentinel.status) == $null ) {
sentinel.rcon server_game_time
.timersentinel.rcon.mp_timelimit 1 1 sentinel.rcon mp_timelimit
.timersentinel.rcon.sm_nextmap 1 1 sentinel.rcon sm_nextmap
}
.timersentinel.status 1 3 sentinel.status $iif($chan != $null,$chan,$nick)
}
alias sentinel.status {
if ($hget(sentinel.server.status,sv_visiblemaxplayers) == -1) var %max = $hget(sentinel.server.status,player.max)
else var %max = $hget(sentinel.server.status,sv_visiblemaxplayers)
hadd -m sentinel.server.status playersNbots $hget(sentinel.server.status,player.count) $+ / $+ %max players
if ( $hget(sentinel.server.status,bot.count) > 0 ) { hadd -m sentinel.server.status playersNbots $hget(sentinel.server.status,playersNbots) $chr($asc(|)) + $+ $hget(sentinel.server.status,bot.count) bots }
if ( $hget(sentinel.server.status,mp_timelimit) = 0 ) var %timeleft = No Time Limit
else {
var %timeleft = $calc($hget(sentinel.server.status,mp_timelimit) * 60 - $hget(sentinel.server.status,server_game_time))
if (%timeleft < 0) var %timeleft = 0
var %timeleft = $duration(%timeleft) left
}
var %status = 14 $+ $hget(sentinel.server.status,hostname) $+  $chr($asc(|)) 5Status: $hget(sentinel.server.status,map)  ::  $hget(sentinel.server.status,playersNbots)  ::  %timeleft
if ($hget(sentinel.server.status,sm_nextmap) != $null) var %status = %status  :: Next map is " $+ $hget(sentinel.server.status,sm_nextmap) $+ "
var %status = %status  :: steam://connect/ $+ $hget(sentinel.server,address)
msg $1 %status
}
on *:TEXT:=players*:*:{
if ( $nick !isop $gettok($hget(sentinel.irc,monitor),1,32) && $nick !isop $gettok($hget(sentinel.irc,tv),1,32) && $nick !isop $gettok($hget(sentinel.irc,admin),1,32) ) { return }
.timersentinel.players 1 3 sentinel.players $iif($chan != $null,$chan,$nick) $2-
}
alias sentinel.players {
;--Remove the mapchange status at this point
unset %sentinel.status
if ( $hget(sentinel.rcon.status,0).item == 0 ) { msg $1 5Players: None :~( | return }
if ( $2 != $null ) {
var %i = 1
while ( %i <= $hget(sentinel.rcon.status,0).item ) {
if ( $gettok($hget(sentinel.rcon.status,%i).data,7-,32) == $2- ) {
tokenize 32 $1 $hget(sentinel.rcon.status,%i).data
;--sentinel.rcon.status = ID2 time3 ping4 loss5 adr6 state7 name8-
var %team = $hget(sentinel.players,$hget(sentinel.rcon.status,%i).item)
var %pinfo = $8- $+ $chr($asc(<)) $+ 0 $+ $chr($asc(>)) $+ $chr($asc(<)) $+ 0 $+ $chr($asc(>)) $+ $chr($asc(<)) $+ %team $+ $chr($asc(>))
msg $1 5Player Info: $sentinel.format.player(%pinfo) $+  $chr($asc(|)) $2 $chr($asc(|)) $4 $+ ms $+ $chr(40) $+ loss= $+ $5 $+ $chr(41) $chr($asc(|)) $3 $iif($7 != active,$chr($asc(|)) $7,)
}
inc %i
}
}
else {
var %i = 1
:nextplayer
tokenize 32 $1 $hget(sentinel.rcon.status,%i).data
;--sentinel.rcon.status = ID2 time3 ping4 loss5 adr6 state7 name8-
var %team = $hget(sentinel.players,$hget(sentinel.rcon.status,%i).item)
var %pinfo = $8- $+ $chr($asc(<)) $+ 0 $+ $chr($asc(>)) $+ $chr($asc(<)) $+ 0 $+ $chr($asc(>)) $+ $chr($asc(<)) $+ %team $+ $chr($asc(>))
var %names = %names " $+ $sentinel.format.player(%pinfo) $+  $+ "
if ( $gettok($calc(%i / 5),2,$asc(.)) == $null ) {
msg $1 5Players: %names
var %names = $null
}
inc %i
if ( $hget(sentinel.rcon.status,%i).data != $null ) {
if ( %names != $null ) var %names = %names $+ ,
goto nextplayer
}
else {
if ( %names != $null ) msg $1 5Players: %names
var %names = $null
}
}
}
alias sentinel.notify {
;--Check individual channel notification rules
var %i = -1
while ( %i <= $hget(sentinel.invited,0).item ) {
if ( %i == -1 ) { var %chan = $gettok($hget(sentinel.irc,monitor),1,32) }
elseif ( %i == 0 ) { var %chan = $gettok($hget(sentinel.irc,admin),1,32) }
else { var %chan = $hget(sentinel.invited,%i).item }
;--Setup status and channel rule variables
var %status.map = $hget(sentinel.server.status,map)
var %status.player.count = $hget(sentinel.server.status,player.count)
var %status.hostname = $hget(sentinel.server.status,hostname)
if ($hget(sentinel.server.status,sv_visiblemaxplayers) == -1) var %max = $hget(sentinel.server.status,player.max)
else var %max = $hget(sentinel.server.status,sv_visiblemaxplayers)
var %status.player.max = %max
var %rules.players = $gettok($hget([ sentinel.rules. $+ [ %chan ] ],players),3,32)
var %rules.maps = $gettok($hget([ sentinel.rules. $+ [ %chan ] ],maps),3-,32)
var %rules.type = $gettok($hget([ sentinel.rules. $+ [ %chan ] ],type),3,32)
var %rules.max = $gettok($hget([ sentinel.rules. $+ [ %chan ] ],max),3,32)
var %rules.min = $gettok($hget([ sentinel.rules. $+ [ %chan ] ],min),3,32)
var %rules.default.players = $gettok($hget(sentinel.rules.default,players),3,32)
var %rules.default.maps = $gettok($hget(sentinel.rules.default,maps),3-,32)
var %rules.default.type = $gettok($hget(sentinel.rules.default,type),3,32)
var %rules.default.max = $gettok($hget(sentinel.rules.default,max),3,32)
var %rules.default.min = $gettok($hget(sentinel.rules.default,min),3,32)
if ( %rules.players == $null ) { var %rules.players = %rules.default.players }
if ( %rules.maps == $null ) { var %rules.maps = %rules.default.maps }
if ( %rules.type == $null ) { var %rules.type = %rules.default.type }
if ( %rules.max == $null ) { var %rules.max = %rules.default.max }
if ( %rules.min == $null ) { var %rules.min = %rules.default.min }
;--Compare status to rules for channel, if a rule is broken, the loop continues on the next channel
if ( %status.player.count < %rules.players ) { inc %i | continue }
if ( %rules.type != notify ) { if ( $findtok($lower(%rules.maps),$lower(%status.map),1,32) != $null ) { inc %i | continue } }
else { if ( $findtok($lower(%rules.maps),$lower(%status.map),1,32) == $null ) { inc %i | continue } }
var %last.ctime = $hget([ sentinel.notify. $+ [ %chan ] ],last.ctime)
var %last.player.count = $hget([ sentinel.notify. $+ [ %chan ] ],last.player.count)
if ( $calc($ctime - %last.ctime) < %rules.min ) { inc %i | continue }
if ( (%status.players <= %last.player.count) && ($calc($ctime - %last.ctime) < %rules.max) ) { inc %i | continue }
;--All rules met with current status, send out the alert
var %info = 5 $+ %status.map is being played on 12 $+ %status.hostname $+  $chr(40) $hget(sentinel.server,address) $chr(41) 5with %status.player.count $+ $chr($asc(/)) $+ %status.player.max players! Join in on the carnage!!
msg %chan %info
hadd -m [ sentinel.notify. $+ [ %chan ] ] last.ctime $ctime
hadd -m [ sentinel.notify. $+ [ %chan ] ] last.player.count %status.player.count
inc %i
}
}
dialog sentinel.options {
title "Sentinel Bot Options"
size -1 -1 186 168
option dbu
tab "General", 29, 1 0 182 149
check "Automatically activate Sentinel Bot on IRC connection", 9, 6 17 144 10, tab 29
edit "", 14, 29 37 50 10, tab 29 autohs
edit "", 18, 128 37 50 10, tab 29 autohs
edit "", 15, 29 48 50 10, tab 29 autohs
edit "", 21, 128 48 50 10, tab 29 autohs
edit "", 16, 29 59 50 10, tab 29 autohs
edit "", 22, 128 59 50 10, tab 29 autohs
edit "", 4, 50 85 67 10, tab 29 autohs
edit "", 5, 50 96 67 10, tab 29 autohs
edit "", 26, 30 121 138 10, tab 29 autohs
edit "", 27, 30 132 67 10, tab 29 autohs
box "IRC Channels", 10, 4 29 178 45, tab 29
text "Admin:", 11, 7 39 22 8, tab 29 right
text "Admin Password:", 17, 81 39 47 8, tab 29 right
text "TV Password:", 19, 81 50 47 8, tab 29 right
text "TV:", 12, 7 50 22 8, tab 29 right
text "Monitor:", 13, 7 61 22 8, tab 29 right
text "Monitor Password:", 20, 81 61 47 8, tab 29 right
box "Server", 1, 4 77 178 34, tab 29
text "Address:", 2, 7 87 43 8, tab 29 right
text "RCON Password:", 3, 7 98 43 8, tab 29 right
text "Example: 1.2.3.4:27015", 8, 118 87 60 8, disable tab 29
text "File:", 24, 7 123 23 8, tab 29 right
text "Address:", 25, 7 134 23 8, tab 29 right
box "Logging", 23, 4 114 178 33, tab 29
text "Example: 1.2.3.4:28888", 28, 98 134 60 8, disable tab 29
button "...", 53, 169 122 9 8, tab 29
tab "Default Rules", 30
edit "", 43, 31 18 21 10, tab 30 autohs
edit "", 42, 31 29 21 10, tab 30 autohs
edit "", 41, 31 40 21 10, tab 30 autohs
edit "", 37, 9 63 71 10, tab 30 autohs right
list 36, 9 73 71 63, tab 30 sort size extsel
button "Add", 38, 81 63 30 10, tab 30
button "Remove", 39, 81 74 30 10, tab 30
radio "Ignore when Maps are played", 40, 89 105 82 10, tab 30
radio "Notify when Maps are played", 47, 89 116 82 10, tab 30
text "Minimum number of players, bots are not counted.", 44, 53 20 124 8, tab 30
text "Shortest time in seconds before bot notifies again.", 45, 53 31 124 8, tab 30
text "Longest time in seconds before bot notifies again.", 46, 53 42 124 8, tab 30
box "Maps", 48, 4 53 178 94, tab 30
text "Players:", 31, 6 20 25 8, tab 30 right
text "Type:", 33, 88 96 27 8, tab 30
text "Min:", 34, 6 31 25 8, tab 30 right
text "Max:", 35, 6 42 25 8, tab 30 right
text ":Selected", 32, 19 136 23 8, tab 30
text "0", 50, 7 136 12 8, tab 30 right
text ":Total", 51, 58 136 15 8, tab 30
text "0", 52, 45 136 13 8, tab 30 right
tab "Features", 300
box "TV Channel Display", 58, 4 17 178 59, tab 300
check "Player connects and disconnects", 305, 9 26 92 10, tab 300
check "Player IP and ID information upon connection", 310, 18 38 122 10, tab 300
check "Team chat (say_team / mm2)", 315, 9 50 158 10, tab 300
check "Allow RCON access to Ops in Admin channel", 325, 9 81 118 10, tab 300
tab "Advanced", 59
radio "Dynamic", 61, 9 26 33 10, tab 59
radio "Static", 62, 9 38 32 10, tab 59
box "Port Options", 68, 4 17 178 60, tab 59
text "Preferrably in the Private Port range of 49152 through 65535", 60, 72 53 78 14, disable tab 59
text "Log:", 66, 24 61 18 8, disable tab 59 right
edit "", 64, 42 60 23 10, disable tab 59 limit 5
link "v0.0.0b - YYYY/MM/DD by EdGruberman", 49, 3 156 98 8
button "OK", 6, 105 154 37 12, ok
button "Cancel", 7, 146 154 37 12, default cancel
}
on *:DIALOG:sentinel.options:sclick:53:{
var %folder = $sdir($iif($did(26) != $null,$did(26),.),Select Log File Location)
did -ra sentinel.options 26 $iif(%folder != $null,%folder,$did(26))
}
on *:DIALOG:sentinel.options:sclick:49:{ url -an $hget(sentinel.general,url) }
on *:DIALOG:sentinel.options:sclick:36:{ sentinel.options.update }
alias sentinel.options.update {
did -ra sentinel.options 50 $did(sentinel.options,36,0).sel
did -ra sentinel.options 52 $did(sentinel.options,36).lines
}
on *:DIALOG:sentinel.options:sclick:38:{
;--Add entered map to list
var %map = $replace($did(37),$chr(32),$null)
if ( %map == $null ) { return }
did -i sentinel.options 36 1 %map
did -r sentinel.options 37
sentinel.options.update
}
on *:DIALOG:sentinel.options:sclick:39:{
;--Remove selected maps from list
var %i = $did(36,0).sel
while ( %i > 0 ) {
var %sel = $did(36,%i).sel
did -ra sentinel.options 37 $did(36,%sel)
did -d sentinel.options 36 %sel
dec %i
}
sentinel.options.update
}
on *:DIALOG:sentinel.options:sclick:61:{
;--Dynamic ports selected
did -b sentinel.options 60,64,66
}
on *:DIALOG:sentinel.options:sclick:62:{
;--Static ports selected
did -e sentinel.options 60,64,66
}
on *:DIALOG:sentinel.options:sclick:305:{
;--Connects/Disconnects display toggled
if ( $did(305).state == 1 ) { did -e sentinel.options 310 }
else { did -b sentinel.options 310 }
}
on *:DIALOG:sentinel.options:init:0:{
;--General Tab
if ( $hget(sentinel.general,autoactivate) == 1 ) { did -c sentinel.options 9 }
did -ra sentinel.options 14 $gettok($hget(sentinel.irc,admin),1,32)
did -ra sentinel.options 18 $gettok($hget(sentinel.irc,admin),2,32)
did -ra sentinel.options 15 $gettok($hget(sentinel.irc,tv),1,32)
did -ra sentinel.options 21 $gettok($hget(sentinel.irc,tv),2,32)
did -ra sentinel.options 16 $gettok($hget(sentinel.irc,monitor),1,32)
did -ra sentinel.options 22 $gettok($hget(sentinel.irc,monitor),2,32)
did -ra sentinel.options 4 $hget(sentinel.server,address)
did -ra sentinel.options 5 $hget(sentinel.server,rcon_password)
did -ra sentinel.options 26 $hget(sentinel.log,file)
did -ra sentinel.options 27 $hget(sentinel.log,address)
;--Default Rules Tab
didtok sentinel.options 36 32 $gettok($hget(sentinel.rules.default,maps),3-,32)
sentinel.options.update
did -ra sentinel.options 43 $gettok($hget(sentinel.rules.default,players),3,32)
did -ra sentinel.options 42 $gettok($hget(sentinel.rules.default,min),3,32)
did -ra sentinel.options 41 $gettok($hget(sentinel.rules.default,max),3,32)
if ( $gettok($hget(sentinel.rules.default,type),3,32) == ignore ) { did -c sentinel.options 40 }
else { did -c sentinel.options 47 }
;--Features Tab
if ( $hget(sentinel.general,show.connects) == 1 ) { did -c sentinel.options 305 }
else { did -b sentinel.options 310 }
if ( $hget(sentinel.general,show.ip) == 1 ) { did -c sentinel.options 310 }
if ( $hget(sentinel.general,show.sayteam) == 1 ) { did -c sentinel.options 315 }
if ( $hget(sentinel.general,allowrcon) == 1 ) { did -c sentinel.options 325 }
;--Advanced Tab
if ( $hget(sentinel.general,port.type) == dynamic ) {
did -c sentinel.options 61
did -b sentinel.options 60,64,66
}
else {
did -c sentinel.options 62
did -e sentinel.options 60,64,66
}
did -ra sentinel.options 64 $hget(sentinel.general,port.log)
;--Link
did -ra sentinel.options 49 $hget(sentinel.general,version)
;--If bot is activated, disable disruptive settings
if ( $hget(sentinel.general.status,activated) == 1 ) { did -b sentinel.options 14,15,16,4,61,62,64 }
}
on *:DIALOG:sentinel.options:sclick:6:{
;--OK clicked, update all hash tables and save to ini file
hadd sentinel.general autoactivate $did(9).state
hadd sentinel.general show.connects $did(305).state
hadd sentinel.general show.ip $did(310).state
hadd sentinel.general show.sayteam $did(315).state
hadd sentinel.general allowrcon $did(325).state
hadd sentinel.general port.type $iif($did(62).state == 1,static,dynamic)
hadd sentinel.general port.log $did(64)
hsave -i sentinel.general sentinel.ini general
hadd sentinel.irc admin $did(14) $did(18)
hadd sentinel.irc tv $did(15) $did(21)
hadd sentinel.irc monitor $did(16) $did(22)
hsave -i sentinel.irc sentinel.ini irc
hadd sentinel.server address $did(4)
hadd sentinel.server rcon_password $did(5)
hsave -i sentinel.server sentinel.ini server
hadd sentinel.log file $did(26)
hadd sentinel.log address $did(27)
hsave -i sentinel.log sentinel.ini log
hadd sentinel.rules.default players $ctime Default $did(43)
hadd sentinel.rules.default min $ctime Default $did(42)
hadd sentinel.rules.default max $ctime Default $did(41)
hadd sentinel.rules.default type $ctime Default $iif($did(40).state == 1,ignore,notify)
var %i = 1
hadd sentinel.rules.default maps $ctime Default
while ( %i <= $did(36).lines ) {
hadd sentinel.rules.default maps $ctime Default $gettok($hget(sentinel.rules.default,maps),3-,32) $did(36,%i)
inc %i
}
hsave -i sentinel.rules.default sentinel.ini rules.default
}
menu status,channel,menubar {
Sentinel Bot
.$iif($status != connected || $hget(sentinel.general.status,activated) == 1,$style(2)) Activate:sentinel.activate
.$iif($hget(sentinel.general.status,activated) != 1,$style(2)) Deactivate:sentinel.deactivate
.-
.Options...:{ if ( $dialog(sentinel.options).hwnd == $null ) { dialog -am sentinel.options sentinel.options } | dialog -v sentinel.options }
}
alias debug.binvar {
;--This is a generic routine that will output the contents of a binary variable to the status window
;--much like a network packet analyzer commonly displays such binary information.
var %header = _OFFSET__00 01 02 03 04 05 06 07___08 09 0A 0B 0C 0D 0E 0F___01234567 89ABCDEF
echo -s $str(_,$len(%header))
echo -s $timestamp $2-
echo -s %header
var %offset = 0
var %i = 1
while (%i <= $bvar($1,0)) {
var %line = %line $+ $chr(32) $+ $base($bvar($1,%i),10,16,2)
var %char = $bvar($1,%i).text
if (($bvar($1,%i) < 33) || ($bvar($1,%i) > 126)) var %char = .
if ($calc((%i - 9) % 8) == 0) {
var %text = %text $+ $chr(32) $+ %char
}
else { var %text = %text $+ %char }
if (($calc(%i % 8) == 0) && ($calc(%i % 16) != 0)) { var %line = %line $+ $chr(32) $+ _ }
if (($calc(%i % 16) == 0) || (%i == $bvar($1,0))) {
if ((%i = $bvar($1,0)) && ($calc($bvar($1,0) % 16) > 0)) {
var %line = %line $+ $str($chr(32) $+ __,$calc(16 - ($bvar($1,0) % 16) - 8))
if ($calc(16 - ($bvar($1,0) % 16) - 8) > 0) var %line = %line _
var %ext = $calc(16 - ($bvar($1,0) % 16))
if ( %ext > 8) var %ext = 8
var %line = %line $str($chr(32) $+ __,%ext)
}
echo -s $base(%offset,10,16,8) %line _ %text
var %line, %text
var %offset = %offset + 16
}
inc %i
}
echo -s $str(¯,$len(%header))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment