botstats.global.template100666 0 0 3116 7403225500 10727 BotStats for %%BOTNICK
BotStats for %%BOTNICK
%%TIMENOW
(Next update %%TIMENEXTUPDATE)
Uptime: %%BOTUPTIME
Connected to: %%BOTIRCSERVER for %%BOTONLINE
Userlist entries: %%BOTUSERLIST
Partyline users: %%BOTNETUSERS

- !!USER@!!BOT, chat !!CHAN

Botnet map:


Bots linked: %%BOTNETSIZE

%%BOTNETBOTS

Uplinks: %%BOTUPLINK
Downlinks: %%BOTDOWNLINKS
Brought to you by %%BOTNICK

chanstats.global.template100666 0 0 10264 7403225500 11076 Channel Stats for %%CHANNEL
Channel Stats for %%CHANNEL
%%TIMENOW
(Next update %%TIMENEXTUPDATE)
Current channel information

Topic:  %%CHANTOPIC
Set by:  %%CHANTOPSETTER
Modes:  %%CHANMODES
Banlist:  !!BAN
Users:  Total: %%CHANUSERS (%%CHAN%USERS)
Opped: %%CHANOPS (%%CHAN%OPS) Voiced: %%CHANVOICES (%%CHAN%VOICES) Others: %%CHANOTHERS (%%CHAN%OTHERS)


Nick Ident@Host Online Idle
. . . .
!!USER !!HOST !!DUR !!IDLE


Overall activity statistics


The graph is an activity indicator for %%CHANNEL. It shows how many words are spoken within a certain timerange of the day. The bot is in timezone %%BOTTIMEZONE with offset %%BOTOFFSET. Time correction: %%TIMEBALANCE

Timerange Words
. .
!!TIME1-!!TIME2 !!VALUE (!!PERCENT)

Total: %%TOTALWORDS words (100%) since %%SCRIPTSTARTED
Brought to you by %%BOTNICK

chatstats.help100666 0 0 12411 7403225500 6756 %{help=chatstats} You can get help on individual commands: .help CHANNEL COMMANDS show %{+nC|+nC} change cyclestats delete merge freeze unfreeze DCC COMMANDS chatstats go stop %{+n} update trim wipechan back-up recover %{help=show} ### show show show chan PUB -|- In the first form, it show the caller's stats or, if ?nick? is given, nick's stats. In the second form, it shows the current TopTen of the type of stats submitted In the third form, it shows all stats of form one on a channel basis %{help=go}%{+Cn|+C} ### go DCC C|C Enables monitoring for . In opposite to earlier versions (- 2.x), it also starts creation of webpages for if configured. See also: stop %{help=stop}%{+Cn|+C} ### stop DCC C|C Disables monitoring for . All other commands remain available. If you want the bot to leave , use this _before_ doing so. In this case, you might also want to use 'wipechan' in order to completely delete from the database. Don't do this if you plan to re-.go one day :) In opposite to earlier versions (- 2.x), it also stops the creation of webpages for . See also: go wipechan %{help=change}%{+Cn|+Cn} ### change toptalker|allstar <+|-amount> PUB C|C There are two forms: change toptalker <+|-amount> Adds or substracts to/from 's TopTalker balance. Make sure you include the plus or minus change allstar <+|-amount> Adds or substracts to/from 's AllStar balance. Make sure you include the plus or minus Note that this command only affects the TopTalker and AllStar balance of a user, while 'merge' and 'delete' affect the whole data set. See also: merge delete %{help=merge}%{+Cn|+Cn} ### merge PUB C|C Merges two users in one for the referring chanel, where the first nick is the source and the second nick is the destination. It then deletes the first nick. Example: merge foo bar Merges foo in bar and deletes foo. Note that this command affects the whole dataset, not only the TopTalker or AllStar balance. See also: change delete %{help=delete}%{+Cn|+Cn} ### delete PUB C|C Deletes from the database for the referring channel. Note that this command affects the whole dataset, not only the TopTalker or AllStar balance. See also: change merge %{help=freeze}%{+Cn|+Cn} ### freeze ?. Their accounts will be frozen to the current balance. This goes for all data, such as spoken words, joins, online time etc. They, however, will still appear in the rankings. You can submit more than one nick to this command. You cannot change a frozen account's balance (well, if you really need to do so, prefix the nick with a dot: .). You can see a per-channel-list of frozen users with '.chatstats' (DCC) Frozen users will be ignored untill you unfreeze them. See also: unfreeze chatstats %{help=unfreeze}%{+Cn|+Cn} ### unfreeze ? DCC n|- This command is useful if you set the trimlimit to 0 and the database has become spammed with flyby's over the time. If is given, all TopTalkers with less than words in will be deleted from the database immediately (no undo). If is omitted, _all_ TopTalkers of _all_ channels with less than words will be deleted from the database immediately (no undo). %{help=update}%{+n} ### update% ?? DCC n|- If is given, only 's webpages will get updated. If no argument is given, the webpages for all channels will get updated. %{help=wipechan}%{+n} ### wipechan ? ..? DCC n|- Once you stop monitoring , you might want to delete the referring data in the databases. In opposite to earlier versions of CHATstats, cannot be abbreviated. You can submit one or more channels as arguments. There's no undo, so don't do this if you plan to re-enable CHATstats for . %{help=back-up}%{+n} ### back-up DCC n|- Save the current data and makes a backup of the database. See also: recover %{help=recover}%{+n} ### recover DCC n|- Reads the backup to memory. No rehash or restart required. See also: back-up chatstats.tcl100666 0 0 137732 7403225500 6646 # ____ _ _ _ _____ _ _ # / ___| | | | / \|_ _|__| |_ __ _| |_ ___ # | | | |_| | / _ \ | |/ __| __/ _` | __/ __| # | |___| _ |/ ___ \| |\__ \ || (_| | |_\__ \ # \____|_| |_/_/ \_\_||___/\__\__,_|\__|___/ # 3.1.1 by Baerchen, December 2001 # for eggdrop 1.4.x+ & TCL 8.3 # baerchen@germany-chat.net # latest versions @ home.dal.net/baerchen # # Read the files DESCRIPTION, CONFIGURATION & INSTALLATION. # # CONFIGURATION set cs(workdir) "/usr/home/bla/mybot/putallfileshere/" set cs(trigger) "." set cs(idle) 300 set cs(dont) "fserv type trigger" set cs(global) { post=2 rankrange=10 timebalance=+0 trimlimit=0 adinterval=0 adsite=http://www.yoursitehere.com update=60 htmsuffix=.htm ulmethod=1 localfolder=/usr/home/bla/public_html/subdir/ ftpname=www.myftp.com ftpport=21 ftpfolder=/subdir/subdir/subdir/ username=username password=password } # CODEBASE bind pub - $cs(trigger)show cs:show bind pub nC|C $cs(trigger)change cs:change bind pub nC|C $cs(trigger)merge cs:merge bind pub nC|C $cs(trigger)delete cs:delete bind pub nC|C $cs(trigger)freeze cs:freeze bind pub nC|C $cs(trigger)unfreeze cs:unfreeze bind pub nC|C $cs(trigger)cyclestats cs:cycle bind dcc nC|C go cs:go bind dcc nC|C stop cs:stop bind dcc nC|C chatstats cs:status bind dcc n trim cs:trim bind dcc n update cs:update bind dcc n wipechan cs:wipechan bind dcc n back-up cs:backup bind dcc n recover cs:recover bind time - "18 * * * *" cs:autosave bind time - "00 06 * * *" cs:autotrim set cs(ver) "CHATstats 3.1.1 by Baerchen" proc cs:ini {} { global cs csa csc csu numversion putlog "TCL LOADED: $cs(ver)" foreach e [timers] {if {[string match *cs:* $e]} {killtimer [lindex $e 2]}} array set csa ""; array set csc ""; array set csu "" if {![file isdirectory $cs(workdir)]} {file mkdir $cs(workdir)} if {![file exists $cs(workdir)CHATstats.dat]} { set fid [open $cs(workdir)CHATstats.dat w] puts $fid "CHATstats datafile v3.0\n--UserData--\n--ChannelData--\n--ActivityData--" close $fid } cs:read if {[file exists scripts/csconvertdb.tcl]} { source scripts/csconvertdb.tcl putlog " Found csconvertdb.tcl, converting database .." cs:convertdb } foreach e [array names csc] { cs:cv $e if {[lindex $csc($e) 0] == "yes"} { bind pubm - "$e *" cs:count; bind topc - "$e *" cs:topic; bind nick - "$e *" cs:nick bind join - "$e *" cs:join; bind rejn - "$e *" cs:join; bind kick - "$e *" cs:kick bind mode - "$e +b" cs:ban; bind splt - "$e *" cs:partquit; bind sign - "$e *" cs:partquit2 bind raw - PART cs:rawpart if {$numversion >= 1050000} {bind part - "$e *" cs:partquit2} else {bind part - "$e *" cs:partquit} if {$cs(update)} { timer $cs(update) "cs:html $e" if {$cs(adinterval)} {timer $cs(adinterval) "cs:advertize $e"} } } } set cs(ftperror) [catch {set cs(ftpclient) [exec which ftp]}] if {$cs(ftperror)} { if {[file executable /usr/bin/ftp]} {set cs(ftpclient) "/usr/bin/ftp"; set cs(ftperror) 0} if {[file executable /bin/ftp]} {set cs(ftpclient) "/bin/ftp"; set cs(ftperror) 0} } if {$cs(ftperror)} {putlog " Error configuring FTP client."} set cs(rehash) 1 } proc cs:go {h idx a} { global botnick cs csa csc csu numversion set c [string tolower $a] if {![validchan $c] || ![botonchan $c]} {putdcc $idx "CHATstats: I'm not on $a."; return} if {[lsearch -exact [bind pubm - "$c *"] cs:count] == -1} { bind pubm - "$c *" cs:count; bind topc - "$c *" cs:topic; bind nick - "$c *" cs:nick bind join - "$c *" cs:join; bind rejn - "$c *" cs:join; bind kick - "$c *" cs:kick bind mode - "$c +b" cs:ban; bind splt - "$c *" cs:partquit; bind sign - "$c *" cs:partquit2 bind raw - PART cs:rawpart if {$numversion >= 1050000} {bind part - "$c *" cs:partquit2} else {bind part - "$c *" cs:partquit} } else {putdcc $idx "CHATstats is already enabled for $c."; return} if {![info exists csa(00$c)]} { foreach e "00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23" {set csa($e$c) 0} set csa(24$c) 1 } if {![info exists csc($c)]} { set csc($c) "yes [clock seconds] 0 0 [llength [chanlist $c]] [clock seconds]" } else { set csc($c) [lreplace $csc($c) 0 0 "yes"] if {[lindex $csc($c) 1] == 0} {set csc($c) [lreplace $csc($c) 1 1 [clock seconds]]} set csc($c) [lreplace $csc($c) 2 2 0] } foreach e [chanlist $c] { if {([matchattr $e b]) || ($e == $botnick) || [string match -nocase "guest*" $e]} {continue} set el [cs:fl [string tolower $e]] if {[info exists csu(.$el$c)]} {continue} if {[info exists csu($el$c)]} { set csu($el$c) [lreplace $csu($el$c) 0 0 [cs:fl $e]] set csu($el$c) [lreplace $csu($el$c) 1 1 [clock seconds]] } else {set csu($el$c) "[cs:fl $e] [clock seconds] 1 0 0 0 0 0 0 0 0 0 0 0"} } cs:checkpeak $c cs:save putdcc $idx "CHATstats enabled for $c" putlog "CHATstats enabled for $c ($h)" cs:cv $c if {$cs(update)} { timer $cs(update) "cs:html $c" if {$cs(adinterval)} {timer $cs(adinterval) "cs:advertize $c"} } } proc cs:stop {h idx a} { global cs csc numversion set c [string tolower $a] if {[lsearch -exact [bind pubm - "$c *"] cs:count] == -1} {putdcc $idx "CHATstats is not enabled for $c."; return} unbind pubm - "$c *" cs:count; unbind topc - "$c *" cs:topic; unbind nick - "$c *" cs:nick unbind join - "$c *" cs:join; unbind rejn - "$c *" cs:join; unbind kick - "$c *" cs:kick unbind mode - "$c +b" cs:ban; unbind splt - "$c *" cs:partquit; unbind sign - "$c *" cs:partquit2 if {$numversion >= 1050000} {unbind part - "$c *" cs:partquit2} else {unbind part - "$c *" cs:partquit} if {[info exists csc($c)]} { set csc($c) [lreplace $csc($c) 0 0 "no"] set csc($c) [lreplace $csc($c) 2 2 [clock seconds]] } foreach e [array names csu *$c] {cs:reset $e} cs:save cs:cv $c if {$cs(update)} {cs:html $c} foreach e [timers] {if {[string match *cs:*$c* $e]} {killtimer [lindex $e 2]}} putdcc $idx "CHATstats disabled for $c, channel commands are still available." putlog "CHATstats disabled for $c ($h)." } proc cs:join {n uh h c} { global cs csu botnick set ni [string tolower [cs:fl $n]]; set c [string tolower $c]; set j [llength [chanlist $c]] if {$n == $botnick} {bind raw - 353 cs:rawjoin; return} if {[matchattr $n b] || [string match -nocase "guest*" $n] || [info exists csu(.$ni$c)]} {return} if {[info exists csu($ni$c)]} { set csu($ni$c) [lreplace $csu($ni$c) 0 0 [cs:fl $n]] set csu($ni$c) [lreplace $csu($ni$c) 1 1 [clock seconds]] set csu($ni$c) [lreplace $csu($ni$c) 2 2 [expr [lindex $csu($ni$c) 2] + 1]] } else {set csu($ni$c) "[cs:fl $n] [clock seconds] 1 0 0 0 0 0 0 0 0 0 0 0"} cs:checkpeak $c } proc cs:rawjoin {f k a} { global botnick cs csc csu set c [string tolower [lindex $a 2]] if {[lsearch -exact [bind pubm - "$c *"] cs:count] == -1} {return 0} set userlist [lindex [split $a :] 1] foreach e $userlist { set e [string trimleft $e "%+@"] if {[matchattr $e b] || ($e == $botnick) || [string match -nocase "guest*" $e]} {continue} set el [cs:fl [string tolower $e]] if {[info exists csu(.$el$c)]} {continue} if {[info exists csu($el$c)]} { set csu($el$c) [lreplace $csu($el$c) 0 0 [cs:fl $e]] set csu($el$c) [lreplace $csu($el$c) 1 1 [clock seconds]] } else {set csu($el$c) "[cs:fl $e] [clock seconds] 1 0 0 0 0 0 0 0 0 0 0 0"} } return 0 } proc cs:count {n uh h c t} { global botnick cs csa csu set t [split $t]; set ni [cs:fl [string tolower $n]]; set c [string tolower $c] foreach e $cs(dont) {if {[lsearch $t *$e*] > -1} {return}} if {([matchattr $n b]) || ($n == $botnick) || [string match -nocase "guest*" $n] || [info exists csu(.$ni$c)]} {return} set t [llength $t]; set u [clock seconds] if {[info exists csu($ni$c)]} { set csu($ni$c) [lreplace $csu($ni$c) 4 4 [expr [lindex $csu($ni$c) 4] + $t]] set csu($ni$c) [lreplace $csu($ni$c) 5 5 [expr [lindex $csu($ni$c) 5] + 1]] set ls [lindex $csu($ni$c) 12]; set idle [expr $u - $ls] if {($ls > 0) && ($idle > $cs(idle))} { set csu($ni$c) [lreplace $csu($ni$c) 13 13 [expr [lindex $csu($ni$c) 13] + $idle]] set csu($ni$c) [lreplace $csu($ni$c) 12 12 $u] } else {set csu($ni$c) [lreplace $csu($ni$c) 12 12 $u]} } else {set csu($ni$c) "[cs:fl $n] $u 1 0 $t 1 0 0 0 0 0 0 $u 0"} cs:cv $c incr csa([strftime "%H" $u]$c) $t } proc cs:nick {n uh h c ne} { set c [string tolower $c] cs:join $ne $uh $h $c cs:partquit2 $n $uh $h $c "" } proc cs:kick {n uh h c t r} { global csu if {$n == "" } {return} set ni [string tolower [cs:fl $n]]; set c [string tolower $c]; set ta [string tolower [cs:fl $t]] if {[info exists csu($ni$c)]} {set csu($ni$c) [lreplace $csu($ni$c) 8 8 [expr [lindex $csu($ni$c) 8] + 1]]} if {[info exists csu($ta$c)]} { cs:reset $ta$c set csu($ta$c) [lreplace $csu($ta$c) 10 10 [expr [lindex $csu($ta$c) 10] + 1]] } } proc cs:ban {n uh h c mc v} { global cs csu if {$n == "" } {return}; set ni [string tolower [cs:fl $n]]; set c [string tolower $c] if {[info exists csu($ni$c)]} {set csu($ni$c) [lreplace $csu($ni$c) 9 9 [expr [lindex $csu($ni$c) 9] + 1]]} foreach e [chanlist $c] { if {[string match -nocase $v $e![getchanhost $e $c]]} { set vi [string tolower [cs:fl $e]] if {[info exists csu($vi$c)]} {set csu($vi$c) [lreplace $csu($vi$c) 11 11 [expr [lindex $csu($vi$c) 11] + 1]]} } } } proc cs:partquit {n uh h c} {cs:partquit2 $n $uh $h $c ""} proc cs:partquit2 {n uh h c t} { global csu set ni [cs:fl [string tolower $n]]; set c [string tolower $c] if {[info exists csu($ni$c)]} {cs:reset $ni$c} } proc cs:rawpart {f k a} { global botnick csc csu if {[lindex [split $f "!"] 0] != $botnick} {return 0} set c [string tolower $a] if {[lsearch -exact [bind pubm - "$c *"] cs:count] == -1} {return 0} foreach e [array names csu *$c] {cs:reset $e} cs:save return 0 } proc cs:show {n uh h c a} { global cs csc csu botnick set c [string tolower $c] cs:cv $c set cs(rankrange) [expr $cs(rankrange) - 1] if {$cs(post) == 0} {puthelp "PRIVMSG $c: Stats not available. Go visit $cs(adsite)"; return} if {![info exists csc($c)]} {return} set a [cs:fl $a]; set what [string tolower [lindex $a 0]]; set who [lindex $a 1]; set ch "04$c\n" if {$who == ""} {set who [cs:fl $n]} switch -exact $what { channel {set o "CHATstats for $ch"; append o [cs:prep 0 0 $c ""]} joins {set o "Top Joiners of $ch"; append o [cs:prep 1 2 $c ""]} toptalkers {set o "Top Talkers of $ch"; append o [cs:prep 1 4 $c ""]} allstars {set o "Top AllStars of $ch"; append o [cs:prep 1 6 $c ""]} kickers {set o "Top Kickers of $ch"; append o [cs:prep 1 8 $c ""]} banners {set o "Top Banners of $ch"; append o [cs:prep 1 9 $c ""]} kicked {set o "Most famous victims"; append o [cs:prep 1 10 $c ""]} banned {set o "Most famous victims"; append o [cs:prep 1 11 $c ""]} visitors {set o "Top Visitors of $ch"; append o [cs:prep 2 3 $c ""]} idlers {set o "Top Idlers of $ch"; append o [cs:prep 2 13 $c ""]} actives {set o "Most active users of $ch"; append o [cs:prep 3 3 $c ""]} stats {set o ""; append o [cs:prep 4 4 $c $who]} titties {set o "Hehe, I wish I had .."} default { puthelp "NOTICE $n :Usage: $cs(trigger)show " puthelp "NOTICE $n : e.g. $cs(trigger)show stats WhoEver or $cs(trigger)show channel" return } } set o [split $o "\n"] switch -exact $cs(post) { 1 {foreach e $o {puthelp "PRIVMSG $c :[cs:dfl $e]"}} 2 {foreach e $o {puthelp "NOTICE $n :[cs:dfl $e]"}} 3 {foreach e $o {puthelp "PRIVMSG $n :[cs:dfl $e]"}} } } proc cs:prep {type sortfor c who} { global cs csc csu if {$type == 0} { foreach e "i j t wc wl k ku b bu id" {set $e 0} foreach e [array names csu *$c] { incr i; incr j [lindex $csu($e) 2]; incr t [lindex $csu($e) 3]; incr k [lindex $csu($e) 8] incr wc [expr [lindex $csu($e) 4] + [lindex $csu($e) 6]]; incr b [lindex $csu($e) 10] incr wl [expr [lindex $csu($e) 5] + [lindex $csu($e) 7]]; incr id [lindex $csu($e) 13] } if {$i == 0} {return "Apparently, $c has no users. Try again later"} if {$k != 0} {set insert ", one kick every [cs:dur [expr $t/$k]])."} else {set insert ")."} regexp {\d{1,10}\..{2}} [expr $k/$i.0] ku regexp {\d{1,10}\..{2}} [expr $b/$i.0] bu append o "Monitoring: [lindex $csc($c) 0] Started: [cs:date $c sta] Stopped: [cs:date $c sto] Last cycle: [cs:date $c lr]\n" append o "Channel peak: [lindex $csc($c) 4] users on [cs:date $c peak]\n" append o "I've seen [cs:fmt $i] people joining [cs:fmt $j] times wasting [cs:dur $t].\n" append o "Their idletimes add up to [cs:dur $id] leaving [cs:dur [expr $t - $id]] of active chatting.\n" if {$wl > 0} { regexp {\d{1,10}\..{1}} [expr $wc/$wl.0] wla append o "All in all, people have spoken [cs:fmt $wc] words on [cs:fmt $wl] lines (avg $wla).\n" } append o "I count [cs:fmt $k] kicks/[cs:fmt $b] bans ($ku kicks/$bu bans per user$insert" return $o } if {$type == 1} { set l "" foreach e [array names csu *$c] {lappend l "[lindex $csu($e) 0] [lindex $csu($e) $sortfor]"} if {$l != ""} {set l [lsort -integer -decreasing -index 1 $l]} else {return "Insufficient data, please try again later"} for {set i 0} {$i <= $cs(rankrange)} {incr i} { set n [lindex [lindex $l $i] 0]; set r [lindex [lindex $l $i] 1] if {$n == "" || $r == 0} {append o "04[expr $i + 1]. N/A "; continue} append o "04[expr $i + 1]. $n $r " } return $o } if {$type == 2} { set l "" foreach e [array names csu *$c] {lappend l "[lindex $csu($e) 0] [lindex $csu($e) $sortfor]"} if {$l != ""} {set l [lsort -integer -decreasing -index 1 $l]} else {return "Insufficient data, please try again later"} for {set i 0} {$i <= $cs(rankrange)} {incr i} { set n [lindex [lindex $l $i] 0]; set r [lindex [lindex $l $i] 1] if {$n == "" || $r == 0} {append o "04[expr $i + 1]. N/A "} else {append o "04[expr $i + 1]. $n [cs:dur $r] "} } return $o } if {$type == 3} { set l "" foreach e [array names csu *$c] {lappend l "[lindex $csu($e) 0] [expr [lindex $csu($e) 3] - [lindex $csu($e) 13]]"} if {$l != ""} {set l [lsort -integer -decreasing -index 1 $l]} else {return "Insufficient data, please try again later"} for {set i 0} {$i <= $cs(rankrange)} {incr i} { set n [lindex [lindex $l $i] 0]; set r [lindex [lindex $l $i] 1] if {$n == "" || $r == 0} {append o "04[expr $i + 1]. N/A "} else {append o "04[expr $i + 1]. $n [cs:dur $r] "} } return $o } if {$type == 4} { set w [string tolower $who]; set l "" if {![info exists csu($w$c)]} { if {[info exists csu(.$w$c)]} {return "$who's account in $c has been frozen. Try again later."} else { return "No record about $who in $c" } } foreach e [array names csu *$c] {lappend l "[lindex $csu($e) 0] [lindex $csu($e) $sortfor]"} if {$l != ""} {set l [lsort -integer -decreasing -index 1 $l]} else {return "Insufficient data, please try again later"} set cw [lindex $csu($w$c) 4]; set aw [lindex $csu($w$c) 6]; set insert1 ""; set insert2 "" set insert3 ""; set cwavg "N/A"; set rc "N/A"; set awavg "N/A"; set ra "N/A" if {[onchan $who $c]} { set ts [expr [lindex $csu($w$c) 3] + ([clock seconds] - [lindex $csu($w$c) 1]) ]} else { set ts [lindex $csu($w$c) 3] } if {$ts == 0} {set ts "N/A"} else { set insert3 "-> Active chatting: [cs:dur [expr $ts - [lindex $csu($w$c) 13]]]" set ts "[cs:dur $ts] " } if {$cw > 0} { set rc [expr [lsearch [string tolower $l] $w*] + 1] regexp {\d{1,3}\..{1}} [expr $cw.0/[lindex $csu($w$c) 5]] cwavg if {$rc > 1} { set rcp [expr $rc - 2] set insert1 "04[lindex [lindex $l $rcp] 0] is [expr [lindex [lindex $l $rcp] 1] - $cw] words ahead" } } if {$aw > 0} { set l "" regexp {\d{1,3}\..{1}} [expr $aw.0/[lindex $csu($w$c) 7]] awavg foreach e [array names csu *$c] {lappend l "[lindex $csu($e) 0] [lindex $csu($e) 6]"} if {$l != ""} {set l [lsort -integer -decreasing -index 1 $l]} set ra [expr [lsearch [string tolower $l] $w*] + 1] if {$ra > 1} { set rap [expr $ra - 2] set insert2 "04[lindex [lindex $l $rap] 0] is [expr [lindex [lindex $l $rap] 1] - $aw] words ahead" } } append o "Personal stats for 04[cs:dfl [lindex $csu($w$c) 0]] on 04$c\n" append o "Joins: [lindex $csu($w$c) 2] Time spent: $ts Idle: [cs:dur [lindex $csu($w$c) 13]] $insert3\n" append o "TopTalkers: Rank $rc - $cw words/[lindex $csu($w$c) 5] lines (avg $cwavg). $insert1\n" append o "AllStars: Rank $ra - $aw words/[lindex $csu($w$c) 7] lines (avg $awavg). $insert2\n" append o "Kicks: [lindex $csu($w$c) 8] Bans: [lindex $csu($w$c) 9] Got kicked: [lindex $csu($w$c) 10] Got banned: [lindex $csu($w$c) 11]" return $o } } proc cs:change {n uh h c a} { global cs csc csu set c [string tolower $c]; if {![info exists csc($c)]} {return} set a [cs:fl $a]; set what [lindex $a 0] if {[string match -nocase toptalker $what]} {set p 4; set q 5; set str "TopTalker"} if {[string match -nocase allstar $what]} {set p 6; set q 7; set str "AllStar"} if {![info exists p]} {puthelp "NOTICE $n :Usage: $cs(trigger)change toptalker|allstar <+|-amount>"; return} set ni [lindex $a 1]; set nic [string tolower $ni]; set value [lindex $a 2] if {[info exists csu($nic$c)]} { if {[string is integer $value]} { set old [lindex $csu($nic$c) $p]; set lines [lindex $csu($nic$c) $q]; set new [expr $old + $value] if {$old > 0} {set x [expr $lines + int($value / ($old / $lines))]} else {set x [expr $lines + int($value / 4)]} if {$new < 0} {set new 0; set x 0; set value $old} set csu($nic$c) [lreplace $csu($nic$c) $p $p $new] set csu($nic$c) [lreplace $csu($nic$c) $q $q $x] puthelp "NOTICE $n :Changed [cs:dfl $ni]'s $str balance from $old by $value to $new" } else {puthelp "NOTICE $n :Use an integer like +1000 or -500"} } else {puthelp "NOTICE $n :No record about [cs:dfl $ni] in $c"} } proc cs:merge {n uh h c a} { global cs csc csu set c [string tolower $c]; if {![info exists csc($c)]} {return} set a [cs:fl $a]; set from [lindex $a 0]; set i 2 set froml [string tolower $from]; set to [lindex $a 1]; set tol [string tolower $to] if {[llength $a] != 2} {puthelp "NOTICE $n :Usage: $cs(trigger)merge "; return} if {[info exists csu($froml$c)]} { if {[info exists csu($tol$c)]} { while {$i < 12} { set csu($tol$c) [lreplace $csu($tol$c) $i $i [expr [lindex $csu($froml$c) $i] + [lindex $csu($tol$c) $i]]] incr i } set csu($tol$c) [lreplace $csu($tol$c) 13 13 [expr [lindex $csu($froml$c) 13] + [lindex $csu($tol$c) 13]]] unset csu($froml$c) puthelp "NOTICE $n :Merged [cs:dfl $from] in [cs:dfl $to], deleted [cs:dfl $from]" } else {puthelp "NOTICE $n :No record about [cs:dfl $to] in $c"} } else {puthelp "NOTICE $n :No record about [cs:dfl $from] in $c"} } proc cs:delete {n uh h c a} { global cs csc csu set c [string tolower $c]; if {![info exists csc($c)]} {return} if {$a == ""} {puthelp "NOTICE $n :Usage: $cs(trigger)delete "; return} set ni [cs:fl $a]; set nic [string tolower $ni] if {[info exists csu($nic$c)]} { unset csu($nic$c) puthelp "NOTICE $n :Deleted [cs:dfl $ni] in $c" } else {puthelp "NOTICE $n :No record about [cs:dfl $ni] in $c"} } proc cs:freeze {n uh h c a} { global cs csu set c [string tolower $c]; set ar [string tolower [cs:fl $a]] if {$ar == ""} {puthelp "NOTICE $n :Usage: $cs(trigger)freeze ? ..?"; return} foreach e $ar { if {[info exists csu($e$c)]} { cs:reset $e$c set csu(.$e$c) $csu($e$c) unset csu($e$c) puthelp "NOTICE $n :[cs:dfl $e]'s account in $c is now frozen" } elseif {[info exists csu(.$e$c)]} {puthelp "NOTICE $n :[cs:dfl $e]'s account in $c is already frozen"} else { puthelp "NOTICE $n :No record about [cs:dfl $e] in $c" } } } proc cs:unfreeze {n uh h c a} { global cs csu set c [string tolower $c]; set ar [string tolower [cs:fl $a]] if {$ar == ""} {puthelp "NOTICE $n :Usage: $cs(trigger)unfreeze ? ..?"; return} foreach e $ar { if {[info exists csu(.$e$c)]} { set csu($e$c) $csu(.$e$c) unset csu(.$e$c) if {[onchan [cs:dfl $e] $c]} {cs:join [lindex $csu($e$c) 0] "unknown" "unknown" $c} puthelp "NOTICE $n :Unfroze [cs:dfl $e]'s account in $c" } else {puthelp "NOTICE $n :[cs:dfl $e]'s account in $c was not frozen."} } } proc cs:cycle {n uh h c a} { global cs csc csu set c [string tolower $c]; set foobar 0 if {![info exists csc($c)]} {return} if {[lsearch -exact [bind pubm - "$c *"] cs:count] > -1} {unbind pubm -|- "$c *" cs:count; set foobar 1} foreach e [array names csu *$c] { set csu($e) [lreplace $csu($e) 6 6 [expr [lindex $csu($e) 6] + [lindex $csu($e) 4]]] set csu($e) [lreplace $csu($e) 7 7 [expr [lindex $csu($e) 7] + [lindex $csu($e) 5]]] set csu($e) [lreplace $csu($e) 4 4 0] set csu($e) [lreplace $csu($e) 5 5 0] } set csc($c) [lreplace $csc($c) 3 3 [clock seconds]] cs:save if {$foobar} {bind pubm - "$c *" cs:count} puthelp "PRIVMSG $c :CHATstats: Finished cycling TopTalkers." putlog "CHATstats: Finished cycling TopTalkers ($h, $c)" } proc cs:status {h idx a} { global cs csa csc csu set bc ""; set i 0; set al "csa csc csu" foreach e $al { foreach el [array names $e] { set el "#[lindex [split $el #] 1]" if {[lsearch $bc $el] > -1} {incr i; continue} if {![validchan $el]} {incr i; lappend bc $el} } } putdcc $idx "CHATstats Info:" if {$bc != ""} { putdcc $idx "Database: $i orphaned entries for channels: [join $bc ", "] ('help wipechan' for more info)" } else {putdcc $idx "Database: clean, no orphaned entries ([array size csu] entries total)"} putdcc $idx "Valid idle time: > $cs(idle) seconds" putdcc $idx "Trigger words for invalid lines: $cs(dont)" putdcc $idx "Channel details:" foreach e [array names csc] { set cname [string trimleft $e #]; set files ""; set cfiles ""; set gfiles ""; set fu ""; cs:cv $e putdcc $idx " \n$e (Active: [lindex $csc($e) 0] Start: [cs:date $e sta] Stop: [cs:date $e sto] Last cycle: [cs:date $e lr])" switch $cs(post) { 0 {set y "None, I tell them to visit $cs(adsite)"} 1 {set y "PRIVMSG to channel"} 2 {set y "NOTICE to user"} 3 {set y "PRIVMSG to user"} } putdcc $idx " Database trim limit: < $cs(trimlimit) words" putdcc $idx " Info posting: $y" foreach el [array names csu .*$e] {lappend fu [lindex $csu($el) 0]} if {$fu == ""} {set fu "None"} putdcc $idx " Frozen users: [join $fu ", "]" putdcc $idx " Time correction: $cs(timebalance) hours (current: [strftime %H:%M [clock seconds]] corrected: [strftime %H:%M [expr [clock seconds] + ($cs(timebalance) * 3600)]])" if {$cs(update)} { switch $cs(ulmethod) { 0 {set y ", left in $cs(workdir)"} 1 {set y ", moved to $cs(localfolder)"} 2 {set y ", uploaded to $cs(ftpname)$cs(ftpfolder) on port $cs(ftpport)"} } putdcc $idx " Webpages: yes$y" putdcc $idx " Interval: $cs(update) minutes" append cfiles "[glob -nocomplain -path $cs(workdir) *.$cname.template] " append gfiles [glob -nocomplain -path $cs(workdir) *.global.template] foreach el $gfiles { set searchfor [string range $el 0 [string first "." $el]] if {[lsearch $cfiles $searchfor$cname.template] > -1} { set pos [lsearch $gfiles $el] set gfiles [lreplace $gfiles $pos $pos] } } foreach el "$cfiles$gfiles" {lappend files [lindex [split $el "/"] end]} putdcc $idx " Templates in use: [join $files ", "]" if {$cs(adinterval)} {putdcc $idx " Advertisements: every $cs(adinterval) minutes"} else {putdcc $idx " Advertisements: no"} } else {putdcc $idx " Webpage creation: no"} } } proc cs:read {} { global cs csa csc csu if {[info exists cs(rehash)]} {return} set fid [open $cs(workdir)CHATstats.dat r] gets $fid l; gets $fid l if {![string match "--UserData--" $l]} { putlog "CHATstats: CHATstats.dat corrupted. Script not loaded properly." return } gets $fid l while {![string match "--ChannelData--" $l]} { if {[llength $l] == 15} { set l [lreplace $l 1 1 0] set l [lreplace $l 12 12 0] if {[lindex $l 3] > 900000000} {set l [lreplace $l 3 3 0]} if {[lindex $l 13] > [lindex $l 3]} {set l [lreplace $l 13 13 [lindex $l 3]]} set csu([string tolower [lindex $l 0]][lindex $l 14]) [lrange $l 0 13] } else {putlog " Corrupted line in CHATstats.dat, not loaded: $l"} gets $fid l } gets $fid l while {![string match "--ActivityData--" $l]} { if {[llength $l] == 7} {set csc([lindex $l 0]) [lrange $l 1 end]} else { putlog " Corrupted line in CHATstats.dat, not loaded: $l" } gets $fid l } gets $fid l while {![eof $fid]} { if {[llength $l] == 26} { set c [lindex $l 0]; set l [lrange $l 1 end] foreach e $l {set csa([lindex $e 0]$c) [lindex $e 1]} } else {putlog " Corrupted line in CHATstats.dat, not loaded: $l"} gets $fid l } close $fid } proc cs:autosave {mi ho da mo ye} {cs:save} proc cs:save {} { global cs csa csc csu set fid [open $cs(workdir)CHATstats.dat w] puts $fid "CHATstats datafile v3.0" puts $fid "--UserData--" if {[array exists csu]} { foreach e [array names csu] {puts $fid "$csu($e) #[lindex [split $e #] 1]"} } puts $fid "--ChannelData--" if {[array exists csc]} { foreach e [array names csc] {puts $fid "$e $csc($e)"} } puts $fid "--ActivityData--" if {[array exists csa]} { foreach e [array names csa] {lappend l(#[lindex [split $e #] 1]) "[lindex [split $e #] 0] $csa($e)"} foreach e [array names l] {puts $fid "$e $l($e)"} } close $fid if {![file exists $cs(workdir)CHATstats.backup]} { file copy -force $cs(workdir)CHATstats.dat $cs(workdir)CHATstats.backup } } proc cs:backup {h idx a} { global cs csa csc csu cs:save file copy -force $cs(workdir)CHATstats.dat $cs(workdir)CHATstats.backup putdcc $idx "Saved current data and copied CHATstats.dat to CHATstats.backup." } proc cs:recover {h idx a} { global cs csa csc csu if {![file exists $cs(workdir)CHATstats.backup]} { putdcc $idx "Can't find file $cs(workdir)CHATstats.backup, aborting." return } unset csa; unset csc; unset csu file copy -force $cs(workdir)CHATstats.backup $cs(workdir)CHATstats.dat unset cs(rehash); cs:read; set cs(rehash) 1 putdcc $idx "Database reloaded from CHATstats.backup." } proc cs:wipechan {h idx a} { global cs csa csc csu set c [string tolower $a]; set l "" if {$c == "" || [string range $c 0 0] != "#"} {putdcc $idx "Usage: .wipechan ? ..?"; return} foreach e $c { if {[lsearch -exact [bind pubm - "$e *"] cs:count] > -1} { putdcc $idx "CHATstats is enabled on $e. Use \".stop $e\" in DCC first." } else { set al "csa csc csu"; set f 0 foreach el $al {foreach ele [array names $el *$e] {set f 1}} if {!$f} {putdcc $idx "Can't find channel $e in database"} else { foreach el [timers] {if {[string match *cs:*$e* $el]} {killtimer [lindex $el 2]}} array unset csa *$e; array unset csc $e; array unset csu *$e; lappend l $e } } } if {$l != ""} {cs:save; putdcc $idx "Deleted all entries for [join $l ", "] from database."} } proc cs:trim {h idx a} { global cs csu set c [lindex [string tolower $a] 0]; set limit [lindex $a 1]; set foo 0; set l "" if {$idx == -1} { foreach e [array names csu *$c] {lappend l $e} if {$l != ""} { foreach e $l { if {([lindex $csu($e) 4] < $limit) && ([lindex $csu($e) 6] < $limit)} {unset csu($e); incr foo} } putlog "Trimmed CHATstats database for $c by $limit words, $foo of [llength $l] users have been removed." } else {putlog "CHATstats: No user in $c has less than $limit words, no need to trim database."} return } if {[string is integer $c]} { set limit $c; set bar [array size csu] foreach e [array names csu] { if {([lindex $csu($e) 4] < $a) && ([lindex $csu($e) 6] < $a)} {unset csu($e); incr foo} } cs:save putdcc $idx "Trimmed complete CHATstats database by $limit words, $foo of $bar users have been removed." } else { foreach e [array names csu *$c] {lappend l $e} if {$l == ""} {putdcc $idx "Can't find channel $c in CHATstats database"; return} foreach e $l { if {([lindex $csu($e) 4] < $limit) && ([lindex $csu($e) 6] < $limit)} {unset csu($e); incr foo} } putdcc $idx "Trimmed CHATstats database for $c by $limit words, $foo of [llength $l] users have been removed." } } proc cs:autotrim {mi ho da mo ye} { global cs set l "" foreach e [binds cs:count] { regexp {#.*?\s} $e e; set e [string trim $e]; lappend l $e } if {$l == ""} {return} else { foreach e $l { cs:cv $e; cs:trim "CHATstats" "-1" "$e $cs(trimlimit)" } } } proc cs:advertize {c} { global cs cs:cv $c puthelp "PRIVMSG $c :I create diverse statistics for $c. Go visit $cs(adsite)" timer $cs(adinterval) "cs:advertize $c" } proc cs:update {h idx a} { global cs set c [string tolower $a]; set l "" foreach e [binds cs:count] { regexp {#.*?\s} $e e set e [string trim $e] cs:cv $e if {$cs(update)} { if {![validchan $e] || ![botonchan $e]} {putdcc $idx "I'm not on $e."; continue} lappend l $e } } if {[llength $l] == 0} {return} else {set l [lsort $l]} if {$c == ""} { foreach e $l { cs:cv $e foreach el [timers] { if {[string match "*cs:html $e*" $el]} {killtimer [lindex $el 2]} } set cs(manual) "$idx Successfully updated webpages for $e" cs:html $e } } else { cs:cv $c if {![validchan $c] || ![botonchan $c]} {putdcc $idx "I'm not on $c."; return} if {[lsearch $l $c] == -1} {putdcc $idx "CHATstats is not enabled in $c"; return} if {$cs(update)} { foreach e [timers] { if {[string match "*cs:html $c*" $e]} {killtimer [lindex $e 2]} } set cs(manual) "$idx Successfully updated webpages for $c"; cs:html $c } else {putdcc $idx "Configuration does not allow webpages for $c"} } } proc cs:html {c} { global cs csa csc csx csy csu cs:save; set cname [string trimleft $c #]; set ullist ""; set cfiles ""; set gfiles ""; cs:cv $c if {$cs(ftperror) && $cs(ulmethod) == 2} { putlog "Skipped creating webpages for $c due to an FTP configuration error" return } if {![botonchan $c]} { putlog "I'm not on $c, skipped creating webpages." if {$cs(update)} {timer $cs(update) "cs:html $c"} return } append cfiles "[glob -nocomplain -path $cs(workdir) *.$cname.template] " append gfiles [glob -nocomplain -path $cs(workdir) *.global.template] foreach e $gfiles { set searchfor [string range $e 0 [string first "." $e]] if {[lsearch $cfiles $searchfor$cname.template] > -1} { set pos [lsearch $gfiles $e] set gfiles [lreplace $gfiles $pos $pos] } } set files "$cfiles$gfiles" if {$files == ""} { putlog "There are no templates in $cs(workdir), skipped creating webpages for $c. " if {$cs(update)} {timer $cs(update) "cs:html $c"} return } foreach e $files { set output "[string range $e 0 [string first "." $e]]$cname$cs(htmsuffix)" cs:create $c $e $output lappend ullist $output } if {[array exists csy]} {unset csy} if {[array exists csx]} {unset csx} if {$ullist != ""} { switch -exact $cs(ulmethod) { 1 {foreach e $ullist { if {[file exists $e]} { file copy -force $e $cs(localfolder) file delete -force $e } else {putlog "CHATstats: Skipped moving $e, file not found."} } } 2 {set ftperror [catch { set ftpid [open "|$cs(ftpclient) -n $cs(ftpname) $cs(ftpport)" w] puts $ftpid "user $cs(username) $cs(password)" foreach a $ullist { if {[file exists $a]} { puts $ftpid "put $a $cs(ftpfolder)[lindex [split $a "/"] end]" } else {putlog "CHATstats: Skipped uploading $a, file not found."} } puts $ftpid "quit" close $ftpid }] if {$ftperror} {putlog "CHATstats: An error occured while trying to use FTP."} foreach a $ullist {file delete -force $a} } } } if {[info exists cs(manual)]} { putdcc [lindex $cs(manual) 0] "[lrange $cs(manual) 1 end]" unset cs(manual) } if {$cs(update)} {timer $cs(update) "cs:html $c"} } proc cs:create {c input output} { global cs csa csc csu csx csy csz cst botnick offset timezone uptime server {server-online} if {![array exists csy]} { set csy(plusers) ""; set csy(ulink) "None" if {[llength [bots]] > 0} {set csy(bots) [join [lsort -dictionary [bots]] ", "]} else {set csy(bots) "None"} foreach e [whom *] {if {$e != ""} {lappend csy(plusers) [cs:fl "[lindex $e 0] [lindex $e 1] [lindex $e 6]"]}} set csy(plusers) [lsort -dictionary $csy(plusers)] foreach e [bots] { if {([validuser $e]) && ([string match *h* [getuser $e BOTFL]]) && ([islinked $e])} {set csy(ulink) [cs:fl $e]} if {([validuser $e]) && ([matchattr $e b]) && (![string match *h* [getuser $e BOTFL]]) && ([islinked $e])} {lappend csy(dlinks) [cs:fl $e]} } if {[info exists csy(dlinks)]} {set csy(dlinks) [join [lsort -dictionary $csy(dlinks)] ", "]} else {set csy(dlinks) "None"} } if {![array exists csx]} { set topic [cs:fltpc [topic $c]] foreach e "all nos ops vos ttoptalkers tallstars tjoins totime ttwords ttlines tawords talines tkicks tbans titime" {set csx($e) 0} foreach e "csx(chbans) csx(chul) csx(topic) oplist volist uslist" {set $e ""} foreach e [chanlist $c] { incr csx(all) if {[isop $e $c]} {incr csx(ops); set e "@[cs:fl $e]"; lappend oplist $e; continue} if {[isvoice $e $c]} {incr csx(vos); set e "+[cs:fl $e]"; lappend volist $e; continue} set e " [cs:fl $e]"; incr csx(nos); lappend uslist $e } set csx(chul) [append csx(chul) " [lsort -dictionary $oplist]" " [lsort -dictionary $volist]" " [lsort -dictionary $uslist]"] set csx(opsp) [expr ($csx(ops) * 100) / $csx(all)] set csx(vosp) [expr ($csx(vos) * 100) / $csx(all)] set csx(nosp) [expr ($csx(nos) * 100) / $csx(all)] foreach e [chanbans $c] {lappend csx(chbans) "[lindex [split $e] 0]"} set csx(chbans) [lsort -dictionary $csx(chbans)] if {![info exists cst($c)]} {set cst($c) "N/A"} # regsub -all "(.{50}\[^http://\])" $csx(topic) {\1
} csx(topic) foreach e [split $topic] { if {[string match "http://*" $e]} { set el $e regsub -all "
" $e "" e append csx(topic) " $el" } else {append csx(topic) " $e"} } if {![info exists timezone] || $timezone == ""} {set timezone "N/A"} if {![info exists offset] || $offset == ""} {set offset "N/A"} foreach e [array names csu *$c] { if {[lindex $csu($e) 4] > 0} {incr csx(ttoptalkers)} if {[lindex $csu($e) 6] > 0} {incr csx(tallstars)} incr csx(tjoins) [lindex $csu($e) 2]; incr csx(totime) [lindex $csu($e) 3]; incr csx(ttwords) [lindex $csu($e) 4] incr csx(ttlines) [lindex $csu($e) 5]; incr csx(tawords) [lindex $csu($e) 6]; incr csx(talines) [lindex $csu($e) 7] incr csx(tkicks) [lindex $csu($e) 8]; incr csx(tbans) [lindex $csu($e) 10]; incr csx(titime) [lindex $csu($e) 13] } } set fid [open $input r] set html [open $output w] while {![eof $fid]} { gets $fid l; set i 0 while {[regexp {%%[A-Z]} $l null]} { if {$i > 50} {break} switch -glob $l { *%%BOTNICK* {regsub -all %%BOTNICK $l $botnick l} *%%BOTIRCSERVER* {regsub -all %%BOTIRCSERVER $l $server l} *%%BOTONLINE* {regsub -all %%BOTONLINE $l [cs:dur [expr [clock seconds] - ${server-online}]] l} *%%BOTNETBOTS* {regsub -all %%BOTNETBOTS $l $csy(bots) l} *%%BOTNETSIZE* {regsub -all %%BOTNETSIZE $l [cs:fmt [llength $csy(bots)]] l} *%%BOTNETUSERS* {regsub -all %%BOTNETUSERS $l [cs:fmt [llength $csy(plusers)]] l} *%%BOTUSERLIST* {regsub -all %%BOTUSERLIST $l [cs:fmt [countusers]] l} *%%BOTUPLINK* {regsub -all %%BOTUPLINK $l [cs:dfl $csy(ulink)] l} *%%BOTDOWNLINKS* {regsub -all %%BOTDOWNLINKS $l [cs:dfl $csy(dlinks)] l} *%%BOTUPTIME* {regsub -all %%BOTUPTIME $l [cs:dur [expr [clock seconds] - $uptime]] l} *%%BOTOFFSET* {regsub -all %%BOTOFFSET $l $offset l} *%%BOTTIMEZONE* {regsub -all %%BOTTIMEZONE $l $timezone l} *%%SCRIPTSTATUS* {regsub -all %%SCRIPTSTATUS $l [lindex $csc($c) 0] l} *%%SCRIPTSTARTED* {regsub -all %%SCRIPTSTARTED $l [cs:date $c sta] l} *%%SCRIPTSTOPPED* {regsub -all %%SCRIPTSTOPPED $l [cs:date $c sto] l} *%%SCRIPTCYCLE* {regsub -all %%SCRIPTCYCLE $l [cs:date $c lr] l} *%%CHANNEL* {regsub -all & $c "\\\\&" cfix; regsub -all %%CHANNEL $l $cfix l} *%%CHANTOPIC* {regsub -all & $csx(topic) "\\\\&" ctopfix; regsub -all %%CHANTOPIC $l $ctopfix l} *%%CHANTOPSETTER* {regsub -all %%CHANTOPSETTER $l $cst($c) l} *%%CHANMODES* {regsub -all %%CHANMODES $l [getchanmode $c] l} *%%CHANUSERS* {regsub -all %%CHANUSERS $l $csx(all) l} *%%CHANOPS* {regsub -all %%CHANOPS $l $csx(ops) l} *%%CHANVOICES* {regsub -all %%CHANVOICES $l $csx(vos) l} *%%CHANOTHERS* {regsub -all %%CHANOTHERS $l $csx(nos) l} *%%CHAN%USERS* {regsub -all %%CHAN%USERS $l "100%" l} *%%CHAN%OPS* {regsub -all %%CHAN%OPS $l "$csx(opsp)%" l} *%%CHAN%VOICES* {regsub -all %%CHAN%VOICES $l "$csx(vosp)%" l} *%%CHAN%OTHERS* {regsub -all %%CHAN%OTHERS $l "$csx(nosp)%" l} *%%TIMEBALANCE* {regsub -all %%TIMEBALANCE $l $cs(timebalance) l} *%%TIMENOW* {regsub -all %%TIMENOW $l [cs:date $c curr] l} *%%TIMENEXTUPDATE* {regsub -all %%TIMENEXTUPDATE $l [cs:date $c nu] l} *%%TOPTALKERSAMOUNT* {regsub -all %%TOPTALKERSAMOUNT $l [cs:fmt $csx(ttoptalkers)] l} *%%TOPTALKERSWORDS* {regsub -all %%TOPTALKERSWORDS $l [cs:fmt $csx(ttwords)] l} *%%TOPTALKERSLINES* {regsub -all %%TOPTALKERSLINES $l [cs:fmt $csx(ttlines)] l} *%%TOPTALKERSRATIO* {set x [expr $csx(ttwords).0 / ($csx(ttlines) + 1)]; regexp {\d+\..{1}} $x x; regsub -all %%TOPTALKERSRATIO $l $x l} *%%ALLSTARSAMOUNT* {regsub -all %%ALLSTARSAMOUNT $l [cs:fmt $csx(tallstars)] l} *%%ALLSTARSWORDS* {regsub -all %%ALLSTARSWORDS $l [cs:fmt $csx(tawords)] l} *%%ALLSTARSLINES* {regsub -all %%ALLSTARSLINES $l [cs:fmt $csx(talines)] l} *%%ALLSTARSRATIO* {set x [expr $csx(tawords).0 / ($csx(talines) + 1)]; regexp {\d+\..{1}} $x x; regsub -all %%ALLSTARSRATIO $l $x l} *%%TOTALWORDS* {regsub -all %%TOTALWORDS $l [cs:fmt [expr $csx(ttwords) + $csx(tawords)]] l} *%%TOTALLINES* {regsub -all %%TOTALLINES $l [cs:fmt [expr $csx(ttlines) + $csx(talines)]] l} *%%TOTALRATIO* {set x [expr ($csx(ttwords) + $csx(tawords)) / ($csx(ttlines).0 + $csx(talines) + 1)] regexp {\d+\..{1}} $x x; regsub -all %%TOTALRATIO $l $x l} *%%TOTALJOINS* {regsub -all %%TOTALJOINS $l [cs:fmt $csx(tjoins)] l} *%%TOTALKICKS* {regsub -all %%TOTALKICKS $l [cs:fmt $csx(tkicks)] l} *%%TOTALBANS* {regsub -all %%TOTALBANS $l [cs:fmt $csx(tbans)] l} *%%TOTALONLINE* {regsub -all %%TOTALONLINE $l [cs:dur $csx(totime)] l} *%%TOTALIDLE* {regsub -all %%TOTALIDLE $l [cs:dur $csx(titime)] l} *%%TOTALACTIVE* {regsub -all %%TOTALACTIVE $l [cs:dur [expr $csx(totime) - $csx(titime)]] l} } incr i } if {[string match -nocase "" $l]} { set width 0; set amount 0; set eoc 0; set what ""; set rep1 ""; set list "" regexp {(\w+)\s(\d+)?,?(\d+)?} $l match what width amount set what [string tolower $what] while {!$eoc} { gets $fid nl if {[string match -nocase "" $nl] || [eof $fid]} {set eoc 1} else {append rep1 $nl} } switch -glob $what { botnetmap { if {[info exists botnet-nick]} {set lb [string range ${botnet-nick} 0 8]} else {set lb [string range $botnick 0 8]} if {[info exists csz]} {unset csz} foreach e [botlist] {set csz([lindex $e 0]) [lindex $e 1]} set csz($lb) $lb puts $html " $lb " cs:botnetmap " " $html $lb continue } partyline { foreach e $csy(plusers) { regsub -all !!USER $rep1 [lindex $e 0] rep2 regsub -all !!BOT $rep2 [lindex $e 1] rep2 regsub -all !!CHAN $rep2 [lindex $e 2] rep2 puts $html [cs:dfl $rep2] } continue } bans { for {set i 0} {$i <= [llength $csx(chbans)]} {incr i} { regsub !!BAN $rep1 [lindex $csx(chbans) $i] rep2 while {[string match *!!BAN* $rep2]} {incr i; regsub !!BAN $rep2 [lindex $csx(chbans) $i] rep2} puts $html [cs:dfl $rep2] } continue } activity { if {$width == 0} { putlog "CHATstats: Syntax error in template $input, must be: (see file INSTALLATION for help)" continue } foreach e [array names csa *$c] {lappend list "[lindex [split $e #] 0] $csa($e)"} set total 0; foreach e $list {incr total [lindex $e 1]} set list [lsort -integer -decreasing -index 1 $list] set max [expr ([lindex [lindex $list 0] 1] * 100.0)/$total] set list [lsort -increasing -index 0 $list] for {set i 0} {$i < 24} {incr i} { set int [lindex [lindex $list $i] 1] set x [expr ($int * 100.0)/$total] regexp {\d{1,3}\..{1}} $x x set barlength [expr int($x * ($width / $max) + ($x / 10))] regsub !!TIME1 $rep1 "[lindex [lindex $list $i] 0]:00" rep2 regsub !!TIME2 $rep2 "[lindex [lindex $list [expr $i + 1]] 0]:00" rep2 regsub !!VALUE $rep2 $int rep2 regsub !!PERCENT $rep2 "$x%" rep2 regsub !!WIDTH $rep2 $barlength rep2 puts $html $rep2 } continue } chanusers { foreach n $csx(chul) { set n [cs:dfl $n]; set ni [string range $n 1 end]; set u [clock seconds] if {![onchan $ni $c]} {continue} set gcj [getchanjoin $ni $c] if {$gcj > 0} {set gcj "[expr round((($u-$gcj)/60)/60)]h:[expr round(fmod((($u-$gcj)/60),60))]m"} else {set gcj "N/A"} if {[nick2hand $ni $c] == "*"} {set flags "-"} else {set flags [chattr $ni $c]} if {$flags == "*"} {set flags "-"} regsub -all "!!USER" $rep1 $n rep2 regsub -all "!!HOST" $rep2 [string range [getchanhost $ni] 0 45] rep2 regsub -all "!!DUR" $rep2 $gcj rep2 regsub -all "!!IDLE" $rep2 "[getchanidle $n $c]m" rep2 regsub -all "!!FLAGS" $rep2 $flags rep2 puts $html $rep2 } continue } top* { if {$width == 0 || $amount == 0} { putlog "CHATstats: Syntax error in template $input, must be: (see file INSTALLATION for help)" continue } set total 0 switch -exact $what { topjoiners {set sortfor 2} topvisitors {set sortfor 3} toptalkers {set sortfor 4} topallstars {set sortfor 6} topkickers {set sortfor 8} topbanners {set sortfor 9} topkicked {set sortfor 10} topbanned {set sortfor 11} topidlers {set sortfor 13} topactiveusers {} default {putlog "CHATstats: unknown listing found in template $what - aborting (see file INSTALLATION for help)."; return} } switch -exact $what { topactiveusers { foreach e [array names csu *$c] { lappend list "[lindex $csu($e) 0] [expr [lindex $csu($e) 3] - [lindex $csu($e) 13]]" } } default { foreach e [array names csu *$c] { lappend list "[lindex $csu($e) 0] [lindex $csu($e) $sortfor]" } } } if {$list != ""} {set list [lsort -integer -decreasing -index 1 $list]} else {continue} foreach e $list {incr total [lindex $e 1]} if {$total == 0} {continue} set max [expr ([lindex [lindex $list 0] 1] * 100.0)/$total]; set i 1 foreach e $list { set int [lindex $e 1] if {$int == 0 || $i > $amount} {continue} set x [expr ($int * 100.0)/$total] regexp {\d{1,3}\..{1}} $x x set barlength [expr int($x * ($width / $max) + ($x / 10))] regsub -all "!!RANK" $rep1 $i rep2 regsub -all "!!USER" $rep2 [lindex $e 0] rep2 switch -exact $what { topactiveusers {regsub -all "!!VALUE" $rep2 [cs:dur $int] rep2} topvisitors {regsub -all "!!VALUE" $rep2 [cs:dur $int] rep2} topidlers {regsub -all "!!VALUE" $rep2 [cs:dur $int] rep2} default {regsub -all "!!VALUE" $rep2 [cs:fmt $int] rep2} } regsub -all "!!PERCENT" $rep2 "$x%" rep2 regsub -all "!!WIDTH" $rep2 $barlength rep2 puts $html [cs:dfl $rep2]; incr i } continue } default {puts $html $l} } } puts $html $l } close $html close $fid } proc cs:botnetmap {ind html bot} { global csz foreach e [array names csz] { if {(![string match [string tolower $bot] [string tolower $e]]) && ([string match [string tolower $csz($e)] [string tolower $bot]])} { lappend dl $e } } if {[info exists dl]} { foreach e $dl { if {[string match [string tolower $e] [string tolower [lindex $dl end]]]} { puts $html "$ind `-$e " cs:botnetmap "$ind " $html $e } else { puts $html "$ind |-$e " cs:botnetmap "$ind | " $html $e } } } } proc cs:checkpeak {c} { global cs csc set i [lindex $csc($c) 4]; set j [llength [chanlist $c]] if {$j > $i} { putserv "PRIVMSG $c :New peak for $c @ $j users! Old peak was $i users on [cs:date $c peak]." cs:cv $c set csc($c) [lreplace $csc($c) 4 4 $j] set csc($c) [lreplace $csc($c) 5 5 [clock seconds]] cs:save } } proc cs:date {c t} { global cs csc switch -exact $t { curr {return [strftime "%A, %d.%m.%Y, %H:%M" [expr [clock seconds] + $cs(timebalance) * 3600]]} nu {if {[lindex $csc($c) 2] == 0} {return [strftime %H:%M [expr [clock seconds] + ($cs(timebalance) * 3600) + ($cs(update) * 60)]]} else {return "N/A"}} sta {set i 1} sto {set i 2} lr {set i 3} peak {set i 5} } if {[lindex $csc($c) $i] == 0} {return "N/A"} else {return [strftime "%d.%m.%Y" [expr [lindex $csc($c) $i] + $cs(timebalance) * 3600]]} } proc cs:reset {w} { global cs csu set jt [lindex $csu($w) 1]; set u [clock seconds]; set nic [lindex [split $w #] 0] set c "#[lindex [split $w #] 1]"; set ls [lindex $csu($w) 12]; set idle 0 if {$jt > 0} { set csu($w) [lreplace $csu($w) 3 3 [expr [lindex $csu($w) 3] + ($u - $jt)]] set csu($w) [lreplace $csu($w) 1 1 0] if {$ls > 0} {set idle [expr $u - $ls]} else {set idle [expr $u - $jt]} } else { if {$ls > 0} {set idle [expr $u - $ls]} } set csu($w) [lreplace $csu($w) 12 12 0] if {$idle > $cs(idle)} {set csu($w) [lreplace $csu($w) 13 13 [expr [lindex $csu($w) 13] + $idle]]} } proc cs:dur {du} { set o ""; set i [expr int($du/31536000)]; if {$i > 0} {lappend o "${i}y"} set i [expr int(fmod($du,31536000)/86400)]; if {$i > 0} {lappend o "${i}d"} set i [expr int(fmod($du,86400)/3600)]; if {$i > 0} {lappend o "${i}h"} set i [expr int(fmod($du,3600)/60)]; if {$i > 0} {lappend o "${i}m"} # set i [expr int(fmod($du,60))]; if {$i > 0} {lappend o "${i}s"} if {$o == ""} {return "N/A"} else {return [join $o ":"]} } proc cs:cv {c} { global cs foreach e $cs(global) {set cs([lindex [split $e =] 0]) [lindex [split $e "="] 1]} if {[info exists cs($c)]} { foreach e $cs($c) {set cs([lindex [split $e =] 0]) [lindex [split $e "="] 1]} } } proc cs:topic {n uh h c t} { global cs cst cs:cv $c if {$n == "\*"} {set n "Unknown"} set cst($c) "$n on [cs:date $c curr]" } proc cs:fltpc {n} { regsub -all {|||(([0-9])?([0-9])?(\,([0-9])?([0-9])?)?)?|([0-9A-F][0-9A-F])?} $n "" n return $n } proc cs:fl {n} { regsub -all \\\[ $n !2 n; regsub -all \\\] $n !3 n regsub -all \\\{ $n !5 n; regsub -all \\\} $n !4 n regsub -all \\\^ $n !6 n; regsub -all \\\" $n !7 n regsub -all \\\\ $n !1 n; return $n } proc cs:dfl {n} { regsub -all !1 $n \\ n; regsub -all !2 $n \[ n regsub -all !3 $n \] n; regsub -all !4 $n \} n regsub -all !5 $n \{ n; regsub -all !6 $n \^ n regsub -all !7 $n \" n; return $n } proc cs:fmt {i} { if {![string is integer $i]} {return $i} while {[regsub {([0-9])([0-9]{3})($|\.)} $i {\1.\2} i]} {} return $i } cs:ini CONFIGURATION100666 0 0 13143 7403225500 6043 ____ _ _ _ _____ _ _ / ___| | | | / \|_ _|__| |_ __ _| |_ ___ | | | |_| | / _ \ | |/ __| __/ _` | __/ __| | |___| _ |/ ___ \| |\__ \ || (_| | |_\__ \ \____|_| |_/_/ \_\_||___/\__\__,_|\__|___/ 3.1.1 by Baerchen, December 2001 for eggdrop 1.4.x+ & TCL 8.3 baerchen@germany-chat.net latest versions @ home.dal.net/baerchen CONFIGURATION ********************************************************************* IMPORTANT: This script was designed for TCL 8.3.x. Don't run it with earlier versions of TCL since it can crash your bot (though I'm not sure about 8.2.x). set cs(workdir) "/usr/home/bla/mybot/putallfileshere/" Directory where CHATstats looks for the files it reads and writes (data files, templates) Leave leading and trailing /'s as shown. Path should not contain any dots "." set cs(trigger) "." This defines what char has to be used in front of a channel command. "" is a valid option. set cs(idle) 300 CHATstats counts people's idle seconds. Set this to what you consider to be idle. Set to 0 to count each and every idle second. Set to a very high value (like 100000000) to virtually disable this feature. set cs(dont) "fserv type trigger" If CHATstats finds one of the words of this space delimited list in a user's line, it will not add the wordcount and the line to the user's stats. You don't need to include the plural of words, e.g. if you include "fserv" you don't need to add "fservs". Set to "" to disable this feature. --- set cs(global) { Here we start with the global configuration. The global configuration represents the default settings for all channels where CHATstats is enabled. The global config includes all settings the script offers. In opposite, if you need different settings for a channel, you will need to create a channel specific variable that contains only the changed settings. These settings will override the global ones. We'll see later, here's the global config first (note the different notation): post=2 Determine how info is to be posted to the user: 0 = Don't post anything, tell users to visit the channel's website 1 = PRIVMSG to the channel 2 = NOTICE to the user 3 = MSG to the user Getting info MSG'ed is very annoying and prevents users from calling info too often while NOTICE seems to be the least spamming way. timebalance=+0 Is your channel German and your bot resides on a server in the US? Then all shown times (e.g. activity stats) refer to the US time, which is probalby not what you want. People would see that there is highest activity in your chnnel at 1500 when it's 2100 in reality. So, you would set timefactor to +6. The other way round, you would set timefactor to -6. trimlimit=0 After a time, the array holding the data gets spammed with dozens of users counting only a few words (flyby's). They play only a minor role for the CHATstats and would get deleted at 0600 (bot-time). Set to whatever or to 0 to disable this feature (50 turned out to be a good value for medium-frequency channels) adinterval=0 Want to advertize your website in the channel? Set this to the amount of minutes after which CHATstats informs the chan about the website. Set to 0 to disable. adsite=http://www.yoursitehere.com Set the website to be advertised (url only, no spaces!) update=60 Defines if and after how many minutes the webpages get updated. Setting this variable to 0 creates no webpage at all. This is needed when the script shall be limited to IRC only. htmsuffix=.htm Set the suffix for the webpages to be created ulmethod=1 Determine where the webpages shall go: 0 = do nothing, leave the webpages in $cs(workdir) 1 = move webpages to a local folder 2 = upload webpages via FTP If you set ulmethod to 1 (otherwise disregard): localfolder=/usr/home/bla/public_html/subdir/ Set to full path where the files should be moved to. Leave leading and trailing /'s as shown. If you set ulmethod to 2, configure the next 5 variables (otherwise disregard): ftpname=www.myftp.com Set the name of the FTP server - do not include any subdirectories. ftpport=21 Set the port of the FTP server (usually 21). ftpfolder=/subdir/subdir/subdir/ Set the subdir, where the HTMLs should be FTP'ed to including leading and trailing /'s as shown. Important: Set to nothing if not required. username=username password=password Set username and password for the FTP given above (Note: for security reasons, you should chmod 600 chatstats.tcl) } --- Done. If that's OK for (all) your channel(s), stop here and install. If you need one or more different settings for one or more channels, you need to insert the following into the script: set cs(#channelname) { setting1=value1 setting2=value2 .. } Here you put all settings that are different from the global config. Do not use uppercase letters for #yourchannel. E.g., if #foobar's webpages need to be uploaded to a different server than all other webpages (as given in cs(global)), you would do this: set cs(#foobar) { ulmethod=2 update=60 ftpname=www.myotherftp.com ftpfolder=/mydir/ username=username2 password=password2 adinterval=120 adsite=http://www.blah.org } This would advertize http://www.blah.org every 120 minutes. The webpages would get uploaded to www.myotherftp.com/mydir/ at a 60 minute interval. Note that the FTP port is 21 too, so it doesn't need to be repeated. Don't forget the {brackets}. They're important! REMEMBER: Do this only for channels that need _different_ settings as given in the global configuration.csconvertdb.tcl100666 0 0 3261 7403225500 7111 proc cs:convertdb {} { global cs csa csu foreach e "cs_toptalkers.dat cs_allstars.dat cs_activity.dat" { if {![file exists $cs(workdir)$e]} { putlog " Can't find file $cs(workdir)$e .. aborting." return } } set fid [open $cs(workdir)cs_toptalkers.dat] while {![eof $fid]} { gets $fid l if {[llength $l] == 3} { set count [lindex $l 2]; if {$count == 0} {continue} set user [lindex $l 0]; set nick [lindex $l 1]; set ls [expr $count / 10] if {[info exists csu($user)]} { set csu($user) [lreplace $csu($user) 4 4 [expr [lindex $csu($user) 4] + $count]] set csu($user) [lreplace $csu($user) 5 5 [expr [lindex $csu($user) 5] + $ls]] } else {set csu($user) "$nick 0 0 0 $count $ls 0 0 0 0 0 0 0 0"} } } close $fid set fid [open $cs(workdir)cs_allstars.dat] while {![eof $fid]} { gets $fid l if {[llength $l] == 3} { set count [lindex $l 2]; if {$count == 0} {continue} set user [lindex $l 0]; set nick [lindex $l 1]; set ls [expr $count / 10] if {[info exists csu($user)]} { set csu($user) [lreplace $csu($user) 6 6 [expr [lindex $csu($user) 6] + $count]] set csu($user) [lreplace $csu($user) 7 7 [expr [lindex $csu($user) 7] + $ls]] } else {set csu($user) "$nick 0 0 0 0 0 $count $ls 0 0 0 0 0 0"} } } close $fid set fid [open $cs(workdir)cs_activity.dat] while {![eof $fid]} { gets $fid l if {[llength $l] == 2} { set atime [lindex $l 0]; set count [lindex $l 1] if {[info exists csa($atime)]} {set csa($atime) [lreplace $csa($atime) 0 0 [expr $csa($atime) + $count]]} else {set csa($atime) $count} } } close $fid putlog " Finished. Everythings seems fine .." }DESCRIPTION100666 0 0 27720 7403225500 5625 ____ _ _ _ _____ _ _ / ___| | | | / \|_ _|__| |_ __ _| |_ ___ | | | |_| | / _ \ | |/ __| __/ _` | __/ __| | |___| _ |/ ___ \| |\__ \ || (_| | |_\__ \ \____|_| |_/_/ \_\_||___/\__\__,_|\__|___/ 3.1.1 by Baerchen, December 2001 for eggdrop 1.4.x+ & TCL 8.3 baerchen@germany-chat.net latest versions @ home.dal.net/baerchen DESCRIPTION ********************************************************************* The script gathers a bunch of info aboutb a channel and creates some sort of statistics. In detail it monitors: - amount of joins - words and lines users speak - user's online times - user's idle times - amount of bans (active/passive) - amount of kicks (active/passive) - channel peaks and peak times The script then creates overall stats and rankings for each of the data, e.g. Personal stats for baerchen on #germany-chat Joins: 30 Time spent: 22h:25m Idle: 20h:23m -> Active chatting: 2h:2m TopTalkers: Rank 9 - 1949 words/146 lines (avg 13.3). Lutz_MMI is 428 words ahead AllStars: Rank 18 - 1020 words/228 lines (avg 4.4). torkel is 217 words ahead Kicks: 0 Bans: 1 Got kicked: 0 Got banned: 0 and much more. Counting user's spoken words often leads to competition among some of them. At a point, when there seem to be no more changes on the top of the ranking, +nC|C flagged users can do a cycle. I.e. the current TopTalkers ranking will be reset to 0 and their wordcount shoved to some kind of eternal ranking (AllStars). A lot of information about the channel (topic, current users etc.) and the bot (partyline users etc.) can be dumped to HTML files which then can be uploaded to an FTP server or moved locally. The webpages created don't base on in-script HTML any longer but instead on templates, which means you can use your own HTML code. Therefore you decide about the HTML design and which information goes into the webpages. If you don't want to bother with this, use the supplied templates and go right away. The distinction of the configuration in global and channel-specific settings gives CHATstats full multi-channel capabilities. See "http://www.germany-chat.net -> Bettenauslastung" to get an idea of how it could look like (using the supplied templates). NOTE: CHATstats' v3.x database is incompatible with earlier versions. Although there is a converting script, I highly recommended to start from the scratch. (Read more in the INSTALLATION file). COMMANDS ********************************************************************* PUB, -|- -------- show <..> Shitload of options what stats to show PUB, nC|C --------- change Manipulates users word balances. Two options merge Merges two users in one delete Deletes 's account for that channel cyclestats Cycles TopTalkers freeze Freezes 's account unfreeze Unfreezes 's account DCC, -|- --------- (only available if helpfile is installed) help chatstats Shows all CHATstats related commands help Shows help on a specific CHATstats command DCC, C|C -------- go Enables CHATstats for stop Disables CHATstats for chatstats Shows the most important configuration info DCC, n|- -------- update [channel] Creates webpages immediately trim Trims the database manually if necessary wipechan Deletes a channel from the databases back-up Makes a backup of the .dat file recover Reads the .backup file to memory For detailled info, install the supplied chatstats.help. NOTES & TRICKS ********************************************************************* - CHATstats automatically ignores all 'Guest' nicks. - Don't forget to .stop and .wipechan when the bot leaves a channel. - Exclude other channel bots from being counted by adding them as bots to the userlist or 'freeze' their nicks. HISTORY ********************************************************************* 3.1.1 Fixed: Another "&" issue: In 3.1.0 I fixed it, but only for one occurence of & in a topic or channelname. Two or more occurences made the webpage creation procedure cause an error. Fixed: Related to the first fix: when two "%%" occured in the channel topic, webpage creation procedure caused an error. Fixed: List processing problem in cs:html - changed the way the templates are parsed from list to string processing. Fixed: Glitch introduced in 3.1.0: in 'show stats', TopTalker's rank was always 0. Changed: Permissions for command 'chatstats' from n|- to nC|C. Changed: Duration of online-time and idle-time in '.show' is now shown the same way as on the webpages. Changed: Minor things, such as wording, grammar, bla. Added: PUB Commands 'freeze'/'unfreeze' make CHATstats temporarily ignore/unignore a user. Added: Per-channel-listing of all frozen accounts to command 'chatstats' Added: Botnet map (similar to '.bottree') to botstats.global.template Some of you might think there's redundant information now, but it's ok for demonstration purposes. Remember, you can change the templates as you like. Thanks to Wcc for idea and code. Used with friendly permission. Added: Variable 'rankrange': until now, 'show ' returned the first ten ranks of each category. With 'rankrange' you can determine individually how many ranks are returned. Added: In 3.1.0, I've added the new listing 'topactiveusers' for the webpages. Consequently, I had to add the option 'actives' to the 'show' command. 3.1.0 - Introduced new listing for templates: topactiveusers, which returns users' onlinetime minus idletime, sorted. %%TOTALACTIVE returns the total active chatting time of all users. - Introduced variable cs(dont): a space delimited list of words, which when spoken, prevents CHATstats from counting. highly recommended. - Three variables weren't available although built-in (TOTALWORDS & TOTALLINES & TOTALRATIO). - Added variable !!FLAGS to the chanusers listings (if user is recognized by bot, it returns his flags) - .dat file was holding data of channels that weren't enabled (wasn't checking for enabled channels in cs:rawpart and cs:rawjoin). This bug didn't affect anything except the size of the .dat file. After rehashing, you can .wipechan those channels. - 'wipechan' now understands more than one argument: wipechan #channel1 #channel2 #channel3 .. - cs(trim) went to the global variables. Each channel can be trimmed with different values - Fixed biggie: 'cyclestats' cycled all channels. Duh. - When script was stopped, %%TIMENEXTUPDATE still returned a value. - Fixed %%CHANNEL returning bs when channel name contained the character '&', e.g. #wine&dine (same for %%CHANTOPIC). - Fixed 'update' - under certain circumstances it was not possible to add a channel and have .update generate the websites - though normal ("scheduled") updating worked fine. - Fixed non-latin language based bug: if a nick used non-latin characters, an error in cs:update was raised stating the nick can't be found on the channel. Since this has nothing to do with the script itself but the way eggdrop and TCL work together, a quick workaround was to simply exclude that nick from further processing. - Updated and changed command 'chatstats' slightly. - Updated helpfile (corrections, additions). - Gained speed improvements in regard to array operations. - Added sanity checks to some commands. - Cleaned up code and improved legibility of the script. 3.0.0 (major rewrite in order to implement new features) - CHATstats now monitors joins, kicks, bans, online times, idle times, channel peaks, users' spoken words and lines. The distinction between TopTalkers and Allstars has been kept - Introduced templates. Make your own HTML output - Introduced division of configuration in global and channel-specific settings, giving CHATstats full multi-channel capabilities - Added advertizing system. Make CHATstats advertize your website for info purposes - Reintroduced .back-up and .recover - Userlist for the channel-stats is now returned in mIRC-style (ops A-z, voices A-z, others A-z) - URLs in topics are now hyperlinked - Once again, changed commands and their syntax to match new features - Once again, changed variable names for better understanding - Took out some unneccessary settings (who cares for the names of the cachefiles ;-)) 2.11 - Fixed bugs of two non-existing variables that occured under certain circumstances when updating .htm pages. Thanks to LangerT of langersoft.de for heavy debugging ;-) - Fixed two other bugs regarding the creation of the AllStars page. - Fixed bug that after .wipechan'ing a channel, it was not deleted from the configuration array, therefore causing .chatstatus to output inaccurate information. - Fixed rare variable declaration bug in .go - Fixed very rare bug that if the bot was on two channels of which names start equal but end different (e.g. #test and #test1), the AllStars of #test1 were mixed up. Except for this one, integrity of data was _not_ affected by all of the above mentioned bugs. - Rewrote .chatstatus, now more accurate & informative. By doing so, the [lsort] error (see below) was eliminated. - Minor changes to html code - Added DCC commands for go and stop 2.1 - Fixed bug that after cycling stats, TopTalkers were not saved when the array was empty. If no word was spoken until a crash, rehash or restart occured, the Toptalkers had the same wordcount as before cycling - Fixed bug in activity.htm when $timezone and $offset are not set - Cleaned code; some speed and efficacy improvements - Introduced cs(botlogo) and cs(timefactor). See README - Introduced cs(interval): Changed method of updating htmls from a bind to a timer, which means that you can set the intervals more flexible (.restart is highly recommended) - Introduced cs(trigger): you can now define the trigger character for the public commands (.restart is highly recommended) - Fixed nasty bugs that occured when using TCL version < v8.3. There is still one error due to using an [lsort] option that has been implemented in 8.3. However, it's not vital to the script and affects .chatstatus only. I decided to live with this one ;-) 2.0 - Added DCC commands .chatstatus .wipechan and .update - Added configuration of colors and font for HTMLs. Now you can adapt the appearance of the HTMLs to your liking - Added some coloured bar.gifs - Added started/stopped/last reset information to .topten .topstars and HTMLs - Upon request, changed commands for .topten and .topstars to .toptalkers and .allstars (makes sense even ;-) - Upon request, chanstats, toptalkers, botstats and activitystats now update simultaneously every hour - Configuration wasn't saved in regular intervals - fixed - Minor changes to the layout of the HTMLs (merged tables and stuff) - Removed link to activity.htm on chanstats.htm - Deleted .backup and .rollback. Who needs that anyway ;-) - Cosmetic changes to the code. Changed cs(visitwebsite) to cs(nochaninfo) for better understanding - Changed README. A.o., added chapter "POSSIBLE PROBLEMS" - Thanks to |Starman| on this one 1.02 - cs(allowhtml) was not stringtolower'ed - if channels were entered with uppercase letters, it caused the TopTalkers.htm not to show the wordcount. 1.01 - Trying to unbind an unexisting bind caused bot not to start. - Limited host length in chanstats.htm to max. 50 chars in order not to mess up the table when using long hostnames. - After starting, TopStars array was read too early, therefore causing an error. Delayed 10 secs now. 1.0 - First public releaseINSTALLATION100666 0 0 27241 7403225500 5741 ____ _ _ _ _____ _ _ / ___| | | | / \|_ _|__| |_ __ _| |_ ___ | | | |_| | / _ \ | |/ __| __/ _` | __/ __| | |___| _ |/ ___ \| |\__ \ || (_| | |_\__ \ \____|_| |_/_/ \_\_||___/\__\__,_|\__|___/ 3.1.1 by Baerchen, December 2001 for eggdrop 1.4.x+ & TCL 8.3 baerchen@germany-chat.net latest versions @ home.dal.net/baerchen INSTALLATION ********************************************************************** - Put chatstats.tcl in your /scripts directory and make an entry in the bot config (e.g. source scripts/chatstats.tcl) Upgraders from 2.x: ------------------- CHATstats 3.0 uses a different database than previous versions. If CHATstats finds "csconvertdb.tcl" in the same directory where chatstats.tcl is in, it assumes that you want to have your old databases converted. It will then search for the files cs_toptalkers.dat cs_allstars.dat cs_activity.dat in $cs(workdir) and convert them to the new database format. It is absolutely necessary to delete csconvertdb.tcl before the next rehash or restart takes place, else it would try to convert again. Although the script offers the possibility, I highly recommend _not_ to upgrade the database, but instead to start from the scratch. Some relational stats will appear really messed up. - Copy chatstats.help in your /help directory and make an entry in the bot config (e.g. loadhelp chatstats.help) - Copy the supplied templates in $cs(workdir) (see configuration) or use your own ones (see the HTML TEMPLATES manual below). - Copy redbar.gif in the same directory where the HTMLs are uploaded/moved to. - First time installers: rehash Upgraders: restart (else the bot will go nuts) - Each user (except the owner), who is allowed to use the channel commands has to be flagged +C| or |+C (UPPERCASE C) (except for the command "show" which is, of course, open to all users) - Use .go in DCC to start monitoring (even if you upgraded) If you decided to use the supplied templates, you're finished. Next chapter deals with how to make your own templates. HTML TEMPLATES ********************************************************************* In earlier versions of CHATstats, you've had to go with my crappy in-script HTML. With the templates, you can now use your own HTML. Just set up a file in notepad.exe, Frontpage or whatever, insert a few variables and you're done. You can define HOW MUCH of WHAT goes WHERE. Use ten webpages or just one, your choice. Although this seems to be more complicated than to go with predefined in-script HTML, it gives all users the opportunity to freely adapt CHATstats' output to their website's design and therefore a high degree of integration. NAMING A TEMPLATE --------------------------------------------------------------------- All templates have to be named like this: blabla.global.template ^--------- Never change this part. Must be ".template". ^----- This part decides whether the template is used for just one channel or for all channels ^------ whatever you want, e.g. 'toptalkers.' to indicate this is the toptalkers template. The script looks for all files in $cs(workdir) with the suffix '.global.template' and uses them for _all_ channels. If there's one channel that needs a different template, copy the global template, apply the HTML changes, replace ".global." by the channelname (no "#" !) and save it. CHATstats will then use this template solely for that channel. There's no need to rehash or restart - just do '.update'. Also, you can add and delete templates from the directoy as you like - CHATstats uses only what it finds and will only complain if it finds no templates at all. Example: You have three channels #foo #bar and #foobar 1. There's only the file toptalkers.global.template: This is used for all three channels #foo, #bar & #foobar 2. Now you add the file toptalkers.foo.template to the directory: This file is used only for channel #foo. #bar and #foobar will still use toptalkers.global.template 3. Now you delete the file toptalkers.global.template: The only webpage generated will be the one for #foo, since there will be no templates found for #bar and #foobar. Don't forget the dots within the filenames. INSERTING PLACEHOLDERS AND VARIABLES --------------------------------------------------------------------- While parsing the templates, the bot needs to know where to put what. For many information, we just have to insert a PLACEHOLDER into the HTML code - like %%CHANNEL would always and everywhere be replaced by the channelname e.g. #foo. Easy. Listings like TopTalkers a.o. work different. What we just can't do is to insert rank1 toptalker1 wordcount1 percent1 rank2 toptalker2 wordcount2 percent2 rank3 toptalker3 wordcount3 percent3 in the HTML. This would work for three TopTalkers, but not for four. Instead we would type rank toptalker wordcount percent and repeat this incl. the HTML code until our listing is complete. Rank, toptalker, wordcount etc. is what I call VARIABLES. Let's get into this. First, we need to tell CHATstats when a listing starts and ends. We do this by inserting an HTML comment and the type of listing within the comment: x and y are parameters discussed later Then comes the HTML stuff and the needed variables Last we define the end of the listing by In opposite to variables and placeholders, these two comments need to be on a line by itself. Never ever add other stuff. AVAILABLE LISTINGS ------------------ x = maximum width in pixels of the graphical bar (the script needs to know how long the drawn bar can be at maximum) y = the amount of max. enumerations (you don't want to see all 354 TopTalkers, do you ?) AVAILABLE VARIABLES WITHIN LISTINGS ----------------------------------- VARIABLE RETURNS .. -------- ---------- No variables available. !!USER a user on the partyline according to 'whom *' !!BOT the bot !!USER is on !!CHAN the botchannel !!USER is on !!USER a channel user according to /names !!FLAGS user's botflags for the channel !!HOST !!USER's ident@host !!DUR !!USER's online time !!IDLE !!USER's idle time !!BAN one ban from the channel's banlist. You can use this more than once e.g. to put two bans in one row !!TIME1 time start range e.g. 00:00 !!TIME2 time end range, e.g. 01:00 !!VALUE words spoken in time range !!PERCENT !!VALUE's percentage in relation to total !!WIDTH the current width of the bar x is the maximum width of the bar in pixels, e.g. 250 pixels All listings starting with "Top" e.g. !!USER user's nick (e.g. from TopTalkers) !!RANK !!USER's ranking !!VALUE !!USER's count (joins, words, etc.) !!PERCENT !!VALUE's percentual share !!WIDTH the current width of the bar x is the maximum width of the bar in pixels, e.g. 250 pixels y is the maximum of returned results, e.g. 10 for 10 TopTalkers e.g. AVAILABLE PLACEHODERS --------------------- Placeholder returns .. %%TOPTALKERSAMOUNT amount of all TopTalkers %%TOPTALKERSWORDS total wordcount of TopTalkers %%TOPTALKERSLINES total linecount of TopTalkers %%TOPTALKERSRATIO TopTalkers' words/line ratio %%ALLSTARSAMOUNT amount of all AllStars %%ALLSTARSWORDS total wordcount of AllStars %%ALLSTARSLINES total linecount of TopTalkers %%ALLSTARSRATIO AllStars' words/line ratio %%TOTALWORDS Toptalkers' + AllStar's words %%TOTALLINES Toptalkers' + AllStar's words %%TOTALRATIO Toptalkers' + AllStar's words/line ratio %%TOTALJOINS amount of joins by all users %%TOTALKICKS amount of all kicks the bot has seen %%TOTALBANS amount of all bans the bot has seen %%TOTALONLINE added online time of all users %%TOTALIDLE added idletime of all users %%TOTALACTIVE added active chatting time of all users %%CHANNEL channel name %%CHANMODES channel modes %%CHANTOPIC channel topic %%CHANTOPSETTER nick who set the topic and date %%CHANUSERS channel's total user amount %%CHANOPS channel's ops %%CHANVOICES channel's voices %%CHANOTHERS channel's other users %%CHAN%USERS channel's total users percentual share %%CHAN%OPS channel's ops percentual share %%CHAN%VOICES channel's voices percentual share %%CHAN%OTHERS channel's other users percentual share %%TIMEBALANCE time balance according to $cs(timebalance) %%TIMENOW current day of week, date and time %%TIMENEXTUPDATE time of next update %%SCRIPTSTATUS "yes" or "no" for CHATstats en- or disbaled %%SCRIPTSTARTED date of CHATstats' very first start %%SCRIPTSTOPPED date when CHATstats was last stopped %%SCRIPTCYCLE date of the last cycle %%BOTNICK botnick %%BOTIRCSERVER IRC server the bot is connected to %%BOTONLINE time the bot is connected to %%BOTIRCSERVER %%BOTNETBOTS list of bots in the botnet delimited by , %%BOTNETSIZE botnet size %%BOTNETUSERS amount of users in the bot channels %%BOTUSERLIST bot's userlist length %%BOTUPLINK bot's uplink %%BOTDOWNLINKS bot's downlinks %%BOTUPTIME bot's uptime %%BOTOFFSET bot's offset %%BOTTIMEZONE bot's timezone That's all you need to know to make your own HTML templates. Have a look at the examples below or at the templates supplied with the script. Load them in your HTML authoring software and start trying. EXAMPLES ********************************************************************* 1. You want a listing of all users currently on your channel: Current users in %%CHANNEL
!!USER!!HOST!!DUR!!IDLE
2. Now, you also want to add the TopVisitors to the same webpage: Current users in %%CHANNEL
!!USER!!HOST!!DUR!!IDLE
The TopVisitors are:
People's total online time in %%CHANNEL is %%TOTALONLINE.
!!RANK !!USER !!VALUE !!PERCENT
Not too difficult. As you might have noticed, it doesn't matter whether you put several variables or placeholders in one line or each of them in a single line. You can also mix them. otherstats.global.template100666 0 0 22625 7403225500 11312 Other Stats for %%CHANNEL
Other stats for %%CHANNEL
%%TIMENOW
(Next update %%TIMENEXTUPDATE)
Monitoring: %%SCRIPTSTATUS
Started: %%SCRIPTSTARTED
Stopped: %%SCRIPTSTOPPED

Top Visitors:

Rank Nick Time spent % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

Total online time: %%TOTALONLINE (100%)

Top Idlers:

Rank Nick Time idle % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

Total idle time: %%TOTALIDLE (100%)

Top active users:

Rank Nick Active chatting % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

Total active chatting time: %%TOTALACTIVE (100%)

Top Kickers:

Rank Nick Kicks % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

Total amount of kicks: %%TOTALKICKS (100%)

Top Banners:

Rank Nick Bans % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

Total amount of bans: %%TOTALBANS (100%)

Most famous victims (Kicks):

Rank Nick Got kicked % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

Most famous victims (Bans):

Rank Nick Got banned % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT


Brought to you by %%BOTNICK

redbar.gif100666 0 0 72 7403225500 5754 GIF89a ¢Óæ..êRRîvvò˜˜îttêTTÔ, !C4"€;toptalls.global.template100666 0 0 7617 7403225500 10740 TopTalkers & AllStars for %%CHANNEL
TopTalkers & AllStars for %%CHANNEL
%%TIMENOW
(Next update %%TIMENEXTUPDATE)
Monitoring: %%SCRIPTSTATUS
Started: %%SCRIPTSTARTED
Stopped: %%SCRIPTSTOPPED
Last cycle: %%SCRIPTCYCLE

Current TopTalkers Ranking:

Rank Nick Words % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

A total of %%TOPTALKERSWORDS words has been spoken on %%TOPTALKERSLINES lines (avg %%TOPTALKERSRATIO words/line) by %%TOPTALKERSAMOUNT TopTalkers.

Current Allstars Ranking:

Rank Nick Words % (of total)
. . . .
!!RANK !!USER !!VALUE !!PERCENT

A total of %%ALLSTARSWORDS words has been spoken on %%ALLSTARSLINES lines (avg %%ALLSTARSRATIO words/line) by %%ALLSTARSAMOUNT AllStars.
Brought to you by %%BOTNICK