import socket, traceback, time, threading, api, globals
from config import Config
[docs]class IRC:
	def __init__(self, server, config, log, wrapper, address, port, nickname, channels):
		self.socket = False
		self.server = server
		self.config = config
		self.wrapper = wrapper
		self.address = address
		self.port = port
		self.nickname = nickname
		self.channels = channels
		self.log = log
		self.timeout = False
		self.msgQueue = []
		
		self.api = api.API(self.wrapper, "IRC")
		self.api.registerEvent("player.message", self.playerMessageHandle)
		self.api.registerEvent("server.say", self.sayMessageHandle)
[docs]	def init(self):
		while not self.wrapper.halt:
			try:
				self.log.info("Connecting to IRC...")
				self.connect()
				t = threading.Thread(target=self.queue, args=())
				t.daemon = True
				t.start()
				self.handle()
			except:
				for line in traceback.format_exc().split("\n"):
					self.log.error(line)
				self.disconnect("Error in Wrapper.py - restarting")
			self.log.info("Disconnected from IRC")
			time.sleep(5) 
[docs]	def connect(self):
		self.socket = socket.socket()
		self.socket.connect((self.address, self.port))
		self.socket.setblocking(120)
		
		self.send("NICK %s" % self.nickname)
		self.send("USER %s 0 * :%s" % (self.nickname, self.nickname)) 
[docs]	def disconnect(self, message):
		try:
			self.send("QUIT :%s" % message)
			self.socket.close()
			self.socket = False
		except:
			pass 
[docs]	def send(self, payload):
		if self.socket:
			self.socket.send("%s\n" % payload)
		else:
			return False 
[docs]	def playerMessageHandle(self, payload):
		player = payload["player"]
		message = payload["message"]
		self.msgQueue.append("<%s> %s" % (player, message)) 
[docs]	def sayMessageHandle(self, payload):
		player = payload["player"]
		message = payload["message"]
		self.msgQueue.append("[%s] %s" % (player, message)) 
[docs]	def handle(self):
		while self.socket:
			try:
				buffer = self.socket.recv(1024)
				if buffer == "":
					self.log.error("Disconnected from IRC")
					self.socket = False
					break
			except socket.timeout:
				if self.timeout:
					self.socket = False
					break
				else:
					self.send("PING :%s" % self.randomString())
					self.timeout = True
				buffer = ""
			except:
				buffer = ""
			for line in buffer.split("\n"):
				self.line = line
				self.parse() 
[docs]	def queue(self):
		while self.socket:
			for i,message in enumerate(self.msgQueue):
				for channel in self.channels:
					self.send("PRIVMSG %s :%s" % (channel, message))
				del self.msgQueue[i]
			self.msgQueue = []
			time.sleep(0.1) 
[docs]	def filterName(self, name):
		if self.config["IRC"]["obstruct-nicknames"]:
			return "_" + name[1:]
		else:
			return name  
[docs]	def rawConsole(self, payload):
		self.server.console(payload) 
[docs]	def console(self, channel, payload):
		if self.config["IRC"]["show-channel-server"]:
			self.rawConsole({"text": "[%s] " % channel, "color": "gold", "extra": payload})
		else:
			self.rawConsole({"extra": payload}) 
[docs]	def parse(self):
		if self.args(1) == "001":
			for command in self.config["IRC"]["autorun-irc-commands"]:
				self.send(command)
			for channel in self.channels:
				self.send("JOIN %s" % channel)
			self.log.info("Connected to IRC!")
			self.state = True
		if self.args(1) == "JOIN":
			nick = self.args(0)[1:self.args(0).find("!")]
			channel = self.args(2)[1:][:-1]
			self.log.info("%s joined %s" % (nick, channel))
			self.console(channel, [{"text": nick, "color": "green"}, {"text": " joined the channel", "color": "white"}])
			self.wrapper.callEvent("irc.channelJoin", {"user": nick, "channel": channel})
		if self.args(1) == "PART":
			nick = self.args(0)[1:self.args(0).find("!")]
			channel = self.args(2)
			self.log.info("%s parted from %s" % (nick, channel))
			self.console(channel, [{"text": nick, "color": "green"}, {"text": " left the channel", "color": "white"}])
			self.wrapper.callEvent("irc.channelPart", {"user": nick, "channel": channel})
		if self.args(1) == "MODE":
			try:
				nick = self.args(0)[1:self.args(0).find('!')]
				channel = self.args(2)
				modes = self.args(3)
				user = self.args(4)[:-1]
				self.console(channel, [{"text": user, "color": "green"}, {"text": " received modes %s from %s" % (modes, nick), "color": "white"}])
			except:
				pass
		if self.args(0) == "PING":
			self.send("PONG %s" % self.args(1))
		if self.args(1) == "QUIT":
			nick = self.args(0)[1:self.args(0).find("!")]
			message = " ".join(self.line.split(" ")[2:])[1:].strip("\n").strip("\r")
			
			self.wrapper.callEvent("irc.quit", {"user": nick, "message": message})
			self.rawConsole({"text": "[IRC] ", "color": "gold", "extra":[{"text": nick, "color": "green"}, {"text": " quit from IRC", "color": "white"}]})
		if self.args(1) == "PRIVMSG":
			channel = self.args(2)
			nick = self.args(0)[1:self.args(0).find("!")]
			message = " ".join(self.line.split(" ")[3:])[1:].strip("\n").strip("\r")
			
			if channel[0] == "#":
				self.wrapper.callEvent("irc.channelMessage", {"user": nick, "channel": channel, "message": message})
				if message.strip() == ".players":
					users = ""
					for user in self.server.players:
						users += "%s " % user
					self.send("PRIVMSG %s :There are currently %s users on the server: %s" % (channel, len(self.server.players), users))
				elif message.strip() == ".about":
					self.send("PRIVMSG %s :Wrapper.py version %s (build #%d)" % (channel, Config.version, globals.build))
				else:
					self.log.info('[%s] (%s) %s' % (channel, nick, message))
					message = message.decode("utf-8", "ignore")
					self.console(channel, [{"text": "(%s) " % nick, "color": "green"}, {"text": message, "color": "white"}])
			elif self.config["IRC"]["control-from-irc"]:
				self.log.info('[PRIVATE] (%s) %s' % (nick, message))
				def args(i):
					try:
						return message.split(" ")[i]
					except:
						return ""
				def msg(string):
					print "[PRIVATE] (%s) %s" % (self.config["IRC"]["nick"], string)
					self.send("PRIVMSG %s :%s" % (nick, string))
				try:
					self.authorized
				except:
					self.authorized = {}
				if nick in self.authorized:
					if int(time.time()) - self.authorized[nick] < 900:
						if args(0) == 'hi':
							msg('Hey there!')
						elif args(0) == 'help':
							# eventually I need to make help only one or two lines, to prevent getting kicked/banned for spam
							msg("run [command] - run command on server")
							msg("togglebackups - temporarily turn backups on or off. this setting is not permanent and will be lost on restart")
							msg("halt - shutdown server and Wrapper.py, will not auto-restart")
							msg("kill - force server restart without clean shutdown - only use when server is unresponsive")
							msg("start/restart/stop - start the server/automatically stop and start server/stop the server without shutting down Wrapper")
							msg('status - show status of the server')
							msg("Wrapper.py Version %s by benbaptist" % self.wrapper.getBuildString())
							#msg('console - toggle console output to this private message')
						elif args(0) == 'togglebackups':
							self.config["Backups"]["enabled"] = not self.config["Backups"]["enabled"]
							if self.config["Backups"]["enabled"]:
								msg('Backups are now on.')
							else:
								msg('Backups are now off.')
							configure.save()
						elif args(0) == 'run':
							if args(1) == '':
								msg('Usage: run [command]')
							else:
								command = " ".join(message.split(' ')[1:])
								self.server.run(command)
						elif args(0) == 'halt':
							self.wrapper.halt = True
							self.server.run("stop")
							self.server.status = 3
						elif args(0) == 'restart':
							self.server.run('stop')
							self.server.status = 3
						elif args(0) == 'stop':
							self.server.run('stop')
							self.server.start = False
							self.server.status = 3
							msg("Server stopping")
						elif args(0) == 'start':
							self.server.start = True
							msg("Server starting")
						elif args(0) == 'kill':
							self.server.status = 0
							self.server.proc.kill()
							msg("Server terminated.")
						elif args(0) == 'status':
							if self.server.status == 2:
								msg("Server is running.")
							elif self.server.status == 1:
								msg("Server is currently starting/frozen.")
							elif self.server.status == 0:
								msg("Server is stopped. Type 'start' to fire it back up.")
							elif self.server.status == 3:
								msg("Server is in the process of shutting down/restarting.")
							else:
								msg("Server is in unknown state. This is probably a Wrapper.py bug - report it!")
						elif args(0) == "about":
							msg("Wrapper.py by benbaptist - version %s (build #%d)" % (Config.version, globals.build))
						else:
							msg('Unknown command. Type help for more commands')
					else:
						msg("Session expired, re-authorize.")
						del self.authorized[nick]
				else:
					if args(0) == 'auth':
						if args(1) == self.config["IRC"]["control-irc-pass"]:
							msg("Authorization success! You'll remain logged in for 15 minutes.")
							self.authorized[nick] = int(time.time())
						else:
							msg("Invalid password.")
					else:
						msg('Not authorized. Type "auth [password]" to login.') 
[docs]	def args(self, i):
		try:
			return self.line.split(" ")[i]
		except:
			pass