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()