from twisted.spread import pb from twisted.internet import reactor from twisted.cred import checkers, portal from zope.interface import implements #[http://twistedmatrix.com/documents/current/core/howto/pb-cred.html] class ChatS: def __init__(self): self.groups = {} self.users = [] #groups should be an option, but not the only option - #figure out how to implement user-to-user chat without a group, or implicitly through a group #for example: clicking on an online user creates a unique group ("user1+user2") and calls join group for the clicker and the clickee def joinGroup(self, groupN, user): #if group doesn't exist...create if not groupN in self.groups: #creates a group object w/ given name, and put in dict w/ name as key self.groups[groupN] = Group(groupN) print "%s created." % (self.groups[groupN].name) #add user to group groupObj = self.groups[groupN] groupObj.addUser(user) print "%s really added to %s" % (user.name, groupObj.name) print "Groups: %s" % (self.groups) return self.groups[groupN] # TODO keep logs (where?) #When the client runs login to request the Perspective, #they can provide it with an optional client argument #(which must be a pb.Referenceable object). #If they do, then a reference to that object will be handed #to the realm's requestAvatar in the mind argument. class MyPerspective(pb.Avatar): def __init__(self, name): self.name = name def attached(self, mind): self.remote = mind def detached(self, mind): self.remote = None def perspective_joinGroup(self, groupN): print "about to call the REAL joinGroup" return self.server.joinGroup(groupN, self) #provide a way for the client to access the user list def perspective_userList(self): return ChatS().users def perspective_groupList(self): return ChatS().groups.keys def send(self, message): self.remote.callRemote("print", message) #using viewable means that the class knows which avatar is using it class Group(pb.Viewable): def __init__(self, groupN): self.name = groupN self.users = [] def addUser(self, user): self.users.append(user) print "Group consists of %s" % (self.users) def view_send(self, sender, message): #"Notice that the client uses perspective_joinGroup to both join a group #and retrieve a RemoteReference to the Group object. However, the reference #they get is actually to a special intermediate object called a pb.ViewPoint. #When they do group.callRemote("send", "message"), their avatar is inserted #into the argument list that Group.view_send actually sees. This lets the group #get their username out of the Avatar without giving the client an opportunity #to spoof someone else." #[http://twistedmatrix.com/documents/10.1.0/core/howto/pb-cred.html#auto18] for user in self.users: user.send("%s: %s" % (sender.name, message)) print "Sent: %s, from %s, to %s" % (message, sender.name, user.name) return self #takes an avatarID and returns a MyPerspective object class MyRealm: implements(portal.IRealm) def requestAvatar(self, avatarID, mind, *interfaces): assert pb.IPerspective in interfaces user = MyPerspective(avatarID) user.server = self.server user.attached(mind) user.server.users.append(user.name) print user.server.users return pb.IPerspective, user, lambda: None def main(): #authuntication happens here c = checkers.InMemoryUsernamePasswordDatabaseDontUse() c.addUser("USER", "none") c.addUser("USER2", "none") c.addUser("USER3", "none") realm = MyRealm() realm.server = ChatS() p = portal.Portal(realm, [c]) server = pb.PBServerFactory(p) #when called upon, make connection reactor.listenTCP(1025, server) reactor.run() if __name__ == '__main__': main()