#! /usr/bin/env python from threading import Thread from Pyro.errors import NamingError import Pyro.core, Pyro.naming import sys CHAT_SERVER_GROUP = ":ChatBox" CHAT_SERVER_NAME = CHAT_SERVER_GROUP+".Server" # Chat box administration server. # Handles logins, logouts, channels and nicknames, and the chatting. class ChatBox(Pyro.core.ObjBase): def __init__(self): Pyro.core.ObjBase.__init__(self) self.channels={} # registered channels { channel --> (nick, client callback) list } self.nicks=[] # all registered nicks on this server def getChannels(self): return self.channels.keys() def getNicks(self): return self.nicks def join(self, channel, nick, callback): if nick in self.nicks: raise ValueError,'this nick is already in use' if not self.channels.has_key(channel): print 'CREATING NEW CHANNEL',channel self.channels[channel]=[] self.channels[channel].append((nick,callback)) self.nicks.append(nick) callback._setOneway('message') # don't wait for results for this method print nick,'JOINED',channel self.publish(channel,'SERVER','** '+nick+' joined **') nicks=[] for (n,c) in self.channels[channel]: nicks.append(n) return nicks def leave(self,channel,nick): if not self.channels.has_key(channel): print 'IGNORED UNKNOWN CHANNEL',channel return for (n,c) in self.channels[channel]: if n==nick: self.channels[channel].remove((n,c)) break self.publish(channel,'SERVER','** '+nick+' left **') if len(self.channels[channel])<1: del self.channels[channel] print 'REMOVED CHANNEL',channel self.nicks.remove(nick) print nick,'LEFT',channel def publish(self, channel, nick, msg): # This must be performed in its own thread otherwise a thread deadlock may occur # when the publish is performed from within a call of a subscriber! Thread(target=self._do_publish, args=(channel, nick ,msg)).start() def _do_publish(self, channel, nick, msg): if not self.channels.has_key(channel): print 'IGNORED UNKNOWN CHANNEL',channel return for (n,c) in self.channels[channel][:]: # use a copy of the list try: c.message(nick,msg) # oneway call except Pyro.errors.ConnectionClosedError,x: # connection dropped, remove the listener if it's still there # check for existence because other thread may have killed it already if (n,c) in self.channels[channel]: self.channels[channel].remove((n,c)) print 'Removed dead listener',n,c except: # ignore all other errors pass def main(): Pyro.core.initServer() daemon = Pyro.core.Daemon() ns = Pyro.naming.NameServerLocator().getNS() daemon.useNameServer(ns) # make sure our namespace group exists, and that our object name doesn't try: ns.createGroup(CHAT_SERVER_GROUP) except NamingError: pass try: ns.unregister(CHAT_SERVER_NAME) except NamingError: pass uri=daemon.connect(ChatBox(),CHAT_SERVER_NAME) # enter the service loop. print 'Chatbox open.' daemon.requestLoop() if __name__=='__main__': main()