 #####
# !!!!!!! NOT A OFFICIAL RELEASE !!!!!!!!
# Multichannel seen implementation by PreSSo (andrejp@luz.fe.uni-lj.si)
# loosely based on seen by Robey
# will show when nicks were last seen on the channel(s) no matter
# if they are on the bot's userlist or not.
# requires eggdrop 1.3.x bot
# needs adbtools.tcl loaded!
# Repaired by Ze_PilOt for 1.3.x
#
# HISTORY:
# version 1.0 (dunno exactly, been a while)
# version 1.1 (6 Aug, 1996)
# - various small fixes
# version 1.2 (18 Aug, 1996)
# - new faster check_expired (this one really blows the socks off:)
# - changed default nickexpire to 30 days
# - various small changes
# version 1.3 (18 Aug, 1996)
# - added configurable bind msg/pub flags for users (see settings)
# version 1.4 (1 Sep, 1996)
# - added force update command. this was needed to fix the
# bug where bot was showing incorrect seen info for users
# that were added to the bot's userlist after they were
# already in the seen nicks list (.seenupd)
# - added dcc seen command
# version 1.5 (2 Oct, 1996)
# - the script now also reports how the user left (leave, kick..)
# - old seen.nicks file is no longer valid (delete it)
# - shows the channel where nick was last on for all nicks, not
# just valid users (the code was already there, just not used:)
# version 1.6 (17 Jan, 1997)
# - the bot now reports 'one of my channels' instead of '#channel'
# if the channel is marked +secret
# version 1.7 (24 Jan, 1997)
# - cosmetical fixes
# version 1.8 (25 Jan, 1997)
# - err...:) a little bug fix:) It won't putlog stupid things now:)
# version 1.9 (29 Jul, 1997)
# - changed and tuned script, so it should run on 1.1.5 now
#   Times for checking for expires (60 M.) and nicksaving (15 M.) are
#   now hardcoded, hey... who cares? I for my part don#t know why it
#   wouldn#t work with variables... Blackb|rd
# version 1.9.1 (10 Aug, 1997)
# - "corrected" and "rewrote" some of the answers (the grammar part
#   wasn't very good - my opinion... :)) - Queux
# version 1.9.2 (16 Dec, 1997) (non official release !!!)
# - I upgraded the script to use with eggdrop 1.3.x .. The seen on the
#   party don't work at 100%, but work perfectly on a channel ...
#
# COMMANDS:
# dcc, msg: seen <nick>
# public: !seen <nick>
#
# TODO:
# - nicks get added to the nicklist even though they are valid bot users - fix this
# - do a configurable 'reply on seen nick'
#####

### *** SETTINGS! ***
# filename of the file where nicks list will be saved
# (defaults to seen.nicks)
set seennicks {seen.nicks}
# save nicks each (this setting) minutes (defaults to 15
# minutes)
set nicksavetime 10
# check for expired users on every this much minutes
# this should be set to a reasonable value like one hour or
# one day or something like that (defaults to 60 minutes)
set expirechk 30
# time in hours for the nick that is NOT on the bot's
# userlist to expire. this keeps the seennicks file from
# getting huge (defaults to 720 hours -> 30 days)
set nickexpire 336
###

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# !!! DO NOT CHANGE ANYTHING BELOW THIS LINE !!!

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

###

set seenver "adbseen.tcl v1.9.2fr"

if {!([info exists seenpubf])} {set seenpubf "-"}

if {!([info exists seenmsgf])} {set seenmsgf "-"}

set nlist ""



# delete fake/existing userlist users from the seennicks list

proc seen_del_existing {uhand} {

  global nlist

  # delete nicks if the user exists in the userlist

  append fwho "+" $uhand

  set ind [lsearch -exact $nlist [string tolower $fwho]]

  set deld 0

  if {$ind != -1} {

    set nlist [lreplace $nlist $ind [expr $ind + 3]]

    return 1

  }

  return 0

}





# force nick update

proc force_seenupd {hand idx arg} {

  global seennicks

  if {[llength $arg] < 1} {

    #update all users

    putdcc $idx "Updating seen information for all users in the userlist..."

    set deld 0

    foreach uhand [userlist] {

      incr deld [seen_del_existing $uhand]

    }

    if {$deld} {

      putdcc $idx "Removed $deld nick(s) from nicks list that existed in the userlist."

    }

  } else {

    #update specified nicks

    foreach uhand $arg {

      seen_del_existing $uhand

    }

  }

  putdcc $idx "Update complete."

}





# update nick's status..

proc nick_upd {chan nick uhand how} {

  global nlist

  #set uhand [string tolower $uhand]

  if {$uhand!="*"} {

    # this user is a valid user

    setlaston $uhand

    setuser $uhand XTRA seenon $chan

    setuser $uhand XTRA seenhow $how

    # delete possible fake user entry in the nick list

    append fwho "-" $uhand

    set ind [lsearch -exact $nlist [string tolower $fwho]]

    if {$ind != -1} {

      set nlist [lreplace $nlist $ind [expr $ind + 3]]

    }

    seen_del_existing $uhand

    return 1

  } else {

    # this user is NOT a valid user..

    if {[validuser $nick]} {

      # ..but he's using the nick of one of the valid users!

      append mnick "-" [string tolower $nick]

    } else {

      # ..and he's using his own nick.. (this one will most probably only happen to $botnick)

      append mnick "+" [string tolower $nick]

    }

  }

  set timeon [unixtime]

  set ind [lsearch -exact $nlist $mnick]

  if {($ind >= 0)} {

    #found nick! replace it

    set nlist [lreplace $nlist [expr $ind + 1] [expr $ind + 3] $timeon $chan $how]

  } else {

    #nick nonexistant. append to the list

    lappend nlist $mnick $timeon $chan $how

  }

}





# save nicks

proc dcc_nick_save {hand idx arg} {

  global seennicks

  nick_save

  putdcc $idx "Nicks saved to file ${seennicks}"

}

proc nick_save {} {

  global seennicks nlist nicksavetime

  set f [open $seennicks w]

  puts $f $nlist

  close $f

  timer $nicksavetime "nick_save"

}





# delete expired users from the list

proc check_expired {} {

  global nickexpire expirechk nlist

  putlog "-Seen- Checking for expired users. The bot will not respond until it's finished."

  set total [expr [llength $nlist] - 1]

  set mtotal [expr $total / 4]

  set nulist ""

  set nrdel 0

  set ind 0

  set nrtot 0

  foreach ent $nlist {

    switch $ind {

      0 {

        set nik $ent

        set ind 1

      }

      1 {

        set loff $ent

        set ind 2

      }

      2 {

        set lchan $ent

        set ind 3

      }

      3 {

        if {[expr ([unixtime] - $loff) / 3600] < $nickexpire} {

        #hasn't yet expired, add to the nulist

        lappend nulist $nik $loff $lchan $ent

        } else {incr nrdel 1}

        set ind 0

      }

    }

  }

  set nlist $nulist

  if {$nrdel > 0} {

    putlog "-Seen- Deleted $nrdel expired nicks out of total $mtotal ([expr 100.0 * $nrdel / $mtotal]%)."

  }

  putlog "-Seen- Done. Next check will be done in $expirechk minutes."

  timer $expirechk "check_expired"

}





proc putseen {out isdcc text} {

  if {$isdcc} {

    putdcc $out $text

  } else {

    putserv "PRIVMSG $out :$text"

  }

}



# return how <who> left

proc adb_seengethow {who} {

  switch [user-get $who seenhow] {

    1 {

      return "*kicked*"

    }

    default {

      return "on"

    }

  }

}



# return 'one of my channels' if the channel

# is +secret

# return 'the channel'/'my party line' if the

# nick was on the same channel as the user is on

# return channel nick was last seen on otherwise

proc thechan {whochan userchan} {

  if {$whochan == "my party line"} {

    return "my party line"

  }

  if {[lsearch [channels] $whochan] == -1} {

    return $whochan

  }

  if {[string tolower $whochan] == [string tolower $userchan]} {

    return "this channel"

  }

  if {[lsearch [channel info $whochan] "+secret"] == -1} {

    return $whochan

  }

  return ""

}





# show when <who> was last on

proc adb_seen {channel nick uhost handle who isdcc} {

  global nlist botnick



  set unick ""

  if {$isdcc == 0} {

    if {[string compare $channel $nick] != 0} {

      append unick $nick ": "

    }

  }



  if {$who == ""} {

    putseen $channel $isdcc "${unick}Correct syntax is 'seen <nick>'"

    return 1

  }



  set who [string trim [lindex $who 0] ?]

  if {[string compare [string tolower $botnick] [string tolower $who]] == 0} {

    putseen $channel $isdcc "${unick}Sure, I guess you don't see me now - right ??"

    return 1

  }

  if {[string compare [string tolower $nick] [string tolower $who]] == 0} {

    putseen $channel $isdcc "${unick}Aren't you in here right now ?? :)"

    return 1

  }



  if {$unick==""} {

    #request via MSG!

    foreach chan [channels] {

      if {[onchan $who $chan]} {

        putseen $channel $isdcc "Hmm... Isn't $who on IRC right now ??"

        return 1

      }

      if {[onchansplit $who $chan]} {

        putseen $channel $isdcc "${who} was just on IRC, but got netsplit.. shame, huh?:)"

        return 1

      }

      foreach i [chanlist $chan] {

        set hand [finduser $i![getchanhost $i $chan]]

        if {($hand != "*") && ([string compare [string tolower $hand] [string tolower $who]] == 0)} {

          if {[onchansplit $i $chan]} {

            putseen $channel $isdcc "$i is ${who}, and $i was just on [thechan $chan $channel] but got netsplit.."

          } {

            putseen $channel $isdcc "$i is ${who}, and $i is on [thechan $chan $channel] right now!"

          }

          return 1

        }

      }

    }



  } else {

    #request via PUB!

    #check if the nick is on YOUR channel

    if {[onchansplit $who $channel]} {

      putseen $channel $isdcc "${unick}${who} was just here, but got netsplit.. shame, huh?:)"

      return 1

    }

    if {[onchan $who $channel]} {

      putseen $channel $isdcc "${unick}${who} is on the channel right now - look in front of you !!"

      return 1

    }

    foreach i [chanlist $channel] {

      set hand [finduser $i![getchanhost $i $channel]]

      if {($hand != "*") && ([string compare [string tolower $hand] [string tolower $who]] == 0)} {

        if {[onchansplit $i $channel]} {

          putseen $channel $isdcc "${unick}$i is ${who}, and $i was just here but got netsplit"

        } {

          putseen $channel $isdcc "${unick}$i is ${who}, and $i is on the channel right now!"

        }

        return 1

      }

    }



    #hmm.. obviously not. let's check if he's on other channels we're on

    set channel [string tolower $channel]

    foreach chan [channels] {

      if {[string compare [string tolower $chan] $channel] == 0} {

        #already did this channel (YOUR channel)

        continue

      }

      if {[onchansplit $who $chan]} {

        putseen $channel $isdcc "${unick}${who} was just on [thechan $chan $channel] but got netsplit.. shame, huh?:)"

        return 1

      }

      if {[onchan $who $chan]} {

        putseen $channel $isdcc "${unick}If I'm not mistaken ${who} is on [thechan $chan $channel] at the moment..."

        return 1

      }

      foreach i [chanlist $chan] {

        set hand [finduser $i![getchanhost $i $chan]]

        if {($hand != "*") && ([string compare [string tolower $hand] [string tolower $who]] == 0)} {

          if {[onchansplit $i $chan]} {

            putseen $channel $isdcc "${unick}$i is ${who}, and $i was just on [thechan $chan $channel] but got netsplit.."

          } {

            putseen $channel $isdcc "${unick}$i is ${who}, and $i is on [thechan $chan $channel] right now!"

          }

          return 1

        }

      }

    }

  }



  if {[hand2idx $who] >= 0} {

    if {[matchattr $handle p]} {

      putseen $channel $isdcc "${unick}$who is on my party line right now !!"

      return 1

    }

  }



  append fwho "-" $who

  set find [lsearch -exact $nlist [string tolower $fwho]]

  if {$find!=-1} {

    set ind $find

    set ending " I cannot guarantee that it really was ${who} though (different host than the one I have stored in my userlist) !!"

  } else {

    append rwho "+" $who

    set ind [lsearch -exact $nlist [string tolower $rwho]]

    set ending ""

  }

  if {$ind == -1} {

    if {[validuser $who]} {

      set last [getuser $who LASTON $channel]

      set seenon [thechan [getuser $who XTRA seenon] $channel]

      switch [getuser $who XTRA seenhow] {

        1 {

          set seenhow "*kicked*"

        }

        default {

          set seenhow ""

        }

      }

      if {$last == 0} {

        putseen $channel $isdcc "${unick}Sorry, but I haven't seen ${who} recently."

        return 1

      } else {

        if {$seenon != ""} {

                putseen $channel $isdcc "${unick}I last saw $who on $seenon - [tdiff [unixtime] $last]ago."

                } else {

                putseen $channel $isdcc "${unick}I last saw $who [tdiff [unixtime] $last]ago."

                }

        return 1

      }

    }

    putseen $channel $isdcc "${unick}I haven't seen anyone with the nick - ${who}."

    return 1

  } else {

    set last [lindex $nlist [expr $ind + 1]]

    set seenon [thechan [lindex $nlist [expr $ind + 2]] $channel]

    switch [lindex $nlist [expr $ind + 3]] {

      1 {

        set seenhow "*kicked*"

      }

      default {

        set seenhow ""

      }

    }

        if {($seenhow != "")&&($seenon != "")} {

                putseen $channel $isdcc "${unick}I last saw $who on $seenon - $seenhow - [tdiff [unixtime] $last]ago."

                } else {

                putseen $channel $isdcc "${unick}I last saw $who [tdiff [unixtime] $last]ago."

                }

    return 1

  }

}





# time difference

proc tdiff {time2 time1} {

  set ltime [expr $time2 - $time1]

  set seconds [expr $ltime % 60]

  set ltime [expr ($ltime - $seconds) / 60]

  set minutes [expr $ltime % 60]

  set ltime [expr ($ltime - $minutes) / 60]

  set hours [expr $ltime % 24]

  set days [expr ($ltime - $hours) / 24]



  set result ""



  if {$days} {

    append result "$days "

    if {$days == 1} {

      append result "day "

    } else {

      append result "days "

    }

  }

  if {$hours} {

    append result "$hours "

    if {$hours == 1} {

      append result "hour "

    } else {

      append result "hours "

    }

  }

  if {$minutes} {

    append result "$minutes "

    if {$minutes == 1} {

      append result "minute "

    } else {

      append result "minutes "

    }

  }

  if {$seconds} {

    append result " $seconds "

    if {$seconds == 1} {

      append result "second "

    } else {

      append result "seconds "

    }

  }

  return $result

}





proc dcc_seen {hand idx arg} {

  adb_seen $idx $hand "" $hand $arg 1

}

bind dcc - seen dcc_seen



proc pub_seen {nick uhost hand channel who} {

  adb_seen $channel $nick $uhost $hand $who 0

}

bind pub - seen pub_seen



proc msg_seen {nick uhost hand who} {

  adb_seen $nick $nick $uhost $hand $who 0

}

bind msg - seen msg_seen



proc seen_join {nick uhost hand channel} {

  nick_upd $channel $nick $hand 0

}

bind join - * seen_join



proc seen_part {nick uhost hand channel} {

  nick_upd $channel $nick $hand 0

}

bind part - * seen_part



proc seen_sign {nick uhost hand channel reason} {

  nick_upd $channel $nick $hand 0

}

bind sign - * seen_sign



proc seen_nickch {nick uhost hand channel newnick} {

  nick_upd $channel $nick $hand 0

  nick_upd $channel $newnick $hand 0

}

bind nick - * seen_nickch



proc seen_kick {nick uhost hand channel kicked reason} {

  set hand [nick2hand $kicked $channel]

  nick_upd $channel $kicked $hand 1

}

bind kick - * seen_kick



proc seen_chof {hand idx} {

  nick_upd "my party line" $hand $hand 0

}

bind chof - * seen_chof



#for testing/emergency nick save only

bind dcc m savenicks dcc_nick_save



#force deletion of nicks in the nickfile that are in the userlist

#(this command should be issued if new users are added to the bot's

#userlist)

bind dcc m seenupd force_seenupd



# script initialization

putlog "$seenver loaded"

# set defaults

if {!([info exists seennicks])} { set seennicks {seen.nicks} }

if {!([info exists nicksavetime])} { set nicksavetime 15 }

if {!([info exists nickexpire])} { set nickexpire 720 }

if {!([info exists expirechk])} { set expirechk 60 }

if [file exists $seennicks] {

  set f [open $seennicks r]

  if {[gets $f nlist] != -1} {

    if {[expr [llength $nlist] % 4] != 0} {

      putlog "-Seen- Invalid nicks list.. Clearing nicks list."

      set nlist ""

    }

  } else {

    putlog "-Seen- Invalid data file. Nicks list not loaded."

    set nlist ""

  }

  close $f

}

# update users on the channel

foreach chan [channels] {

  foreach n [chanlist $chan] {

    set hand [nick2hand $n $chan]

    nick_upd $chan $n $hand 0

  }

}

nick_save

check_expired