# Copyright (c) 2006 by SpideR <spider312@free.fr> http://spiderou.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

# WeeChat clone scanner
# Scans clones on a chan when you ask it to do (/clones)
# Able to scan for a nick's clones on each join, if you ask it to do (/autoscan)

# TODO : 
# * Disable scan for a nick's join on multiple chans
# * ( Scan for previous nick of a host ) DONE
# ** Stock time, to clean on timer and display it
# * Modularize messages display, configure where it displays

import weechat		# WeeChat API
import re		# Regular Expressions
import time		# Time

################################################################## CONSTANTS ###
SCRIPT_NAME="clonescan"
SCRIPT_VERSION="0.3"
SCRIPT_DESC="clonescan script for weechat"
SCRIPT_DISP=SCRIPT_NAME+" v"+SCRIPT_VERSION

######################################################################## API ###
# Register, Handlers, config check/creation
if weechat.register(SCRIPT_NAME, SCRIPT_VERSION, "unload", SCRIPT_DESC):
	weechat.set_plugin_config('test','test')
	# Usefull global declaration
	yes = [ "1", "yes", "enable", "true" ] # Things considered as "true" in settings
	no = [ "0", "no", "disable", "false" ] # Things considered as "false" in settings
	hosts = [[],[],[]]
	#  Messages
	weechat.add_message_handler("join","onjoin")
	weechat.add_message_handler("part","onpart")
	# Default config management
	def check_config_bool(config_name,default_value):
		config_value = weechat.get_plugin_config(config_name)
		if ( ( config_value not in yes ) and ( config_value not in no ) ):
			weechat.set_plugin_config(config_name,default_value)
			weechat.prnt("Unconfigured "+config_name+" set to '"+default_value+"/"+weechat.get_plugin_config(config_name)+"', see /"+config_name)
	check_config_bool("autoscan","false")
	check_config_bool("checkhost","false")
	# Handlers
	#  Commands
	weechat.add_command_handler("clones","scanchan","Scans clones on specified or current chan","[#chan]")
	weechat.add_command_handler("autoscan","autoscan","Manage auto clone-scanning","[enable|disable|show]")
	weechat.add_command_handler("checkhost","checkhost","Manage host checking","[view|enable|disable|list|clear]")
	weechat.add_command_handler("clone_ignore","toggle_ignore","Manage ignores lists (server,chan,nick,user,host)",
		"[server,chan,nick,user,host] [view|add|del|set] [match|coma separated list]")
	# All seems loaded here
	weechat.prnt(SCRIPT_DISP+" loaded")
else:
	weechat.prnt(SCRIPT_DISP+" not loaded")

# Unload handler
def unload():
	weechat.prnt(SCRIPT_DISP+" now unloaded")
	return weechat.PLUGIN_RC_OK

#################################################################### LOADERS ###
# onJOIN : Auto scan 
def onjoin(server,args): # :SpideR_test_script2!spider@RS2I-26C9FCA9.spiderou.net JOIN :#clonescan
	result = weechat.PLUGIN_RC_OK # Default : go on
	# Check activated
	if ( weechat.get_plugin_config("autoscan") != "true" ):
		result = weechat.PLUGIN_RC_KO
	# Check server not igored
	if ( server in weechat.get_plugin_config("server_ignore").split(" ") ):
		result = weechat.PLUGIN_RC_KO
		#weechat.prnt("Join ignored because of server : "+server)
	# Cut nick@user!host JOIN #chan
	if ( result == weechat.PLUGIN_RC_OK ):
		try: # Let's try cutting JOIN with ':'
			nickuserathost, chan = args.split(" JOIN :")
			#weechat.prnt("OK cutting JOIN with ':' : ["+nickuserathost+"] + ["+chan+"] on ["+server+"]",server,server)
		except ValueError:
			#weechat.prnt("NOK cutting ["+args+"] with ':' on ["+server+"]",server,server)
			try: # Didn't work so let's try without ':' (bug on ogamenet)
				nickuserathost, chan = args.split(" JOIN ")
				#weechat.prnt("OK cutting JOIN without ':' : ["+nickuserathost+"] + ["+chan+"] on ["+server+"]",server,server)
			except ValueError: # None worked
				result = weechat.PLUGIN_RC_KO
				weechat.prnt("Eror cutting JOIN : ["+args+"]",server,server)
		# Check server not igored
		if ( chan in weechat.get_plugin_config("chan_ignore").split(" ") ):
			result = weechat.PLUGIN_RC_KO
			#weechat.prnt("Join ignored because of chan : "+chan)
	# Cut nick@user!host
	if ( result == weechat.PLUGIN_RC_OK ):
		try: 
			nick,user,host = cutnickuserhost(nickuserathost)
			#weechat.prnt("OK cutting nick!user@host : ["+nick+"] ! ["+user+"] @ ["+host+"] on ["+chan+"]",server,server)
		except ValueError:
			result = weechat.PLUGIN_RC_KO
			weechat.prnt("Eror cutting nick!user@host : ["+nickuserathost+"] on ["+chan+"]",server,server)
		# Check server not igored
		if ( nick in weechat.get_plugin_config("nick_ignore").split(" ") ):
			result = weechat.PLUGIN_RC_KO
			#weechat.prnt("Join ignored because of nick : "+nick)
		# Check server not igored
		if ( user in weechat.get_plugin_config("user_ignore").split(" ") ):
			result = weechat.PLUGIN_RC_KO
			#weechat.prnt("Join ignored because of user : "+user)
		# Check server not igored
		if ( host in weechat.get_plugin_config("host_ignore").split(" ") ):
			result = weechat.PLUGIN_RC_KO
			#weechat.prnt("Join ignored because of host : "+host)
		
	
	# all cutted correctly, let's scan and display result if needed
	if ( result == weechat.PLUGIN_RC_OK ):
		clones = scannick(server,chan,nick,host) # Scan for that user's clones
		if ( len(clones) > 0):
			clones.sort() # Theorically sorting that list
			display(server,chan,"Clone sur "+chan+"@"+server+" : "+nick+" = "+", ".join(clones)+" ("+host+")")
		# See if user is in saved hosts
		if ( weechat.get_plugin_config("checkhost") == "true" ):
			if host in hosts[0]:
				posinhosts = hosts[0].index(host)
				if ( nick+'!'+user != hosts[1][posinhosts] ):
					display(server,chan,nick+"@"+chan+" seen under nick "+hosts[1][posinhosts])
	return result

# onPART : Save connexion information
def onpart(server,args): # :SpideR_test_script2!spider@RS2I-26C9FCA9.spiderou.net PART #clonescan :emerge -pv byebye
	result = weechat.PLUGIN_RC_OK
	nick,user,host = cutnickuserhost(args) # Cut nick!user@host
	savehost(host,nick,user)
	return result

# onPART subdivision : save nick/host/time for current user
def savehost(host,nick,user):
	result = False
	actualtime = round(time.time())
	if host in hosts[0]: # Host existing in list : update informations
		posinhosts = hosts[0].index(host)
		hosts[1][posinhosts] = nick+'!'+user # Change stored value for nick
		hosts[2][posinhosts] = actualtime
	else: # Host not existing in list : add it
		result = True
		hosts[0].append(host)
		hosts[1].append(nick+'!'+user)
		hosts[2].append(actualtime)
	return result

# Manual channel scan
def scanchan(server,args):
	result = weechat.PLUGIN_RC_OK
	# Defining chan to scan (contained in args, current chan otherwise)
	if ( args == "" ):
		chan = weechat.get_info("channel",server)
	else:
		chan = args
	# Scan
	if ( chan == "" ):
		result = weechat.PLUGIN_RC_KO
		weechat.prnt("Not on a chan")
	else:
		nicks = weechat.get_nick_info(server,chan)
		if nicks == None:
			result = weechat.PLUGIN_RC_KO
			weechat.prnt("Eror reading nick list")
		else:
			if nicks == {}:
				result = weechat.PLUGIN_RC_KO
				weechat.prnt("Nobody on "+chan+", are you sure it's a chan and you are present on it ?")
			else:
				weechat.prnt("Scanning "+chan+" ...")
				allclones = [] # List containing all detected clones, for not to re-scan them
				nbclones = 0 # number of clones
				for nick in nicks:
					if nick not in allclones:
						host = removeuser(nicks[nick]["host"])
						clones = scannick(server,chan,nick,host)
						if ( len(clones) > 0 ):
							allclones = allclones + clones
							nbclones = nbclones+1
							clones.append(nick) # Regrouping nick and its clones in one list
							clones.sort() # Theorically sorting that list
							weechat.prnt(" - "+", ".join(clones)+" ("+host+")",chan)
				s = "s"
				if ( len(clones) == 1 ):
					s = ""
				weechat.prnt(str(nbclones)+" clone"+s+" found")
	return result
	
#################################################################### DISPLAY ###
# Display messages where they were configures to be displayed (TODO)
def display(server,chan,disp):
	result = weechat.PLUGIN_RC_OK
	#weechat.print_infobar(5,disp) # Display on infobar
	weechat.prnt(disp) # Display on current buffer
	if ( chan != weechat.get_info("channel",server) ): # if current buffer isn't concerned chan
		weechat.prnt(disp,chan) # Display on concerned chan
	#weechat.prnt(disp,server,server) # Display on server buffer
	return result

####################################################################### SCAN ###
# Returns list of nick clones (not containing nick himself)
def scannick(server,chan,nick,host):
	cloneof = [] # Default return value : list containing no clones
	compares = weechat.get_nick_info(server,chan)
	if compares == None:
		weechat.prnt("Eror reading nicklist on "+chan+" on "+server)
	else:
		if compares == {}:
			weechat.prnt("No nicks on "+chan+" on "+server)
		else:
			for compare in compares:
				if ( ( nick != compare ) and ( host == removeuser(compares[compare]["host"])) ):
					cloneof.append(compare)
	return cloneof
################################################################## FUNCTIONS ###
# Return host by user@host
def removeuser(userathost):
	splitted = userathost.split("@")
	return splitted[1]

# Cut :nick!user@host
def cutnickuserhost(nickuserhost):
	mask = re.compile(':(\S*)!(\S*)@(\S*)') # Define RegExp
			# :SaT_!~sat@ANancy-751-1-14-249.w90-6.abo.wanadoo.fr
	# maskmatch = mask.match(nickuserhost) # Apply Regexp
	matches = mask.findall(nickuserhost)
	if len(matches) == 0 :
		result = "","",""
	else:
		result = matches[0][0],matches[0][1],matches[0][2]
	return result

########################################################## CONFIG MANAGEMENT ###
# Config auto scan
def autoscan(server,args):
	# Get current value
	autoscan = weechat.get_plugin_config("autoscan")
	# Testing / repairing
	if ( autoscan == "true" ):
		auto = True
	elif ( autoscan == "false" ):
		auto = False
	else:
		weechat.prnt("Unknown value ["+autoscan+"], disabling")
		weechat.set_plugin_config("autoscan","false")
		auto = False
	# Manage arg
	if ( args in yes ):
		if auto:
			weechat.prnt("Auto clone scanning remain enabled")
		else:
			weechat.set_plugin_config("autoscan","true")
			weechat.prnt("Auto clone scanning is now enabled")
	elif ( args in no ):
		if auto:
			weechat.set_plugin_config("autoscan","false")
			weechat.prnt("Auto clone scanning is now disabled")
		else:
			weechat.prnt("Auto clone scanning remain disabled")
	else:
		if auto:
			weechat.prnt("Auto clone scanning enabled")
		else:
			weechat.prnt("Auto clone scanning disabled")
	return weechat.PLUGIN_RC_OK
# Config host checking
def checkhost(server,args):
	global hosts
	# Get current value
	checkhost = weechat.get_plugin_config("checkhost")
	# Testing / repairing
	if ( checkhost == "true" ):
		auto = True
	elif ( checkhost == "false" ):
		auto = False
	else:
		weechat.prnt("Unknown value ["+checkhost+"], disabling")
		weechat.set_plugin_config("checkhost","false")
		auto = False
	# Manage arg
	if ( args in yes ):
		if auto:
			weechat.prnt("Host checking remain enabled")
		else:
			weechat.set_plugin_config("checkhost","true")
			weechat.prnt("Host checking is now enabled")
	elif ( args in no ):
		if auto:
			weechat.set_plugin_config("checkhost","false")
			weechat.prnt("Host checking is now disabled")
		else:
			weechat.prnt("Host checking remain disabled")
		hosts = [[],[],[]]
	elif ( args == "list" ):
		actualtime = round(time.time())
		weechat.prnt("Host list : ( host : last nick (age) )")
		for i in range(0, len(hosts[0])):
			weechat.prnt(" - "+hosts[0][i]+" : "+hosts[1][i]+" ("+str(actualtime - hosts[2][i])+")")
		weechat.prnt(str(len(hosts[0]))+" saved hosts in list")
	elif ( args == "clear" ):
		hosts = [[],[],[]]
	else: # "show", empty, other cases
		if auto:
			weechat.prnt("Host checking enabled")
		else:
			weechat.prnt("Host checking disabled")
	return weechat.PLUGIN_RC_OK
# Config servers to ignore
def toggle_ignore(server,arg):
	result = weechat.PLUGIN_RC_OK
	# Parse args
	args = arg.split(" ")
	if ( len(args) > 0 ):
		target = args[0]
	else:
		target = ""
	if ( len(args) > 1 ):
		action = args[1]
	else:
		action = ""
	if ( len(args) > 2 ):
		params = args[2:]
	else:
		params = []
	# Check target
	if ( target not in ["server","chan","nick","user","host"]):
		weechat.prnt("Unknown target '"+target+"', please use server, chan, host or nick")
		result = weechat.PLUGIN_RC_KO
	else:
		# Parse setting
		ignore_config = weechat.get_plugin_config(target+"_ignore")
		if ( ignore_config != "" ):
			ignore = ignore_config.split(" ")
		else:
			weechat.prnt("clone "+target+" ignore list not set, setting it empty")
			ignore = []
		# Manage action
		if ( action in ["view","show"] ):
			weechat.prnt("Current clone "+target+" ignore list : "+" ".join(ignore))
		elif ( action in ["+","add"] ):
			weechat.prnt("Adding to clone "+target+" ignore list : "+" ".join(params))
			if ( len(params) > 0 ):
				for i in params:
					if ( i in ignore ):
						weechat.prnt("Not adding "+i+" from clone "+target+" ignore list because it was in list")
					else:
						ignore.append(i)
			else:
				weechat.prnt("Nothing to add to clone "+target+" ignore list")
				result = weechat.PLUGIN_RC_KO
		elif ( action in ["-","del"] ):
			weechat.prnt("Deleting from clone "+target+" ignore list : "+" ".join(params))
			if ( len(params) > 0 ):
				for i in params:
					if ( i in ignore ):
						ignore.remove(i)
					else:
						weechat.prnt("Not removing "+i+" from clone "+target+" ignore list because it was not in list")
			else:
				weechat.prnt("Nothing to remove from clone "+target+" ignore list")
				result = weechat.PLUGIN_RC_KO
		elif ( action == "set" ):
			ignore = params
			weechat.prnt("Setting clone "+target+" ignores to : "+" ".join(ignore))
		else:
			weechat.prnt("Unknown action '"+action+"' ("+str(params)+"), please use view, add, del or set")
			result = weechat.PLUGIN_RC_KO
		# Synching new list with conf
		if ( len(ignore) > 2 ): # Sorting it if needed
			ignore.sort()
		new_ignore_config = " ".join(ignore)
		if ( new_ignore_config == ignore_config ):
			weechat.prnt("Clone "+target+" ignores list unchanged")
		else:
			if ( weechat.set_plugin_config(target+"_ignore",new_ignore_config) == 1 ):
				weechat.prnt("Clone "+target+" ignores list set to : "+new_ignore_config)
			else:
				weechat.prnt("Eror while setting clone "+target+" ignores list")
				result = weechat.PLUGIN_RC_KO
	return result
