Author: dylan
Date: 2006-05-21 22:11:59 -0400 (Sun, 21 May 2006)
New Revision: 980

Added:
   trunk/python/
   trunk/python/client/
   trunk/python/client/haver/
   trunk/python/client/haver/__init__.py
   trunk/python/client/haver/client.py
   trunk/python/client/haver/factory.py
   trunk/python/client/haver/format.py
   trunk/python/client/haver/protocol.py
   trunk/python/client/haver/qtui.py
   trunk/python/client/proclaimer
   trunk/python/client/proclaimer.tac
   trunk/python/server/
   trunk/python/server/.todo
   trunk/python/server/TODO
   trunk/python/server/haver.tac
   trunk/python/server/haver/
   trunk/python/server/haver/__init__.py
   trunk/python/server/haver/server/
   trunk/python/server/haver/server/__init__.py
   trunk/python/server/haver/server/entity.py
   trunk/python/server/haver/server/errors.py
   trunk/python/server/haver/server/factory.py
   trunk/python/server/haver/server/states.py
   trunk/python/server/haver/server/talker.py
   trunk/python/server/test/
Modified:
   trunk/
Log:
 [EMAIL PROTECTED]:  dylan | 2006-03-25 13:47:04 -0500
 adding python client. It sort of works!
 [EMAIL PROTECTED]:  dylan | 2006-05-21 18:48:23 -0400
 Adding python haver server.
 [EMAIL PROTECTED]:  dylan | 2006-05-21 22:11:41 -0400
 Some fixes, TODO and .todo file listing what I need to do.


Property changes on: trunk
___________________________________________________________________
Name: svk:merge
   - 1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/havercurs-objc:43089
1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk:11166
1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk-merge-10131:11178
1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/winch/trunk:43192
27e50396-46e3-0310-8b22-ae223a1f35ce:/local:212
ca0be05d-e60e-0410-9596-96133d777aec:/haver/local:1380
e9404bb1-7af0-0310-a7ff-e22194cd388b:/haver/local:2455
edfcd8bd-4ce7-0310-a97e-bb1efd40edf3:/local:238
   + 1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/havercurs-objc:43089
1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk:11166
1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk-merge-10131:11178
1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/winch/trunk:43192
27e50396-46e3-0310-8b22-ae223a1f35ce:/local:212
ca0be05d-e60e-0410-9596-96133d777aec:/haver/local:3259
e9404bb1-7af0-0310-a7ff-e22194cd388b:/haver/local:2455
edfcd8bd-4ce7-0310-a97e-bb1efd40edf3:/local:238

Added: trunk/python/client/haver/__init__.py
===================================================================

Added: trunk/python/client/haver/client.py
===================================================================
--- trunk/python/client/haver/client.py 2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/client/haver/client.py 2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,99 @@
+from twisted.internet.protocol import ReconnectingClientFactory
+from twisted.protocols.basic import LineReceiver
+
+class Factory(ReconnectingClientFactory):
+       def __init__(self, ui, version = "haver.client/0.10"):
+               self.ui = ui
+               self.version = version
+       
+       def getVersion(self):
+               return self.version
+
+       def getUserName(self):
+               return "dylan" # ui.getUserName()
+
+       def startedConnecting(self, connector):
+               self.ui.display('net.connect')
+
+       def buildProtocol(self, addr):
+               self.ui.display('net.connected', host = addr.host, port = 
addr.port)
+               print 'Resetting reconnection delay'
+               self.resetDelay()
+               prot = self.protocol(addr, self.ui)
+               self.ui.protocol = prot
+               prot.factory = self;
+               return prot
+
+       def clientConnectionLost(self, connector, reason):
+               self.ui.display('net.connectionLost', reason = reason)
+               ReconnectingClientFactory.clientConnectionLost(self, connector, 
reason)
+
+       def clientConnectionFailed(self, connector, reason):
+               self.ui.display('net.connectionFailed', reason = reason)
+               ReconnectingClientFactory.clientConnectionFailed(self, 
connector, reason)
+
+       
+class Formatter:
+       Formats = {
+               'msg.hello': "Logged in as %(name)s",
+               'msg.from': "<%(user)s> %(msg)s",
+               'msg.in': "%(channel)s: <%(user)s> %(msg)s",
+       }
+       
+       def __init__(self, formats = None):
+               if formats is None:
+                       self.formats = Formatter.Formats.copy()
+               else:
+                       self.formats = formats
+
+       def format(self, fmtname, params):
+               if self.formats.has_key(fmtname):
+                       return self.formats[fmtname] % params
+               else:
+                       return "%s %r" % (fmtname, params)
+
+       def addFormat(self, fmtname, string):
+               self.formats[fmtname] = string
+
+class Protocol(LineReceiver):
+       def __init__(self, addr, ui):
+               self.addr = addr
+               self.ui = ui
+               
+       def lineReceived(self, line):
+               
+               msg = line.split("\t")
+               method = "msg_" + msg[0]
+               
+               if hasattr(self, method):
+                       f = getattr(self, method)
+                       f(msg[1:])
+               else:
+                       self.default_msg(msg[0], msg[1:])
+
+       # HAVER host version supports
+       def msg_HAVER(self, args):
+               h, ver = args[0:2]
+               # FIXME: make this raise a different exception.
+               assert h == self.addr.host
+               self.ui.display('msg.haver', version = ver, host = h)
+               self.sendMsg('IDENT', self.factory.getUserName())
+
+       def msg_HELLO(self, args):
+               self.ui.display('msg.hello', name = args[0])
+
+       def msg_FROM(self, args):
+               self.ui.display('msg.from', user = args[0], kind = args[1], msg 
= args[2])
+
+       def msg_IN(self, args):
+               self.ui.display('msg.in', channel = args[0], user = args[1], 
kind = args[2], msg = args[3])
+               
+
+       def default_msg(self, n, a):
+               self.ui.display('unknown.msg', name = n, args = a)
+               
+       def sendMsg(self, *msg):
+               self.sendLine("\t".join(msg))
+
+       def connectionMade(self):
+               self.sendMsg('HAVER', self.factory.getVersion())

Added: trunk/python/client/haver/factory.py
===================================================================
--- trunk/python/client/haver/factory.py        2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/client/haver/factory.py        2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,34 @@
+from twisted.internet.protocol import ReconnectingClientFactory
+
+class Factory(ReconnectingClientFactory):
+       def __init__(self, ui, version = "haver.client/0.10"):
+               self.ui = ui
+               self.version = version
+       
+       def getVersion(self):
+               return self.version
+
+       def getUserName(self):
+               return "dylan" # ui.getUserName()
+
+       def startedConnecting(self, connector):
+               self.ui.display('net.connect')
+
+       def buildProtocol(self, addr):
+               self.ui.display('net.connected', host = addr.host, port = 
addr.port)
+               print 'Resetting reconnection delay'
+               self.resetDelay()
+               prot = self.protocol(addr, self.ui)
+               self.ui.protocol = prot
+               prot.factory = self;
+               return prot
+
+       def clientConnectionLost(self, connector, reason):
+               self.ui.display('net.connectionLost', reason = reason)
+               ReconnectingClientFactory.clientConnectionLost(self, connector, 
reason)
+
+       def clientConnectionFailed(self, connector, reason):
+               self.ui.display('net.connectionFailed', reason = reason)
+               ReconnectingClientFactory.clientConnectionFailed(self, 
connector, reason)
+
+       

Added: trunk/python/client/haver/format.py
===================================================================
--- trunk/python/client/haver/format.py 2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/client/haver/format.py 2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,22 @@
+class Formatter:
+       Formats = {
+               'msg.hello': "Logged in as %(name)s",
+               'msg.from': "<%(user)s> %(msg)s",
+               'msg.in': "%(channel)s: <%(user)s> %(msg)s",
+       }
+       
+       def __init__(self, formats = None):
+               if formats is None:
+                       self.formats = Formatter.Formats.copy()
+               else:
+                       self.formats = formats
+
+       def format(self, fmtname, params):
+               if self.formats.has_key(fmtname):
+                       return self.formats[fmtname] % params
+               else:
+                       return "%s %r" % (fmtname, params)
+
+       def addFormat(self, fmtname, string):
+               self.formats[fmtname] = string
+       

Added: trunk/python/client/haver/protocol.py
===================================================================
--- trunk/python/client/haver/protocol.py       2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/client/haver/protocol.py       2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,44 @@
+from twisted.protocols.basic import LineReceiver
+
+class Protocol(LineReceiver):
+       def __init__(self, addr, ui):
+               self.addr = addr
+               self.ui = ui
+               
+       def lineReceived(self, line):
+               
+               msg = line.split("\t")
+               method = "msg_" + msg[0]
+               
+               if hasattr(self, method):
+                       f = getattr(self, method)
+                       f(msg[1:])
+               else:
+                       self.default_msg(msg[0], msg[1:])
+
+       # HAVER host version supports
+       def msg_HAVER(self, args):
+               h, ver = args[0:2]
+               # FIXME: make this raise a different exception.
+               assert h == self.addr.host
+               self.ui.display('msg.haver', version = ver, host = h)
+               self.sendMsg('IDENT', self.factory.getUserName())
+
+       def msg_HELLO(self, args):
+               self.ui.display('msg.hello', name = args[0])
+
+       def msg_FROM(self, args):
+               self.ui.display('msg.from', user = args[0], kind = args[1], msg 
= args[2])
+
+       def msg_IN(self, args):
+               self.ui.display('msg.in', channel = args[0], user = args[1], 
kind = args[2], msg = args[3])
+               
+
+       def default_msg(self, n, a):
+               self.ui.display('unknown.msg', name = n, args = a)
+               
+       def sendMsg(self, *msg):
+               self.sendLine("\t".join(msg))
+
+       def connectionMade(self):
+               self.sendMsg('HAVER', self.factory.getVersion())

Added: trunk/python/client/haver/qtui.py
===================================================================
--- trunk/python/client/haver/qtui.py   2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/client/haver/qtui.py   2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,62 @@
+from qt import *
+
+class HaverWindow(QMainWindow):
+       def __init__(self, *args):
+               QMainWindow.__init__(self, *args)
+
+               self.setCaption("Haver")
+
+               vbox = QVBox(self)
+               vbox.setMargin(2)
+               vbox.setSpacing(3)
+
+               self.tabs = QTabWidget(vbox)
+               self.views = dict()
+               self.views['status'] = QTextView(None)
+               self.tabs.addTab(self.views['status'], 'status')
+               
+               hbox = QHBox(vbox)
+               label = QLabel("Chat: ", hbox)
+
+               self.line  = QLineEdit("", hbox)
+
+               self.setCentralWidget(vbox)
+
+       def foobar(self):
+               self.views['status2'] = QTextView(None)
+               self.tabs.addTab(self.views['status2'], 'status2')
+
+class HaverQt:
+       def __init__(self, name, formatter):
+               win = HaverWindow()
+               win.setCaption(name)
+               
+               win.connect(win.line, SIGNAL('returnPressed()'), self.sendLine)
+               
+               self.win = win
+               self.formatter = formatter
+       
+       def sendLine(self):
+               line = self.win.line.text()
+               self.protocol.sendMsg('TO', '&echo', 'say', str(line))
+               self.win.foobar()
+               self.win.line.clear()
+
+       def displayLine(self, line):
+               # self.win.edit.append(line + "\n")
+               print line
+               
+       def display(self, fmtname, **kwargs):
+               self.displayLine(self.formatter.format(fmtname, kwargs))
+
+       def setup(self, reactor):
+               self.win.show()
+               
+               app = reactor.qApp
+               # make sure stopping twisted event also shuts down QT
+               reactor.addSystemEventTrigger('after', 'shutdown', app.quit )
+
+               # shutdown twisted when window is closed
+               app.connect(app, SIGNAL("lastWindowClosed()"), reactor.stop)
+
+

Added: trunk/python/client/proclaimer
===================================================================
--- trunk/python/client/proclaimer      2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/client/proclaimer      2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec twistd2.4 --reactor qt -ny proclaimer.tac


Property changes on: trunk/python/client/proclaimer
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/python/client/proclaimer.tac
===================================================================
--- trunk/python/client/proclaimer.tac  2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/client/proclaimer.tac  2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,27 @@
+#!/usr/bin/python2.4
+
+from haver.client         import *
+from haver.qtui           import HaverQt
+from twisted.application  import service, internet
+
+application  = service.Application("proclaimer")
+app          = service.IService(application)
+#sc = service.IServiceCollection(application)
+#proc = service.IProcess(application)
+#per = sob.IPersistable(application)
+
+formatter = Formatter()
+ui = HaverQt("Proclaimer", formatter)
+import twisted.internet.reactor
+ui.setup(twisted.internet.reactor)
+# Create the protocol factory
+client          = QtClient("Proclaimer")
+factory.protocol = Protocol
+
+# Create the (sole) client
+# Normally, the echo protocol lives on port 7, but since that
+# is a privileged port, for this example we'll use port 7001
+client = internet.TCPClient("hardison.net", 7575, factory)
+
+# Tie the service to the application
+client.setServiceParent(app)


Property changes on: trunk/python/client/proclaimer.tac
___________________________________________________________________
Name: svn:mime-type
   + text/script

Added: trunk/python/server/.todo
===================================================================
--- trunk/python/server/.todo   2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/server/.todo   2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,11 @@
+<todo version="0.1.19">
+    <note priority="high" time="1148263802">
+        Bug: when a client connection crashes (raises an exception), make sure 
to remove a user from all joined channels...
+    </note>
+    <note priority="high" time="1148263853">
+        Bug: PART main shows unknown.user main...
+    </note>
+    <note priority="medium" time="1148263612">
+        Add a command to list groups a user belongs to.
+    </note>
+</todo>

Added: trunk/python/server/TODO
===================================================================
--- trunk/python/server/TODO    2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/server/TODO    2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,9 @@
+- Bug: when a client connection crashes (raises an exception), make sure to 
remove a user from all joined channels...
+  (added Sun May 21 22:10:02 2006, incomplete, priority high)
+
+- Bug: PART main shows unknown.user main...
+  (added Sun May 21 22:10:53 2006, incomplete, priority high)
+
+- Add a command to list groups a user belongs to.
+  (added Sun May 21 22:06:52 2006, incomplete, priority medium)
+

Added: trunk/python/server/haver/__init__.py
===================================================================

Added: trunk/python/server/haver/server/__init__.py
===================================================================

Added: trunk/python/server/haver/server/entity.py
===================================================================
--- trunk/python/server/haver/server/entity.py  2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/server/haver/server/entity.py  2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,156 @@
+from haver.server.errors import Fail, Bork
+import time, re
+
+namepattern = re.compile("^&?[A-Za-z][A-Za-z0-9_.'[EMAIL PROTECTED]")
+
+def assert_name(n):
+       if not namepattern.match(n):
+               raise Fail('invalid.name', n)
+
+def mask_ip(ip):
+       parts = ip.split('.')
+       assert len(parts) == 4
+       parts[3] = '*'
+       return '.'.join(parts)
+
+class Entity(object):
+       def __init__(self, name):
+               assert_name(name)
+               self.attr = {}
+               self.__name = name
+
+       def __str__(self):
+               return self.__name
+       
+       def getName(self): return self.__name
+       def delName(self): del self.__name
+       def setName(self, name):
+               assert_name(name)
+               self.__name = name
+
+       def getInfo(self): return [ ]
+       name = property(getName, setName, delName, "I'm the 'name' property.")
+       info = property(lambda self: self.getInfo())
+
+class Avatar(Entity):
+       def __init__(self, name, address = '0.0.0.0', version = None, talker  = 
None):
+               Entity.__init__(self, name)
+               
+               self.address       = mask_ip(address)
+               self.real_address  = address
+               self.version       = version
+               self.talker        = talker
+
+       def getInfo(self):
+               return [
+                       'version', self.version,
+                       'address', self.address,
+               ]
+
+       def sendMsg(self, *msg):
+               self.talker.sendMsg(*msg)
+
+class User(Avatar):
+       namespace = 'user'
+
+       def __init__(self, *args, **kwargs):
+               Avatar.__init__(self, *args, **kwargs)
+               self.email        = None
+               self.lastActivity = int(time.time())
+               self.groups     = set()
+
+       def joinGroup(self, group):
+               name = group.name.lower()
+               if name in self.groups:
+                       raise Fail('already.joined', group.name)
+               else:
+                       self.groups.add(name)
+                       group.add(self)
+                       
+       def partGroup(self, group):
+               name = group.name.lower()
+               if name in self.groups:
+                       self.groups.remove(name)
+                       group.remove('user', name)
+               else:
+                       raise Fail('already.parted', group.name)
+               
+       def updateIdle(self):
+               self.lastActivity = int(time.time())
+
+       def getIdle(self):
+               return int(time.time()) - self.lastActivity
+               
+       def getInfo(self):
+               infos = Avatar.getInfo(self)
+               if self.email is not None:
+                       infos.extend(['email', self.email])
+
+               infos.extend([ 'idle', str( self.getIdle() )])
+
+               return infos
+
+       idle     = property(getIdle)
+
+class Group(Entity):
+       namespace = 'group'
+
+       def __init__(self, name, owner = '&root'):
+               Entity.__init__(self, name)
+               self.owner = owner
+               self.__users = dict()
+               self.__members = dict(user = {}, group = {})
+               
+       def getInfo(self):
+               infos = Entity.getInfo(self)
+               return infos + [ 'owner', self.owner ]
+               
+       def sendMsg(self, *msg):
+               for user in self.members('user'):
+                       user.sendMsg(*msg)
+
+       def _get_ns(self, ns):
+               try:
+                       return self.__members[ns]
+               except KeyError:
+                       raise Fail('invalid.namespace', ns)
+
+       def lookup(self, ns, name):
+               assert_name(name)
+               ents = self._get_ns(ns)
+               try:
+                       return ents[ name.lower() ]
+               except KeyError:
+                       raise Fail('unknown.%s' % ns, name)
+               
+       def add(self, entity):
+               ns, name = (entity.namespace, entity.name.lower())
+               
+               ents = self._get_ns(ns)
+               if ents.has_key(name):
+                       raise Fail('exists.%s' % ns, entity.name)
+               ents[name] = entity
+               
+       def remove(self, ns, name):
+               assert_name(name)
+               lname = name.lower()
+               ents = self._get_ns(ns)
+               try:
+                       del ents[lname]
+               except KeyError:
+                       raise Fail('unknown.%s' % ns, name)
+
+       def members(self, ns):
+               ents = self._get_ns(ns)
+               return ents.values()
+
+class Lobby(Group):
+       def __init__(self):
+               Group.__init__(self, '&lobby')
+
+       def lookup(self, ns, name):
+               if name == '&lobby':
+                       return self
+               else:
+                       return Group.lookup(self, ns, name)
+                       

Added: trunk/python/server/haver/server/errors.py
===================================================================
--- trunk/python/server/haver/server/errors.py  2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/server/haver/server/errors.py  2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,11 @@
+
+class Fail(Exception):
+       def __init__(self, name, *args):
+               self.name = name
+               self.args = args
+               
+class Bork(Exception):
+       def __init__(self, msg):
+               self.msg = msg
+
+

Added: trunk/python/server/haver/server/factory.py
===================================================================
--- trunk/python/server/haver/server/factory.py 2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/server/haver/server/factory.py 2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,4 @@
+from twisted.internet.protocol import ReconnectingClientFactory
+from qt import *
+
+       

Added: trunk/python/server/haver/server/states.py
===================================================================
--- trunk/python/server/haver/server/states.py  2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/server/haver/server/states.py  2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,152 @@
+from haver.server.entity import User, Group
+from haver.server.errors import Fail, Bork
+
+class State:
+       def __init__(self, talker):
+               self.talker = talker
+               self.factory = talker.factory
+
+       def sendMsg(self, *args):
+               self.talker.sendMsg(*args)
+
+       def switchState(self, stateClass):
+               print "Switching state to %s" % stateClass.__name__
+               state = stateClass(self.talker)
+               self.talker.state = state
+
+       def default_msg(self, name, args):
+               print "unknown message: %s" % name
+
+class ConnectedState(State):
+       
+       def msg_HAVER(self, args):
+               self.sendMsg('HAVER', "hardison.net", 'Twisted Haver/0.1')
+               self.talker.version = args[0]
+               try:
+                       self.talker.supports = args[1].split(',')
+               except IndexError:
+                       self.talker.supports = []
+
+
+               self.switchState(LoginState)
+
+       def default_msg(self, name, args):
+               self.talker.transport.loseConnection()
+
+class LoginState(State):
+
+       def msg_IDENT(self, args):
+               name  = args[0]
+               lobby = self.factory.lobby
+                       
+               user = User(name,
+                               version = self.talker.version,
+                               address = self.talker.addr.host,
+                               talker  = self)
+               lobby.add(user)
+
+               self.talker.user = user
+
+               self.sendMsg('HELLO', name)
+               self.switchState(NormalState)
+
+       
+
+class NormalState(State):
+
+       def msg_TO(self, args):
+               target, kind = args[0:2]
+               msg = args[2:]
+               lobby = self.factory.lobby
+               self.talker.user.updateIdle()
+               user = lobby.lookup('user', target)
+               user.sendMsg('FROM', self.talker.user.name, kind, *msg)
+
+       def msg_IN(self, args):
+               target, kind = args[0:2]
+               msg = args[2:]
+               lobby = self.factory.lobby
+               self.talker.user.updateIdle()
+               group = lobby.lookup('group', target)
+               group.sendMsg('IN', group.name, self.talker.user.name, kind, 
*msg)
+
+       def msg_JOIN(self, args):
+               name  = args[0]
+               lobby = self.factory.lobby
+               user  = self.talker.user
+               user.updateIdle()
+               group  = lobby.lookup('group', name)
+               user.joinGroup(group)
+               group.sendMsg('JOIN', name, user.name)
+
+       def msg_PART(self, args):
+               name  = args[0]
+               lobby = self.factory.lobby
+               user  = self.talker.user
+               user.updateIdle()
+               group  = lobby.lookup('group', name)
+               user  = user.partGroup(group)
+               
+               for x in [user, group]:
+                       x.sendMsg('PART', name, user.name)
+
+       def msg_OPEN(self, args):
+               name  = args[0]
+               lobby = self.factory.lobby
+               user  = self.talker.user
+               group  = Group(name, owner = user.name)
+               
+               lobby.add(group)
+               user.sendMsg('OPEN', name)
+
+       def msg_INFO(self, args):
+               ns, name = args[0:2]
+               if ns == 'channel':
+                       ns = 'group'
+               
+               self.talker.user.updateIdle()
+               entity = self.factory.lobby.lookup(ns, name)
+               infos  = entity.info
+
+               self.sendMsg('INFO', ns, name, *infos)
+
+       def msg_LIST(self, args):
+               name, ns = args
+               if ns == 'channel':
+                       ns = 'group'
+
+               if ns == 'user':
+                       msg = 'You might want to try USERS %s' % name
+               elif ns == 'group':
+                       msg = 'You might want to try GROUPS' % name
+               
+               group = self.factory.lobby.lookup('group', name)
+               self.talker.sendMsg('FROM', '&', 'say', msg)
+               names = [ x.name for x in group.members(ns) ]
+               self.talker.sendMsg('LIST', name, ns, *names)
+
+       def msg_USERS(self, args):
+               name = args[0]
+               group = self.factory.lobby.lookup('group', name)
+               names = [ x.name for x in group.members('user') ]
+               self.talker.sendMsg('USERS', *names)
+
+       def msg_GROUPS(self, args):
+               lobby = self.factory.lobby
+               names = [ x.name for x in lobby.members('group') ]
+               self.talker.sendMsg('GROUPS', *names)
+
+       def msg_SERVICES(self, args):
+               lobby = self.factory.lobby
+               names = [ x.name for x in lobby.members('service') ]
+               self.talker.sendMsg('SERVICES', *names)
+               
+       def msg_POKE(self, args):
+               self.sendMsg('OUCH', *args)
+
+       def msg_BYE(self, args):
+               if args: detail = args[0]
+               else:    detail = None
+
+               self.talker.quit('bye', detail)
+               

Added: trunk/python/server/haver/server/talker.py
===================================================================
--- trunk/python/server/haver/server/talker.py  2006-03-20 21:04:28 UTC (rev 
979)
+++ trunk/python/server/haver/server/talker.py  2006-05-22 02:11:59 UTC (rev 
980)
@@ -0,0 +1,90 @@
+import re
+
+from twisted.python import log
+from twisted.protocols.basic import LineReceiver
+from twisted.internet.protocol import Factory
+from haver.server.errors import Fail, Bork
+from haver.server.states import *
+
+class HaverServerFactory(Factory):
+
+       def __init__(self, lobby):
+               self.lobby = lobby
+
+       def buildProtocol(self, addr):
+               p = self.protocol(addr)
+               p.factory = self
+               return p
+               
+
+class HaverServer(LineReceiver):
+       def __init__(self, addr):
+               self.addr = addr
+               self.cmdpat = re.compile('^[A-Z][A-Z:_-]+$')
+               
+       
+       def lineReceived(self, line):
+               try:
+                       if len(line) == 0:
+                               print "Got empty line"
+                               raise Bork('Your line is empty')
+                       
+                       msg = line.split("\t")
+                       name = msg[0]
+                       method = "msg_" + name
+                       state = self.state
+
+                       if not self.cmdpat.match(name):
+                               print "This is an example of a badly formed 
command: %s" % name
+                               raise Bork("You're not the man I married!")
+                       
+                       self.cmd = name
+                       if hasattr(state, method):
+                               f = getattr(state, method)
+                               f(msg[1:])
+                       else:
+                               state.default_msg(msg[0], msg[1:])
+                       
+               except Fail, failure:
+                       log.msg('Command %s failed with failure %s' % 
(self.cmd, failure.name))
+                       self.sendMsg('FAIL', self.cmd, failure.name, 
*failure.args)
+               except Bork, bork:
+                       log.msg('Borking client: %s' % bork.msg)
+                       if not isinstance(self.state, ConnectedState):
+                               self.sendMsg('BORK', bork.msg)
+                       self.transport.loseConnection()
+                       
+       def sendMsg(self, *msg):
+               self.sendLine("\t".join(msg))
+       
+       def quit(self, reason, detail = None):
+               log.msg("Client quits, reason = %s" % reason)
+               user = self.user
+               if detail is None:
+                       args = [reason]
+               else:
+                       args = [reason, detail]
+                       
+               self.sendMsg('BYE', *args)
+               name = user.name
+               for group in user.members('group'):
+                       user.partGroup(group)
+                       group.sendMsg('QUIT', name, *args)
+       
+       def connectionMade(self):
+               log.msg('New client from ' + str(self.addr))
+               self.state = ConnectedState(self)
+
+       def connectionLost(self, reason):
+               log.msg('Lost client from ' + str(self.addr))
+
+               try:
+                       user = self.user
+                       lobby = self.factory.lobby
+                       lobby.remove(user.namespace, user.name)
+                       del self.user
+               except AttributeError:
+                       pass
+
+               del self.state
+       

Added: trunk/python/server/haver.tac
===================================================================
--- trunk/python/server/haver.tac       2006-03-20 21:04:28 UTC (rev 979)
+++ trunk/python/server/haver.tac       2006-05-22 02:11:59 UTC (rev 980)
@@ -0,0 +1,30 @@
+#!/usr/bin/python2.4
+from haver.server.talker import HaverServerFactory, HaverServer
+from haver.server.entity import Lobby, Group
+from twisted.application import service, internet
+from twisted.persisted   import sob
+
+application  = service.Application("haverd")
+service          = service.IService(application)
+#sc = service.IServiceCollection(application)
+#proc = service.IProcess(application)
+applicationper = sob.IPersistable(application)
+
+
+lobby   = Lobby()
+main = Group('main')
+lobby.add(main)
+factory = HaverServerFactory(lobby)
+factory.protocol = HaverServer
+
+# Create the (sole) client
+# Normally, the echo protocol lives on port 7, but since that
+# is a privileged port, for this example we'll use port 7001
+
+servers = [
+       internet.TCPServer(7575, factory)
+]
+
+# Tie the service to the application
+for server in servers:
+       server.setServiceParent(service)


Property changes on: trunk/python/server/haver.tac
___________________________________________________________________
Name: svn:mime-type
   + text/script


_______________________________________________
Haver-commits mailing list
[email protected]
https://mail.gna.org/listinfo/haver-commits

Reply via email to