# Offline logging for sbnc by Worrum
# Script edited by KHobbits, changes tracked at http://khobbits.net/sbnc/
# Questions, remarks, suggestions: join #sbnc
# Clientside options settable through /sbnc set
# This version is for 1.2!
internalbind svrlogon log:redetach
internalbind attach log:attach
internalbind detach log:detach
internalbind command log:settings
if {[bncgetglobaltag chanlog] == ""} {
	bncsetglobaltag chanlog "privmsg,notice,join,part,kick,quit,nick,topic,mode"
}

proc log:settings {client parameters} {
	if {[string equal -nocase [lindex $parameters 0] "set"]} {
	  if {[string equal -nocase [lindex $parameters 1] "offlinelog"]} {
			if {[string equal [lindex $parameters 2] ""]} {
				bncreply "Syntax: /sbnc set offlinelog on/off"
				bncreply "This setting enables the logging of all channels, the logs will be played back on connect if the method is left to automatic."
				if {[string equal -nocase [getbncuser $client tag offlinelog] ""]} {
					bncreply "Current value: off"
				} else  {
					bncreply "Current value: [getbncuser $client tag offlinelog]"
				}
				haltoutput
				return
			} elseif {![string equal -nocase [lindex $parameters 2] "on"] && ![string equal -nocase [lindex $parameters  2] "off"]} {
				bncreply "Value should be either on or off."
				haltoutput
				return
			} else {
				if {[string equal -nocase [lindex $parameters 2] "off"]} {
					setbncuser $client tag offlinelog
					bncreply "Done."
					haltoutput
					return
				}
				setbncuser $client tag offlinelog [string tolower [lindex $parameters 2]]
				bncreply "Done.  You will probably want to disable autobacklog if have enabled auto replay."
				haltoutput
			}
		} elseif {[string equal -nocase [lindex $parameters 1] "offlinelog.method"]} {
			if {[string equal [lindex $parameters 2] ""]} {
				bncreply "Syntax: /sbnc set offlinelog.method auto/query"
				bncreply "Auto: logs are played automatically. Query: manual trigger with query based warning."
				if {[string equal -nocase [getbncuser $client tag offlinelog.method] ""]} {
					bncreply "Current value: auto"
				} else  {
					bncreply "Current value: [getbncuser $client tag offlinelog.method]"
				}
				haltoutput
				return
			} elseif {![string equal -nocase [lindex $parameters 2] "auto"] && ![string equal -nocase [lindex $parameters  2] "query"]} {
				bncreply "Value should be either auto or query."
				haltoutput
				return
			} else {
				setbncuser $client tag offlinelog.method [lindex $parameters 2]
				bncreply "Done."
				haltoutput
			}
		} elseif {[string equal -nocase [lindex $parameters 1] "offlinelog.exclude"]} {
			if {[string equal [lindex $parameters 2] ""]} {
				bncreply "Syntax: /sbnc set offlinelog.exclude #channel1,#channel2,#channel3 or none to unset it"
				bncreply "Channels added to this list won't be logged.  Use this to except channels like #msl or #irchelp to save log space and reduce BNC lag."
        bncreply "You can only use this list OR offlinelog.include not both."
				if {[string equal -nocase [getbncuser $client tag offlinelog.exclude] ""]} {
					bncreply "Current value: none"
				} else  {
					bncreply "Current value: [getbncuser $client tag offlinelog.exclude]"
				}
				haltoutput
				return
			} else {
				if {!([string equal -nocase [lindex $parameters 3] ""])} {
					bncreply "One of the settings you supplied are incorrect, either wrong channel was specified or wrong syntax used."
					bncreply "Syntax: offlinelog.exclude #channel1,#channel2,#channel3"
					bncreply "Valid options are: [join [internalchannels] , ],none"
          bncreply "You can only use this list OR offlinelog.include not both."
					haltoutput
					return
				}
				if {[string equal -nocase [lindex $parameters 2] "none"]} {
					setbncuser $client tag offlinelog.exclude
					bncreply "Done."
					haltoutput
					return
				}
				setbncuser $client tag offlinelog.exclude [string tolower [lindex $parameters 2]]
        setbncuser $client tag offlinelog.include
        bncreply "Done."
				haltoutput
			}
		} elseif {[string equal -nocase [lindex $parameters 1] "offlinelog.include"]} {
			if {[string equal [lindex $parameters 2] ""]} {
				bncreply "Syntax: /sbnc set offlinelog.include #channel1,#channel2,#channel3 or none to unset it"
				bncreply "Channels added to this list will be the only channels logged.  Use this to if you only want to log channels such as a private channel to save log space and reduce BNC lag."
        bncreply "You can only use this list OR offlinelog.exclude not both."
				if {[string equal -nocase [getbncuser $client tag offlinelog.include] ""]} {
					bncreply "Current value: none"
				} else  {
					bncreply "Current value: [getbncuser $client tag offlinelog.include]"
				}
				haltoutput
				return
			} else {
				if {!([string equal -nocase [lindex $parameters 3] ""])} {
					bncreply "One of the settings you supplied are incorrect, either wrong channel was specified or wrong syntax used."
					bncreply "Syntax: offlinelog.include #channel1,#channel2,#channel3"
					bncreply "Valid options are: [join [internalchannels] , ],none"
          bncreply "You can only use this list OR offlinelog.exclude not both."
					haltoutput
					return
				}
				if {[string equal -nocase [lindex $parameters 2] "none"]} {
					setbncuser $client tag offlinelog.include
					bncreply "Done."
					haltoutput
					return
				}
				setbncuser $client tag offlinelog.include [string tolower [lindex $parameters 2]]
        setbncuser $client tag offlinelog.exclude
        bncreply "Done."
				haltoutput
			}
		} elseif {[string equal -nocase [lindex $parameters 1] "offlinelog.options"]} {
			if {[string equal [lindex $parameters 2] ""]} {
				bncreply "Syntax: /sbnc set offlinelog.options option1,option2,option3"
				bncreply "Valid options are: [bncgetglobaltag chanlog] or reset."
				if {[string equal -nocase [getbncuser $client tag offlinelog.options] ""]} {
					bncreply "Current value: [bncgetglobaltag chanlog]"
				} else  {
					bncreply "Current value: [getbncuser $client tag offlinelog.options]"
				}
				haltoutput
				return
			} else {
				foreach item [split [lindex $parameters 2] ,] {
					if {[lsearch -exact [split [list [bncgetglobaltag chanlog],reset] ,] [string tolower $item]] == -1  || !([string equal -nocase [lindex $parameters 3] ""])} {
						bncreply "One of the settings you supplied are incorrect or wrong syntax used."
						bncreply "Syntax: offlinelog.options option1,option2,option3 or reset."
						bncreply "Valid options are: [bncgetglobaltag chanlog],reset"
						haltoutput
						return
					}
				}
				if {[string equal -nocase [lindex $parameters 2] "reset"]} {
					setbncuser $client tag offlinelog.options
					bncreply "Done."
					haltoutput
					return
				}
				setbncuser $client tag offlinelog.options [string tolower [lindex $parameters 2]]
				bncreply "Done."
				haltoutput
			}
		} elseif {[string equal [lindex $parameters 1] ""]} {
			if {[string equal -nocase [getbncuser $client tag offlinelog] ""]} {
				utimer 0 [list bncreply "offlinelog - off"]
			} else  {
				utimer 0 [list bncreply "offlinelog - [getbncuser $client tag offlinelog]"]
			}
			if {[string equal -nocase [getbncuser $client tag offlinelog.method] ""]} {
				utimer 0 [list bncreply "offlinelog.method - Auto"]
			} else  {
				utimer 0 [list bncreply "offlinelog.method - [getbncuser $client tag offlinelog.method]"]
			}
			if {[string equal -nocase [getbncuser $client tag offlinelog.exclude] ""]} {
				utimer 0 [list bncreply "offlinelog.exclude - None set"]
			} else  {
				utimer 0 [list bncreply "offlinelog.exclude - [getbncuser $client tag offlinelog.exclude]"]
			}
      if {[string equal -nocase [getbncuser $client tag offlinelog.include] ""]} {
				utimer 0 [list bncreply "offlinelog.include - None set"]
			} else  {
				utimer 0 [list bncreply "offlinelog.include - [getbncuser $client tag offlinelog.include]"]
			}
			if {[string equal -nocase [getbncuser $client tag offlinelog.options] ""]} {
				utimer 0 [list bncreply "offlinelog.options - [bncgetglobaltag chanlog]"]
			} else  {
				utimer 0 [list bncreply "offlinelog.options - [getbncuser $client tag offlinelog.options]"]
			}
		}
	}
	if {[string equal [lindex $parameters 0] "unset"]} {
		if {[string equal -nocase [lindex $parameters 1] "offlinelog"]} {
			setbncuser $client tag offlinelog
			bncreply "Done."
			haltoutput
		}
		if {[string equal -nocase [lindex $parameters 1] "offlinelog.method"]} {
			setbncuser $client tag offlinelog.method
			bncreply "Done."
			haltoutput
		}
		if {[string equal -nocase [lindex $parameters 1] "offlinelog.exclude"]} {
			setbncuser $client tag offlinelog.exclude
			bncreply "Done."
			haltoutput
		}
    if {[string equal -nocase [lindex $parameters 1] "offlinelog.include"]} {
			setbncuser $client tag offlinelog.include
			bncreply "Done."
			haltoutput
		}
		if {[string equal -nocase [lindex $parameters 1] "offlinelog.options"]} {
			setbncuser $client tag offlinelog.options
			bncreply "Done."
			haltoutput
		}
	}
	if {[string equal -nocase [lindex $parameters 0] "playlog"]} {
		if {![file exists users/$client.clog]} {
			bncreply "Your channel log is empty. Type '/sbnc help playlog' for help with this feature."
		} else {
			log:playlog $client
			bncreply "Done."
		}
		haltoutput
	}
	if {[string equal -nocase [lindex $parameters 0] "abortlog"]} {
		if {![file exists users/$client.clog]} {
			bncreply "Your channel log is empty. Type '/sbnc help abortlog' for help with this feature."
		} else {
			setbncuser $client tag playinglogs 0
			bncreply "Done."
		}
		haltoutput
	}
	if {[string equal -nocase [lindex $parameters 0] "eraselog"]} {
		if {![file exists users/$client.clog]} {
			bncreply "Your channel log is empty. Type '/sbnc help eraselog' for help with this feature."
		} else {
			file delete "users/$client.clog"
			bncreply "Done."
		}
		haltoutput
	}
	if {[string equal -nocase [lindex $parameters 0] "logwhat"] && [getbncuser [getctx] admin]} {
		if {[string equal [lindex $parameters 1] ""]} {
			bncreply "Current value: [bncgetglobaltag chanlog]"
			bncreply "For help type : /msg -sbnc help logwhat"
			haltoutput
			return
		} else  {
			foreach item [split [lindex $parameters 1] ,] {
				if {[lsearch -exact [list none privmsg notice join part kick quit nick topic mode] [string tolower $item]] == -1  || !([string equal -nocase [lindex $parameters 2] ""])} {
					bncreply "One of the settings you supplied are incorrect or wrong syntax used."
					bncreply "Syntax: /sbnc logwhat option1,option2,option3"
					bncreply "Valid options are: none (log nothing), privmsg, notice, join* , part, kick, quit*, nick*, topic, mode.   *: Can be VERY spammy"
					haltoutput
					return
				}
			}
			bncsetglobaltag chanlog [string tolower [lindex $parameters 1]]
			bncreply "Done."
			haltoutput
		}
	}
	if {[string equal -nocase [lindex $parameters 0] "help"]} {
		set omghelp "You can include/exclude channels and control what is logged from /sbnc set.\nIf you set offlinelog.method to query, you will be warned you have logs to review instead of the logs playing automatically on connect."
		bncaddcommand playlog KHUser "plays your channel logs (if available)" "Play's your channel logs.\n$omghelp\nNote: Gets auto-erased after being played"
		bncaddcommand eraselog KHUser "erases your channel logs (if available)" "Erase your channel logs if you aren't interested in them.\n$omghelp"
		bncaddcommand abortlog KHUser "aborts log playback (if playing)" "Aborts the playback proccess, all following log files will be erased, only perform if you aren't interested in the remaining logs.\n$omghelp"
		if {[getbncuser [getctx] admin]} {
			bncaddcommand logwhat KHAdmin "set logging options" "Tell sbnc what to log for all clients \nSyntax: /sbnc logwhat option1,option2,option3 \nValid options are: none (log nothing), privmsg, notice, join* , part, kick, quit*, nick*, topic, mode.   *: Can be VERY spammy"
		}
	}
}

proc log:attach {client} {
	internalunbind server log:pubm PRIVMSG $client
	internalunbind server log:notc NOTICE $client
	internalunbind server log:join JOIN $client
	internalunbind server log:part PART $client
	internalunbind server log:sign QUIT $client
	internalunbind server log:kick KICK $client
	internalunbind server log:topic TOPIC $client
	internalunbind server log:nick NICK $client
	internalunbind server log:mode MODE $client
	if {[file exists users/$client.clog]} {
		if {[string equal -nocase [getbncuser $client tag offlinelog.method] "query"]} {
			setctx $client
			bncreply "You have new channel messages. Use '/msg -sBNC playlog' to view them."
			return
		} else  {
			utimer 5 [list log:playlog $client]
		}
	}
}

proc log:redetach {client} {
	log:detach $client
}

proc log:detach {client} {
	if {[getbncuser $client hasclient] || [string equal -nocase [getbncuser $client tag offlinelog] ""] || [string equal -nocase [bncgetglobaltag chanlog] "none"]} {return "Client attached, or none/off set"}
	set x [split [getbncuser $client tag offlinelog.options] ,]
	if {[string equal -nocase $x ""]} {
		set x [split [bncgetglobaltag chanlog] ,]
	}
	if {!([lsearch -exact $x "privmsg"] == -1)} {
		internalbind server log:pubm PRIVMSG $client
	}
	if {!([lsearch -exact $x "notice"] == -1)} {
		internalbind server log:notc NOTICE $client
	}
	if {!([lsearch -exact $x "join"] == -1)} {
		internalbind server log:join JOIN $client
	}
	if {!([lsearch -exact $x "part"] == -1)} {
		internalbind server log:part PART $client
	}
	if {!([lsearch -exact $x "quit"] == -1)} {
		internalbind server log:sign QUIT $client
	}
	if {!([lsearch -exact $x "kick"] == -1)} {
		internalbind server log:kick KICK $client
	}
	if {!([lsearch -exact $x "topic"] == -1)} {
		internalbind server log:topic TOPIC $client
	}
	if {!([lsearch -exact $x "nick"] == -1)} {
		internalbind server log:nick NICK $client
	}
	if {!([lsearch -exact $x "mode"] == -1)} {
		internalbind server log:mode MODE $client
	}
	putmainlog "Starting offline log for $client"
}

proc log:validchan {client chan} {
  set include [split [getbncuser $client tag offlinelog.include] ,]
  set exclude [split [getbncuser $client tag offlinelog.exclude] ,]
  if {[llength $include] > 0} {
    if {[lsearch $include [string tolower $chan]] == -1} { return 0 }
  }
  if {[lsearch $exclude [string tolower $chan]] != -1} { return 0 }
  return 1
}

proc log:pubm {client arg} {
	if {![string equal -nocase [lindex $arg 1] "PRIVMSG"] || ![log:validchan $client [lindex $arg 2]] || [isbotnick [lindex [split [lindex $arg 0] !] 0]]} {return exclude}
	if {[regexp "^\001ACTION (.*?)\001$" [lindex $arg 3] foo text]} {
		log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00306 *[lindex [split [lindex $arg 0] !] 0]* [lrange [lindex $arg 3] 1 end] \017\00306*"
	} else  {
		log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00304 <[lindex [split [lindex $arg 0] !] 0]>\003 [lindex $arg 3]"
	}
	haltoutput
}

proc log:notc {client arg} {
	if {![string equal -nocase [lindex $arg 1] "NOTICE"] || ![log:validchan $client [lindex $arg 2]] || [isbotnick [lindex [split [lindex $arg 0] !] 0]]} {return exclude}
	log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00305 -[lindex [split [lindex $arg 0] !] 0]- [lindex $arg 3]"
	haltoutput
}

proc log:join {client arg} {
	if {![string equal -nocase [lindex $arg 1] "JOIN"] || ![log:validchan $client [lindex $arg 2]]} {return exclude}
	log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00310 [lindex $arg 0] joined [lindex $arg 2]"
}

proc log:part {client arg} {
	if {![string equal -nocase [lindex $arg 1] "PART"] || ![log:validchan $client [lindex $arg 2]]} {return exclude}
	log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00314 [lindex $arg 0] parted [lindex $arg 2] ([lindex $arg 3]\017\00314)"
}

proc log:sign {client arg} {
	if {![string equal -nocase [lindex $arg 1] "QUIT"]} {return exclude}
	setctx $client
	foreach chan [internalchannels] {
		if {[log:validchan $client $chan]} {
			if {[onchan [lindex [split [lindex $arg 0] !] 0] $chan]} {
				log:everything $client $chan "\[[log:time $client]\]:\00302 [lindex $arg 0] Quit ([lindex $arg 2]\017\00302)"
			}
		}
	}
}

proc log:kick {client arg} {
	if {![string equal -nocase [lindex $arg 1] "KICK"] || ![log:validchan $client [lindex $arg 2]]} {return exclude}
	log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00310 [lindex $arg 0] Kicked [lindex $arg 3] ([lindex $arg 4]\017\00310)"
}

proc log:topic {client arg} {
	if {![string equal -nocase [lindex $arg 1] "TOPIC"] || ![log:validchan $client [lindex $arg 2]]} {return exclude}
	log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00310 [lindex $arg 0] changed topic to \"[lindex $arg 3]\017\00310\""
}

proc log:nick {client arg} {
	if {![string equal -nocase [lindex $arg 1] "NICK"]} {return exclude}
	setctx $client
	foreach chan [internalchannels] {
		if {[log:validchan $client $chan]} {
			if {[onchan [lindex $arg 2] $chan]} {
				log:everything $client $chan "\[[log:time $client]\]:\00310 [lindex [split [lindex $arg 0] !] 0] changed nicks to [lindex $arg 2]"
			}
		}
	}
}

proc log:mode {client arg} {
	if {![string equal -nocase [lindex $arg 1] "MODE"] || ![log:validchan $client [lindex $arg 2]]} {return exclude}
	log:everything $client [lindex $arg 2] "\[[log:time $client]\]:\00313 [lindex [split [lindex $arg 0] !] 0] changed mode [lindex $arg 3] [lrange $arg 4 end]"
}

proc log:time {client} {
  return [clock format [expr {[clock seconds] + [getbncuser $client timezone] * 60}] -format {%H:%M:%S} -gmt 1]
}

proc log:everything {client chan arg} {
	set r [open users/$client.clog a+]; puts $r ":-sBNC!core@shroudbnc.info PRIVMSG $chan :$arg"; close $r
}

proc log:playlog {client} {
	setctx $client
	if {![file exists users/$client.clog]} {return empty}
	set fd [open users/$client.clog r]
	set logfile { }
	while {![eof $fd]} {
		set tmp [gets $fd]
		if {[eof $fd]} {break}
		set logfile [lappend logfile [string trim $tmp]]
	}
	close $fd
	setbncuser $client tag playinglogs 1
  utimer 2 [list log:putclientlog $client $logfile]
	file delete "users/$client.clog"
	return done
}

proc log:putclientlog {client text} {
  if {[getbncuser $client tag playinglogs] == 0} {
    bncreply "Aborting log playback, removing [llength $text] log entries."
    return
  }
  bncreply "Playing [llength $text] log entries in batches of 100."
  if {[llength $text] > 100} {
    set newtext [lrange $text 100 end]
    set text [lrange $text 0 99]
  }
	foreach line $text {
		putclient "$line"
	}
  if {[info exists newtext]} {
    utimer 2 [list log:putclientlog $client $newtext]
  } else {
    setbncuser $client tag playinglogs 0
  }
}




