コード例 #1
0
ファイル: IRCserver.py プロジェクト: Clarvel/Py3bot
class IRCServer():
	"""
	class for connecting to an IRC server
	1 server per connection
	"""
	_reconnectTimeout = 300


	def __init__(self, host, port, terminalID = "", password = None):
		self.receivers = {}
		self.quitMessage = ""
		self.password = password
		self.nickNames = Cycle(NICKNAMES)

		self._terminalID = terminalID # shorthand for server in terminal
		self._currentReceiverKey = None
		self._connection = Connection(host, port, self._parseInput)
		self._logger = Logger("%s/%s" % (host, "SERVER"))
		self._clientObject = self._newReceiver(self.nickNames.val())
		self._welcomeEvent = threading.Event()
		self._reconnectEvent = threading.Event()
		self._reconnectEvent.set()

	#-------------------------------------------------------------------------

	def connect(self):
		"""
		connect to the host/port
		raises error on fail
		"""
		try:
			connection = self._connection.connect()
		except Exception as e:
			raise IRCServerError("Connection failed: [%s] %s"%(e.__name__, e))

		if(self.password):
			self.sendRaw("PASS %s" % (self.password))
		self.sendRaw("NICK %s" % self.nickNames.val())
		self.sendRaw("USER %s %s * :%s"%(LOGIN_NAME, socket.gethostname(), 
			REAL_NAME))

		self._welcomeEvent.clear()
		self._welcomeEvent.wait()

	def disconnect(self, message = None):
		if(self.isConnected()):
			if not message:
				message = self.quitMessage
			self.sendCmd("QUIT", None, message)
		self._connection.disconnect()

	def reconnect(self):
		"""
		reconnecting function
		prevents multiple threads from trying to reconnect at once
		pauses all reconnect tries until reconnect succeeds or fails
		"""
		if not self._reconnectEvent.is_set():
			self._reconnectEvent.wait()
		else:
			self._reconnectEvent.clear()
			self.disconnect()
			error = None
			for timer in range(5, self._reconnectTimeout, 15):
				self.log("Reconnecting...")
				try:
					self.connect()
				except Exception as e:
					error = e
					time.sleep(timer)
				else:
					break
			else:
				self.logE("Reconnect failed after %ds: %s"%(timer/60, error))
			self._reconnectEvent.set()

	#-------------------------------------------------------------------------

	def sendRaw(self, data):
		"""sends raw data strings to the server connection"""
		try:
			self._connection.send(data.rstrip())
		except Exception as e:
			self.reconnect()

			self.logE("Data send failed: %s" % e)

	def sendCmd(self, command, meta=None, *message): # TODO limit message length my receiver's full name@host
		"""
		sends command with optional meta data and message
		will split messages to ensure they're under the maximum length
		"""
		if meta:
			if isinstance(meta, list):
				meta = " ".join(meta)
			command = "%s %s" % (command, meta)
		if message:
			msg = " ".join(list(message))
			strLim = MESSAGE_LENGTH_LIMIT - (len(command) + 2)
			a=0
			for a in range(strLim, len(msg), strLim):
				self.sendRaw("%s :%s" % (command, msg[a-strLim:a]))
			self.sendRaw("%s :%s" % (command, msg[a:len(msg)]))
		else:
			self.sendRaw(command)

	def _parseInput(self, line):
		"""overloadable function to receive commands from conected server"""
		self.log(line)

	#-------------------------------------------------------------------------
	
	def _newReceiver(self, name):
		if any(name.startswith(prefix) for prefix in CHANNEL_PREFIXES):
			newRec = IRCChannel(name, self._connection.host)
		else:
			newRec = IRCClient(name, self._connection.host)
		self.receivers[name.lower()] = newRec
		if len(self.receivers) == 1:
			self.swapCurrentReceiver(name)
		return newRec

	def _delReceiver(self, name):
		"""
		removes the specified receiver from the list
		"""
		self.receivers.pop(name.lower())

	def _renameReceiver(self, oldName, newName):
		"""
		renames the receiver and alters the key to it
		"""
		old = self.receivers.pop(oldName.lower())
		old.name = newName
		self.receivers[newName.lower()] = old


	def swapCurrentReceiver(self, name):
		name = name.lower()
		if name in self.receivers:
			self._currentReceiverKey = name
		else:
			raise IRCServerError("Unknown user/channel: %s" % name)

	def listReceivers(self):
		return [b.name for b in self.receivers.values()]

	#-------------------------------------------------------------------------

	def getReceiver(self, name=None):
		"""
		returns a Receiver class object 
		if no name specified, uses the current receiver
		"""
		if not name:
			if(not self._currentReceiverKey):
				raise IRCServerError("No known channels/users for this Server!")
			return self.receivers[self._currentReceiverKey]
		elif isinstance(name, IRCReceiver):
			name = str(name)
		name = name.lower()
		try:
			receiver = self.receivers[name]
		except KeyError as e:
			raise IRCServerError("No channel/user found matching [%s]" % (name))
		else:
			return receiver

	def getChannel(self, name=None):
		receiver = self.getReceiver(name)
		if(isinstance(receiver, IRCChannel)):
			return receiver
		raise IRCServerError("[%s] is not a channel" % name)

	def getClient(self, name=None):
		receiver = self.getReceiver(name)
		if(isinstance(receiver, IRCClient)):
			return receiver
		raise IRCServerError("[%s] is not a client" % name)

	#-------------------------------------------------------------------------

	def isConnected(self):
		""""""
		return self._connection.isConnected()

	def getHost(self):
		"""returns host string"""
		return self._connection.host

	def getNick(self):
		"""returns the current nickname"""
		return self._clientObject.name

	#-------------------------------------------------------------------------

	def log(self, message):
		self._logger.log(message)
		print("[%s]%s" % (self._terminalID, message))

	def logE(self, message):
		self.log("[ERROR]: %s" % (message))