Browse Source

In place reloading FTW!

Douglas William Thrift 14 years ago
parent
commit
b03d26af98
1 changed files with 90 additions and 52 deletions
  1. 90 52
      locationbot.py

+ 90 - 52
locationbot.py

@@ -21,7 +21,7 @@ import urllib, urllib2
 import warnings
 
 class LocationBot(ircbot.SingleServerIRCBot):
-	def __init__(self):
+	def __init__(self, bot = None):
 		config = SafeConfigParser({'hostname': ''})
 
 		config.read('locationbot.ini')
@@ -56,11 +56,24 @@ class LocationBot(ircbot.SingleServerIRCBot):
 		except NoOptionError, error:
 			sys.exit(error)
 
+		ircbot.SingleServerIRCBot.__init__(self, servers, nick, 'Location Bot')
+
+		self.__latitude_timer_lock = threading.Lock()
+		self.__latitude_timer = None
 		self.__locations_lock = threading.Lock()
-		self.__locations = []
-		self.__logins = {}
 
-		ircbot.SingleServerIRCBot.__init__(self, servers, nick, 'Location Bot')
+		if bot is None:
+			self.__locations = []
+			self.__logins = {}
+			self.__reloading = False
+		else:
+			irclibobj = self.ircobj.connections[0].irclibobj
+			self.ircobj.connections[0] = bot.ircobj.connections[0]
+			self.ircobj.connections[0].irclibobj = irclibobj
+			self.channels = bot.channels
+			self.__locations = bot.__locations
+			self.__logins = bot.__logins
+			self.__reloading = True
 
 	def __admin(self, nickmask):
 		return _or(lambda admin: irclib.mask_matches(nickmask, admin), self.__admins)
@@ -74,7 +87,43 @@ class LocationBot(ircbot.SingleServerIRCBot):
 
 		return _or(lambda channel: channel.has_user(nick), channels)
 
-	def __do_latitude(self):
+	def __help(self, connection, nick, admin, login, arguments):
+		command = arguments.split(None, 1)[0].lstrip('!') if arguments else None
+		commands = {
+			'help': ('[command]', 'show this help message'),
+			'login': ('[nick] [secret]', 'log in as nick with secret or using masks'),
+			'register': ('[nick] secret', 'register as nick with secret'),
+			'status': ('[nick]', 'show where everybody or a nick is'),
+		}
+
+		if login:
+			commands.update({
+				'latitude': ('[id]', 'shortcut for !set latitude [id]'),
+				'logout': ('', 'log out as nick'),
+				'set': ('[key [value]]', 'display or set variables'),
+				'unset': ('key', 'unset a variable'),
+			})
+
+		if admin:
+			commands.update({
+				'reload': ('', 'reload with more up to date code'),
+				'restart': ('', 'quit and join running more up to date code'),
+				'say': ('nick|channel message', 'say message to nick or channel'),
+				'who': ('', 'show who is logged in'),
+			})
+
+		connection.privmsg(nick, 'Command     Arguments               Description')
+
+		def help(command, arguments, description):
+			connection.privmsg(nick, '!%-10s %-23s %s' % (command, arguments, description))
+
+		if command in commands:
+			help(command, *commands[command])
+		else:
+			for command, (arguments, description) in sorted(commands.iteritems()):
+				help(command, arguments, description)
+
+	def __latitude(self):
 		now = datetime.utcnow()
 
 		try:
@@ -125,44 +174,10 @@ class LocationBot(ircbot.SingleServerIRCBot):
 					for channel in frozenset(channels).intersection(self.__channels):
 						self.__locations.append((nick, channel, locations[latitude]))
 
-		self.__latitude = threading.Timer((self.__latitude_next - datetime.utcnow()).seconds, self.__do_latitude)
-
-		self.__latitude.start()
-
-	def __help(self, connection, nick, admin, login, arguments):
-		command = arguments.split(None, 1)[0].lstrip('!') if arguments else None
-		commands = {
-			'help': ('[command]', 'show this help message'),
-			'login': ('[nick] [secret]', 'log in as nick with secret or using masks'),
-			'register': ('[nick] secret', 'register as nick with secret'),
-			'status': ('[nick]', 'show where everybody or a nick is'),
-		}
-
-		if login:
-			commands.update({
-				'latitude': ('[id]', 'shortcut for !set latitude [id]'),
-				'logout': ('', 'log out as nick'),
-				'set': ('[key [value]]', 'display or set variables'),
-				'unset': ('key', 'unset a variable'),
-			})
-
-		if admin:
-			commands.update({
-				'restart': ('', 'quit and join running more up to date code'),
-				'say': ('nick|channel message', 'say message to nick or channel'),
-				'who': ('', 'show who is logged in'),
-			})
+		with self.__latitude_timer_lock:
+			self.__latitude_timer = threading.Timer((self.__latitude_next - datetime.utcnow()).seconds, self.__latitude)
 
-		connection.privmsg(nick, 'Command     Arguments              Description')
-
-		def help(command, arguments, description):
-			connection.privmsg(nick, '!%-10s %-22s %s' % (command, arguments, description))
-
-		if command in commands:
-			help(command, *commands[command])
-		else:
-			for command, (arguments, description) in sorted(commands.iteritems()):
-				help(command, arguments, description)
+			self.__latitude_timer.start()
 
 	def __login(self, connection, nickmask, nick, arguments = ''):
 		login = nick
@@ -217,6 +232,11 @@ class LocationBot(ircbot.SingleServerIRCBot):
 	def __logout(self, connection, nick):
 		connection.privmsg(nick, 'logged out as "%s"' % self.__logins.pop(nick)[0])
 
+	def __reload(self, connection, nick):
+		self.__reloading = True
+
+		connection.privmsg(nick, 'reloading')
+
 	def __restart(self, connection):
 		connection.disconnect('Restarting')
 		os.execvp(sys.argv[0], sys.argv)
@@ -225,7 +245,7 @@ class LocationBot(ircbot.SingleServerIRCBot):
 		try:
 			nick_channel, message = arguments.split(None, 1)
 		except ValueError:
-			self.__help(connection, nick, True, False, 'say')
+			return self.__help(connection, nick, True, False, 'say')
 
 		if irclib.is_channel(nick_channel):
 			if nick_channel not in self.channels:
@@ -313,6 +333,8 @@ class LocationBot(ircbot.SingleServerIRCBot):
 				self.__login(connection, nickmask, nick, arguments)
 			elif login and command == 'logout':
 				self.__logout(connection, nick)
+			elif admin and command == 'reload':
+				self.__reload(connection, nick)
 			elif admin and command == 'restart':
 				self.__restart(connection)
 			elif admin and command == 'say':
@@ -332,15 +354,17 @@ class LocationBot(ircbot.SingleServerIRCBot):
 			connection.join(channel)
 
 	def start(self):
-		self.__latitude = threading.Thread(None, self.__do_latitude)
-		self.__latitude.daemon = True
+		self.__latitude_thread = threading.Thread(None, self.__latitude)
+		self.__latitude_thread.daemon = True
 
-		self.__latitude.start()
-		self._connect()
+		self.__latitude_thread.start()
 
-		while True:
-			self.ircobj.process_once(0.2)
+		if not self.__reloading:
+			self._connect()
+		else:
+			self.__reloading = False
 
+		while not self.__reloading:
 			if self.__locations_lock.acquire(False):
 				if self.__locations and sorted(self.__channels) == sorted(self.channels.keys()):
 					for nick, channel, location in self.__locations:
@@ -350,13 +374,27 @@ class LocationBot(ircbot.SingleServerIRCBot):
 
 				self.__locations_lock.release()
 
+			self.ircobj.process_once(0.2)
+
+		self.__latitude_thread.join()
+
+		with self.__latitude_timer_lock:
+			if self.__latitude_timer is not None:
+				self.__latitude_timer.cancel()
+				self.__latitude_timer.join()
+
 def _or(function, values):
 	return reduce(lambda a, b: a or b, map(function, values) if values else [False])
 
 if __name__ == '__main__':
-	locationbot = LocationBot()
+	import locationbot
+
+	bot = None
 
 	try:
-		locationbot.start()
+		while True:
+			bot = reload(locationbot).LocationBot(bot)
+
+			bot.start()
 	except KeyboardInterrupt:
-		locationbot.connection.disconnect('Oh no!')
+		bot.connection.disconnect('Oh no!')